Merge branch 'main' into add_set_ex_friend

pull/1681/head
AndrewZuo01 2 years ago committed by GitHub
commit 038f0d7d98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -33,7 +33,7 @@ env:
OPEN_IM_SERVER_CLA_DOCUMENT: https://github.com/openim-sigs/cla/blob/main/README.md 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_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: jobs:
CLAAssistant: CLAAssistant:

@ -106,6 +106,16 @@ jobs:
ghcr.io/openimsdk/openim-api ghcr.io/openimsdk/openim-api
openim/openim-api openim/openim-api
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-api
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -127,6 +137,16 @@ jobs:
ghcr.io/openimsdk/openim-cmdutils ghcr.io/openimsdk/openim-cmdutils
openim/openim-cmdutils openim/openim-cmdutils
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-cmdutils
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -148,6 +168,16 @@ jobs:
ghcr.io/openimsdk/openim-crontask ghcr.io/openimsdk/openim-crontask
openim/openim-crontask openim/openim-crontask
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-crontask
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -169,6 +199,16 @@ jobs:
ghcr.io/openimsdk/openim-msggateway ghcr.io/openimsdk/openim-msggateway
openim/openim-msggateway openim/openim-msggateway
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-msggateway
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -190,6 +230,16 @@ jobs:
ghcr.io/openimsdk/openim-msgtransfer ghcr.io/openimsdk/openim-msgtransfer
openim/openim-msgtransfer openim/openim-msgtransfer
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-msgtransfer
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -211,6 +261,16 @@ jobs:
ghcr.io/openimsdk/openim-push ghcr.io/openimsdk/openim-push
openim/openim-push openim/openim-push
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-push
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -232,6 +292,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-auth ghcr.io/openimsdk/openim-rpc-auth
openim/openim-rpc-auth openim/openim-rpc-auth
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-auth
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -253,6 +323,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-conversation ghcr.io/openimsdk/openim-rpc-conversation
openim/openim-rpc-conversation openim/openim-rpc-conversation
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-conversation
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -274,6 +354,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-friend ghcr.io/openimsdk/openim-rpc-friend
openim/openim-rpc-friend openim/openim-rpc-friend
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-friend
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -295,6 +385,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-group ghcr.io/openimsdk/openim-rpc-group
openim/openim-rpc-group openim/openim-rpc-group
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-group
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -316,6 +416,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-msg ghcr.io/openimsdk/openim-rpc-msg
openim/openim-rpc-msg openim/openim-rpc-msg
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-msg
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -337,6 +447,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-third ghcr.io/openimsdk/openim-rpc-third
openim/openim-rpc-third openim/openim-rpc-third
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-third
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5
@ -358,6 +478,16 @@ jobs:
ghcr.io/openimsdk/openim-rpc-user ghcr.io/openimsdk/openim-rpc-user
openim/openim-rpc-user openim/openim-rpc-user
registry.cn-hangzhou.aliyuncs.com/openimsdk/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 - name: Build and push Docker image for openim-rpc-user
uses: docker/build-push-action@v5 uses: docker/build-push-action@v5

@ -92,11 +92,40 @@ jobs:
- name: Exec OpenIM API test - name: Exec OpenIM API test
run: | run: |
sudo make test-api touch /tmp/test.md
echo "# OpenIM Test" >> /tmp/test.md
echo "## OpenIM API Test" >> /tmp/test.md
echo "<details><summary>Command Output for OpenIM API Test</summary>" >> /tmp/test.md
echo "<pre><code>" >> /tmp/test.md
sudo make test-api | tee -a /tmp/test.md
echo "</code></pre>" >> /tmp/test.md
echo "</details>" >> /tmp/test.md
- name: Exec OpenIM E2E Test
run: |
echo "" >> /tmp/test.md
echo "## OpenIM E2E Test" >> /tmp/test.md
echo "<details><summary>Command Output for OpenIM E2E Test</summary>" >> /tmp/test.md
echo "<pre><code>" >> /tmp/test.md
sudo make test-e2e | tee -a /tmp/test.md
echo "</code></pre>" >> /tmp/test.md
echo "</details>" >> /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: Exec OpenIM E2E test - name: Check outputs
run: | run: |
sudo make test-e2e 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 - name: Exec OpenIM System uninstall
run: | run: |

