diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 540c8a48b..ae27c8a0a 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -33,7 +33,7 @@ env: OPEN_IM_SERVER_CLA_DOCUMENT: https://github.com/openim-sigs/cla/blob/main/README.md OPEN_IM_SERVER_SIGNATURES_PATH: signatures/${{ github.event.repository.name }}/cla.json - OPEN_IM_SERVER_ALLOWLIST: kubbot,bot*,bot-*,bot/*,bot-/*,bot,*[bot] + OPEN_IM_SERVER_ALLOWLIST: kubbot,openimbot,bot*,dependabot,sweep-ai,*bot,bot-*,bot/*,bot-/*,bot,*[bot] jobs: CLAAssistant: diff --git a/.github/workflows/docker-buildx.yml b/.github/workflows/docker-buildx.yml index 6113cfae5..b3b0b5683 100644 --- a/.github/workflows/docker-buildx.yml +++ b/.github/workflows/docker-buildx.yml @@ -106,6 +106,16 @@ jobs: ghcr.io/openimsdk/openim-api openim/openim-api registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-api + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-api uses: docker/build-push-action@v5 @@ -127,6 +137,16 @@ jobs: ghcr.io/openimsdk/openim-cmdutils openim/openim-cmdutils registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-cmdutils + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-cmdutils uses: docker/build-push-action@v5 @@ -148,6 +168,16 @@ jobs: ghcr.io/openimsdk/openim-crontask openim/openim-crontask registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-crontask + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-crontask uses: docker/build-push-action@v5 @@ -169,6 +199,16 @@ jobs: ghcr.io/openimsdk/openim-msggateway openim/openim-msggateway registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-msggateway + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-msggateway uses: docker/build-push-action@v5 @@ -190,6 +230,16 @@ jobs: ghcr.io/openimsdk/openim-msgtransfer openim/openim-msgtransfer registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-msgtransfer + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-msgtransfer uses: docker/build-push-action@v5 @@ -211,6 +261,16 @@ jobs: ghcr.io/openimsdk/openim-push openim/openim-push registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-push + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-push uses: docker/build-push-action@v5 @@ -232,6 +292,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-auth openim/openim-rpc-auth registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-auth + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-auth uses: docker/build-push-action@v5 @@ -253,6 +323,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-conversation openim/openim-rpc-conversation registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-conversation + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-conversation uses: docker/build-push-action@v5 @@ -274,6 +354,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-friend openim/openim-rpc-friend registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-friend + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-friend uses: docker/build-push-action@v5 @@ -295,6 +385,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-group openim/openim-rpc-group registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-group + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-group uses: docker/build-push-action@v5 @@ -316,6 +416,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-msg openim/openim-rpc-msg registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-msg + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-msg uses: docker/build-push-action@v5 @@ -337,6 +447,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-third openim/openim-rpc-third registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-third + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-third uses: docker/build-push-action@v5 @@ -358,6 +478,16 @@ jobs: ghcr.io/openimsdk/openim-rpc-user openim/openim-rpc-user registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-rpc-user + tags: | + type=ref,event=tag + type=schedule + type=ref,event=branch + type=ref,event=pr + type=semver,pattern={{version}} + type=semver,pattern=v{{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + type=sha - name: Build and push Docker image for openim-rpc-user uses: docker/build-push-action@v5 diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 2f6d7fe05..6d8112eb4 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -92,11 +92,40 @@ jobs: - name: Exec OpenIM API test run: | - sudo make test-api - - - name: Exec OpenIM E2E test + touch /tmp/test.md + echo "# OpenIM Test" >> /tmp/test.md + echo "## OpenIM API Test" >> /tmp/test.md + echo "
Command Output for OpenIM API Test" >> /tmp/test.md + echo "
" >> /tmp/test.md
+        sudo make test-api | tee -a /tmp/test.md
+        echo "
" >> /tmp/test.md + echo "
" >> /tmp/test.md + + - name: Exec OpenIM E2E Test 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 + echo "
" >> /tmp/test.md
+        sudo make test-e2e | tee -a /tmp/test.md
+        echo "
" >> /tmp/test.md + echo "
" >> /tmp/test.md + + - name: Comment PR with file + uses: thollander/actions-comment-pull-request@v2 + with: + filePath: /tmp/test.md + comment_tag: nrt_file + reactions: eyes, rocket + mode: recreate + GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }} + continue-on-error: true + + - name: Check outputs + run: | + echo "id : ${{ steps.nrt_message.outputs.id }}" + echo "body : ${{ steps.nrt_message.outputs.body }}" + echo "html_url : ${{ steps.nrt_message.outputs.html_url }}" - name: Exec OpenIM System uninstall run: | diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml index d16e91ba4..00339110c 100644 --- a/.github/workflows/openimci.yml +++ b/.github/workflows/openimci.yml @@ -36,21 +36,19 @@ env: GO_VERSION: "1.19" GOLANGCI_VERSION: "v1.50.1" - jobs: openim: name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} runs-on: ${{ matrix.os }} permissions: contents: write + pull-requests: write environment: name: openim - strategy: matrix: go_version: ["1.19","1.20","1.21"] os: [ubuntu-latest] - steps: - name: Setup uses: actions/checkout@v4 @@ -111,6 +109,9 @@ jobs: openim-start: name: Test OpenIM install/start on ${{ matrix.os }} runs-on: ${{ matrix.os }} + permissions: + contents: write + pull-requests: write environment: name: openim strategy: @@ -129,9 +130,46 @@ jobs: run: | sudo make install + # - name: Check the OpenIM environment and status + # run: | + # sudo docker images + # sudo docker ps + + - name: Check the OpenIM environment and status + id: docker_info + run: | + sleep 30 + echo "images<> $GITHUB_ENV + sudo docker images >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + echo "containers<> $GITHUB_ENV + sudo docker ps >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Comment PR + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + > [!TIP] + > Run make install to check the status + + ### Docker Images: + ``` + ${{ env.images }} + ``` + ### Docker Processes: + ``` + ${{ env.containers }} + ``` + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + continue-on-error: true + execute-scripts: name: Execute OpenIM Script On ${{ matrix.os }} runs-on: ${{ matrix.os }} + permissions: + contents: write + pull-requests: write environment: name: openim strategy: @@ -201,10 +239,9 @@ jobs: - name: Build, Start, Check Services and Print Logs for Ubuntu if: runner.os == 'Linux' run: | - sudo make build && \ - sudo make start && \ - sudo make check || \ - (echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null) + sudo make build + sudo make start + sudo make check - name: Restart Services and Print Logs for Ubuntu if: runner.os == 'Linux' @@ -212,18 +249,17 @@ jobs: sudo make restart sudo make check - # - name: Build, Start, Check Services and Print Logs for macOS - # if: runner.os == 'macOS' - # run: | - # make init && \ - # make build && \ - # make start && \ - # make check || \ - # (echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null) + - name: Build, Start, Check Services and Print Logs for macOS + if: runner.os == 'macOS' + run: | + make build openim-test-build-image: name: Build OpenIM Docker Image runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write environment: name: openim steps: @@ -245,3 +281,9 @@ jobs: run: | sudo make init sudo make image + + - name: Get OpenIM Docker Images Status + id: docker_processes + run: | + sudo docker images + sudo docker ps diff --git a/README-zh_CN.md b/README-zh_CN.md index e2df68a56..12a56d4f6 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -35,52 +35,62 @@ ## Ⓜ️ 关于 OpenIM -OpenIM 不仅仅是一个开源的即时消息组件,它是你的应用程序生态系统的一个不可或缺的部分。查看下面的图表,了解 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 是如何交互的。 +OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通话、通知以及AI聊天机器人等通信功能的服务平台。它通过提供一系列强大的API和Webhooks,使开发者可以轻松地在他们的应用中加入这些交互特性。OpenIM 本身并不是一个独立运行的聊天应用,而是作为一个平台,为其他应用提供支持,实现丰富的通信功能。下图展示 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 之间的交互关系来具体说明。 + + + + ![App-OpenIM 关系](./docs/images/oepnim-design.png) ## 🚀 关于 OpenIMSDK -**OpenIMSDK** 无缝集成到您的应用中,提供丰富、实时的消息体验,无需复杂的 UI 集成。它提供: +**OpenIMSDK** 是为 **OpenIMServer** 设计的IM SDK,专为嵌入客户端应用而生。其主要功能及模块如下: -+ **本地存储**:用于快速数据检索和消息同步。 -+ **监听器回调**:确保实时消息交互性。 -+ **API 封装**:简化开发流程。 -+ **连接管理**:保证可靠的消息传递。 ++ 🌟 主要功能: -它使用 Golang 构建,并支持跨平台部署,确保在所有平台上提供一致的消息体验。 + - 📦 本地存储 + - 🔔 监听器回调 + - 🛡️ API封装 + - 🌐 连接管理 + + ## 📚 主要模块: + + 1. 🚀 初始化及登录 + 2. 👤 用户管理 + 3. 👫 好友管理 + 4. 🤖 群组功能 + 5. 💬 会话处理 + +它使用 Golang 构建,并支持跨平台部署,确保在所有平台上提供一致的接入体验。 👉 **[探索 GO SDK](https://github.com/openimsdk/openim-sdk-core)** ## 🌐 关于 OpenIMServer -精心用 Golang 开发的 **OpenIMServer** 通过多重方式确保了卓越的即时消息服务器能力: - -+ **模块组成**:它由多个模块组成,例如网关和多个 RPC 服务,提供一个多功能的消息环境。 -+ **微服务架构**:支持集群模式,确保出色的性能和可伸缩性,以有效管理各个实例间的通信。 -+ **多样的部署选项**:适应你的操作偏好,通过源代码、Kubernetes 或 Docker 提供部署选项。 ++ **OpenIMServer** 具有以下特点: + - 🌐 微服务架构:支持集群模式,包括网关(gateway)和多个rpc服务。 + - 🚀 部署方式多样:支持源代码、kubernetes或docker部署。 + - 海量用户支持:十万超级大群,千万用户,及百亿消息 ### 增强的业务功能: -+ **REST API**:OpenIMServer 为业务系统提供 REST API,旨在通过后端接口为您的操作提供附加功能,如群组创建和消息推送。 -+ **回调**:为了扩展其在各种业务形式中的实用性,OpenIMServer 提供了回调能力。即,在事件发生之前或之后,它向业务服务器发送请求,比如发送消息,丰富通信过程中的交互和数据交换流。 ++ **REST API**:OpenIMServer 提供了REST API供业务系统使用,旨在赋予业务更多功能,例如通过后台接口建立群组、发送推送消息等。 ++ **Webhooks**:OpenIMServer提供了回调能力以扩展更多的业务形态,所谓回调,即OpenIMServer会在某一事件发生之前或者之后,向业务服务器发送请求,如发送消息之前或之后的回调。 -👉 **[了解更多](https://doc.rentsoft.cn/guides/introduction/product)** +👉 **[了解更多](https://docs.openim.io/guides/introduction/product)** ## :rocket: 快速开始 -你只需要一个简单的命令,就可以快速学习 OpenIM 的工程解决方案: +在线体验iOS/Android/H5/PC/Web: -``` -bashCopy code -$ make demo -``` +👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)** 🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法: -+ **[源代码部署指南](https://doc.rentsoft.cn/guides/gettingStarted/imSourceCodeDeployment)** -+ **[Docker 部署指南](https://doc.rentsoft.cn/guides/gettingStarted/dockerCompose)** -+ **[Kubernetes 部署指南](https://github.com/openimsdk/open-im-server/tree/main/deployments)** ++ **[源代码部署指南](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Docker 部署指南](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Kubernetes 部署指南](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ## :hammer_and_wrench: 开始开发 OpenIM diff --git a/README.md b/README.md index 781db1217..025672a0b 100644 --- a/README.md +++ b/README.md @@ -25,178 +25,56 @@