@ -36,21 +36,19 @@ env:
GO_VERSION: "1.19" GO_VERSION: "1.19"
GOLANGCI_VERSION: "v1.50.1" GOLANGCI_VERSION: "v1.50.1"
jobs: jobs:
openim: openim:
name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }} name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions: permissions:
contents: write contents: write
pull-requests: write
environment: environment:
name: openim name: openim
strategy: strategy:
matrix: matrix:
go_version: ["1.19","1.20","1.21"] go_version: ["1.19","1.20","1.21"]
os: [ubuntu-latest] os: [ubuntu-latest]
steps: steps:
- name: Setup - name: Setup
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -111,6 +109,9 @@ jobs:
openim-start: openim-start:
name: Test OpenIM install/start on ${{ matrix.os }} name: Test OpenIM install/start on ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment: environment:
name: openim name: openim
strategy: strategy:
@ -129,9 +130,46 @@ jobs:
run: | run: |
sudo make install 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<<EOF" >> $GITHUB_ENV
sudo docker images >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "containers<<EOF" >> $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: execute-scripts:
name: Execute OpenIM Script On ${{ matrix.os }} name: Execute OpenIM Script On ${{ matrix.os }}
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
permissions:
contents: write
pull-requests: write
environment: environment:
name: openim name: openim
strategy: strategy:
@ -201,10 +239,9 @@ jobs:
- name: Build, Start, Check Services and Print Logs for Ubuntu - name: Build, Start, Check Services and Print Logs for Ubuntu
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: | run: |
sudo make build && \ sudo make build
sudo make start && \ sudo make start
sudo make check || \ sudo make check
(echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
- name: Restart Services and Print Logs for Ubuntu - name: Restart Services and Print Logs for Ubuntu
if: runner.os == 'Linux' if: runner.os == 'Linux'
@ -212,18 +249,17 @@ jobs:
sudo make restart sudo make restart
sudo make check sudo make check
# - name: Build, Start, Check Services and Print Logs for macOS - name: Build, Start, Check Services and Print Logs for macOS
# if: runner.os == 'macOS' if: runner.os == 'macOS'
# run: | run: |
# make init && \ make build
# make build && \
# make start && \
# make check || \
# (echo "An error occurred, printing logs:" && sudo cat ./_output/logs/* 2>/dev/null)
openim-test-build-image: openim-test-build-image:
name: Build OpenIM Docker Image name: Build OpenIM Docker Image
runs-on: ubuntu-latest runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
environment: environment:
name: openim name: openim
steps: steps:
@ -245,3 +281,9 @@ jobs:
run: | run: |
sudo make init sudo make init
sudo make image sudo make image
- name: Get OpenIM Docker Images Status
id: docker_processes
run: |
sudo docker images
sudo docker ps

@ -35,52 +35,62 @@
## Ⓜ️ 关于 OpenIM ## Ⓜ️ 关于 OpenIM
OpenIM 不仅仅是一个开源的即时消息组件,它是你的应用程序生态系统的一个不可或缺的部分。查看下面的图表,了解 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 是如何交互的。 OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通话、通知以及AI聊天机器人等通信功能的服务平台。它通过提供一系列强大的API和Webhooks使开发者可以轻松地在他们的应用中加入这些交互特性。OpenIM 本身并不是一个独立运行的聊天应用,而是作为一个平台,为其他应用提供支持,实现丰富的通信功能。下图展示 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 之间的交互关系来具体说明。
![App-OpenIM 关系](./docs/images/oepnim-design.png) ![App-OpenIM 关系](./docs/images/oepnim-design.png)
## 🚀 关于 OpenIMSDK ## 🚀 关于 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)** 👉 **[探索 GO SDK](https://github.com/openimsdk/openim-sdk-core)**
## 🌐 关于 OpenIMServer ## 🌐 关于 OpenIMServer
精心用 Golang 开发的 **OpenIMServer** 通过多重方式确保了卓越的即时消息服务器能力: + **OpenIMServer** 具有以下特点:
- 🌐 微服务架构:支持集群模式,包括网关(gateway)和多个rpc服务。
+ **模块组成**:它由多个模块组成,例如网关和多个 RPC 服务,提供一个多功能的消息环境。 - 🚀 部署方式多样支持源代码、kubernetes或docker部署。
+ **微服务架构**:支持集群模式,确保出色的性能和可伸缩性,以有效管理各个实例间的通信。 - 海量用户支持:十万超级大群,千万用户,及百亿消息
+ **多样的部署选项**适应你的操作偏好通过源代码、Kubernetes 或 Docker 提供部署选项。
### 增强的业务功能: ### 增强的业务功能:
+ **REST API**OpenIMServer 为业务系统提供 REST API旨在通过后端接口为您的操作提供附加功能如群组创建和消息推送 + **REST API**OpenIMServer 提供了REST API供业务系统使用旨在赋予业务更多功能例如通过后台接口建立群组、发送推送消息等
+ **回调**为了扩展其在各种业务形式中的实用性OpenIMServer 提供了回调能力。即,在事件发生之前或之后,它向业务服务器发送请求,比如发送消息,丰富通信过程中的交互和数据交换流 + **Webhooks**OpenIMServer提供了回调能力以扩展更多的业务形态所谓回调即OpenIMServer会在某一事件发生之前或者之后向业务服务器发送请求如发送消息之前或之后的回调
👉 **[了解更多](https://doc.rentsoft.cn/guides/introduction/product)** 👉 **[了解更多](https://docs.openim.io/guides/introduction/product)**
## :rocket: 快速开始 ## :rocket: 快速开始
你只需要一个简单的命令,就可以快速学习 OpenIM 的工程解决方案 在线体验iOS/Android/H5/PC/Web
``` 👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)**
bashCopy code
$ make demo
```
🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法: 🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法:
+ **[源代码部署指南](https://doc.rentsoft.cn/guides/gettingStarted/imSourceCodeDeployment)** + **[源代码部署指南](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)**
+ **[Docker 部署指南](https://doc.rentsoft.cn/guides/gettingStarted/dockerCompose)** + **[Docker 部署指南](https://docs.openim.io/guides/gettingStarted/dockerCompose)**
+ **[Kubernetes 部署指南](https://github.com/openimsdk/open-im-server/tree/main/deployments)** + **[Kubernetes 部署指南](https://docs.openim.io/guides/gettingStarted/k8s-deployment)**
## :hammer_and_wrench: 开始开发 OpenIM ## :hammer_and_wrench: 开始开发 OpenIM

@ -25,178 +25,56 @@
</p> </p>
## Ⓜ️ About OpenIM ## Ⓜ️ 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) ![App-OpenIM Relationship](./docs/images/oepnim-design.png)
## 🚀 About OpenIMSDK ## 🚀 About OpenIMSDK
**OpenIMSDK** seamlessly integrates into your application, delivering a rich, real-time messaging experience without requiring intricate UI integration. It provides: **OpenIMSDK** is an IM SDK designed for **OpenIMServer**, created specifically for embedding in client applications. Its main features and modules are as follows:
+ **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)**
<!--
## :star2: Why OpenIM
**🔍 Function screenshot display**
<div align="center">
| 💻🔄📱 Multi Terminal Synchronization 🔄🖥️ | 📅⚡ Efficient Meetings 🚀💼 |
| :----------------------------------------------------------: | :---------------------------------------------------------: |
| ![multiple-message](./assets/demo/multi-terminal-synchronization.png) | ![efficient-meetings](./assets/demo/efficient-meetings.png) |
| 📲🔄 **One-to-one and Group Chats** 👥🗣️ | 🎁💻 **Special Features - Custom Messages** ✉️🎨|
| ![group-chat](./assets/demo/group-chat.png) | ![special-function](./assets/demo/special-function.png) |
</div> + 🌟 Main Features:
**OpenIM** offers a powerful and reliable instant messaging platform, ensuring versatile communication across multiple platforms with the following key features: - 📦 Local storage
- 🔔 Listener callbacks
- 🛡️ API wrapping
- 🌐 Connection management
**Versatile Messaging:** Support for text, images, emojis, voice, video, and more, alongside one-on-one and multi-person audio/video calls. ## 📚 Main Modules:
**Robust Chat Capabilities:** Including roles (application administrator, group owner, etc.) and features like muting, group announcements, and dynamic message loading. 1. 🚀 Initialization and Login
2. 👤 User Management
3. 👫 Friend Management
4. 🤖 Group Functions
5. 💬 Conversation Handling
**Unique Interaction Features:** Offering read-and-burn private chats and a message editing function to broaden social scenarios. It is built using Golang and supports cross-platform deployment, ensuring a consistent access experience across all platforms.
**Open Source:** The code of OpenIM is open source and aims to build a leading global IM open source community. [GitHub Repository](https://github.com/OpenIMSDK) 👉 **[Explore GO SDK](https://github.com/openimsdk/openim-sdk-core)**
**Extensibility:** Implemented in Golang, OpenIM introduces an "everything is a message" communication model, simplifying custom messages and feature extension. ## 🌐 About OpenIMServer
**High Performance:** Supports a hierarchical governance architecture tested and abstracts the storage model of various message types. + **OpenIMServer** has the following characteristics:
- 🌐 Microservice architecture: Supports cluster mode, including a gateway and multiple rpc services.
- 🚀 Diverse deployment methods: Supports deployment via source code, Kubernetes, or Docker.
- Support for massive user base: Super large groups with hundreds of thousands of users, tens of millions of users, and billions of messages.
**Full Platform Support:** Native support for iOS, Android, Flutter, uni-app, ReactNative, Electron, and Web. ### Enhanced Business Functionality:
--> + **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 ## :rocket: Quick Start
We support many platforms. Here are the addresses for quick experience on the web side 🤲 To facilitate user experience, we offer various deployment solutions. You can choose your deployment method from the list below:
👉 **[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:
<!--
<details> <summary>Deploying with Docker Compose</summary>
It is recommended to use Docker Compose for deployment, which can easily and quickly deploy the entire OpenIM service on a single node
+ [https://github.com/openimsdk/openim-docker](https://github.com/openimsdk/openim-docker)
> **Note**
>
> If you don't know OpenIM's versioning policy, 📚Read our release policy: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md
</details>
<details> <summary>Compile from Source</summary>
Ur need `Go 1.20` or higher version, and `make`.
```bash
go version && make --version || echo "Error: One of the commands failed."
```
Version Details: https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md
You can get the version number from the command below or from [github releases](https://github.com/openimsdk/open-im-server/tags).
```bash
$ curl --silent "https://api.github.com/repos/openimsdk/open-im-server/releases" | jq -r '.[].tag_name'
```
We have our own version management policy, if you are interested in our version management, I recommend reading [📚 OpenIM Version](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md), We recommend using stable versions such as `v3.3.0` and `v3.2.0` whenever possible. `v3.1.1-alpha.3` as well as `v3.3.0-beta.0` and `v3.2.0-rc.0` are pre-release or beta versions and are not recommended.
Set `OPENIM_VERSION` environment variables for the latest `OPENIM_VERSION` number, or replace the `OPENIM_VERSION` for you to install the OpenIM-Server `OPENIM_VERSION`:
```bash
$ OPENIM_VERSION=`curl -s https://api.github.com/repos/openimsdk/open-im-server/releases/latest | grep -oE '"tag_name": "[^"]+"' | head -n1 | cut -d'"' -f4`
# OPENIM_VERSION=v3.3.0
```
Deploy basic components at the click of a command:
```bash
# install openim dependency
$ git clone https://github.com/openimsdk/open-im-server openim/openim-server && export openim=$(pwd)/openim/openim-server && cd $openim/openim-server && git checkout $OPENIM_VERSION
$ make init && docker compose up -d && make start && make check
```
> `make help` to help you see the instructions supported by OpenIM.
You can use the `make help-all` see OpenIM in action.
</details>
<details> <summary>Component Configuration Instructions</summary>
Read: Configuration center documenthttps://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md
</details>
<details> <summary>Deployed with kubernetes</summary>
+ https://github.com/openimsdk/open-im-server/blob/main/deployments/README.md
</details>
-->
+ **[Source Code Deployment Guide](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** + **[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)** + **[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)**
<!--
## :link: OpenIM and your application
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.
![App-OpenIM Relationship](./docs/images/oepnim-design.png)
## :building_construction: Overall Architecture
Delve into the heart of Open-IM-Server's functionality with our architecture diagram.
![Overall Architecture](./docs/images/Architecture.jpg) -->
## :hammer_and_wrench: To start developing OpenIM ## :hammer_and_wrench: To start developing OpenIM

@ -3,12 +3,36 @@
before: before:
hooks: hooks:
- make clean
# You may remove this if you don't use go modules. # You may remove this if you don't use go modules.
- make tidy - make tidy
- make copyright.add - make copyright.add
# you may remove this if you don't need go generate # you may remove this if you don't need go generate
- go generate ./... - go generate ./...
git:
# What should be used to sort tags when gathering the current and previous
# tags if there are more than one tag in the same commit.
#
# Default: '-version:refname'
tag_sort: -version:creatordate
# What should be used to specify prerelease suffix while sorting tags when gathering
# the current and previous tags if there are more than one tag in the same commit.
#
# Since: v1.17
prerelease_suffix: "-"
# Tags to be ignored by GoReleaser.
# This means that GoReleaser will not pick up tags that match any of the
# provided values as either previous or current tags.
#
# Templates: allowed.
# Since: v1.21.
ignore_tags:
- nightly
# - "{{.Env.IGNORE_TAG}}"
snapshot: snapshot:
name_template: "{{ incpatch .Version }}-next" name_template: "{{ incpatch .Version }}-next"

@ -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 # General Configuration
# This section contains general configuration options for the entire environment. # This section contains general configuration options for the entire environment.
@ -84,19 +98,23 @@ OPENIM_IP=${OPENIM_IP}
# Default: ZOOKEEPER_PORT=12181 # Default: ZOOKEEPER_PORT=12181
ZOOKEEPER_PORT=${ZOOKEEPER_PORT} ZOOKEEPER_PORT=${ZOOKEEPER_PORT}
# Port on which MongoDB service is running. # MongoDB service port configuration.
# Default: MONGO_PORT=37017 # Default: MONGO_PORT=37017
# MONGO_PORT=${MONGO_PORT} # MONGO_PORT=${MONGO_PORT}
# Username to authenticate with the MongoDB service. # Password for MongoDB admin user. Used for service authentication.
# Default: MONGO_USERNAME=root
# MONGO_USERNAME=${MONGO_USERNAME}
# Password to authenticate with the MongoDB service.
# Default: MONGO_PASSWORD=openIM123 # Default: MONGO_PASSWORD=openIM123
MONGO_PASSWORD=${MONGO_PASSWORD} 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 # Default: MONGO_DATABASE=openIM_v3
MONGO_DATABASE=${MONGO_DATABASE} MONGO_DATABASE=${MONGO_DATABASE}
@ -177,7 +195,6 @@ OPENIM_WS_PORT=${OPENIM_WS_PORT}
# Default: API_OPENIM_PORT=10002 # Default: API_OPENIM_PORT=10002
API_OPENIM_PORT=${API_OPENIM_PORT} API_OPENIM_PORT=${API_OPENIM_PORT}
# ====================================== # ======================================
# ========== OpenIM Chat =============== # ========== OpenIM Chat ===============
# ====================================== # ======================================
@ -190,9 +207,9 @@ CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION}
# Default: OPENIM_CHAT_API_PORT=10008 # Default: OPENIM_CHAT_API_PORT=10008
OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT} OPENIM_CHAT_API_PORT=${OPENIM_CHAT_API_PORT}
# Directory path for storing data files or related information for OpenIM chat. # Port for the OpenIM admin API.
# Default: OPENIM_CHAT_DATA_DIR=./openim-chat/main # Default: OPENIM_ADMIN_API_PORT=10009
OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR} OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT}
# ====================================== # ======================================
# ========== OpenIM Admin ============== # ========== OpenIM Admin ==============
@ -202,10 +219,6 @@ OPENIM_CHAT_DATA_DIR=${OPENIM_CHAT_DATA_DIR}
# Default: SERVER_IMAGE_VERSION=main # Default: SERVER_IMAGE_VERSION=main
SERVER_IMAGE_VERSION=${SERVER_IMAGE_VERSION} SERVER_IMAGE_VERSION=${SERVER_IMAGE_VERSION}
# Port for the OpenIM admin API.
# Default: OPENIM_ADMIN_API_PORT=10009
OPENIM_ADMIN_API_PORT=${OPENIM_ADMIN_API_PORT}
# Port for the node exporter. # Port for the node exporter.
# Default: NODE_EXPORTER_PORT=19100 # Default: NODE_EXPORTER_PORT=19100
NODE_EXPORTER_PORT=${NODE_EXPORTER_PORT} NODE_EXPORTER_PORT=${NODE_EXPORTER_PORT}

@ -53,8 +53,8 @@ mongo:
# Maximum connection pool size # Maximum connection pool size
address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ] address: [ ${MONGO_ADDRESS}:${MONGO_PORT} ]
database: ${MONGO_DATABASE} database: ${MONGO_DATABASE}
username: ${MONGO_USERNAME} username: ${MONGO_OPENIM_USERNAME}
password: ${MONGO_PASSWORD} password: ${MONGO_OPENIM_PASSWORD}
maxPoolSize: ${MONGO_MAX_POOL_SIZE} maxPoolSize: ${MONGO_MAX_POOL_SIZE}
###################### Redis configuration information ###################### ###################### Redis configuration information ######################

@ -16,18 +16,20 @@ services:
ports: ports:
- "${MONGO_PORT:-37017}:27017" - "${MONGO_PORT:-37017}:27017"
container_name: mongo 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: volumes:
- "${DATA_DIR:-./}/components/mongodb/data/db:/data/db" - "${DATA_DIR:-./}/components/mongodb/data/db:/data/db"
- "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs" - "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs"
- "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo" - "${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: environment:
- TZ=Asia/Shanghai - TZ=Asia/Shanghai
- wiredTigerCacheSizeGB=1 - wiredTigerCacheSizeGB=1
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root} - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123} - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
- MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openIM_v3} - 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 restart: always
networks: networks:
server: server:
@ -122,9 +124,9 @@ services:
server: server:
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7} 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: # 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 # container_name: openim-admin
# restart: always # restart: always
# ports: # ports:
@ -167,12 +169,6 @@ services:
# hostname: grafana # hostname: grafana
# user: root # user: root
# restart: always # 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: # ports:
# - "${GRAFANA_PORT:-13000}:3000" # - "${GRAFANA_PORT:-13000}:3000"
# volumes: # volumes:

@ -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 CHAT_IMAGE_VERSION=${CHAT_IMAGE_VERSION:-'main'}
readonly SERVER_IMAGE_VERSION=${SERVER_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`). 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 + PASSWORD
+ **Description**: Password for mysql, mongodb, redis, and minio. + **Description**: Password for mongodb, redis, and minio.
+ **Default**: `openIM123` + **Default**: `openIM123`
+ Notes: + Notes:
+ Minimum password length: 8 characters. + Minimum password length: 8 characters.
@ -139,20 +142,22 @@ For convenience, configuration through modifying environment variables is recomm
+ OPENIM_USER + OPENIM_USER
+ **Description**: Username for mysql, mongodb, redis, and minio. + **Description**: Username for redis, and minio.
+ **Default**: `root` + **Default**: `root`
```bash ```bash
export OPENIM_USER="root" export OPENIM_USER="root"
``` ```
+ API_URL > mongo is `openIM`, use `export MONGO_OPENIM_USERNAME="openIM"` to modify
+ OPENIM_IP
+ **Description**: API address. + **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. + **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 ```bash
export API_URL="http://ip:10002" export OPENIM_IP="ip"
``` ```
+ DATA_DIR + 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_PORT | "27017" | Port used by MongoDB. |
| MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. | | MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. |
| MONGO_USERNAME | [User Defined] | Username for MongoDB. | | MONGO_USERNAME | [User Defined] | Admin Username for MongoDB. |
| MONGO_PASSWORD | [User Defined] | Password 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. <a name='TencentCloudCOSConfiguration'></a>Tencent Cloud COS Configuration ### 2.8. <a name='TencentCloudCOSConfiguration'></a>Tencent Cloud COS Configuration

@ -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. 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. 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. 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 ### Multi-Architecture Images

@ -1,6 +1,7 @@
# 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 TOC**](#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) - [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) - [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) - [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) - [Release Management: A Guided Tour](#release-management-a-guided-tour)
- [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs) - [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs)
- [Version Skew Policy](#version-skew-policy) - [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) - [Applying Principles: A Git Workflow Example](#applying-principles-a-git-workflow-example)
- [Release Process](#release-process)
- [Docker Images Version Management](#docker-images-version-management) - [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/). 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 ## 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). 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

@ -4,8 +4,8 @@ go 1.19
require ( require (
firebase.google.com/go v3.13.0+incompatible firebase.google.com/go v3.13.0+incompatible
github.com/OpenIMSDK/protocol v0.0.45 github.com/OpenIMSDK/protocol v0.0.46
github.com/OpenIMSDK/tools v0.0.21 github.com/OpenIMSDK/tools v0.0.23
github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/dtm-labs/rockscache v0.1.1 github.com/dtm-labs/rockscache v0.1.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1

@ -18,10 +18,10 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/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 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
github.com/OpenIMSDK/protocol v0.0.45 h1:0MxdGddw09k17xgR6Kn6ZLtcTrEosTCWXA/rhgSmbQQ= github.com/OpenIMSDK/protocol v0.0.46 h1:LKfwcC3pUcJKSxiIyj82fc479BuDbDtsCrPxa7bHxmo=
github.com/OpenIMSDK/protocol v0.0.45/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/protocol v0.0.46/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/OpenIMSDK/tools v0.0.21 h1:iTapc2mIEVH/xl5Nd6jfwPub11Pgp44tVcE1rjB3a48= github.com/OpenIMSDK/tools v0.0.23 h1:xozfrGzhbpNPlDTap5DLVPk+JfgZ/ZyIj4Cuu3/bm9w=
github.com/OpenIMSDK/tools v0.0.21/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= 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/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 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=

@ -164,6 +164,8 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
data = apistruct.VideoElem{} data = apistruct.VideoElem{}
case constant.File: case constant.File:
data = apistruct.FileElem{} data = apistruct.FileElem{}
case constant.AtText:
data = apistruct.AtElem{}
case constant.Custom: case constant.Custom:
data = apistruct.CustomElem{} data = apistruct.CustomElem{}
case constant.OANotification: 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 { if err = m.userRpcClient.GetNotificationByID(c, req.SendID); err != nil {
return nil, err return nil, err
} }
default: default:
return nil, errs.ErrArgs.WithDetail("not support err contentType") return nil, errs.ErrArgs.WithDetail("not support err contentType")
} }

@ -26,6 +26,7 @@ const (
Compression = "compression" Compression = "compression"
GzipCompressionProtocol = "gzip" GzipCompressionProtocol = "gzip"
BackgroundStatus = "isBackground" BackgroundStatus = "isBackground"
MsgResp = "isMsgResp"
) )
const ( const (

@ -16,7 +16,10 @@ package msggateway
import ( import (
"context" "context"
"encoding/json"
"errors" "errors"
"fmt"
"github.com/OpenIMSDK/tools/apiresp"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
@ -342,11 +345,7 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
if !clientOK { if !clientOK {
return return
} }
ws.clients.deleteClients(newClient.UserID, oldClients)
isDeleteUser := ws.clients.deleteClients(newClient.UserID, oldClients)
if isDeleteUser {
ws.onlineUserNum.Add(-1)
}
for _, c := range oldClients { for _, c := range oldClients {
err := c.KickOnlineMessage() err := c.KickOnlineMessage()
if err != nil { if err != nil {
@ -422,84 +421,102 @@ func (ws *WsServer) unregisterClient(client *Client) {
) )
} }
func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) { func (ws *WsServer) ParseWSArgs(r *http.Request) (args *WSArgs, err error) {
connContext := newContext(w, r) var v WSArgs
defer func() {
args = &v
}()
query := r.URL.Query()
v.MsgResp, _ = strconv.ParseBool(query.Get(MsgResp))
if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum { if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
httpError(connContext, errs.ErrConnOverMaxNumLimit) return nil, errs.ErrConnOverMaxNumLimit.Wrap("over max conn num limit")
return
} }
var ( if v.Token = query.Get(Token); v.Token == "" {
token string return nil, errs.ErrConnArgsErr.Wrap("token is empty")
userID string
platformIDStr string
exists bool
compression bool
)
token, exists = connContext.Query(Token)
if !exists {
httpError(connContext, errs.ErrConnArgsErr)
return
} }
userID, exists = connContext.Query(WsUserID) if v.UserID = query.Get(WsUserID); v.UserID == "" {
if !exists { return nil, errs.ErrConnArgsErr.Wrap("sendID is empty")
httpError(connContext, errs.ErrConnArgsErr)
return
} }
platformIDStr, exists = connContext.Query(PlatformID) platformIDStr := query.Get(PlatformID)
if !exists { if platformIDStr == "" {
httpError(connContext, errs.ErrConnArgsErr) return nil, errs.ErrConnArgsErr.Wrap("platformID is empty")
return
} }
platformID, err := strconv.Atoi(platformIDStr) platformID, err := strconv.Atoi(platformIDStr)
if err != nil { if err != nil {
httpError(connContext, errs.ErrConnArgsErr) return nil, errs.ErrConnArgsErr.Wrap("platformID is not int")
return
} }
if err = authverify.WsVerifyToken(token, userID, platformID); err != nil { v.PlatformID = platformID
httpError(connContext, err) if err = authverify.WsVerifyToken(v.Token, v.UserID, platformID); err != nil {
return return nil, err
}
if query.Get(Compression) == GzipCompressionProtocol {
v.Compression = true
} }
m, err := ws.cache.GetTokensWithoutError(context.Background(), userID, platformID) if r.Header.Get(Compression) == GzipCompressionProtocol {
v.Compression = true
}
m, err := ws.cache.GetTokensWithoutError(context.Background(), v.UserID, platformID)
if err != nil { if err != nil {
httpError(connContext, err) return nil, err
return
} }
if v, ok := m[token]; ok { if v, ok := m[v.Token]; ok {
switch v { switch v {
case constant.NormalToken: case constant.NormalToken:
case constant.KickedToken: case constant.KickedToken:
httpError(connContext, errs.ErrTokenKicked.Wrap()) return nil, errs.ErrTokenKicked.Wrap()
return
default: default:
httpError(connContext, errs.ErrTokenUnknown.Wrap()) return nil, errs.ErrTokenUnknown.Wrap(fmt.Sprintf("token status is %d", v))
return
} }
} else { } else {
httpError(connContext, errs.ErrTokenNotExist.Wrap()) return nil, errs.ErrTokenNotExist.Wrap()
return }
return &v, nil
} }
wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize) type WSArgs struct {
err = wsLongConn.GenerateLongConn(w, r) Token string
if err != nil { UserID string
PlatformID int
Compression bool
MsgResp bool
}
func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
connContext := newContext(w, r)
args, pErr := ws.ParseWSArgs(r)
var wsLongConn *GWebSocket
if args.MsgResp {
wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
if err := wsLongConn.GenerateLongConn(w, r); err != nil {
httpError(connContext, err) httpError(connContext, err)
return return
} }
compressProtoc, exists := connContext.Query(Compression) data, err := json.Marshal(apiresp.ParseError(pErr))
if exists { if err != nil {
if compressProtoc == GzipCompressionProtocol { _ = wsLongConn.Close()
compression = true return
}
if err := wsLongConn.WriteMessage(MessageText, data); err != nil {
_ = wsLongConn.Close()
return
}
if pErr != nil {
_ = wsLongConn.Close()
return
} }
} else {
if pErr != nil {
httpError(connContext, pErr)
return
} }
compressProtoc, exists = connContext.GetHeader(Compression) wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
if exists { if err := wsLongConn.GenerateLongConn(w, r); err != nil {
if compressProtoc == GzipCompressionProtocol { httpError(connContext, err)
compression = true return
} }
} }
client := ws.clientPool.Get().(*Client) client := ws.clientPool.Get().(*Client)
client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), compression, ws, token) client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), args.Compression, ws, args.Token)
ws.registerChan <- client ws.registerChan <- client
go client.readMessage() go client.readMessage()
} }

@ -476,13 +476,42 @@ func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbgroup.GetGro
func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) { func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) {
resp := &pbgroup.GetGroupMemberListResp{} 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 { if err != nil {
return nil, err return nil, err
} }
if err := s.PopulateGroupMember(ctx, members...); err != nil { if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err 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.Total = uint32(total)
resp.Members = utils.Batch(convert.Db2PbGroupMember, members) resp.Members = utils.Batch(convert.Db2PbGroupMember, members)
return resp, nil return resp, nil
@ -1042,20 +1071,29 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans
func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) (*pbgroup.GetGroupsResp, error) { func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) (*pbgroup.GetGroupsResp, error) {
resp := &pbgroup.GetGroupsResp{} resp := &pbgroup.GetGroupsResp{}
var ( var (
groups []*relationtb.GroupModel group []*relationtb.GroupModel
err error err error
) )
if req.GroupID != "" { if req.GroupID != "" {
groups, err = s.db.FindGroup(ctx, []string{req.GroupID}) group, err = s.db.FindGroup(ctx, []string{req.GroupID})
resp.Total = uint32(len(groups)) resp.Total = uint32(len(group))
} else { } else {
var total int64 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) resp.Total = uint32(total)
} }
if err != nil { if err != nil {
return nil, err 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 { groupIDs := utils.Slice(groups, func(e *relationtb.GroupModel) string {
return e.GroupID return e.GroupID
}) })

@ -17,6 +17,7 @@ package user
import ( import (
"context" "context"
"errors" "errors"
"github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"math/rand" "math/rand"
"strings" "strings"
@ -58,6 +59,11 @@ type userServer struct {
RegisterCenter registry.SvcDiscoveryRegistry RegisterCenter registry.SvcDiscoveryRegistry
} }
func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) {
//TODO implement me
panic("implement me")
}
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
rdb, err := cache.NewRedis() rdb, err := cache.NewRedis()
if err != nil { if err != nil {
@ -228,7 +234,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) { 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 { if err != nil {
return nil, err return nil, err
} }
@ -384,6 +390,7 @@ func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.Proc
return nil, err return nil, err
} }
var value string var value string
if req.Value != nil { if req.Value != nil {
value = req.Value.Value value = req.Value.Value
@ -516,22 +523,23 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
return nil, err return nil, err
} }
var userID string if req.UserID == "" {
for i := 0; i < 20; i++ { for i := 0; i < 20; i++ {
userId := s.genUserID() userId := s.genUserID()
_, err := s.UserDatabase.FindWithError(ctx, []string{userId}) _, err := s.UserDatabase.FindWithError(ctx, []string{userId})
if err == nil { if err == nil {
continue continue
} }
userID = userId req.UserID = userId
break break
} }
if userID == "" { if req.UserID == "" {
return nil, errs.ErrInternalServer.Wrap("gen user id failed") return nil, errs.ErrInternalServer.Wrap("gen user id failed")
} }
}
user := &tablerelation.UserModel{ user := &tablerelation.UserModel{
UserID: userID, UserID: req.UserID,
Nickname: req.NickName, Nickname: req.NickName,
FaceURL: req.FaceURL, FaceURL: req.FaceURL,
CreateTime: time.Now(), CreateTime: time.Now(),
@ -541,7 +549,11 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
return nil, err 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) { func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) {
@ -575,30 +587,33 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
return nil, err return nil, err
} }
if req.NickName != "" { var users []*relation.UserModel
users, err := s.UserDatabase.FindByNickname(ctx, req.NickName) var err error
if req.Keyword != "" {
users, err = s.UserDatabase.Find(ctx, []string{req.Keyword})
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp := s.userModelToResp(users) resp := s.userModelToResp(users, req.Pagination)
if resp.Total != 0 {
return resp, nil return resp, nil
} }
users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword)
if req.UserID != "" {
users, err := s.UserDatabase.Find(ctx, []string{req.UserID})
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp := s.userModelToResp(users) resp = s.userModelToResp(users, req.Pagination)
return resp, nil
return resp, nil return resp, nil
} }
_, users, err := s.UserDatabase.Page(ctx, req.Pagination) users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
if err != nil { if err != nil {
return nil, err return nil, err
} }
resp := s.userModelToResp(users) resp := s.userModelToResp(users, req.Pagination)
return resp, nil return resp, nil
} }
@ -632,11 +647,11 @@ func (s *userServer) genUserID() string {
return string(data) 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) accounts := make([]*pbuser.NotificationAccountInfo, 0)
var total int64 var total int64
for _, v := range users { for _, v := range users {
if v.AppMangerLevel == constant.AppNotificationAdmin || v.AppMangerLevel == constant.AppAdmin { if v.AppMangerLevel == constant.AppNotificationAdmin && !utils.IsContain(v.UserID, config.Config.IMAdmin.UserID) {
temp := &pbuser.NotificationAccountInfo{ temp := &pbuser.NotificationAccountInfo{
UserID: v.UserID, UserID: v.UserID,
FaceURL: v.FaceURL, FaceURL: v.FaceURL,
@ -646,5 +661,8 @@ func (s *userServer) userModelToResp(users []*relation.UserModel) *pbuser.Search
total += 1 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}
} }

@ -38,6 +38,9 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
if utils.IsContain(opUserID, config.Config.Manager.UserID) { if utils.IsContain(opUserID, config.Config.Manager.UserID) {
return nil return nil
} }
if utils.IsContain(opUserID, config.Config.IMAdmin.UserID) {
return nil
}
if opUserID == ownerUserID { if opUserID == ownerUserID {
return nil return nil
} }
@ -45,13 +48,16 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
} }
func IsAppManagerUid(ctx context.Context) bool { func IsAppManagerUid(ctx context.Context) bool {
return utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) return utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID)
} }
func CheckAdmin(ctx context.Context) error { func CheckAdmin(ctx context.Context) error {
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) { if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) {
return nil return nil
} }
if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) {
return nil
}
return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx)))
} }
func CheckIMAdmin(ctx context.Context) error { func CheckIMAdmin(ctx context.Context) error {
@ -69,7 +75,7 @@ func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) {
} }
func IsManagerUserID(opUserID string) bool { func IsManagerUserID(opUserID string) bool {
return utils.IsContain(opUserID, config.Config.Manager.UserID) return utils.IsContain(opUserID, config.Config.Manager.UserID) || utils.IsContain(opUserID, config.Config.IMAdmin.UserID)
} }
func WsVerifyToken(token, userID string, platformID int) error { func WsVerifyToken(token, userID string, platformID int) error {

@ -279,7 +279,7 @@ func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context,
for _, v := range existConversationUserIDs { for _, v := range existConversationUserIDs {
cache = cache.DelConversations(v, conversationID) cache = cache.DelConversations(v, conversationID)
} }
return c.cache.ExecDel(ctx) return cache.ExecDel(ctx)
}) })
} }

@ -197,7 +197,7 @@ func (g *groupDatabase) UpdateGroup(ctx context.Context, groupID string, data ma
func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, deleteMember bool) error { func (g *groupDatabase) DismissGroup(ctx context.Context, groupID string, deleteMember bool) error {
return g.ctxTx.Transaction(ctx, func(ctx context.Context) error { return g.ctxTx.Transaction(ctx, func(ctx context.Context) error {
c := g.cache.NewCache() c := g.cache.NewCache()
if err := g.groupDB.UpdateState(ctx, groupID, constant.GroupStatusDismissed); err != nil { if err := g.groupDB.UpdateStatus(ctx, groupID, constant.GroupStatusDismissed); err != nil {
return err return err
} }
if deleteMember { if deleteMember {

@ -40,6 +40,8 @@ type UserDatabase interface {
Find(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error) Find(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error)
// Find userInfo By Nickname // Find userInfo By Nickname
FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error)
// Find notificationAccounts
FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error)
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db // Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db
Create(ctx context.Context, users []*relation.UserModel) (err error) Create(ctx context.Context, users []*relation.UserModel) (err error)
// Update update (non-zero value) external guarantee userID exists // Update update (non-zero value) external guarantee userID exists
@ -48,6 +50,8 @@ type UserDatabase interface {
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
// Page If not found, no error is returned // Page If not found, no error is returned
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) 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 true as long as one exists
IsExist(ctx context.Context, userIDs []string) (exist bool, err error) IsExist(ctx context.Context, userIDs []string) (exist bool, err error)
// GetAllUserID Get all user IDs // GetAllUserID Get all user IDs
@ -141,6 +145,11 @@ func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (use
return u.userDB.TakeByNickname(ctx, nickname) return u.userDB.TakeByNickname(ctx, nickname)
} }
// Find notificationAccouts
func (u *userDatabase) FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error) {
return u.userDB.TakeNotification(ctx, level)
}
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db. // Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db.
func (u *userDatabase) Create(ctx context.Context, users []*relation.UserModel) (err error) { func (u *userDatabase) Create(ctx context.Context, users []*relation.UserModel) (err error) {
return u.tx.Transaction(ctx, func(ctx context.Context) error { return u.tx.Transaction(ctx, func(ctx context.Context) error {
@ -176,6 +185,10 @@ func (u *userDatabase) Page(ctx context.Context, pagination pagination.Paginatio
return u.userDB.Page(ctx, pagination) 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. // 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) { func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) {
users, err := u.userDB.Find(ctx, userIDs) users, err := u.userDB.Find(ctx, userIDs)

@ -49,8 +49,8 @@ func (g *GroupMgo) Create(ctx context.Context, groups []*relation.GroupModel) (e
return mgoutil.InsertMany(ctx, g.coll, groups) return mgoutil.InsertMany(ctx, g.coll, groups)
} }
func (g *GroupMgo) UpdateState(ctx context.Context, groupID string, state int32) (err error) { func (g *GroupMgo) UpdateStatus(ctx context.Context, groupID string, status int32) (err error) {
return g.UpdateMap(ctx, groupID, map[string]any{"state": state}) return g.UpdateMap(ctx, groupID, map[string]any{"status": status})
} }
func (g *GroupMgo) UpdateMap(ctx context.Context, groupID string, args map[string]any) (err error) { func (g *GroupMgo) UpdateMap(ctx context.Context, groupID string, args map[string]any) (err error) {

@ -51,7 +51,11 @@ func (g *GroupMemberMgo) Create(ctx context.Context, groupMembers []*relation.Gr
} }
func (g *GroupMemberMgo) Delete(ctx context.Context, groupID string, userIDs []string) (err error) { func (g *GroupMemberMgo) Delete(ctx context.Context, groupID string, userIDs []string) (err error) {
return mgoutil.DeleteMany(ctx, g.coll, bson.M{"group_id": groupID, "user_id": bson.M{"$in": userIDs}}) filter := bson.M{"group_id": groupID}
if len(userIDs) > 0 {
filter["user_id"] = bson.M{"$in": userIDs}
}
return mgoutil.DeleteMany(ctx, g.coll, filter)
} }
func (g *GroupMemberMgo) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) error { func (g *GroupMemberMgo) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) error {
@ -84,8 +88,8 @@ func (g *GroupMemberMgo) FindRoleLevelUserIDs(ctx context.Context, groupID strin
} }
func (g *GroupMemberMgo) SearchMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (total int64, groupList []*relation.GroupMemberModel, err error) { func (g *GroupMemberMgo) SearchMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (total int64, groupList []*relation.GroupMemberModel, err error) {
//TODO implement me filter := bson.M{"group_id": groupID, "nickname": bson.M{"$regex": keyword}}
panic("implement me") return mgoutil.FindPage[*relation.GroupMemberModel](ctx, g.coll, filter, pagination)
} }
func (g *GroupMemberMgo) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) { func (g *GroupMemberMgo) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {

@ -66,6 +66,10 @@ func (u *UserMgo) Take(ctx context.Context, userID string) (user *relation.UserM
return mgoutil.FindOne[*relation.UserModel](ctx, u.coll, bson.M{"user_id": userID}) return mgoutil.FindOne[*relation.UserModel](ctx, u.coll, bson.M{"user_id": userID})
} }
func (u *UserMgo) TakeNotification(ctx context.Context, level int64) (user []*relation.UserModel, err error) {
return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"app_manger_level": level})
}
func (u *UserMgo) TakeByNickname(ctx context.Context, nickname string) (user []*relation.UserModel, err error) { func (u *UserMgo) TakeByNickname(ctx context.Context, nickname string) (user []*relation.UserModel, err error) {
return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"nickname": nickname}) return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"nickname": nickname})
} }
@ -74,6 +78,10 @@ func (u *UserMgo) Page(ctx context.Context, pagination pagination.Pagination) (c
return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{}, pagination) 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) { 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{"user_id": 1}))
} }

@ -42,7 +42,7 @@ type GroupModel struct {
type GroupModelInterface interface { type GroupModelInterface interface {
Create(ctx context.Context, groups []*GroupModel) (err error) Create(ctx context.Context, groups []*GroupModel) (err error)
UpdateMap(ctx context.Context, groupID string, args map[string]any) (err error) UpdateMap(ctx context.Context, groupID string, args map[string]any) (err error)
UpdateState(ctx context.Context, groupID string, state int32) (err error) UpdateStatus(ctx context.Context, groupID string, status int32) (err error)
Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error) Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error)
Take(ctx context.Context, groupID string) (group *GroupModel, err error) Take(ctx context.Context, groupID string) (group *GroupModel, err error)
Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*GroupModel, err error) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*GroupModel, err error)

@ -53,8 +53,10 @@ type UserModelInterface interface {
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
Find(ctx context.Context, userIDs []string) (users []*UserModel, err error) Find(ctx context.Context, userIDs []string) (users []*UserModel, err error)
Take(ctx context.Context, userID string) (user *UserModel, err error) Take(ctx context.Context, userID string) (user *UserModel, err error)
TakeNotification(ctx context.Context, level int64) (user []*UserModel, err error)
TakeByNickname(ctx context.Context, nickname string) (user []*UserModel, err error) TakeByNickname(ctx context.Context, nickname string) (user []*UserModel, err error)
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*UserModel, err error) 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) Exist(ctx context.Context, userID string) (exist bool, err error)
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, 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) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error)

@ -78,8 +78,8 @@ func buildMongoURI() string {
return config.Config.Mongo.Uri return config.Config.Mongo.Uri
} }
username := os.Getenv("MONGO_USERNAME") username := os.Getenv("MONGO_OPENIM_USERNAME")
password := os.Getenv("MONGO_PASSWORD") password := os.Getenv("MONGO_OPENIM_PASSWORD")
address := os.Getenv("MONGO_ADDRESS") address := os.Getenv("MONGO_ADDRESS")
port := os.Getenv("MONGO_PORT") port := os.Getenv("MONGO_PORT")
database := os.Getenv("MONGO_DATABASE") database := os.Getenv("MONGO_DATABASE")
@ -103,9 +103,9 @@ func buildMongoURI() string {
maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize) maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize)
} }
uriFormat := "mongodb://%s/%s?maxPoolSize=%s&authSource=admin" uriFormat := "mongodb://%s/%s?maxPoolSize=%s"
if username != "" && password != "" { 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, username, password, address, database, maxPoolSize)
} }
return fmt.Sprintf(uriFormat, address, database, maxPoolSize) return fmt.Sprintf(uriFormat, address, database, maxPoolSize)

@ -52,6 +52,7 @@ func GetChatConversationIDByMsg(msg *sdkws.MsgData) string {
case constant.NotificationChatType: case constant.NotificationChatType:
return "sn_" + msg.SendID + "_" + msg.RecvID return "sn_" + msg.SendID + "_" + msg.RecvID
} }
return "" return ""
} }

@ -37,6 +37,7 @@ func NewOptions(opts ...OptionsOpt) Options {
for _, opt := range opts { for _, opt := range opts {
opt(options) opt(options)
} }
return options return options
} }

@ -64,6 +64,9 @@ func NewUserRpcClient(client discoveryregistry.SvcDiscoveryRegistry) UserRpcClie
// GetUsersInfo retrieves information for multiple users based on their user IDs. // GetUsersInfo retrieves information for multiple users based on their user IDs.
func (u *UserRpcClient) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) { func (u *UserRpcClient) GetUsersInfo(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error) {
if len(userIDs) == 0 {
return []*sdkws.UserInfo{}, nil
}
resp, err := u.Client.GetDesignateUsers(ctx, &user.GetDesignateUsersReq{ resp, err := u.Client.GetDesignateUsers(ctx, &user.GetDesignateUsersReq{
UserIDs: userIDs, UserIDs: userIDs,
}) })

@ -29,16 +29,8 @@ if [ $# -ne 2 ];then
exit 1 exit 1
fi fi
if [ -z "${OPENIM_IP}" ]; then
openim::util::require-dig 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."
fi fi
source "${env_file}" source "${env_file}"

@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/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"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with 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 # See the License for the specific language governing permissions and
# limitations under the License. # 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 errexit
set -o nounset set -o nounset
set -o pipefail set -o pipefail
@ -28,24 +40,45 @@ chmod +x "${OPENIM_ROOT}"/scripts/*.sh
openim::util::ensure_docker_daemon_connectivity 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= DOCKER_COMPOSE_COMMAND=
# Check if docker-compose command is available # Check if docker-compose command is available
openim::util::check_docker_and_compose_versions 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" openim::log::info "docker compose command is available"
DOCKER_COMPOSE_COMMAND="docker compose" DOCKER_COMPOSE_COMMAND="docker compose"
else else
DOCKER_COMPOSE_COMMAND="docker-compose" DOCKER_COMPOSE_COMMAND="docker-compose"
fi fi
export SERVER_IMAGE_VERSION
export IMAGE_REGISTRY
"${OPENIM_ROOT}"/scripts/init-config.sh "${OPENIM_ROOT}"/scripts/init-config.sh
pushd "${OPENIM_ROOT}" pushd "${OPENIM_ROOT}"
docker build -t "${IMAGE_REGISTRY}/openim-server:${SERVER_IMAGE_VERSION}" .
${DOCKER_COMPOSE_COMMAND} stop ${DOCKER_COMPOSE_COMMAND} stop
curl https://gitee.com/openimsdk/openim-docker/raw/main/example/full-openim-server-and-chat.yml -o docker-compose.yml curl "${DOCKER_COMPOSE_FILE_URL}" -o docker-compose.yml
${DOCKER_COMPOSE_COMMAND} up -d ${DOCKER_COMPOSE_COMMAND} up -d
sleep 60
# 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
${DOCKER_COMPOSE_COMMAND} logs openim-server ${DOCKER_COMPOSE_COMMAND} logs openim-server
${DOCKER_COMPOSE_COMMAND} ps ${DOCKER_COMPOSE_COMMAND} ps

@ -35,8 +35,8 @@ docker run -d \
-e MONGO_INITDB_ROOT_USERNAME=${OPENIM_USER} \ -e MONGO_INITDB_ROOT_USERNAME=${OPENIM_USER} \
-e MONGO_INITDB_ROOT_PASSWORD=${PASSWORD} \ -e MONGO_INITDB_ROOT_PASSWORD=${PASSWORD} \
-e MONGO_INITDB_DATABASE=openIM \ -e MONGO_INITDB_DATABASE=openIM \
-e MONGO_USERNAME=${OPENIM_USER} \ -e MONGO_OPENIM_USERNAME=${OPENIM_USER} \
-e MONGO_PASSWORD=${PASSWORD} \ -e MONGO_OPENIM_PASSWORD=${PASSWORD} \
--restart always \ --restart always \
mongo:6.0.2 --wiredTigerCacheSizeGB 1 --auth mongo:6.0.2 --wiredTigerCacheSizeGB 1 --auth

@ -171,9 +171,14 @@ def "MONGO_URI" # MongoDB的URI
def "MONGO_PORT" "37017" # MongoDB的端口 def "MONGO_PORT" "37017" # MongoDB的端口
def "MONGO_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # MongoDB的地址 def "MONGO_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # MongoDB的地址
def "MONGO_DATABASE" "${DATABASE_NAME}" # MongoDB的数据库名 def "MONGO_DATABASE" "${DATABASE_NAME}" # MongoDB的数据库名
def "MONGO_USERNAME" "${OPENIM_USER}" # MongoDB的用户名 def "MONGO_USERNAME" "root" # MongoDB的管理员身份用户名
# MongoDB的密码 # MongoDB的管理员身份密码
readonly MONGO_PASSWORD=${MONGO_PASSWORD:-"${PASSWORD}"} 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" # 最大连接池大小 def "MONGO_MAX_POOL_SIZE" "100" # 最大连接池大小
###################### Object 配置信息 ###################### ###################### Object 配置信息 ######################

@ -1146,8 +1146,13 @@ function openim::util::require-jq {
# Checks whether dig is installed and provides installation instructions if it is not. # Checks whether dig is installed and provides installation instructions if it is not.
function openim::util::require-dig { function openim::util::require-dig {
if ! command -v dig &>/dev/null; then if ! command -v dig &>/dev/null; then
openim::log::error "dig command not found." openim::log::error "Please install 'dig' to use this feature."
return 1 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 fi
return 0 return 0
} }

@ -129,7 +129,7 @@ FIND := find . ! -path './utils/*' ! -path './vendor/*' ! -path './third_party/*
XARGS := xargs -r --no-run-if-empty XARGS := xargs -r --no-run-if-empty
# Linux command settings-CODE DIRS Copyright # 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) FINDS := find $(CODE_DIRS)
# Makefile settings: Select different behaviors by determining whether V option is set # Makefile settings: Select different behaviors by determining whether V option is set

@ -12,15 +12,19 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
mongo -- "$MONGO_INITDB_DATABASE" <<EOF set -e
db = db.getSiblingDB('admin')
mongosh <<EOF
use admin
db.auth('$MONGO_INITDB_ROOT_USERNAME', '$MONGO_INITDB_ROOT_PASSWORD') db.auth('$MONGO_INITDB_ROOT_USERNAME', '$MONGO_INITDB_ROOT_PASSWORD')
db = db.getSiblingDB('$MONGO_INITDB_DATABASE') db = db.getSiblingDB('$MONGO_INITDB_DATABASE')
db.createUser({ db.createUser({
user: "$MONGO_USERNAME", user: "$MONGO_OPENIM_USERNAME",
pwd: "$MONGO_PASSWORD", pwd: "$MONGO_OPENIM_PASSWORD",
roles: [ roles: [
{ role: 'root', db: '$MONGO_INITDB_DATABASE' } // Assign appropriate roles here
{ role: 'readWrite', db: '$MONGO_INITDB_DATABASE' }
] ]
}) });
EOF EOF

@ -25,10 +25,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/minio/minio-go/v7"
"github.com/redis/go-redis/v9"
"gopkg.in/yaml.v3"
"github.com/IBM/sarama" "github.com/IBM/sarama"
"github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/errs"
"github.com/go-zookeeper/zk" "github.com/go-zookeeper/zk"
@ -38,6 +34,9 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/credentials"
"github.com/minio/minio-go/v7"
"github.com/redis/go-redis/v9"
"gopkg.in/yaml.v3"
) )
const ( const (
@ -172,10 +171,10 @@ func buildMongoURI() string {
mongodbHosts := strings.Join(config.Config.Mongo.Address, ",") mongodbHosts := strings.Join(config.Config.Mongo.Address, ",")
if username != "" && password != "" { if username != "" && password != "" {
return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d&authSource=admin", return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d",
username, password, mongodbHosts, database, maxPoolSize) username, password, mongodbHosts, database, maxPoolSize)
} }
return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d&authSource=admin", return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d",
mongodbHosts, database, maxPoolSize) mongodbHosts, database, maxPoolSize)
} }

Loading…
Cancel
Save