- ## Ⓜ️ About OpenIM -OpenIM isn't just an open-source instant messaging component, it's an integral part of your application ecosystem. Check out this diagram to understand how AppServer, AppClient, OpenIMServer, and OpenIMSDK interact. +OpenIM is a service platform specifically designed for integrating chat, audio-video calls, notifications, and AI chatbots into applications. It provides a range of powerful APIs and Webhooks, enabling developers to easily incorporate these interactive features into their applications. OpenIM is not a standalone chat application, but rather serves as a platform to support other applications in achieving rich communication functionalities. The following diagram illustrates the interaction between AppServer, AppClient, OpenIMServer, and OpenIMSDK to explain in detail. ![App-OpenIM Relationship](./docs/images/oepnim-design.png) ## 🚀 About OpenIMSDK -**OpenIMSDK** seamlessly integrates into your application, delivering a rich, real-time messaging experience without requiring intricate UI integration. It provides: - -+ **Local Storage**: For quick data retrieval and message synchronization. -+ **Listener Callbacks**: Ensuring real-time message interactivity. -+ **API Encapsulation**: Streamlining development processes. -+ **Connection Management**: Guaranteeing reliable message delivery. - -It's crafted in Golang and supports cross-platform deployment, ensuring a coherent messaging experience across all platforms. - -👉 **[Explore GO SDK](https://github.com/openimsdk/openim-sdk-core)** - -## 🌐 About OpenIMServer - -**OpenIMServer**, meticulously developed in Golang, ensures a stellar instant messaging server capability with a multifold approach: - -+ **Modular Composition**: It's comprised of several modules, such as the gateway and multiple RPC services, offering a versatile messaging environment. -+ **Microservices Architecture**: Supporting cluster modes, it assures outstanding performance and scalability to manage communication effectively across various instances. -+ **Diverse Deployment Options**: Adapts to your operational preferences, offering deployment via source code, Kubernetes, or Docker. - -### Enhanced Business Functionalities: - -+ **REST API**: OpenIMServer provides REST API for business systems, aiming to empower your operations with additional functionalities like group creation and message push via backend interfaces. -+ **Callbacks**: To expand its utility across varied business forms, OpenIMServer offers callback capabilities. That is, it sends a request to the business server before or after an event occurs, such as sending a message, enriching the interaction and data exchange flow in the communication processes. - -👉 **[Learn More](https://docs.openim.io/guides/introduction/product)** - - ++ **REST API**: OpenIMServer offers REST APIs for business systems, aimed at empowering businesses with more functionalities, such as creating groups and sending push messages through backend interfaces. ++ **Webhooks**: OpenIMServer provides callback capabilities to extend more business forms. A callback means that OpenIMServer sends a request to the business server before or after a certain event, like callbacks before or after sending a message. +👉 **[Learn more](https://docs.openim.io/guides/introduction/product)** ## :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 -$ make demo -``` - -🤲 In order to facilitate the user experience, we have provided a variety of deployment solutions, you can choose your own deployment method according to the list below: - - +🤲 To facilitate user experience, we offer various deployment solutions. You can choose your deployment method from the list below: + **[Source Code Deployment Guide](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** -+ **[Production deployment of Linux systems](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-openim-linux-system.md)** + **[Docker Deployment Guide](https://docs.openim.io/guides/gettingStarted/dockerCompose)** -+ **[Kubernetes Deployment Guide](https://github.com/openimsdk/open-im-server/tree/main/deployments)** - - ++ **[Kubernetes Deployment Guide](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ## :hammer_and_wrench: To start developing OpenIM diff --git a/deployments/templates/env-template.yaml b/deployments/templates/env-template.yaml index 1772af644..e7166ff2d 100644 --- a/deployments/templates/env-template.yaml +++ b/deployments/templates/env-template.yaml @@ -1,3 +1,17 @@ +# 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. + # ----------------------------------------------------------------------------- # General Configuration # This section contains general configuration options for the entire environment. @@ -84,19 +98,23 @@ OPENIM_IP=${OPENIM_IP} # Default: ZOOKEEPER_PORT=12181 ZOOKEEPER_PORT=${ZOOKEEPER_PORT} -# Port on which MongoDB service is running. +# MongoDB service port configuration. # Default: MONGO_PORT=37017 # MONGO_PORT=${MONGO_PORT} -# Username to authenticate with the MongoDB service. -# Default: MONGO_USERNAME=root -# MONGO_USERNAME=${MONGO_USERNAME} - -# Password to authenticate with the MongoDB service. +# Password for MongoDB admin user. Used for service authentication. # Default: MONGO_PASSWORD=openIM123 MONGO_PASSWORD=${MONGO_PASSWORD} -# Name of the database in MongoDB to be used. +# Username for a regular OpenIM user in MongoDB. +# Default: MONGO_OPENIM_USERNAME=openIM +MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME} + +# Password for a regular OpenIM user in MongoDB. +# Default: MONGO_OPENIM_PASSWORD=openIM123456 +MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD} + +# Specifies the database name to be used within MongoDB. # Default: MONGO_DATABASE=openIM_v3 MONGO_DATABASE=${MONGO_DATABASE} @@ -187,18 +205,12 @@ CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION} # Port for the OpenIM chat API. # Default: OPENIM_CHAT_API_PORT=10008 -# !!! TODO: Do not change the chat port https://github.com/openimsdk/chat/issues/365 OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT} # Port for the OpenIM admin API. # Default: OPENIM_ADMIN_API_PORT=10009 -# !!! TODO: Do not change the chat port https://github.com/openimsdk/chat/issues/365 OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT} -# Directory path for storing data files or related information for OpenIM chat. -# Default: OPENIM_CHAT_DATA_DIR=./openim-chat/main -OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR} - # ====================================== # ========== OpenIM Admin ============== # ====================================== diff --git a/deployments/templates/openim.yaml b/deployments/templates/openim.yaml index 96d867e10..4c84373a5 100644 --- a/deployments/templates/openim.yaml +++ b/deployments/templates/openim.yaml @@ -53,8 +53,8 @@ mongo: # Maximum connection pool size address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ] database: ${MONGO_DATABASE} - username: ${MONGO_USERNAME} - password: ${MONGO_PASSWORD} + username: ${MONGO_OPENIM_USERNAME} + password: ${MONGO_OPENIM_PASSWORD} maxPoolSize: ${MONGO_MAX_POOL_SIZE} ###################### Redis configuration information ###################### @@ -320,6 +320,14 @@ callback: enable: ${CALLBACK_ENABLE} timeout: ${CALLBACK_TIMEOUT} failedContinue: ${CALLBACK_FAILED_CONTINUE} + beforeUpdateUserInfoEx: + enable: ${CALLBACK_ENABLE} + timeout: ${CALLBACK_TIMEOUT} + failedContinue: ${CALLBACK_FAILED_CONTINUE} + afterUpdateUserInfoEx: + enable: ${CALLBACK_ENABLE} + timeout: ${CALLBACK_TIMEOUT} + failedContinue: ${CALLBACK_FAILED_CONTINUE} afterSendSingleMsg: enable: ${CALLBACK_ENABLE} timeout: ${CALLBACK_TIMEOUT} diff --git a/docker-compose.yml b/docker-compose.yml index fd71896a7..defc910c4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,18 +16,20 @@ services: ports: - "${MONGO_PORT:-37017}:27017" container_name: mongo - command: --wiredTigerCacheSizeGB 1 --auth + command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh || true; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"] volumes: - "${DATA_DIR:-./}/components/mongodb/data/db:/data/db" - "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs" - "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo" - - ./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro + - "./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro" environment: - TZ=Asia/Shanghai - wiredTigerCacheSizeGB=1 - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root} - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123} - MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openIM_v3} + - MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username + - MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password restart: always networks: server: @@ -122,9 +124,9 @@ 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:toc-base-open-docker.35 + # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin-front:v3.4.0 # container_name: openim-admin # restart: always # ports: @@ -167,12 +169,6 @@ 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/docs/contrib/environment.md b/docs/contrib/environment.md index e52d57235..7425c8022 100644 --- a/docs/contrib/environment.md +++ b/docs/contrib/environment.md @@ -104,8 +104,8 @@ Docker deployment offers a slightly more intricate template. Within the [openim- Configuration file modifications can be made by specifying corresponding environment variables, for instance: ```bash -export CHAT_IMAGE_VERSION="main" -export SERVER_IMAGE_VERSION="main" +export CHAT_IMAGE_VERSION="main" +export SERVER_IMAGE_VERSION="main" ``` These variables are stored within the [`environment.sh`](https://github.com/OpenIMSDK/openim-docker/blob/main/scripts/install/environment.sh) configuration: @@ -114,6 +114,9 @@ These variables are stored within the [`environment.sh`](https://github.com/Open readonly CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION:-'main'} readonly SERVER_IMAGE_VERSION=${SERVER_IMAGE_VERSION:-'main'} ``` +> [!IMPORTANT] +> Can learn to read our mirror version strategy: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md + Setting a variable, e.g., `export CHAT_IMAGE_VERSION="release-v1.3"`, will prioritize `CHAT_IMAGE_VERSION="release-v1.3"` as the variable value. Ultimately, the chosen image version is determined, and rendering is achieved through `make init` (or `./scripts/init-config.sh`). @@ -127,7 +130,7 @@ For convenience, configuration through modifying environment variables is recomm + PASSWORD - + **Description**: Password for mysql, mongodb, redis, and minio. + + **Description**: Password for mongodb, redis, and minio. + **Default**: `openIM123` + Notes: + Minimum password length: 8 characters. @@ -139,20 +142,22 @@ For convenience, configuration through modifying environment variables is recomm + OPENIM_USER - + **Description**: Username for mysql, mongodb, redis, and minio. + + **Description**: Username for redis, and minio. + **Default**: `root` ```bash export OPENIM_USER="root" ``` -+ API_URL +> mongo is `openIM`, use `export MONGO_OPENIM_USERNAME="openIM"` to modify + ++ OPENIM_IP + **Description**: API address. + **Note**: If the server has an external IP, it will be automatically obtained. For internal networks, set this variable to the IP serving internally. ```bash - export API_URL="http://ip:10002" + export OPENIM_IP="ip" ``` + DATA_DIR @@ -304,8 +309,10 @@ This section involves setting up MongoDB, including its port, address, and crede | -------------- | -------------- | ----------------------- | | MONGO_PORT | "27017" | Port used by MongoDB. | | MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. | -| MONGO_USERNAME | [User Defined] | Username for MongoDB. | -| MONGO_PASSWORD | [User Defined] | Password for MongoDB. | +| MONGO_USERNAME | [User Defined] | Admin Username for MongoDB. | +| MONGO_PASSWORD | [User Defined] | Admin Password for MongoDB. | +| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Username for MongoDB. | +| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Password for MongoDB. | ### 2.8. Tencent Cloud COS Configuration diff --git a/docs/contrib/images.md b/docs/contrib/images.md index 44bd7b5bf..d1a83d639 100644 --- a/docs/contrib/images.md +++ b/docs/contrib/images.md @@ -26,6 +26,7 @@ We provide multiple versions of our images to meet different project requirement 1. `main`: This image corresponds to the latest version of the main branch in OpenIM. It is updated frequently, making it perfect for users who want to stay at the cutting edge of our features. 2. `release-v3.*`: This is the image that corresponds to the latest version of OpenIM's stable release branch. It's ideal for users who prefer a balance between new features and stability. 3. `v3.*.*`: These images are specific to each tag in OpenIM. They are preserved in their original state and are never overwritten. These are the go-to images for users who need a specific, unchanging version of OpenIM. +4. The image versions adhere to Semantic Versioning 2.0.0 strategy. Taking the `openim-server` image as an example, available at [openim-server container package](https://github.com/openimsdk/open-im-server/pkgs/container/openim-server): upon tagging with v3.5.0, the CI automatically releases the following tags - `openim-server:3`, `openim-server:3.5`, `openim-server:3.5.0`, `openim-server:v3.5.0`, `openim-server:latest`, and `sha-e0244d9`. It's important to note that only `sha-e0244d9` is absolutely unique, whereas `openim-server:v3.5.0` and `openim-server:3.5.0` maintain a degree of uniqueness. ### Multi-Architecture Images diff --git a/docs/contrib/version.md b/docs/contrib/version.md index 0e03b8d8b..574badf59 100644 --- a/docs/contrib/version.md +++ b/docs/contrib/version.md @@ -1,6 +1,7 @@ # OpenIM Branch Management and Versioning: A Blueprint for High-Grade Software Development [📚 **OpenIM TOC**](#openim-branch-management-and-versioning-a-blueprint-for-high-grade-software-development) +- [OpenIM Branch Management and Versioning: A Blueprint for High-Grade Software Development](#openim-branch-management-and-versioning-a-blueprint-for-high-grade-software-development) - [Unfolding the Mechanism of OpenIM Version Maintenance](#unfolding-the-mechanism-of-openim-version-maintenance) - [Main Branch: The Heart of OpenIM Development](#main-branch-the-heart-of-openim-development) - [Release Branch: The Beacon of Stability](#release-branch-the-beacon-of-stability) @@ -8,8 +9,21 @@ - [Release Management: A Guided Tour](#release-management-a-guided-tour) - [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs) - [Version Skew Policy](#version-skew-policy) + - [Supported version skew](#supported-version-skew) + - [OpenIM Versioning, Branching, and Tag Strategy](#openim-versioning-branching-and-tag-strategy) + - [Supported Version Skew](#supported-version-skew-1) + - [openim-api](#openim-api) + - [openim-rpc-\* Components](#openim-rpc--components) + - [Other OpenIM Services](#other-openim-services) + - [Supported Component Upgrade Order](#supported-component-upgrade-order) + - [openim-api](#openim-api-1) + - [openim-rpc-\* Components](#openim-rpc--components-1) + - [Other OpenIM Services](#other-openim-services-1) + - [Conclusion](#conclusion) - [Applying Principles: A Git Workflow Example](#applying-principles-a-git-workflow-example) + - [Release Process](#release-process) - [Docker Images Version Management](#docker-images-version-management) + - [More](#more) At OpenIM, we acknowledge the profound impact of implementing a robust and efficient version management system, hence we abide by the established standards of [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/). @@ -213,3 +227,10 @@ Throughout this process, active communication within the team is pivotal to main ## Docker Images Version Management For more details on managing Docker image versions, visit [OpenIM Docker Images Administration](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md). + +## More + +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 diff --git a/go.mod b/go.mod index 924917027..6466848e3 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,8 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.43 - github.com/OpenIMSDK/tools v0.0.21 + github.com/OpenIMSDK/protocol v0.0.47 + github.com/OpenIMSDK/tools v0.0.23 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index 5d2f53a06..c5b71a3de 100644 --- a/go.sum +++ b/go.sum @@ -20,10 +20,10 @@ github.com/AndrewZuo01/protocol v0.0.0-20231219031520-648989b91fca/go.mod h1:F25 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.43 h1:8B921vEyO7r0AfQfZd7kCycYja+hJ2vuIZsKge/WRhU= -github.com/OpenIMSDK/protocol v0.0.43/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/OpenIMSDK/protocol v0.0.47 h1:DTJMFSONzqT0i/wa4Q1CtDT/jVATVudIRHcpY1zSWYE= +github.com/OpenIMSDK/protocol v0.0.47/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/tools v0.0.23 h1:xozfrGzhbpNPlDTap5DLVPk+JfgZ/ZyIj4Cuu3/bm9w= +github.com/OpenIMSDK/tools v0.0.23/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= diff --git a/internal/api/conversation.go b/internal/api/conversation.go index 6463cbde6..eb735e550 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -33,8 +33,8 @@ 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) GetSortedConversationList(c *gin.Context) { + a2r.Call(conversation.ConversationClient.GetSortedConversationList, o.Client, c) } func (o *ConversationApi) GetConversation(c *gin.Context) { diff --git a/internal/api/msg.go b/internal/api/msg.go index 548450534..9348596ac 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -164,6 +164,8 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM data = apistruct.VideoElem{} case constant.File: data = apistruct.FileElem{} + case constant.AtText: + data = apistruct.AtElem{} case constant.Custom: data = apistruct.CustomElem{} case constant.OANotification: @@ -172,7 +174,6 @@ 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") } diff --git a/internal/api/route.go b/internal/api/route.go index 1c91f4dde..3f16d3e50 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -83,6 +83,7 @@ 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("/process_user_command_get_all", ParseToken, u.ProcessUserCommandGetAll) userRouterGroup.POST("/add_notification_account", ParseToken, u.AddNotificationAccount) userRouterGroup.POST("/update_notification_account", ParseToken, u.UpdateNotificationAccountInfo) @@ -204,7 +205,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_sorted_conversation_list", c.GetSortedConversationList) conversationGroup.POST("/get_all_conversations", c.GetAllConversations) conversationGroup.POST("/get_conversation", c.GetConversation) conversationGroup.POST("/get_conversations", c.GetConversations) diff --git a/internal/api/user.go b/internal/api/user.go index 5f0e23631..03d22c354 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -221,6 +221,11 @@ func (u *UserApi) ProcessUserCommandGet(c *gin.Context) { a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c) } +// ProcessUserCommandGet user general function get all +func (u *UserApi) ProcessUserCommandGetAll(c *gin.Context) { + a2r.Call(user.UserClient.ProcessUserCommandGetAll, u.Client, c) +} + func (u *UserApi) AddNotificationAccount(c *gin.Context) { a2r.Call(user.UserClient.AddNotificationAccount, u.Client, c) } diff --git a/internal/push/callback.go b/internal/push/callback.go index 2085493c5..99a58fb07 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -37,7 +37,7 @@ func callbackOfflinePush( msg *sdkws.MsgData, offlinePushUserIDs *[]string, ) error { - if !config.Config.Callback.CallbackOfflinePush.Enable { + if !config.Config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing { return nil } req := &callbackstruct.CallbackBeforePushReq{ @@ -73,7 +73,7 @@ func callbackOfflinePush( } func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { - if !config.Config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) { + if !config.Config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing { return nil } req := callbackstruct.CallbackBeforePushReq{ @@ -107,7 +107,7 @@ func callbackBeforeSuperGroupOnlinePush( msg *sdkws.MsgData, pushToUserIDs *[]string, ) error { - if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable { + if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable || msg.ContentType == constant.Typing { return nil } req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{ diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 12b78ea2d..7cee7b99d 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -101,11 +101,9 @@ func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) - // callback if err := callbackOnlinePush(ctx, userIDs, msg); err != nil { return err } - // push wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs) if err != nil { @@ -120,7 +118,7 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg } for _, v := range wsResults { - if msg.SendID != v.UserID && (!v.OnlinePush) { + if !v.OnlinePush && msg.SendID == v.UserID { if err = callbackOfflinePush(ctx, userIDs, msg, &[]string{}); err != nil { return err } @@ -130,6 +128,7 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg return err } } + } return nil } diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index b80e32953..d0d59547c 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -89,8 +89,8 @@ 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) +func (m *conversationServer) GetSortedConversationList(ctx context.Context, req *pbconversation.GetSortedConversationListReq) (resp *pbconversation.GetSortedConversationListResp, err error) { + log.ZDebug(ctx, "GetSortedConversationList", "seqs", req, "userID", req.UserID) var conversationIDs []string if len(req.ConversationIDs) == 0 { conversationIDs, err = m.conversationDatabase.GetConversationIDs(ctx, req.UserID) @@ -129,30 +129,37 @@ func (m *conversationServer) GetConversationList(ctx context.Context, req *pbcon return nil, err } + var unreadTotal int64 conversation_unreadCount := make(map[string]int64) for conversationID, maxSeq := range maxSeqs { - conversation_unreadCount[conversationID] = maxSeq - hasReadSeqs[conversationID] + unreadCount := maxSeq - hasReadSeqs[conversationID] + conversation_unreadCount[conversationID] = unreadCount + unreadTotal += unreadCount } - conversation_isPinkTime := make(map[int64]string) - conversation_notPinkTime := make(map[int64]string) + conversation_isPinTime := make(map[int64]string) + conversation_notPinTime := 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 + conversation_isPinTime[time] = conversationID continue } - conversation_notPinkTime[time] = conversationID + conversation_notPinTime[time] = conversationID } - resp = &pbconversation.GetConversationListResp{ + resp = &pbconversation.GetSortedConversationListResp{ + ConversationTotal: int64(len(chatLogs)), ConversationElems: []*pbconversation.ConversationElem{}, + UnreadTotal: unreadTotal, } - m.conversationSort(conversation_isPinkTime, resp, conversation_unreadCount, conversationMsg) - m.conversationSort(conversation_notPinkTime, resp, conversation_unreadCount, conversationMsg) + m.conversationSort(conversation_isPinTime, resp, conversation_unreadCount, conversationMsg) + m.conversationSort(conversation_notPinTime, resp, conversation_unreadCount, conversationMsg) + + resp.ConversationElems = utils.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber())) return resp, nil } @@ -425,7 +432,7 @@ func (c *conversationServer) GetConversationOfflinePushUserIDs( func (c *conversationServer) conversationSort( conversations map[int64]string, - resp *pbconversation.GetConversationListResp, + resp *pbconversation.GetSortedConversationListResp, conversation_unreadCount map[string]int64, conversationMsg map[string]*pbconversation.ConversationElem, ) { diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index c53cb88f5..84702f548 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -452,22 +452,19 @@ func (s *friendServer) UpdateFriends( return nil, err } - for _, friendID := range req.FriendUserIDs { - 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 - } - } + val := make(map[string]any) + + if req.IsPinned != nil { + val["is_pinned"] = req.IsPinned.Value + } + if req.Remark != nil { + val["remark"] = req.Remark.Value + } + if req.Ex != nil { + val["ex"] = req.Ex.Value + } + if err = s.friendDatabase.UpdateFriends(ctx, req.OwnerUserID, req.FriendUserIDs, val); err != nil { + return nil, err } resp := &pbfriend.UpdateFriendsResp{} diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go index 8779cb89b..d891f4d1e 100644 --- a/internal/rpc/group/callback.go +++ b/internal/rpc/group/callback.go @@ -279,20 +279,20 @@ func CallbackApplyJoinGroupBefore(ctx context.Context, req *callbackstruct.Callb return nil } -func CallbackTransferGroupOwnerAfter(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (err error) { - if !config.Config.Callback.CallbackTransferGroupOwnerAfter.Enable { +func CallbackAfterTransferGroupOwner(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (err error) { + if !config.Config.Callback.CallbackAfterTransferGroupOwner.Enable { return nil } cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{ - CallbackCommand: callbackstruct.CallbackTransferGroupOwnerAfter, + CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwner, GroupID: req.GroupID, OldOwnerUserID: req.OldOwnerUserID, NewOwnerUserID: req.NewOwnerUserID, } resp := &callbackstruct.CallbackTransferGroupOwnerResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeJoinGroup); err != nil { + if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterTransferGroupOwner); err != nil { return err } return nil diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 5153726a2..b1789523e 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -109,14 +109,11 @@ type groupServer struct { } func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) { - defer log.ZDebug(ctx, "return") + defer log.ZDebug(ctx, "NotificationUserInfoUpdate return") members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID) if err != nil { return nil, err } - if err := s.PopulateGroupMember(ctx, members...); err != nil { - return nil, err - } groupIDs := make([]string, 0, len(members)) for _, member := range members { if member.Nickname != "" && member.FaceURL != "" { @@ -476,13 +473,42 @@ func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbgroup.GetGro func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) { resp := &pbgroup.GetGroupMemberListResp{} - total, members, err := s.db.PageGetGroupMember(ctx, req.GroupID, req.Pagination) + var ( + total int64 + members []*relationtb.GroupMemberModel + err error + ) + if req.Keyword == "" { + total, members, err = s.db.PageGetGroupMember(ctx, req.GroupID, req.Pagination) + } else { + members, err = s.db.FindGroupMemberAll(ctx, req.GroupID) + } if err != nil { return nil, err } if err := s.PopulateGroupMember(ctx, members...); err != nil { return nil, err } + if req.Keyword != "" { + groupMembers := make([]*relationtb.GroupMemberModel, 0) + for _, member := range members { + if member.UserID == req.Keyword { + groupMembers = append(groupMembers, member) + total++ + continue + } + if member.Nickname == req.Keyword { + groupMembers = append(groupMembers, member) + total++ + continue + } + } + + GMembers := utils.Paginate(groupMembers, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber())) + resp.Members = utils.Batch(convert.Db2PbGroupMember, GMembers) + resp.Total = uint32(total) + return resp, nil + } resp.Total = uint32(total) resp.Members = utils.Batch(convert.Db2PbGroupMember, members) return resp, nil @@ -1032,7 +1058,7 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans return nil, err } - if err := CallbackTransferGroupOwnerAfter(ctx, req); err != nil { + if err := CallbackAfterTransferGroupOwner(ctx, req); err != nil { return nil, err } s.Notification.GroupOwnerTransferredNotification(ctx, req) @@ -1042,20 +1068,29 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) (*pbgroup.GetGroupsResp, error) { resp := &pbgroup.GetGroupsResp{} var ( - groups []*relationtb.GroupModel - err error + group []*relationtb.GroupModel + err error ) if req.GroupID != "" { - groups, err = s.db.FindGroup(ctx, []string{req.GroupID}) - resp.Total = uint32(len(groups)) + group, err = s.db.FindGroup(ctx, []string{req.GroupID}) + resp.Total = uint32(len(group)) } else { var total int64 - total, groups, err = s.db.SearchGroup(ctx, req.GroupName, req.Pagination) + total, group, err = s.db.SearchGroup(ctx, req.GroupName, req.Pagination) resp.Total = uint32(total) } if err != nil { return nil, err } + + var groups []*relationtb.GroupModel + for _, v := range group { + if v.Status == constant.GroupStatusDismissed { + resp.Total-- + continue + } + groups = append(groups, v) + } groupIDs := utils.Slice(groups, func(e *relationtb.GroupModel) string { return e.GroupID }) diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index 5d192fb87..f98318bba 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -70,7 +70,7 @@ func GetContent(msg *sdkws.MsgData) string { } func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable { + if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackBeforeSendSingleMsgReq{ @@ -85,7 +85,7 @@ func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) er } func callbackAfterSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackAfterSendSingleMsg.Enable { + if !config.Config.Callback.CallbackAfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackAfterSendSingleMsgReq{ @@ -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.CallbackBeforeSendGroupMsg.Enable { + if !config.Config.Callback.CallbackBeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackBeforeSendGroupMsgReq{ @@ -115,7 +115,7 @@ func callbackBeforeSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) err } func callbackAfterSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable { + if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackAfterSendGroupMsgReq{ diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index dd08292bd..630b74a4a 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -65,6 +65,7 @@ func (m *msgServer) sendMsgSuperGroupChat( if err = callbackBeforeSendGroupMsg(ctx, req); err != nil { return nil, err } + if err := callbackMsgModify(ctx, req); err != nil { return nil, err } @@ -167,6 +168,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq if err = callbackBeforeSendSingleMsg(ctx, req); err != nil { return nil, err } + if err := callbackMsgModify(ctx, req); err != nil { return nil, err } diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 51403d631..158e37d70 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/tools/pagination" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "math/rand" "strings" @@ -228,7 +229,7 @@ func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckR } func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) { - total, users, err := s.Page(ctx, req.Pagination) + total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, req.Pagination) if err != nil { return nil, err } @@ -379,38 +380,94 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context, // ProcessUserCommandAdd user general function add func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) { - // Assuming you have a method in s.UserDatabase to add a user command - err := s.UserDatabase.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, req.Value) + err := authverify.CheckAccessV3(ctx, req.UserID) if err != nil { return nil, err } + var value string + if req.Value != nil { + value = req.Value.Value + } + var ex string + if req.Ex != nil { + value = req.Ex.Value + } + // Assuming you have a method in s.UserDatabase to add a user command + err = s.UserDatabase.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, value, ex) + if err != nil { + return nil, err + } + tips := &sdkws.UserCommandAddTips{ + FromUserID: req.UserID, + ToUserID: req.UserID, + } + err = s.userNotificationSender.UserCommandAddNotification(ctx, tips) + if err != nil { + return nil, err + } return &pbuser.ProcessUserCommandAddResp{}, nil } // ProcessUserCommandDelete user general function delete func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) { - // Assuming you have a method in s.UserDatabase to delete a user command - err := s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid) + err := authverify.CheckAccessV3(ctx, req.UserID) if err != nil { return nil, err } + err = s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid) + if err != nil { + return nil, err + } + tips := &sdkws.UserCommandDeleteTips{ + FromUserID: req.UserID, + ToUserID: req.UserID, + } + err = s.userNotificationSender.UserCommandDeleteNotification(ctx, tips) + if err != nil { + return nil, err + } return &pbuser.ProcessUserCommandDeleteResp{}, nil } // ProcessUserCommandUpdate user general function update func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) { - // Assuming you have a method in s.UserDatabase to update a user command - err := s.UserDatabase.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, req.Value) + err := authverify.CheckAccessV3(ctx, req.UserID) if err != nil { return nil, err } + val := make(map[string]any) + + // Map fields from eax to val + if req.Value != nil { + val["value"] = req.Value.Value + } + if req.Ex != nil { + val["ex"] = req.Ex.Value + } + // Assuming you have a method in s.UserDatabase to update a user command + err = s.UserDatabase.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, val) + if err != nil { + return nil, err + } + tips := &sdkws.UserCommandUpdateTips{ + FromUserID: req.UserID, + ToUserID: req.UserID, + } + err = s.userNotificationSender.UserCommandUpdateNotification(ctx, tips) + if err != nil { + return nil, err + } return &pbuser.ProcessUserCommandUpdateResp{}, nil } func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) { + err := authverify.CheckAccessV3(ctx, req.UserID) + if err != nil { + return nil, err + } // Fetch user commands from the database commands, err := s.UserDatabase.GetUserCommands(ctx, req.UserID, req.Type) if err != nil { @@ -423,14 +480,45 @@ func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.Proc for _, command := range commands { // No need to use index since command is already a pointer commandInfoSlice = append(commandInfoSlice, &pbuser.CommandInfoResp{ + Type: command.Type, + Uuid: command.Uuid, + Value: command.Value, + CreateTime: command.CreateTime, + Ex: command.Ex, + }) + } + + // Return the response with the slice + return &pbuser.ProcessUserCommandGetResp{CommandResp: commandInfoSlice}, nil +} + +func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) { + err := authverify.CheckAccessV3(ctx, req.UserID) + if err != nil { + return nil, err + } + // Fetch user commands from the database + commands, err := s.UserDatabase.GetAllUserCommands(ctx, req.UserID) + if err != nil { + return nil, err + } + + // Initialize commandInfoSlice as an empty slice + commandInfoSlice := make([]*pbuser.AllCommandInfoResp, 0, len(commands)) + + for _, command := range commands { + // No need to use index since command is already a pointer + commandInfoSlice = append(commandInfoSlice, &pbuser.AllCommandInfoResp{ + Type: command.Type, Uuid: command.Uuid, Value: command.Value, CreateTime: command.CreateTime, + Ex: command.Ex, }) } // Return the response with the slice - return &pbuser.ProcessUserCommandGetResp{KVArray: commandInfoSlice}, nil + return &pbuser.ProcessUserCommandGetAllResp{CommandResp: commandInfoSlice}, nil } func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) { @@ -438,22 +526,28 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add return nil, err } - var userID string - for i := 0; i < 20; i++ { - userId := s.genUserID() - _, err := s.UserDatabase.FindWithError(ctx, []string{userId}) + if req.UserID == "" { + for i := 0; i < 20; i++ { + userId := s.genUserID() + _, err := s.UserDatabase.FindWithError(ctx, []string{userId}) + if err == nil { + continue + } + req.UserID = userId + break + } + if req.UserID == "" { + return nil, errs.ErrInternalServer.Wrap("gen user id failed") + } + } else { + _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID}) if err == nil { - continue + return nil, errs.ErrArgs.Wrap("userID is used") } - userID = userId - break - } - if userID == "" { - return nil, errs.ErrInternalServer.Wrap("gen user id failed") } user := &tablerelation.UserModel{ - UserID: userID, + UserID: req.UserID, Nickname: req.NickName, FaceURL: req.FaceURL, CreateTime: time.Now(), @@ -463,7 +557,11 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add return nil, err } - return &pbuser.AddNotificationAccountResp{}, nil + return &pbuser.AddNotificationAccountResp{ + UserID: req.UserID, + NickName: req.NickName, + FaceURL: req.FaceURL, + }, nil } func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) { @@ -497,30 +595,33 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser. return nil, err } - if req.NickName != "" { - users, err := s.UserDatabase.FindByNickname(ctx, req.NickName) + var users []*relation.UserModel + var err error + if req.Keyword != "" { + users, err = s.UserDatabase.Find(ctx, []string{req.Keyword}) 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}) + resp := s.userModelToResp(users, req.Pagination) + if resp.Total != 0 { + return resp, nil + } + users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword) if err != nil { return nil, err } - resp := s.userModelToResp(users) + resp = s.userModelToResp(users, req.Pagination) + return resp, nil + return resp, nil } - users, err := s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin) + users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin) if err != nil { return nil, err } - resp := s.userModelToResp(users) + resp := s.userModelToResp(users, req.Pagination) return resp, nil } @@ -554,7 +655,7 @@ func (s *userServer) genUserID() string { return string(data) } -func (s *userServer) userModelToResp(users []*relation.UserModel) *pbuser.SearchNotificationAccountResp { +func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pagination.Pagination) *pbuser.SearchNotificationAccountResp { accounts := make([]*pbuser.NotificationAccountInfo, 0) var total int64 for _, v := range users { @@ -568,5 +669,8 @@ func (s *userServer) userModelToResp(users []*relation.UserModel) *pbuser.Search total += 1 } } - return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: accounts} + + notificationAccounts := utils.Paginate(accounts, int(pagination.GetPageNumber()), int(pagination.GetShowNumber())) + + return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: notificationAccounts} } diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index e810dfecf..4c71224ee 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -76,7 +76,6 @@ func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) { func IsManagerUserID(opUserID string) bool { return utils.IsContain(opUserID, config.Config.Manager.UserID) || utils.IsContain(opUserID, config.Config.IMAdmin.UserID) - } func WsVerifyToken(token, userID string, platformID int) error { diff --git a/pkg/callbackstruct/constant.go b/pkg/callbackstruct/constant.go index cda98af16..f3bcf1383 100644 --- a/pkg/callbackstruct/constant.go +++ b/pkg/callbackstruct/constant.go @@ -41,7 +41,7 @@ const ( CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand" CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand" CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand" - CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter" + CallbackAfterTransferGroupOwner = "callbackAfterTransferGroupOwner" CallbackBeforeSetFriendRemark = "callbackBeforeSetFriendRemark" CallbackAfterSetFriendRemark = "callbackAfterSetFriendRemark" CallbackSingleMsgRead = "callbackSingleMsgRead" diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 88e87e709..9696e9367 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -296,7 +296,7 @@ type configStruct struct { CallbackKillGroupMember CallBackConfig `yaml:"killGroupMember"` CallbackDismissGroup CallBackConfig `yaml:"dismissGroup"` CallbackBeforeJoinGroup CallBackConfig `yaml:"joinGroup"` - CallbackTransferGroupOwnerAfter CallBackConfig `yaml:"transferGroupOwner"` + CallbackAfterTransferGroupOwner CallBackConfig `yaml:"transferGroupOwner"` CallbackBeforeInviteUserToGroup CallBackConfig `yaml:"beforeInviteUserToGroup"` CallbackAfterJoinGroup CallBackConfig `yaml:"joinGroupAfter"` CallbackAfterSetGroupInfo CallBackConfig `yaml:"setGroupInfoAfter"` diff --git a/pkg/common/config/version b/pkg/common/config/version index e682ea429..4d0729e54 100644 --- a/pkg/common/config/version +++ b/pkg/common/config/version @@ -1 +1 @@ -v3.3.0 \ No newline at end of file +v3.5.0 \ No newline at end of file diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index 1708f7664..a2b60d48f 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -44,6 +44,8 @@ type FriendCache interface { GetFriend(ctx context.Context, ownerUserID, friendUserID string) (friend *relationtb.FriendModel, err error) // Delete friend when friend info changed DelFriend(ownerUserID, friendUserID string) FriendCache + // Delete friends when friends' info changed + DelFriends(ownerUserID string, friendUserIDs []string) FriendCache } // FriendCacheRedis is an implementation of the FriendCache interface using Redis. @@ -152,3 +154,15 @@ func (f *FriendCacheRedis) DelFriend(ownerUserID, friendUserID string) FriendCac return newFriendCache } + +// DelFriends deletes multiple friend infos from the cache. +func (f *FriendCacheRedis) DelFriends(ownerUserID string, friendUserIDs []string) FriendCache { + newFriendCache := f.NewCache() + + for _, friendUserID := range friendUserIDs { + key := f.getFriendKey(ownerUserID, friendUserID) + newFriendCache.AddKeys(key) // Assuming AddKeys marks the keys for deletion + } + + return newFriendCache +} diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index 2a0cb63e4..c6629e9c8 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -279,7 +279,7 @@ func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, for _, v := range existConversationUserIDs { cache = cache.DelConversations(v, conversationID) } - return c.cache.ExecDel(ctx) + return cache.ExecDel(ctx) }) } diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index 924a179ba..3b98f5d7b 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -74,15 +74,8 @@ type FriendDatabase interface { // 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) - + // UpdateFriends updates fields for friends + UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error) } type friendDatabase struct { @@ -323,21 +316,12 @@ func (f *friendDatabase) FindFriendUserIDs(ctx context.Context, ownerUserID stri func (f *friendDatabase) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error) { return f.friendRequest.FindBothFriendRequests(ctx, fromUserID, toUserID) } -func (f *friendDatabase) UpdateFriendPinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (err error) { - if err := f.friend.UpdatePinStatus(ctx, ownerUserID, friendUserID, isPinned); err != nil { - return err +func (f *friendDatabase) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error) { + if len(val) == 0 { + return nil } - 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 { + if err := f.friend.UpdateFriends(ctx, ownerUserID, friendUserIDs, val); 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) + return f.cache.DelFriends(ownerUserID, friendUserIDs).ExecDel(ctx) } diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index a109b81ef..cedae5c97 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -50,6 +50,8 @@ type UserDatabase interface { UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) // Page If not found, no error is returned Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) + // FindUser + PageFindUser(ctx context.Context, level int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) // IsExist true as long as one exists IsExist(ctx context.Context, userIDs []string) (exist bool, err error) // GetAllUserID Get all user IDs @@ -76,10 +78,11 @@ type UserDatabase interface { SetUserStatus(ctx context.Context, userID string, status, platformID int32) error //CRUD user command - AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error + AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error - UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error + UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) + GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) } type userDatabase struct { @@ -182,6 +185,10 @@ func (u *userDatabase) Page(ctx context.Context, pagination pagination.Paginatio return u.userDB.Page(ctx, pagination) } +func (u *userDatabase) PageFindUser(ctx context.Context, level int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) { + return u.userDB.PageFindUser(ctx, level, pagination) +} + // IsExist Does userIDs exist? As long as there is one, it will be true. func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) { users, err := u.userDB.Find(ctx, userIDs) @@ -253,16 +260,20 @@ func (u *userDatabase) GetUserStatus(ctx context.Context, userIDs []string) ([]* func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error { return u.cache.SetUserStatus(ctx, userID, status, platformID) } -func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error { - return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value) +func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error { + return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value, ex) } func (u *userDatabase) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error { return u.userDB.DeleteUserCommand(ctx, userID, Type, UUID) } -func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error { - return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, value) +func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error { + return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, val) } func (u *userDatabase) GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) { commands, err := u.userDB.GetUserCommand(ctx, userID, Type) return commands, err } +func (u *userDatabase) GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) { + commands, err := u.userDB.GetAllUserCommand(ctx, userID) + return commands, err +} diff --git a/pkg/common/db/mgo/friend.go b/pkg/common/db/mgo/friend.go index 72289181b..b4172d0fb 100644 --- a/pkg/common/db/mgo/friend.go +++ b/pkg/common/db/mgo/friend.go @@ -16,7 +16,6 @@ package mgo import ( "context" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/mongo/options" @@ -144,49 +143,22 @@ func (f *FriendMgo) FindFriendUserIDs(ctx context.Context, ownerUserID string) ( return mgoutil.Find[string](ctx, f.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1})) } -// UpdatePinStatus update friend's pin status -func (f *FriendMgo) UpdatePinStatus(ctx context.Context, ownerUserID string, friendUserID string, isPinned bool) (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{"is_pinned": isPinned}} - - // 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 pin error") +func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) error { + // Ensure there are IDs to update + if len(friendUserIDs) == 0 { + return nil // Or return an error if you expect there to always be IDs } - 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") + // Create a filter to match documents with the specified ownerUserID and any of the friendUserIDs + filter := bson.M{ + "owner_user_id": ownerUserID, + "friend_user_id": bson.M{"$in": friendUserIDs}, } - 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") - } + // Create an update document + update := bson.M{"$set": val} - return nil + // Perform the update operation for all matching documents + _, err := mgoutil.UpdateMany(ctx, f.coll, filter, update) + return err } diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index 27ca264dd..b07f82928 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -17,6 +17,7 @@ package mgo import ( "context" "github.com/OpenIMSDK/protocol/user" + "github.com/OpenIMSDK/tools/errs" "time" "github.com/OpenIMSDK/tools/mgoutil" @@ -77,8 +78,12 @@ func (u *UserMgo) Page(ctx context.Context, pagination pagination.Pagination) (c return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{}, pagination) } +func (u *UserMgo) PageFindUser(ctx context.Context, level int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) { + return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{"app_manger_level": level}, pagination) +} + func (u *UserMgo) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) { - return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"user_id": 1})) + return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1})) } func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err error) { @@ -86,7 +91,7 @@ func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err err } func (u *UserMgo) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) { - return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"global_recv_msg_opt": 1})) + return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"_id": 0, "global_recv_msg_opt": 1})) } func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) { @@ -96,7 +101,7 @@ func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int6 return mgoutil.Count(ctx, u.coll, bson.M{"create_time": bson.M{"$lt": before}}) } -func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error { +func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error { collection := u.coll.Database().Collection("userCommands") // Create a new document instead of updating an existing one @@ -106,28 +111,48 @@ func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, "uuid": UUID, "createTime": time.Now().Unix(), // assuming you want the creation time in Unix timestamp "value": value, + "ex": ex, } _, err := collection.InsertOne(ctx, doc) return err } + func (u *UserMgo) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error { collection := u.coll.Database().Collection("userCommands") filter := bson.M{"userID": userID, "type": Type, "uuid": UUID} - _, err := collection.DeleteOne(ctx, filter) + result, err := collection.DeleteOne(ctx, filter) + if result.DeletedCount == 0 { + // No records found to update + return errs.Wrap(errs.ErrRecordNotFound) + } return err } -func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error { +func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error { + if len(val) == 0 { + return nil + } + collection := u.coll.Database().Collection("userCommands") filter := bson.M{"userID": userID, "type": Type, "uuid": UUID} - update := bson.M{"$set": bson.M{"value": value}} + update := bson.M{"$set": val} - _, err := collection.UpdateOne(ctx, filter, update) - return err + result, err := collection.UpdateOne(ctx, filter, update) + if err != nil { + return err + } + + if result.MatchedCount == 0 { + // No records found to update + return errs.Wrap(errs.ErrRecordNotFound) + } + + return nil } + func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) { collection := u.coll.Database().Collection("userCommands") filter := bson.M{"userID": userID, "type": Type} @@ -143,19 +168,23 @@ func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) for cursor.Next(ctx) { var document struct { + Type int32 `bson:"type"` UUID string `bson:"uuid"` Value string `bson:"value"` CreateTime int64 `bson:"createTime"` + Ex string `bson:"ex"` } if err := cursor.Decode(&document); err != nil { return nil, err } - commandInfo := &user.CommandInfoResp{ // Change here: use a pointer to the struct + commandInfo := &user.CommandInfoResp{ + Type: document.Type, Uuid: document.UUID, Value: document.Value, CreateTime: document.CreateTime, + Ex: document.Ex, } commands = append(commands, commandInfo) @@ -167,7 +196,48 @@ func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) return commands, nil } +func (u *UserMgo) GetAllUserCommand(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) { + collection := u.coll.Database().Collection("userCommands") + filter := bson.M{"userID": userID} + + cursor, err := collection.Find(ctx, filter) + if err != nil { + return nil, err + } + defer cursor.Close(ctx) + // Initialize commands as a slice of pointers + commands := []*user.AllCommandInfoResp{} + + for cursor.Next(ctx) { + var document struct { + Type int32 `bson:"type"` + UUID string `bson:"uuid"` + Value string `bson:"value"` + CreateTime int64 `bson:"createTime"` + Ex string `bson:"ex"` + } + + if err := cursor.Decode(&document); err != nil { + return nil, err + } + + commandInfo := &user.AllCommandInfoResp{ + Type: document.Type, + Uuid: document.UUID, + Value: document.Value, + CreateTime: document.CreateTime, + Ex: document.Ex, + } + + commands = append(commands, commandInfo) + } + + if err := cursor.Err(); err != nil { + return nil, err + } + return commands, nil +} func (u *UserMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) { pipeline := bson.A{ bson.M{ diff --git a/pkg/common/db/table/relation/friend.go b/pkg/common/db/table/relation/friend.go index cc337701d..73f7454df 100644 --- a/pkg/common/db/table/relation/friend.go +++ b/pkg/common/db/table/relation/friend.go @@ -57,10 +57,6 @@ type FriendModelInterface interface { FindInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*FriendModel, err error) // FindFriendUserIDs retrieves a list of friend user IDs for a given owner. 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) + // UpdateFriends update friends' fields + UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) (err error) } diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index fc116adc2..a1b4269d1 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -56,6 +56,7 @@ type UserModelInterface interface { TakeNotification(ctx context.Context, level int64) (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) + PageFindUser(ctx context.Context, level int64, 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) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) @@ -64,8 +65,9 @@ type UserModelInterface interface { // 获取范围内用户增量 CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) //CRUD user command - AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error + AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error - UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error + UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) + GetAllUserCommand(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) } diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 8cfb97a98..b8184d767 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -78,8 +78,8 @@ func buildMongoURI() string { return config.Config.Mongo.Uri } - username := os.Getenv("MONGO_USERNAME") - password := os.Getenv("MONGO_PASSWORD") + username := os.Getenv("MONGO_OPENIM_USERNAME") + password := os.Getenv("MONGO_OPENIM_PASSWORD") address := os.Getenv("MONGO_ADDRESS") port := os.Getenv("MONGO_PORT") database := os.Getenv("MONGO_DATABASE") @@ -103,9 +103,9 @@ func buildMongoURI() string { maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize) } - uriFormat := "mongodb://%s/%s?maxPoolSize=%s&authSource=admin" + uriFormat := "mongodb://%s/%s?maxPoolSize=%s" if username != "" && password != "" { - uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s&authSource=admin" + uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s" return fmt.Sprintf(uriFormat, username, password, address, database, maxPoolSize) } return fmt.Sprintf(uriFormat, address, database, maxPoolSize) diff --git a/pkg/msgprocessor/conversation.go b/pkg/msgprocessor/conversation.go index 56255f37c..7477bea7a 100644 --- a/pkg/msgprocessor/conversation.go +++ b/pkg/msgprocessor/conversation.go @@ -52,6 +52,7 @@ func GetChatConversationIDByMsg(msg *sdkws.MsgData) string { case constant.NotificationChatType: return "sn_" + msg.SendID + "_" + msg.RecvID } + return "" } diff --git a/pkg/msgprocessor/options.go b/pkg/msgprocessor/options.go index ea383a1af..c6e209b98 100644 --- a/pkg/msgprocessor/options.go +++ b/pkg/msgprocessor/options.go @@ -37,6 +37,7 @@ func NewOptions(opts ...OptionsOpt) Options { for _, opt := range opts { opt(options) } + return options } diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index 00759b1b2..b98a1d38e 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -197,7 +197,7 @@ func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Conte 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 := sdkws.FriendsInfoUpdateTips{FromToUserID: &sdkws.FromToUserID{}} tips.FromToUserID.ToUserID = toUserID tips.FriendIDs = friendIDs return f.Notification(ctx, toUserID, toUserID, constant.FriendsInfoUpdateNotification, &tips) diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index cbae7c49b..8c3719b2c 100755 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -409,11 +409,16 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte if err != nil { return err } - tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg, ReceiverAs: 1} + tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg} if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { + for _, userID := range append(userIDs, req.FromUserID) { + if userID == req.FromUserID { + tips.ReceiverAs = 0 + } else { + tips.ReceiverAs = 1 + } err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) @@ -441,7 +446,12 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } - for _, userID := range append(userIDs, mcontext.GetOpUserID(ctx)) { + for _, userID := range append(userIDs, req.FromUserID) { + if userID == req.FromUserID { + tips.ReceiverAs = 0 + } else { + tips.ReceiverAs = 1 + } err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips) if err != nil { log.ZError(ctx, "failed", err) diff --git a/pkg/rpcclient/notification/user.go b/pkg/rpcclient/notification/user.go index 4feebf7b9..4347faece 100644 --- a/pkg/rpcclient/notification/user.go +++ b/pkg/rpcclient/notification/user.go @@ -103,3 +103,21 @@ func (u *UserNotificationSender) UserStatusChangeNotification( ) error { return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserStatusChangeNotification, tips) } +func (u *UserNotificationSender) UserCommandUpdateNotification( + ctx context.Context, + tips *sdkws.UserCommandUpdateTips, +) error { + return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandUpdateNotification, tips) +} +func (u *UserNotificationSender) UserCommandAddNotification( + ctx context.Context, + tips *sdkws.UserCommandAddTips, +) error { + return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandAddNotification, tips) +} +func (u *UserNotificationSender) UserCommandDeleteNotification( + ctx context.Context, + tips *sdkws.UserCommandDeleteTips, +) error { + return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandDeleteNotification, tips) +} diff --git a/scripts/docker-start-all.sh b/scripts/docker-start-all.sh index 3c83b02bb..2616b7bd1 100755 --- a/scripts/docker-start-all.sh +++ b/scripts/docker-start-all.sh @@ -36,4 +36,4 @@ sleep 5 "${OPENIM_ROOT}"/scripts/check-all.sh -tail -f ${LOG_FILE} \ No newline at end of file +tail -f ${LOG_FILE} diff --git a/scripts/genconfig.sh b/scripts/genconfig.sh index 2371edc9d..8ded38b22 100755 --- a/scripts/genconfig.sh +++ b/scripts/genconfig.sh @@ -29,16 +29,8 @@ if [ $# -ne 2 ];then exit 1 fi -openim::util::require-dig -result=$? -if [ $result -ne 0 ]; then - openim::log::info "Please install 'dig' to use this feature." - openim::log::info "Installation instructions:" - openim::log::info " For Ubuntu/Debian: sudo apt-get install dnsutils" - openim::log::info " For CentOS/RedHat: sudo yum install bind-utils" - openim::log::info " For macOS: 'dig' should be preinstalled. If missing, try: brew install bind" - openim::log::info " For Windows: Install BIND9 tools from https://www.isc.org/download/" - openim::log::error_exit "Error: 'dig' command is required but not installed." +if [ -z "${OPENIM_IP}" ]; then + openim::util::require-dig fi source "${env_file}" diff --git a/scripts/install-im-server.sh b/scripts/install-im-server.sh index a21ae134d..9afbb97c9 100755 --- a/scripts/install-im-server.sh +++ b/scripts/install-im-server.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Copyright © 2023 OpenIM. All rights reserved. +# 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. @@ -13,8 +13,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +# +# OpenIM Docker Deployment Script +# +# This script automates the process of building the OpenIM server image +# and deploying it using Docker Compose. +# +# Variables: +# - SERVER_IMAGE_VERSION: Version of the server image (default: test) +# - IMAGE_REGISTRY: Docker image registry (default: openim) +# - DOCKER_COMPOSE_FILE_URL: URL to the docker-compose.yml file +# +# Usage: +# SERVER_IMAGE_VERSION=latest IMAGE_REGISTRY=myregistry ./this_script.sh -# Common utilities, variables and checks for all build scripts. set -o errexit set -o nounset set -o pipefail @@ -28,35 +40,46 @@ chmod +x "${OPENIM_ROOT}"/scripts/*.sh openim::util::ensure_docker_daemon_connectivity +# Default values for variables +: ${SERVER_IMAGE_VERSION:=test} +: ${IMAGE_REGISTRY:=openim} +: ${DOCKER_COMPOSE_FILE_URL:="https://raw.githubusercontent.com/openimsdk/openim-docker/main/docker-compose.yaml"} + DOCKER_COMPOSE_COMMAND= # Check if docker-compose command is available openim::util::check_docker_and_compose_versions - -if command -v docker compose &> /dev/null -then +if command -v docker compose &> /dev/null; then openim::log::info "docker compose command is available" DOCKER_COMPOSE_COMMAND="docker compose" else DOCKER_COMPOSE_COMMAND="docker-compose" fi +export SERVER_IMAGE_VERSION +export IMAGE_REGISTRY "${OPENIM_ROOT}"/scripts/init-config.sh + pushd "${OPENIM_ROOT}" +docker build -t "${IMAGE_REGISTRY}/openim-server:${SERVER_IMAGE_VERSION}" . ${DOCKER_COMPOSE_COMMAND} stop -curl https://raw.githubusercontent.com/openimsdk/openim-docker/main/docker-compose.yaml -o docker-compose.yml +curl "${DOCKER_COMPOSE_FILE_URL}" -o docker-compose.yml ${DOCKER_COMPOSE_COMMAND} up -d +# Function to check container status +check_containers() { + if ! ${DOCKER_COMPOSE_COMMAND} ps | grep -q 'Up'; then + echo "Error: One or more docker containers failed to start." + ${DOCKER_COMPOSE_COMMAND} logs + return 1 + fi + return 0 +} + # Wait for a short period to allow containers to initialize sleep 30 +check_containers -# Check the status of the containers -if ! ${DOCKER_COMPOSE_COMMAND} ps | grep -q 'Up'; then - echo "Error: One or more docker containers failed to start." - ${DOCKER_COMPOSE_COMMAND} logs -fi - -sleep 30 # Keep the original 60-second wait, adjusted for the 10-second check above ${DOCKER_COMPOSE_COMMAND} logs openim-server ${DOCKER_COMPOSE_COMMAND} ps -popd +popd \ No newline at end of file diff --git a/scripts/install/dependency.sh b/scripts/install/dependency.sh index bfa0909e2..78995bcf9 100755 --- a/scripts/install/dependency.sh +++ b/scripts/install/dependency.sh @@ -35,8 +35,8 @@ docker run -d \ -e MONGO_INITDB_ROOT_USERNAME=${OPENIM_USER} \ -e MONGO_INITDB_ROOT_PASSWORD=${PASSWORD} \ -e MONGO_INITDB_DATABASE=openIM \ - -e MONGO_USERNAME=${OPENIM_USER} \ - -e MONGO_PASSWORD=${PASSWORD} \ + -e MONGO_OPENIM_USERNAME=${OPENIM_USER} \ + -e MONGO_OPENIM_PASSWORD=${PASSWORD} \ --restart always \ mongo:6.0.2 --wiredTigerCacheSizeGB 1 --auth diff --git a/scripts/install/environment.sh b/scripts/install/environment.sh index 22a0996fc..9d9fc4028 100755 --- a/scripts/install/environment.sh +++ b/scripts/install/environment.sh @@ -171,9 +171,14 @@ def "MONGO_URI" # MongoDB的URI def "MONGO_PORT" "37017" # MongoDB的端口 def "MONGO_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # MongoDB的地址 def "MONGO_DATABASE" "${DATABASE_NAME}" # MongoDB的数据库名 -def "MONGO_USERNAME" "${OPENIM_USER}" # MongoDB的用户名 -# MongoDB的密码 +def "MONGO_USERNAME" "root" # MongoDB的管理员身份用户名 +# MongoDB的管理员身份密码 readonly MONGO_PASSWORD=${MONGO_PASSWORD:-"${PASSWORD}"} +# Mongo OpenIM 身份用户名 +def "MONGO_OPENIM_USERNAME" "openIM" +# Mongo OpenIM 身份密码 +readonly MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-"${PASSWORD}"} + def "MONGO_MAX_POOL_SIZE" "100" # 最大连接池大小 ###################### Object 配置信息 ###################### diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index b8d76edeb..4de240972 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -1146,8 +1146,13 @@ function openim::util::require-jq { # Checks whether dig is installed and provides installation instructions if it is not. function openim::util::require-dig { if ! command -v dig &>/dev/null; then - openim::log::error "dig command not found." - return 1 + openim::log::error "Please install 'dig' to use this feature." + openim::log::error "Installation instructions:" + openim::log::error " For Ubuntu/Debian: sudo apt-get install dnsutils" + openim::log::error " For CentOS/RedHat: sudo yum install bind-utils" + openim::log::error " For macOS: 'dig' should be preinstalled. If missing, try: brew install bind" + openim::log::error " For Windows: Install BIND9 tools from https://www.isc.org/download/" + openim::log::error_exit "dig command not found." fi return 0 } diff --git a/scripts/make-rules/common.mk b/scripts/make-rules/common.mk index 81b44826b..c56f9f071 100644 --- a/scripts/make-rules/common.mk +++ b/scripts/make-rules/common.mk @@ -129,7 +129,7 @@ FIND := find . ! -path './utils/*' ! -path './vendor/*' ! -path './third_party/* XARGS := xargs -r --no-run-if-empty # Linux command settings-CODE DIRS Copyright -CODE_DIRS := $(ROOT_DIR)/pkg $(ROOT_DIR)/cmd $(ROOT_DIR)/config $(ROOT_DIR)/.docker-compose_cfg $(ROOT_DIR)/internal $(ROOT_DIR)/scripts $(ROOT_DIR)/test $(ROOT_DIR)/.github $(ROOT_DIR)/build $(ROOT_DIR)/tools $(ROOT_DIR)/deployments +CODE_DIRS := $(ROOT_DIR)/pkg $(ROOT_DIR)/cmd $(ROOT_DIR)/config $(ROOT_DIR)/internal $(ROOT_DIR)/scripts $(ROOT_DIR)/test $(ROOT_DIR)/.github $(ROOT_DIR)/build $(ROOT_DIR)/tools $(ROOT_DIR)/deployments FINDS := find $(CODE_DIRS) # Makefile settings: Select different behaviors by determining whether V option is set diff --git a/scripts/mongo-init.sh b/scripts/mongo-init.sh index 07d0e3d03..41d9ca0aa 100755 --- a/scripts/mongo-init.sh +++ b/scripts/mongo-init.sh @@ -12,15 +12,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -mongo -- "$MONGO_INITDB_DATABASE" <