Compare commits
113 Commits
v3.5.1-alp
...
optimizati
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24a0479761 | ||
|
|
b2ca1366f8 | ||
|
|
1a14191d66 | ||
|
|
05e66e9f8d | ||
|
|
9527278239 | ||
|
|
d356f7a035 | ||
|
|
9d313cb308 | ||
|
|
38e11dc678 | ||
|
|
b940bcaafe | ||
|
|
efa74a95a7 | ||
|
|
119a2c2247 | ||
|
|
4266fed44f | ||
|
|
c81e199698 | ||
|
|
9660556f35 | ||
|
|
c7067bc48a | ||
|
|
84f97a72f4 | ||
|
|
fa55c44113 | ||
|
|
e182d5e7df | ||
|
|
7155d1acb7 | ||
|
|
907104701a | ||
|
|
19f5c94d25 | ||
|
|
5a81a7a560 | ||
|
|
0ee68e57b9 | ||
|
|
59529718bb | ||
|
|
e3d78de887 | ||
|
|
e3ff304394 | ||
|
|
899f1f15a4 | ||
|
|
33094ec532 | ||
|
|
e26a582836 | ||
|
|
77894b104e | ||
|
|
c16f17582a | ||
|
|
2a5129b1ae | ||
|
|
445db67568 | ||
|
|
30ef247a59 | ||
|
|
025eb44113 | ||
|
|
af5e191144 | ||
|
|
4a042489bf | ||
|
|
9e4a7d082d | ||
|
|
000f6ce595 | ||
|
|
f53dd855aa | ||
|
|
f1506e40c4 | ||
|
|
b9eb642616 | ||
|
|
1c93f87e95 | ||
|
|
080ddb82cd | ||
|
|
b2eae0999c | ||
|
|
1afdd0bcf7 | ||
|
|
45859d9cab | ||
|
|
d26a1b2090 | ||
|
|
1e81d29e86 | ||
|
|
6e1f96a720 | ||
|
|
656bfef069 | ||
|
|
64d6b04320 | ||
|
|
1db6e3b389 | ||
|
|
32221da613 | ||
|
|
ad5e4e1540 | ||
|
|
c9193302a8 | ||
|
|
640c363eee | ||
|
|
abbb701192 | ||
|
|
b87e366d74 | ||
|
|
3e30e50a09 | ||
|
|
41d2c7be7a | ||
|
|
166d51ab23 | ||
|
|
98d3646167 | ||
|
|
fdf055d328 | ||
|
|
a24eedb66d | ||
|
|
0375361294 | ||
|
|
955520471f | ||
|
|
749eb11830 | ||
|
|
c795304df1 | ||
|
|
a81bc3fc23 | ||
|
|
9908e2c658 | ||
|
|
45064ae5ca | ||
|
|
1f1ab65375 | ||
|
|
bf46412c5a | ||
|
|
48ff03f854 | ||
|
|
a00d77a705 | ||
|
|
ad979d493d | ||
|
|
f5f2cf4746 | ||
|
|
4c3c4555a3 | ||
|
|
04c77bff7e | ||
|
|
006e4a1e93 | ||
|
|
8ff8861603 | ||
|
|
2d21ab3aeb | ||
|
|
a84ad774f7 | ||
|
|
ee9c9eb97c | ||
|
|
da2c0d11f5 | ||
|
|
7a67beb7a1 | ||
|
|
28292be37d | ||
|
|
1ea5fe04f1 | ||
|
|
c16cd0bf7c | ||
|
|
03e15766ab | ||
|
|
f0e5c587ad | ||
|
|
b0ec48d35d | ||
|
|
8360c72300 | ||
|
|
18ad656087 | ||
|
|
1c72e46cc9 | ||
|
|
22c00c411e | ||
|
|
a2a082f681 | ||
|
|
f0075265d5 | ||
|
|
86aebfc0bd | ||
|
|
bbb5ef5ccc | ||
|
|
535ae19f7a | ||
|
|
3ff588896f | ||
|
|
d91a665891 | ||
|
|
0248fbb47d | ||
|
|
938622b1fe | ||
|
|
3d77c1c8cf | ||
|
|
1ae523dcc2 | ||
|
|
d9921248a7 | ||
|
|
ceb950a57f | ||
|
|
f27b1e43f5 | ||
|
|
c241f5bea1 | ||
|
|
8aac6c6f81 |
140
.devcontainer/README.md
Normal file
140
.devcontainer/README.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# OpenIM - OSS Development Container
|
||||
|
||||
[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/openimsdk/open-im-server)
|
||||
|
||||
This repository includes configuration for a development container for working with OpenIM - OSS in a local container or using [GitHub Codespaces](https://github.com/features/codespaces).
|
||||
|
||||
> **Tip:** The default VNC password is `openIM123`. The VNC server runs on port `5901` and a web client is available on port `11001`, openim-admin on port `11002`.
|
||||
|
||||
## Quick start - local
|
||||
|
||||
If you already have VS Code and Docker installed, you can click the badge above or [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/openimsdk/open-im-server) to get started. Clicking these links will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use.
|
||||
|
||||
1. Install Docker Desktop or Docker for Linux on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.)
|
||||
|
||||
2. **Important**: Docker needs at least **4 Cores and 8 GB of RAM** to run a full build with **9 GB of RAM** being recommended. If you are on macOS, or are using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item and going to **Preferences/Settings > Resources > Advanced**.
|
||||
|
||||
> **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar.
|
||||
|
||||
3. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Dev Containers](https://aka.ms/vscode-remote/download/containers) extension.
|
||||
|
||||

|
||||
|
||||
> **Note:** The Dev Containers extension requires the Visual Studio Code distribution of OpenIM - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details.
|
||||
|
||||
4. Press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd> and select **Dev Containers: Clone Repository in Container Volume...**.
|
||||
|
||||
> **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or when using the Hyper-V engine on Windows. We recommend using the WSL filesystem on Windows or the "clone repository in container" approach on Windows and macOS instead since it uses "named volume" rather than the local filesystem.
|
||||
|
||||
5. Type `https://github.com/openimsdk/open-im-server` (or a branch or PR URL) in the input box and press <kbd>Enter</kbd>.
|
||||
|
||||
6. After the container is running:
|
||||
1. If you have the `DISPLAY` or `WAYLAND_DISPLAY` environment variables set locally (or in WSL on Windows), desktop apps in the container will be shown in local windows.
|
||||
2. If these are not set, open a web browser and go to [http://localhost:11001](http://localhost:11001), or use a [VNC Viewer][def] to connect to `localhost:11001` and enter `vscode` as the password. Anything you start in VS Code, or the integrated terminal, will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
## Quick start - GitHub Codespaces
|
||||
|
||||
1. From the [openimsdk/open-im-server GitHub repository](https://github.com/openimsdk/open-im-server), click on the **Code** dropdown, select **Open with Codespaces**, and then click on **New codespace**. If prompted, select the **Standard** machine size (which is also the default).
|
||||
|
||||
> **Note:** You will not see these options within GitHub if you are not in the Codespaces beta.
|
||||
|
||||
2. After the codespace is up and running in your browser, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd> and select **Ports: Focus on Ports View**.
|
||||
|
||||
3. You should see **VNC web client (11001)** under in the list of ports. Select the line and click on the globe icon to open it in a browser tab.
|
||||
|
||||
> **Tip:** If you do not see the port, <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd>, select **Forward a Port** and enter port `11001`.
|
||||
|
||||
4. In the new tab, you should see noVNC. Click **Connect** and enter `vscode` as the password.
|
||||
|
||||
Anything you start in VS Code, or the integrated terminal, will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
### Using VS Code with GitHub Codespaces
|
||||
|
||||
You may see improved VNC responsiveness when accessing a codespace from VS Code client since you can use a [VNC Viewer][def]. Here's how to do it.
|
||||
|
||||
1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces).
|
||||
|
||||
> **Note:** The GitHub Codespaces extension requires the Visual Studio Code distribution of OpenIM - OSS.
|
||||
|
||||
2. After the VS Code is up and running, press <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>P</kbd> or <kbd>F1</kbd>, choose **Codespaces: Create New Codespace**, and use the following settings:
|
||||
|
||||
- `openimsdk/open-im-server` for the repository.
|
||||
- Select any branch (e.g. **main**) - you can select a different one later.
|
||||
- Choose **Standard** (4-core, 8GB) as the size.
|
||||
|
||||
3. After you have connected to the codespace, you can use a [VNC Viewer][def] to connect to `localhost:5901` and enter `vscode` as the password.
|
||||
|
||||
> **Tip:** You may also need change your VNC client's **Picture Quality** setting to **High** to get a full color desktop.
|
||||
|
||||
4. Anything you start in VS Code, or the integrated terminal, will appear here.
|
||||
|
||||
Next: **[Try it out!](#try-it)**
|
||||
|
||||
## Try it
|
||||
|
||||
This container uses the [Fluxbox](http://fluxbox.org/) window manager to keep things lean. **Right-click on the desktop** to see menu options. It works with GNOME and GTK applications, so other tools can be installed if needed.
|
||||
|
||||
> **Note:** You can also set the resolution from the command line by typing `set-resolution`.
|
||||
|
||||
To start working with OpenIM - OSS, follow these steps:
|
||||
|
||||
1. In your local VS Code client, open a terminal (<kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>\`</kbd>) and type the following commands:
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
bash scripts/code.sh
|
||||
```
|
||||
|
||||
2. After the build is complete, open a web browser or a [VNC Viewer][def] to connect to the desktop environment as described in the quick start and enter `vscode` as the password.
|
||||
|
||||
3. You should now see OpenIM - OSS!
|
||||
|
||||
Next, let's try debugging.
|
||||
|
||||
1. Shut down OpenIM - OSS by clicking the box in the upper right corner of the OpenIM - OSS window through your browser or VNC viewer.
|
||||
|
||||
2. Go to your local VS Code client, and use the **Run / Debug** view to launch the **VS Code** configuration. (Typically the default, so you can likely just press <kbd>F5</kbd>).
|
||||
|
||||
> **Note:** If launching times out, you can increase the value of `timeout` in the "VS Code", "Attach Main Process", "Attach Extension Host", and "Attach to Shared Process" configurations in [launch.json](../../.vscode/launch.json). However, running `./scripts/code.sh` first will set up Electron which will usually solve timeout issues.
|
||||
|
||||
3. After a bit, OpenIM - OSS will appear with the debugger attached!
|
||||
|
||||
Enjoy!
|
||||
|
||||
|
||||
### Dotfiles
|
||||
|
||||
Dotfiles are files and folders on Unix-like systems starting with `.` that control the configuration of applications and shells on your system. You can store and manage your dotfiles in a repository on GitHub. For advice and tutorials about what to include in your dotfiles repository, see [GitHub does dotfiles](https://dotfiles.github.io/).
|
||||
|
||||
Your dotfiles repository might include your shell aliases and preferences, any tools you want to install, or any other codespace personalization you want to make.
|
||||
|
||||
You can configure GitHub Codespaces to use dotfiles from any repository you own by selecting that repository in your [personal GitHub Codespaces settings](https://github.com/settings/codespaces).
|
||||
|
||||
When you create a new codespace, GitHub clones your selected dotfiles repository to the codespace environment, and looks for one of the following files to set up the environment.
|
||||
|
||||
- *install.sh*
|
||||
- *install*
|
||||
- *bootstrap.sh*
|
||||
- *bootstrap*
|
||||
- *script/bootstrap*
|
||||
- *setup.sh*
|
||||
- *setup*
|
||||
- *script/setup*
|
||||
|
||||
If none of these files are found, then any files or folders in your selected dotfiles repository starting with `.` are symlinked to the codespace's `~` or `$HOME` directory.
|
||||
|
||||
Any changes to your selected dotfiles repository will apply only to each new codespace, and do not affect any existing codespace.
|
||||
|
||||
**Note:** Currently, Codespaces does not support personalizing the User-scoped settings for VS Code with your `dotfiles` repository. You can set default Workspace and Remote [Codespaces] settings for a specific project in the project's repository. For more information, see "[Introduction to dev containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers#creating-a-custom-dev-container-configuration)."
|
||||
|
||||
In addition, you can also configure Codespaces secrets on your personal profile page at [github.com/settings/codespaces](https://github.com/settings/codespaces). Development environment secrets are environment variables that are encrypted, and they are accessible to any codespace you create using repositories that have access to these secrets.
|
||||
|
||||
### Notes
|
||||
|
||||
The container comes with VS Code Insiders installed. To run it from an Integrated Terminal use `VSCODE_IPC_HOOK_CLI= /usr/bin/code-insiders .`.
|
||||
|
||||
[def]: https://www.realvnc.com/en/connect/download/viewer/
|
||||
72
.devcontainer/devcontainer.json
Normal file
72
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,72 @@
|
||||
{
|
||||
// Reference Doc: https://code.visualstudio.com/remote/advancedcontainers/overview
|
||||
"name": "OpenIM Dev Environment",
|
||||
// Update the container version when you publish dev-container
|
||||
"build": { "dockerfile": "Dockerfile" },
|
||||
// Replace with uncommented line below to build your own local copy of the image
|
||||
// "dockerFile": "../docker/Dockerfile-dev",
|
||||
"remoteEnv": {
|
||||
"GO111MODULE": "on",
|
||||
"GOPROXY": "https://goproxy.cn",
|
||||
"GOSUMDB": "sum.golang.org",
|
||||
"GONOPROXY": "github.com/openimsdk",
|
||||
"GONOSUMDB": "github.com/openimsdk",
|
||||
"GOPRIVATE": "github.com/openimsdk"
|
||||
},
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"davidanson.vscode-markdownlint",
|
||||
"golang.go",
|
||||
"ms-azuretools.vscode-dapr",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools"
|
||||
],
|
||||
"settings": {
|
||||
"go.toolsManagement.checkForUpdates": "local",
|
||||
"go.useLanguageServer": true,
|
||||
"go.gopath": "/go"
|
||||
}
|
||||
}
|
||||
},
|
||||
"mounts": [
|
||||
// Mount docker-in-docker library volume
|
||||
"type=volume,source=dind-var-lib-docker,target=/var/lib/docker",
|
||||
|
||||
// Bind mount docker socket under an alias to support docker-from-docker
|
||||
"type=bind,source=/var/run/docker.sock,target=/var/run/docker-host.sock",
|
||||
|
||||
// Bind mount docker socket under an alias to support docker-from-docker
|
||||
// "type=bind,source=${env:HOME}${env:USERPROFILE}/.minikube/cache,target=/home/openim/.minikube/cache",
|
||||
|
||||
// Uncomment to clone local .kube/config into devcontainer
|
||||
"type=bind,source=${env:HOME}${env:USERPROFILE}/.kube,target=/home/openim/.kube-localhost"
|
||||
|
||||
// Uncomment to additionally clone minikube certs into devcontainer for use with .kube/config
|
||||
// "type=bind,source=${env:HOME}${env:USERPROFILE}/.minikube,target=/home/openim/.minikube-localhost"
|
||||
],
|
||||
// Always run image-defined default command
|
||||
"overrideCommand": false,
|
||||
// On Linux, this will prevent new files getting created as root, but you
|
||||
// may need to update the USER_UID and USER_GID in docker/Dockerfile-dev
|
||||
// to match your user if not 1000.
|
||||
// "remoteUser": "openimsdk",
|
||||
"runArgs": [
|
||||
// Enable ptrace-based debugging for go
|
||||
"--cap-add=SYS_PTRACE",
|
||||
"--security-opt",
|
||||
"seccomp=unconfined",
|
||||
|
||||
// Uncomment to bind to host network for local devcontainer; this is necessary if using the
|
||||
// bind-mounted /var/run/docker-host.sock directly.
|
||||
"--net=host",
|
||||
|
||||
// Enable docker-in-docker configuration. Comment out if not using for better security.
|
||||
"--privileged",
|
||||
|
||||
// Run the entrypoint defined in container image.
|
||||
"--init"
|
||||
],
|
||||
"workspaceFolder": "/workspaces/openim",
|
||||
"workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/workspaces/openim"
|
||||
}
|
||||
2
.github/workflows/cla.yml
vendored
2
.github/workflows/cla.yml
vendored
@@ -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:
|
||||
|
||||
130
.github/workflows/docker-buildx.yml
vendored
130
.github/workflows/docker-buildx.yml
vendored
@@ -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
|
||||
|
||||
38
.github/workflows/e2e-test.yml
vendored
38
.github/workflows/e2e-test.yml
vendored
@@ -92,11 +92,41 @@ jobs:
|
||||
|
||||
- name: Exec OpenIM API test
|
||||
run: |
|
||||
sudo make test-api
|
||||
|
||||
- name: Exec OpenIM E2E test
|
||||
mkdir -p ./tmp
|
||||
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: |
|
||||
sudo make test-e2e
|
||||
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: 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: |
|
||||
|
||||
1
.github/workflows/help-comment-issue.yml
vendored
1
.github/workflows/help-comment-issue.yml
vendored
@@ -17,6 +17,7 @@ on:
|
||||
issues:
|
||||
types:
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
add-comment:
|
||||
if: github.event.label.name == 'help wanted' || github.event.label.name == 'good first issue'
|
||||
|
||||
113
.github/workflows/openimci.yml
vendored
113
.github/workflows/openimci.yml
vendored
@@ -18,39 +18,42 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
paths-ignore:
|
||||
- "docs/**"
|
||||
- "README.md"
|
||||
- "README_zh-CN.md"
|
||||
- "**.md"
|
||||
- "docs/**"
|
||||
- "CONTRIBUTING.md"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- release-*
|
||||
paths-ignore:
|
||||
- "README.md"
|
||||
- "README_zh-CN.md"
|
||||
- "CONTRIBUTING.md"
|
||||
- "CONTRIBUTING/**"
|
||||
- "**.md"
|
||||
- "docs/**"
|
||||
|
||||
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
|
||||
@@ -67,6 +70,9 @@ jobs:
|
||||
version: '3.x' # If available, use the latest major version that's compatible
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: OpenIM Scripts Verification(make verify)
|
||||
run: sudo make verify
|
||||
|
||||
- name: Module Operations
|
||||
run: |
|
||||
sudo make tidy
|
||||
@@ -83,16 +89,14 @@ jobs:
|
||||
- name: Build Source
|
||||
run: sudo make build
|
||||
|
||||
- name: Build multiarch PLATFORMS
|
||||
if: startsWith(github.ref, 'refs/heads/release-')
|
||||
run: |
|
||||
sudo make multiarch
|
||||
|
||||
- name: Cleanup Build
|
||||
run: sudo make clean
|
||||
|
||||
- name: Push Changes to Main
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: "cicd: robot automated Change"
|
||||
branch: main
|
||||
continue-on-error: true
|
||||
|
||||
- name: Set Current Directory
|
||||
id: set_directory
|
||||
run: echo "::set-output name=directory::$(pwd)"
|
||||
@@ -109,8 +113,11 @@ jobs:
|
||||
continue-on-error: true
|
||||
|
||||
openim-start:
|
||||
name: Test OpenIM install/start on ${{ matrix.os }}
|
||||
name: Test OpenIM install/start on ${{ matrix.os }}-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
environment:
|
||||
name: openim
|
||||
strategy:
|
||||
@@ -129,15 +136,59 @@ 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
|
||||
if: runner.os == 'Linux' && matrix.arch == 'amd64'
|
||||
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
|
||||
if: runner.os == 'Linux' && matrix.arch == 'amd64'
|
||||
with:
|
||||
message: |
|
||||
> [!TIP]
|
||||
> Run make install to check the status
|
||||
|
||||
### Docker Images:
|
||||
<details><summary>Click to expand docker images</summary>
|
||||
```bash
|
||||
${{ env.images }}
|
||||
```
|
||||
</details>
|
||||
|
||||
### Docker Processes:
|
||||
<details><summary>Click to expand docker ps</summary>
|
||||
```bash
|
||||
${{ env.containers }}
|
||||
```
|
||||
</details>
|
||||
GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
|
||||
|
||||
execute-scripts:
|
||||
name: Execute OpenIM Script On ${{ matrix.os }}
|
||||
name: Execute OpenIM Script On ${{ matrix.os }}-${{ matrix.arch }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
environment:
|
||||
name: openim
|
||||
strategy:
|
||||
matrix:
|
||||
go_version: ["1.20"]
|
||||
go_version: ["1.21"]
|
||||
os: ["ubuntu-latest", "macos-latest"]
|
||||
arch: [arm64, armv7, amd64]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
@@ -155,7 +206,7 @@ jobs:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# - name: Install latest Bash (macOS only)
|
||||
# if: runner.os == 'macOS'
|
||||
# if: runner.os == 'macOS' && matrix.arch == 'arm64'
|
||||
# run: |
|
||||
# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
# brew update
|
||||
@@ -175,7 +226,7 @@ jobs:
|
||||
sudo sleep 20
|
||||
|
||||
# - name: Set up Docker for macOS
|
||||
# if: runner.os == 'macOS'
|
||||
# if: runner.os == 'macOS' && matrix.arch == 'arm64'
|
||||
# run: |
|
||||
# brew install --cask docker
|
||||
# open /Applications/Docker.app
|
||||
@@ -201,29 +252,27 @@ 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'
|
||||
if: runner.os == 'Linux' && matrix.arch == 'amd64'
|
||||
run: |
|
||||
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' && matrix.arch == 'arm64'
|
||||
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 +294,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
|
||||
|
||||
3
.github/workflows/pull-request.yml
vendored
3
.github/workflows/pull-request.yml
vendored
@@ -78,9 +78,6 @@ jobs:
|
||||
echo "Run unit test and get test coverage successfully"
|
||||
continue-on-error: true
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
- name: OpenIM verify copyright
|
||||
run: |
|
||||
sudo make verify-copyright
|
||||
|
||||
3
.github/workflows/sync-release.yml
vendored
3
.github/workflows/sync-release.yml
vendored
@@ -3,7 +3,7 @@
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
# https://github.com/BetaHuhn/repo-file-sync-action
|
||||
name: Synchronize kubecub public code to other repositories
|
||||
name: Synchronize OpenIM Release Branch Public Code To Other Repositories
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
@@ -41,3 +41,4 @@ jobs:
|
||||
automerge
|
||||
ASSIGNEES: |
|
||||
kubbot
|
||||
continue-on-error: true
|
||||
5
.github/workflows/sync.yml
vendored
5
.github/workflows/sync.yml
vendored
@@ -3,7 +3,7 @@
|
||||
# you may not use this file except in compliance with the License.
|
||||
|
||||
# https://github.com/BetaHuhn/repo-file-sync-action
|
||||
name: Synchronize kubecub public code to other repositories
|
||||
name: Synchronize OpenIM Main Branch Public Code To Other Repositories
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
@@ -36,4 +36,5 @@ jobs:
|
||||
file-sync
|
||||
automerge
|
||||
ASSIGNEES: |
|
||||
kubbot
|
||||
kubbot
|
||||
continue-on-error: true
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,6 +34,7 @@ deployments/charts/generated-configs/
|
||||
### OpenIM Config ###
|
||||
.env
|
||||
config/config.yaml
|
||||
config/openim.yaml
|
||||
config/alertmanager.yml
|
||||
config/prometheus.yml
|
||||
config/email.tmpl
|
||||
|
||||
32
CHANGELOG/CHANGELOG-3.4.md
Normal file
32
CHANGELOG/CHANGELOG-3.4.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Version logging for OpenIM
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_TOC -->
|
||||
|
||||
<!-- END MUNGE: GENERATED_TOC -->
|
||||
|
||||
<a name="unreleased"></a>
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
<a name="v3.4.2"></a>
|
||||
## [v3.4.2] - 2023-12-14
|
||||
|
||||
<a name="v3.4.0"></a>
|
||||
## [v3.4.0] - 2023-11-10
|
||||
|
||||
<a name="v3.4.0-rc.1"></a>
|
||||
## [v3.4.0-rc.1] - 2023-11-09
|
||||
|
||||
<a name="v3.4.0-rc.0"></a>
|
||||
## v3.4.0-rc.0 - 2023-11-09
|
||||
### Reverts
|
||||
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
|
||||
|
||||
### Pull Requests
|
||||
- Merge branch 'tuoyun'
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v3.4.2...HEAD
|
||||
[v3.4.2]: https://github.com/openimsdk/open-im-server/compare/v3.4.0...v3.4.2
|
||||
[v3.4.0]: https://github.com/openimsdk/open-im-server/compare/v3.4.0-rc.1...v3.4.0
|
||||
[v3.4.0-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.4.0-rc.0...v3.4.0-rc.1
|
||||
76
CHANGELOG/CHANGELOG-3.5.md
Normal file
76
CHANGELOG/CHANGELOG-3.5.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Version logging for OpenIM
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_TOC -->
|
||||
|
||||
<!-- END MUNGE: GENERATED_TOC -->
|
||||
|
||||
<a name="unreleased"></a>
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
<a name="v3.5.1-alpha.1"></a>
|
||||
## [v3.5.1-alpha.1] - 2024-01-09
|
||||
|
||||
<a name="v3.5.0"></a>
|
||||
## [v3.5.0] - 2024-01-02
|
||||
|
||||
<a name="v3.5.1"></a>
|
||||
## [v3.5.1] - 2024-01-02
|
||||
|
||||
<a name="v3.5.1-bate.1"></a>
|
||||
## [v3.5.1-bate.1] - 2024-01-02
|
||||
|
||||
<a name="v3.5.1-rc.0"></a>
|
||||
## [v3.5.1-rc.0] - 2023-12-30
|
||||
|
||||
<a name="v3.5.0-rc.8"></a>
|
||||
## [v3.5.0-rc.8] - 2023-12-28
|
||||
|
||||
<a name="v3.5.0-rc.7"></a>
|
||||
## [v3.5.0-rc.7] - 2023-12-18
|
||||
|
||||
<a name="v3.5.0-rc.6"></a>
|
||||
## [v3.5.0-rc.6] - 2023-12-15
|
||||
|
||||
<a name="v3.5.0-rc.5"></a>
|
||||
## [v3.5.0-rc.5] - 2023-12-15
|
||||
|
||||
<a name="v3.5.0-rc.4"></a>
|
||||
## [v3.5.0-rc.4] - 2023-12-14
|
||||
|
||||
<a name="v3.5.0-rc.3"></a>
|
||||
## [v3.5.0-rc.3] - 2023-12-14
|
||||
|
||||
<a name="v3.5.0-rc.2"></a>
|
||||
## [v3.5.0-rc.2] - 2023-12-14
|
||||
|
||||
<a name="v3.5.0-rc.1"></a>
|
||||
## [v3.5.0-rc.1] - 2023-12-14
|
||||
|
||||
<a name="v3.5.0-rc.0"></a>
|
||||
## [v3.5.0-rc.0] - 2023-12-14
|
||||
|
||||
<a name="v3.5.0-beta.1"></a>
|
||||
## v3.5.0-beta.1 - 2023-11-29
|
||||
### Reverts
|
||||
- update etcd to v3.5.2 ([#206](https://github.com/openimsdk/open-im-server/issues/206))
|
||||
|
||||
### Pull Requests
|
||||
- Merge branch 'tuoyun'
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.1...HEAD
|
||||
[v3.5.1-alpha.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0...v3.5.1-alpha.1
|
||||
[v3.5.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.1...v3.5.0
|
||||
[v3.5.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-bate.1...v3.5.1
|
||||
[v3.5.1-bate.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-rc.0...v3.5.1-bate.1
|
||||
[v3.5.1-rc.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.8...v3.5.1-rc.0
|
||||
[v3.5.0-rc.8]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.7...v3.5.0-rc.8
|
||||
[v3.5.0-rc.7]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.6...v3.5.0-rc.7
|
||||
[v3.5.0-rc.6]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.5...v3.5.0-rc.6
|
||||
[v3.5.0-rc.5]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.4...v3.5.0-rc.5
|
||||
[v3.5.0-rc.4]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.3...v3.5.0-rc.4
|
||||
[v3.5.0-rc.3]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.2...v3.5.0-rc.3
|
||||
[v3.5.0-rc.2]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.1...v3.5.0-rc.2
|
||||
[v3.5.0-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-rc.0...v3.5.0-rc.1
|
||||
[v3.5.0-rc.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.0-beta.1...v3.5.0-rc.0
|
||||
@@ -35,52 +35,62 @@
|
||||
|
||||
## Ⓜ️ 关于 OpenIM
|
||||
|
||||
OpenIM 不仅仅是一个开源的即时消息组件,它是你的应用程序生态系统的一个不可或缺的部分。查看下面的图表,了解 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 是如何交互的。
|
||||
OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通话、通知以及AI聊天机器人等通信功能的服务平台。它通过提供一系列强大的API和Webhooks,使开发者可以轻松地在他们的应用中加入这些交互特性。OpenIM 本身并不是一个独立运行的聊天应用,而是作为一个平台,为其他应用提供支持,实现丰富的通信功能。下图展示 AppServer、AppClient、OpenIMServer 和 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)**
|
||||
|
||||
## 🌐 关于 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
|
||||
|
||||
|
||||
179
README.md
179
README.md
@@ -25,75 +25,48 @@
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
## Ⓜ️ 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.
|
||||
|
||||

|
||||
|
||||
## 🚀 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.
|
||||
+ 🌟 Main Features:
|
||||
|
||||
It's crafted in Golang and supports cross-platform deployment, ensuring a coherent messaging experience across all platforms.
|
||||
- 📦 Local storage
|
||||
- 🔔 Listener callbacks
|
||||
- 🛡️ API wrapping
|
||||
- 🌐 Connection management
|
||||
|
||||
+ 📚 Main Modules:
|
||||
|
||||
1. 🚀 Initialization and Login
|
||||
2. 👤 User Management
|
||||
3. 👫 Friend Management
|
||||
4. 🤖 Group Functions
|
||||
5. 💬 Conversation Handling
|
||||
|
||||
It is built using Golang and supports cross-platform deployment, ensuring a consistent access 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:
|
||||
+ **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.
|
||||
|
||||
+ **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 Functionality:
|
||||
|
||||
### 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 🚀💼 |
|
||||
| :----------------------------------------------------------: | :---------------------------------------------------------: |
|
||||
|  |  |
|
||||
| 📲🔄 **One-to-one and Group Chats** 👥🗣️ | 🎁💻 **Special Features - Custom Messages** ✉️🎨|
|
||||
|  |  |
|
||||
|
||||
</div>
|
||||
|
||||
**OpenIM** offers a powerful and reliable instant messaging platform, ensuring versatile communication across multiple platforms with the following key features:
|
||||
|
||||
✅ **Versatile Messaging:** Support for text, images, emojis, voice, video, and more, alongside one-on-one and multi-person audio/video calls.
|
||||
|
||||
✅ **Robust Chat Capabilities:** Including roles (application administrator, group owner, etc.) and features like muting, group announcements, and dynamic message loading.
|
||||
|
||||
✅ **Unique Interaction Features:** Offering read-and-burn private chats and a message editing function to broaden social scenarios.
|
||||
|
||||
✅ **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)
|
||||
|
||||
✅ **Extensibility:** Implemented in Golang, OpenIM introduces an "everything is a message" communication model, simplifying custom messages and feature extension.
|
||||
|
||||
✅ **High Performance:** Supports a hierarchical governance architecture tested and abstracts the storage model of various message types.
|
||||
|
||||
✅ **Full Platform Support:** Native support for iOS, Android, Flutter, uni-app, ReactNative, Electron, and Web.
|
||||
|
||||
-->
|
||||
+ **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
|
||||
|
||||
@@ -101,104 +74,14 @@ We support many platforms. Here are the addresses for quick experience on the we
|
||||
|
||||
👉 **[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 document:https://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>
|
||||
-->
|
||||
🤲 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)**
|
||||
+ **[Mac Developer Deployment Guide](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)**
|
||||
|
||||
<!--
|
||||
## :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.
|
||||
|
||||

|
||||
|
||||
## :building_construction: Overall Architecture
|
||||
|
||||
Delve into the heart of Open-IM-Server's functionality with our architecture diagram.
|
||||
|
||||
 -->
|
||||
|
||||
## :hammer_and_wrench: To start developing OpenIM
|
||||
## :hammer_and_wrench: To Start Developing OpenIM
|
||||
|
||||
[](https://vscode.dev/github/openimsdk/open-im-server)
|
||||
|
||||
@@ -236,6 +119,8 @@ Before you start, please make sure your changes are in demand. The best for that
|
||||
- [OpenIM Script Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md)
|
||||
- [OpenIM Versioning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md)
|
||||
- [Manage backend and monitor deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md)
|
||||
- [Mac Developer Deployment Guide for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md)
|
||||
|
||||
|
||||
## :busts_in_silhouette: Community
|
||||
|
||||
@@ -252,7 +137,7 @@ Our conference is in the [OpenIM Slack](https://join.slack.com/t/openimsdk/share
|
||||
|
||||
We take notes of each [biweekly meeting](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) in [GitHub discussions](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Our historical meeting notes, as well as replays of the meetings are available at [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing).
|
||||
|
||||
## :eyes: Who are using OpenIM
|
||||
## :eyes: Who Are Using OpenIM
|
||||
|
||||
Check out our [user case studies](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) page for a list of the project users. Don't hesitate to leave a [📝comment](https://github.com/openimsdk/open-im-server/issues/379) and share your use case.
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ assets/
|
||||
|
||||
## Copyright Notice:
|
||||
|
||||
The OpenIM logo, including its variations and animated versions, displayed in this repository [OpenIM](https://github.com/OpenIMSDK/openim) under the `/assets/logo` and `/assets/logo-gif` directories, are protected by copyright laws.
|
||||
The OpenIM logo, including its variations and animated versions, displayed in this repository [OpenIM](https://github.com/openimsdk/open-im-server) under the `/assets/logo` and `/assets/logo-gif` directories, are protected by copyright laws.
|
||||
|
||||
The logo design is credited to @Xx(席欣).
|
||||
|
||||
|
||||
11
assets/colors.md
Normal file
11
assets/colors.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# Official Colors
|
||||
|
||||
The openim logo has an official blue color. When reproducing the logo, please use the official color, when possible.
|
||||
|
||||
## Pantone
|
||||
|
||||
When possible, the Pantone color is preferred for print material. The official Pantone color is *285C*.
|
||||
|
||||
## RGB
|
||||
|
||||
When used digitally, the official RGB color code is *#326CE5*.
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
||||
ginProm "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||
ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
)
|
||||
|
||||
@@ -88,7 +88,7 @@ func run(port int, proPort int) error {
|
||||
log.ZInfo(context.Background(), "api register public config to discov success")
|
||||
router := api.NewGinRouter(client, rdb)
|
||||
if config.Config.Prometheus.Enable {
|
||||
p := ginProm.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
|
||||
p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
|
||||
p.SetListenAddress(fmt.Sprintf(":%d", proPort))
|
||||
p.Use(router)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# TODO: This config file is the template file
|
||||
# --| source: deployments/templates/openim.yaml
|
||||
# --| source: deployments/templates/config.yaml
|
||||
# --| env: scripts/install/environment
|
||||
# --| target: config/config.yaml
|
||||
# -----------------------------------------------------------------
|
||||
@@ -52,8 +52,8 @@ mongo:
|
||||
# Default MongoDB database name
|
||||
# Maximum connection pool size
|
||||
address: [ 172.28.0.1:37017 ]
|
||||
database: openIM_v3
|
||||
username: root
|
||||
database: openim_v3
|
||||
username: openIM
|
||||
password: openIM123
|
||||
maxPoolSize: 100
|
||||
|
||||
@@ -122,14 +122,14 @@ api:
|
||||
# minio.signEndpoint is minio public network address
|
||||
object:
|
||||
enable: "minio"
|
||||
apiURL: "http://14.155.64.202:10002"
|
||||
apiURL: "http://172.28.0.1:10002"
|
||||
minio:
|
||||
bucket: "openim"
|
||||
endpoint: "http://172.28.0.1:10005"
|
||||
accessKeyID: "root"
|
||||
secretAccessKey: "openIM123"
|
||||
sessionToken: ''
|
||||
signEndpoint: "http://14.155.64.202:10005"
|
||||
signEndpoint: "http://172.28.0.1:10005"
|
||||
publicRead: false
|
||||
cos:
|
||||
bucketURL: https://temp-1252357374.cos.ap-chengdu.myqcloud.com
|
||||
@@ -193,7 +193,7 @@ rpcRegisterName:
|
||||
# Whether to output in json format
|
||||
# Whether to include stack trace in logs
|
||||
log:
|
||||
storageLocation: /data/workspaces/open-im-server/logs/
|
||||
storageLocation: /workspaces/open-im-server/logs/
|
||||
rotationTime: 24
|
||||
remainRotationCount: 2
|
||||
remainLogLevel: 6
|
||||
@@ -247,6 +247,14 @@ manager:
|
||||
userID: [ "openIM123456", "openIM654321", "openIMAdmin" ]
|
||||
nickname: [ "system1", "system2", "system3" ]
|
||||
|
||||
# chatAdmin, use for send notification
|
||||
#
|
||||
# Built-in app system notification account ID
|
||||
# Built-in app system notification account nickname
|
||||
im-admin:
|
||||
userID: [ "imAdmin" ]
|
||||
nickname: [ "imAdmin" ]
|
||||
|
||||
# Multi-platform login policy
|
||||
# For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time
|
||||
multiLoginPolicy: 1
|
||||
@@ -307,21 +315,21 @@ iosPush:
|
||||
# Timeout in seconds
|
||||
# Whether to continue execution if callback fails
|
||||
callback:
|
||||
url: ""
|
||||
url: "http://127.0.0.1:10008/callbackExample"
|
||||
beforeSendSingleMsg:
|
||||
enable: false
|
||||
timeout: 5
|
||||
failedContinue: true
|
||||
beforeUpdateUserInfoEx:
|
||||
enable: false
|
||||
enable: false
|
||||
timeout: 5
|
||||
failedContinue: true
|
||||
afterUpdateUserInfoEx:
|
||||
enable: false
|
||||
enable: false
|
||||
timeout: 5
|
||||
failedContinue: true
|
||||
afterSendSingleMsg:
|
||||
enable: false
|
||||
enable: true
|
||||
timeout: 5
|
||||
failedContinue: true
|
||||
beforeSendGroupMsg:
|
||||
@@ -505,8 +513,8 @@ callback:
|
||||
# The number of Prometheus ports per service needs to correspond to rpcPort
|
||||
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
|
||||
prometheus:
|
||||
enable: false
|
||||
grafanaUrl: 172.28.0.1:13000
|
||||
enable: true
|
||||
grafanaUrl: http://172.28.0.1:13000/
|
||||
apiPrometheusPort: [20100]
|
||||
userPrometheusPort: [ 20110 ]
|
||||
friendPrometheusPort: [ 20120 ]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 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.
|
||||
@@ -12,31 +12,26 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# ======================================
|
||||
# ========= Basic Configuration ========
|
||||
# ======================================
|
||||
# -----------------------------------------------------------------------------
|
||||
# General Configuration
|
||||
# This section contains general configuration options for the entire environment.
|
||||
# These options can be set via environment variables. If both environment variables
|
||||
# and settings in this .env file exist, the environment variables take precedence.
|
||||
# -----------------------------------------------------------------------------
|
||||
# ==========================
|
||||
# General Configuration
|
||||
# ==========================
|
||||
# These settings apply to the overall environment.
|
||||
|
||||
# The user for authentication or system operations.
|
||||
# Default: OPENIM_USER=root
|
||||
USER=root
|
||||
# Data storage directory for persistent data.
|
||||
# Example: DATA_DIR=/path/to/data
|
||||
DATA_DIR=/workspaces/open-im-server
|
||||
|
||||
# Password associated with the specified user for authentication.
|
||||
# Default: PASSWORD=openIM123
|
||||
PASSWORD=openIM123
|
||||
|
||||
# Base URL for the application programming interface (API).
|
||||
# Default: API_URL=http://172.28.0.1:10002
|
||||
API_URL=http://14.155.64.202:10002
|
||||
|
||||
# Directory path for storing data files or related information.
|
||||
# Default: DATA_DIR=./
|
||||
DATA_DIR=/data/workspaces/open-im-server
|
||||
|
||||
# Choose the appropriate image address, the default is GITHUB image,
|
||||
# you can choose docker hub, for Chinese users can choose Ali Cloud
|
||||
# export IMAGE_REGISTRY="ghcr.io/openimsdk"
|
||||
# export IMAGE_REGISTRY="openim"
|
||||
# export IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk"
|
||||
# Docker image registry. Uncomment the preferred one.
|
||||
# Options: ghcr.io/openimsdk, openim, registry.cn-hangzhou.aliyuncs.com/openimsdk
|
||||
# IMAGE_REGISTRY="ghcr.io/openimsdk"
|
||||
# IMAGE_REGISTRY="openim"
|
||||
# IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk"
|
||||
IMAGE_REGISTRY=ghcr.io/openimsdk
|
||||
|
||||
# ======================================
|
||||
@@ -47,10 +42,9 @@ IMAGE_REGISTRY=ghcr.io/openimsdk
|
||||
# Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16
|
||||
DOCKER_BRIDGE_SUBNET=172.28.0.0/16
|
||||
|
||||
# Gateway for the Docker network.
|
||||
# Default: DOCKER_BRIDGE_GATEWAY=172.28.0.1
|
||||
# Set and specify the IP addresses of some containers. Generally speaking,
|
||||
# you do not need to modify these configurations to facilitate debugging
|
||||
DOCKER_BRIDGE_GATEWAY=172.28.0.1
|
||||
|
||||
MONGO_NETWORK_ADDRESS=172.28.0.2
|
||||
REDIS_NETWORK_ADDRESS=172.28.0.3
|
||||
KAFKA_NETWORK_ADDRESS=172.28.0.4
|
||||
@@ -65,45 +59,66 @@ NODE_EXPORTER_NETWORK_ADDRESS=172.28.0.12
|
||||
OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=172.28.0.13
|
||||
ALERT_MANAGER_NETWORK_ADDRESS=172.28.0.14
|
||||
|
||||
# ===============================================
|
||||
# = Component Extension Configuration =
|
||||
# ===============================================
|
||||
# ==============================================================================
|
||||
# Configuration Update Instructions
|
||||
# ==============================================================================
|
||||
# This header outlines the methods to update common variables in config.yaml and .env files.
|
||||
# These instructions are vital for maintaining the OpenIM environment's configuration.
|
||||
#
|
||||
# METHOD 1: Regenerate All Configurations
|
||||
# ----------------------------------------
|
||||
# Use this method to regenerate all configurations.
|
||||
# Steps:
|
||||
# 1. Delete existing config files:
|
||||
# - openim-server/config/config.yaml
|
||||
# - openim-chat/config/config.yaml
|
||||
# 2. Modify the .env file as required.
|
||||
# 3. Run 'docker compose up -d'. This will regenerate:
|
||||
# - config/config.yaml
|
||||
#
|
||||
# METHOD 2: Modify Individual Configuration Files
|
||||
# -----------------------------------------------
|
||||
# Use this method to update specific configuration files.
|
||||
# Steps:
|
||||
# 1. Modify the .env file as necessary.
|
||||
# 2. Update the corresponding entries in:
|
||||
# - config/config.yaml
|
||||
# 3. Restart the services with 'docker compose up -d'.
|
||||
# 4. Special Note: If you modify OPENIM_IP, API_OPENIM_PORT, or MINIO_PORT in .env,
|
||||
# ensure to update the corresponding services and configurations accordingly.
|
||||
#
|
||||
# It is essential to follow these methods to ensure consistent and correct application behavior.
|
||||
# ==============================================================================
|
||||
# Local IP address of the service. Modify if necessary.
|
||||
# Example: OPENIM_IP=172.28.0.1,
|
||||
OPENIM_IP=172.28.0.1
|
||||
|
||||
# ============ Component Extension Configuration ==========
|
||||
# ----- ZooKeeper Configuration -----
|
||||
# Address or hostname for the ZooKeeper service.
|
||||
# Default: ZOOKEEPER_ADDRESS=172.28.0.1
|
||||
ZOOKEEPER_ADDRESS=172.28.0.5
|
||||
|
||||
# Port for ZooKeeper service.
|
||||
# Default: ZOOKEEPER_PORT=12181
|
||||
ZOOKEEPER_PORT=12181
|
||||
|
||||
# ----- MongoDB Configuration -----
|
||||
# Address or hostname for the MongoDB service.
|
||||
# Default: MONGO_ADDRESS=172.28.0.1
|
||||
MONGO_ADDRESS=172.28.0.2
|
||||
|
||||
# Port on which MongoDB service is running.
|
||||
# MongoDB service port configuration.
|
||||
# Default: MONGO_PORT=37017
|
||||
# MONGO_PORT=37017
|
||||
|
||||
# Username to authenticate with the MongoDB service.
|
||||
# Default: MONGO_USERNAME=root
|
||||
# MONGO_USERNAME=root
|
||||
|
||||
# Password to authenticate with the MongoDB service.
|
||||
# Password for MongoDB admin user. Used for service authentication.
|
||||
# Default: MONGO_PASSWORD=openIM123
|
||||
MONGO_PASSWORD=openIM123
|
||||
|
||||
# Name of the database in MongoDB to be used.
|
||||
# Default: MONGO_DATABASE=openIM_v3
|
||||
MONGO_DATABASE=openIM_v3
|
||||
# Username for a regular OpenIM user in MongoDB.
|
||||
# Default: MONGO_OPENIM_USERNAME=openIM
|
||||
MONGO_OPENIM_USERNAME=openIM
|
||||
|
||||
# Password for a regular OpenIM user in MongoDB.
|
||||
# Default: MONGO_OPENIM_PASSWORD=openIM123456
|
||||
MONGO_OPENIM_PASSWORD=openIM123
|
||||
|
||||
# Specifies the database name to be used within MongoDB.
|
||||
# Default: MONGO_DATABASE=openim_v3
|
||||
MONGO_DATABASE=openim_v3
|
||||
|
||||
# ----- Redis Configuration -----
|
||||
# Address or hostname for the Redis service.
|
||||
# Default: REDIS_ADDRESS=172.28.0.1
|
||||
REDIS_ADDRESS=172.28.0.3
|
||||
|
||||
# Port on which Redis in-memory data structure store is running.
|
||||
# Default: REDIS_PORT=16379
|
||||
@@ -113,11 +128,6 @@ REDIS_PORT=16379
|
||||
# Default: REDIS_PASSWORD=openIM123
|
||||
REDIS_PASSWORD=openIM123
|
||||
|
||||
# ----- Kafka Configuration -----
|
||||
# Address or hostname for the Kafka service.
|
||||
# Default: KAFKA_ADDRESS=172.28.0.1
|
||||
KAFKA_ADDRESS=172.28.0.4
|
||||
|
||||
# Kakfa username to authenticate with the Kafka service.
|
||||
# KAFKA_USERNAME=''
|
||||
|
||||
@@ -129,20 +139,13 @@ KAFKA_PORT=19094
|
||||
# Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
|
||||
KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
|
||||
|
||||
# Topic in Kafka for pushing messages (e.g. notifications or updates).
|
||||
# Default: KAFKA_MSG_PUSH_TOPIC=msgToPush
|
||||
KAFKA_MSG_PUSH_TOPIC=msgToPush
|
||||
|
||||
# Topic in Kafka for storing offline messages in MongoDB.
|
||||
# Default: KAFKA_OFFLINEMSG_MONGO_TOPIC=offlineMsgToMongoMysql
|
||||
KAFKA_OFFLINEMSG_MONGO_TOPIC=offlineMsgToMongoMysql
|
||||
|
||||
# ----- MinIO Configuration ----
|
||||
# Address or hostname for the MinIO object storage service.
|
||||
# Default: MINIO_ADDRESS=172.28.0.1
|
||||
MINIO_ADDRESS=172.28.0.6
|
||||
|
||||
# Port on which MinIO object storage service is running.
|
||||
# MINIO_PORT
|
||||
# ----------
|
||||
# MINIO_PORT sets the port for the MinIO object storage service.
|
||||
# Upon changing this port, the MinIO endpoint URLs in the file must be updated
|
||||
# to reflect this change. The endpoints include both the 'endpoint' and 'signEndpoint'
|
||||
# under the MinIO configuration.
|
||||
#
|
||||
# Default: MINIO_PORT=10005
|
||||
MINIO_PORT=10005
|
||||
|
||||
@@ -155,19 +158,11 @@ MINIO_PORT=10005
|
||||
MINIO_SECRET_KEY=openIM123
|
||||
|
||||
# ----- Prometheus Configuration -----
|
||||
# Address or hostname for the Prometheus service.
|
||||
# Default: PROMETHEUS_ADDRESS=172.28.0.1
|
||||
PROMETHEUS_ADDRESS=172.28.0.10
|
||||
|
||||
# Port on which Prometheus service is running.
|
||||
# Default: PROMETHEUS_PORT=19090
|
||||
PROMETHEUS_PORT=19090
|
||||
|
||||
# ----- Grafana Configuration -----
|
||||
# Address or hostname for the Grafana service.
|
||||
# Default: GRAFANA_ADDRESS=172.28.0.1
|
||||
GRAFANA_ADDRESS=172.28.0.11
|
||||
|
||||
# Port on which Grafana service is running.
|
||||
# Default: GRAFANA_PORT=13000
|
||||
GRAFANA_PORT=13000
|
||||
@@ -176,56 +171,41 @@ GRAFANA_PORT=13000
|
||||
# ============ OpenIM Web ===============
|
||||
# ======================================
|
||||
|
||||
# Path to the OpenIM web distribution.
|
||||
# Default: OPENIM_WEB_DIST_PATH=/app/dist
|
||||
OPENIM_WEB_DIST_PATH=/app/dist
|
||||
|
||||
# Port on which OpenIM web service is running.
|
||||
# Default: OPENIM_WEB_PORT=11001
|
||||
OPENIM_WEB_PORT=11001
|
||||
|
||||
# Address or hostname for the OpenIM web service.
|
||||
# Default: OPENIM_WEB_ADDRESS=172.28.0.1
|
||||
OPENIM_WEB_ADDRESS=172.28.0.7
|
||||
|
||||
# ======================================
|
||||
# ========= OpenIM Server ==============
|
||||
# ======================================
|
||||
|
||||
# Address or hostname for the OpenIM server.
|
||||
# Default: OPENIM_SERVER_ADDRESS=172.28.0.1
|
||||
OPENIM_SERVER_ADDRESS=172.28.0.8
|
||||
|
||||
# Port for the OpenIM WebSockets.
|
||||
# Default: OPENIM_WS_PORT=10001
|
||||
OPENIM_WS_PORT=10001
|
||||
|
||||
# Port for the OpenIM API.
|
||||
# API_OPENIM_PORT
|
||||
# ---------------
|
||||
# This variable defines the port on which the OpenIM API service will listen.
|
||||
# When changing this port, it's essential to update the apiURL in the config.yaml file
|
||||
# to ensure the API service is accessible at the new port.
|
||||
#
|
||||
# Default: API_OPENIM_PORT=10002
|
||||
API_OPENIM_PORT=10002
|
||||
|
||||
|
||||
# ======================================
|
||||
# ========== OpenIM Chat ===============
|
||||
# ======================================
|
||||
|
||||
# Branch name for OpenIM chat.
|
||||
# Default: CHAT_IMAGE_VERSION=main
|
||||
# https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md
|
||||
CHAT_IMAGE_VERSION=main
|
||||
|
||||
# Address or hostname for the OpenIM chat service.
|
||||
# Default: OPENIM_CHAT_ADDRESS=172.28.0.1
|
||||
OPENIM_CHAT_ADDRESS=172.28.0.9
|
||||
|
||||
# Port for the OpenIM chat API.
|
||||
# Default: OPENIM_CHAT_API_PORT=10008
|
||||
OPENIM_CHAT_API_PORT=10008
|
||||
|
||||
# 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/main
|
||||
|
||||
# Port for the OpenIM admin API.
|
||||
# Default: OPENIM_ADMIN_API_PORT=10009
|
||||
OPENIM_ADMIN_API_PORT=10009
|
||||
|
||||
# ======================================
|
||||
# ========== OpenIM Admin ==============
|
||||
@@ -234,10 +214,6 @@ OPENIM_CHAT_DATA_DIR=./openim-chat/main
|
||||
# Branch name for OpenIM server.
|
||||
# Default: SERVER_IMAGE_VERSION=main
|
||||
SERVER_IMAGE_VERSION=main
|
||||
|
||||
# Port for the OpenIM admin API.
|
||||
# Default: OPENIM_ADMIN_API_PORT=10009
|
||||
OPENIM_ADMIN_API_PORT=10009
|
||||
|
||||
# Port for the node exporter.
|
||||
# Default: NODE_EXPORTER_PORT=19100
|
||||
|
||||
@@ -169,4 +169,4 @@ server {
|
||||
|
||||
proxy_pass http://minio_console_2; # This uses the upstream directive definition to load balance
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1471,4 +1471,4 @@
|
||||
"uid": "f5f5de9a-6ec5-499a-841e-6e901c33b1f7",
|
||||
"version": 16,
|
||||
"weekStart": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ export MINIO_ENDPOINT="http://im-minio:9000"
|
||||
export MINIO_SIGN_ENDPOINT="https://openim.server.com/im-minio-api"
|
||||
|
||||
mkdir ./charts/generated-configs
|
||||
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/openim.yaml > ./charts/generated-configs/config.yaml
|
||||
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/config.yaml > ./charts/generated-configs/config.yaml
|
||||
cp ../config/notification.yaml ./charts/generated-configs/notification.yaml
|
||||
../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/helm-image.yaml > ./charts/generated-configs/helm-image.yaml
|
||||
```
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,7 +14,7 @@
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
# TODO: This config file is the template file
|
||||
# --| source: deployments/templates/openim.yaml
|
||||
# --| source: deployments/templates/config.yaml
|
||||
# --| env: scripts/install/environment
|
||||
# --| target: config/config.yaml
|
||||
# -----------------------------------------------------------------
|
||||
@@ -315,13 +315,21 @@ iosPush:
|
||||
# Timeout in seconds
|
||||
# Whether to continue execution if callback fails
|
||||
callback:
|
||||
url: ""
|
||||
url: "http://127.0.0.1:10008/callbackExample"
|
||||
beforeSendSingleMsg:
|
||||
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}
|
||||
enable: true
|
||||
timeout: ${CALLBACK_TIMEOUT}
|
||||
failedContinue: ${CALLBACK_FAILED_CONTINUE}
|
||||
beforeSendGroupMsg:
|
||||
@@ -519,3 +527,39 @@ prometheus:
|
||||
rtcPrometheusPort: [ ${RTC_PROM_PORT} ]
|
||||
thirdPrometheusPort: [ ${THIRD_PROM_PORT} ]
|
||||
messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports
|
||||
|
||||
###################### LocalCache configuration information ######################
|
||||
# topic: redis subscriber channel
|
||||
# slotNum: number of slots, multiple slots can prevent too many keys from competing for a lock
|
||||
# slotSize: number of slots, the number of cached keys per slot, the overall cache quantity is slotNum * slotSize
|
||||
# successExpire: successful cache time seconds
|
||||
# failedExpire: failed cache time seconds
|
||||
# disable local caching and annotate topic, slotNum, and slotSize
|
||||
localCache:
|
||||
user:
|
||||
topic: DELETE_CACHE_USER
|
||||
slotNum: 100
|
||||
slotSize: 2000
|
||||
successExpire: 300
|
||||
failedExpire: 5
|
||||
|
||||
group:
|
||||
topic: DELETE_CACHE_GROUP
|
||||
slotNum: 100
|
||||
slotSize: 2000
|
||||
successExpire: 300
|
||||
failedExpire: 5
|
||||
|
||||
friend:
|
||||
topic: DELETE_CACHE_FRIEND
|
||||
slotNum: 100
|
||||
slotSize: 2000
|
||||
successExpire: 300
|
||||
failedExpire: 5
|
||||
|
||||
conversation:
|
||||
topic: DELETE_CACHE_CONVERSATION
|
||||
slotNum: 100
|
||||
slotSize: 2000
|
||||
successExpire: 300
|
||||
failedExpire: 5
|
||||
@@ -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.
|
||||
@@ -88,10 +102,6 @@ ZOOKEEPER_PORT=${ZOOKEEPER_PORT}
|
||||
# Default: MONGO_PORT=37017
|
||||
# MONGO_PORT=${MONGO_PORT}
|
||||
|
||||
# Username for MongoDB admin user. Used for service authentication.
|
||||
# Default: MONGO_USERNAME=root
|
||||
# MONGO_USERNAME=${MONGO_USERNAME}
|
||||
|
||||
# Password for MongoDB admin user. Used for service authentication.
|
||||
# Default: MONGO_PASSWORD=openIM123
|
||||
MONGO_PASSWORD=${MONGO_PASSWORD}
|
||||
@@ -105,7 +115,7 @@ MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME}
|
||||
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}
|
||||
|
||||
# ----- Redis Configuration -----
|
||||
@@ -161,10 +171,6 @@ GRAFANA_PORT=${GRAFANA_PORT}
|
||||
# ============ OpenIM Web ===============
|
||||
# ======================================
|
||||
|
||||
# Path to the OpenIM web distribution.
|
||||
# Default: OPENIM_WEB_DIST_PATH=/app/dist
|
||||
OPENIM_WEB_DIST_PATH=${OPENIM_WEB_DIST_PATH}
|
||||
|
||||
# Port on which OpenIM web service is running.
|
||||
# Default: OPENIM_WEB_PORT=11001
|
||||
OPENIM_WEB_PORT=${OPENIM_WEB_PORT}
|
||||
|
||||
@@ -10,15 +10,13 @@ networks:
|
||||
- subnet: '${DOCKER_BRIDGE_SUBNET:-172.28.0.0/16}'
|
||||
gateway: '${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}'
|
||||
|
||||
|
||||
|
||||
services:
|
||||
mongodb:
|
||||
image: mongo:${MONGODB_IMAGE_VERSION-6.0.2}
|
||||
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"
|
||||
@@ -29,7 +27,7 @@ services:
|
||||
- wiredTigerCacheSizeGB=1
|
||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
|
||||
- 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
|
||||
@@ -89,6 +87,7 @@ services:
|
||||
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@<your_host>:9093
|
||||
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094
|
||||
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}:${KAFKA_PORT:-19094}
|
||||
# - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://127.0.0.1:${KAFKA_PORT:-19094} # Mac Deployment
|
||||
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
||||
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
|
||||
networks:
|
||||
@@ -96,7 +95,7 @@ services:
|
||||
ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4}
|
||||
|
||||
minio:
|
||||
image: minio/minio:${MINIO_IMAGE_VERSION:-latest}
|
||||
image: minio/minio:${MINIO_IMAGE_VERSION:-RELEASE.2024-01-11T07-46-16Z}
|
||||
ports:
|
||||
- "${MINIO_PORT:-10005}:9000"
|
||||
- "9090:9090"
|
||||
@@ -114,22 +113,130 @@ services:
|
||||
ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6}
|
||||
|
||||
openim-web:
|
||||
image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-latest}
|
||||
image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker}
|
||||
container_name: openim-web
|
||||
environment:
|
||||
- OPENIM_WEB_DIST_PATH=${OPENIM_WEB_DIST_PATH:-/app/dist}
|
||||
- OPENIM_WEB_PORT=${OPENIM_WEB_PORT:-11001}
|
||||
platform: linux/amd64
|
||||
restart: always
|
||||
ports:
|
||||
- "${OPENIM_WEB_PORT:-11001}:11001"
|
||||
- "${OPENIM_WEB_PORT:-11001}:80"
|
||||
networks:
|
||||
server:
|
||||
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7}
|
||||
|
||||
# Uncomment and configure the following services as needed
|
||||
### TODO: Uncomment, or deploy using openim docker: https://github.com/openimsdk/openim-docker
|
||||
### Uncomment and configure the following services as needed
|
||||
|
||||
# openim-server:
|
||||
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main}
|
||||
# container_name: openim-server
|
||||
# ports:
|
||||
# - "${OPENIM_WS_PORT:-10001}:${OPENIM_WS_PORT:-10001}"
|
||||
# - "${API_OPENIM_PORT:-10002}:${API_OPENIM_PORT:-10002}"
|
||||
# - "${API_PROM_PORT:-20100}:${API_PROM_PORT:-20100}"
|
||||
# - "${USER_PROM_PORT:-20110}:${USER_PROM_PORT:-20110}"
|
||||
# - "${FRIEND_PROM_PORT:-20120}:${FRIEND_PROM_PORT:-20120}"
|
||||
# - "${MESSAGE_PROM_PORT:-20130}:${MESSAGE_PROM_PORT:-20130}"
|
||||
# - "${MSG_GATEWAY_PROM_PORT:-20140}:${MSG_GATEWAY_PROM_PORT:-20140}"
|
||||
# - "${GROUP_PROM_PORT:-20150}:${GROUP_PROM_PORT:-20150}"
|
||||
# - "${AUTH_PROM_PORT:-20160}:${AUTH_PROM_PORT:-20160}"
|
||||
# - "${PUSH_PROM_PORT:-20170}:${PUSH_PROM_PORT:-20170}"
|
||||
# - "${CONVERSATION_PROM_PORT:-20230}:${CONVERSATION_PROM_PORT:-20230}"
|
||||
# - "${RTC_PROM_PORT:-21300}:${RTC_PROM_PORT:-21300}"
|
||||
# - "${THIRD_PROM_PORT:-21301}:${THIRD_PROM_PORT:-21301}"
|
||||
# - "21400-21403:21400-21403"
|
||||
# healthcheck:
|
||||
# test: ["CMD", "/openim/openim-server/scripts/check-all.sh"]
|
||||
# interval: 120s
|
||||
# timeout: 30s
|
||||
# retries: 5
|
||||
# env_file:
|
||||
# - .env
|
||||
# environment:
|
||||
# - OPENIM_IP=${OPENIM_IP:-127.0.0.1}
|
||||
# volumes:
|
||||
# - "${DATA_DIR:-./}/openim-server/logs:/openim/openim-server/logs"
|
||||
# - "${DATA_DIR:-./}/openim-server/_output/logs:/openim/openim-server/_output/logs"
|
||||
# - "${DATA_DIR:-./}/openim-server/config:/openim/openim-server/config"
|
||||
# restart: always
|
||||
# depends_on:
|
||||
# - kafka
|
||||
# - mysql
|
||||
# - mongodb
|
||||
# - redis
|
||||
# - minio
|
||||
# logging:
|
||||
# driver: json-file
|
||||
# options:
|
||||
# max-size: "1g"
|
||||
# max-file: "2"
|
||||
# networks:
|
||||
# server:
|
||||
# ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8}
|
||||
|
||||
### TODO: mysql is required to deploy the openim-chat component
|
||||
# mysql:
|
||||
# image: mysql:${MYSQL_IMAGE_VERSION:-5.7}
|
||||
# platform: linux/amd64
|
||||
# ports:
|
||||
# - "${MYSQL_PORT:-13306}:3306"
|
||||
# container_name: mysql
|
||||
# volumes:
|
||||
# - "${DATA_DIR:-./}/components/mysql/data:/var/lib/mysql"
|
||||
# - "/etc/localtime:/etc/localtime"
|
||||
# environment:
|
||||
# MYSQL_ROOT_PASSWORD: "${MYSQL_PASSWORD:-openIM123}"
|
||||
# restart: always
|
||||
# networks:
|
||||
# server:
|
||||
# ipv4_address: ${MYSQL_NETWORK_ADDRESS:-172.28.0.15}
|
||||
|
||||
# openim-chat:
|
||||
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main}
|
||||
# container_name: openim-chat
|
||||
# healthcheck:
|
||||
# test: ["CMD", "/openim/openim-chat/scripts/check_all.sh"]
|
||||
# interval: 60s
|
||||
# timeout: 30s
|
||||
# retries: 5
|
||||
# env_file:
|
||||
# - .env
|
||||
# environment:
|
||||
# - ZOOKEEPER_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
|
||||
# - ZOOKEEPER_PORT=${ZOOKEEPER_PORT:-12181}
|
||||
# - OPENIM_SERVER_ADDRESS=http://${OPENIM_SERVER_ADDRESS:-172.28.0.1}
|
||||
# - API_OPENIM_PORT=${API_OPENIM_PORT:-10002}
|
||||
# - MYSQL_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
|
||||
# - MYSQL_PORT=${MYSQL_PORT:-13306}
|
||||
# - REDIS_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
|
||||
# - REDIS_PORT=${REDIS_PORT:-16379}
|
||||
# ports:
|
||||
# - "${OPENIM_CHAT_API_PORT:-10008}:10008"
|
||||
# - "${OPENIM_ADMIN_API_PORT:-10009}:10009"
|
||||
# volumes:
|
||||
# - "${DATA_DIR:-./}/components/openim-chat/logs:/openim/openim-chat/logs"
|
||||
# - "${DATA_DIR:-./}/components/openim-chat/_output/logs:/openim/openim-chat/_output/logs"
|
||||
# - "${DATA_DIR:-./}/components/openim-chat/config:/openim/openim-chat/config"
|
||||
# restart: always
|
||||
# # user: root:root
|
||||
# depends_on:
|
||||
# - mysql
|
||||
# - kafka
|
||||
# - redis
|
||||
# - zookeeper
|
||||
# logging:
|
||||
# driver: json-file
|
||||
# options:
|
||||
# max-size: "1g"
|
||||
# max-file: "2"
|
||||
# networks:
|
||||
# server:
|
||||
# ipv4_address: ${OPENIM_CHAT_NETWORK_ADDRESS:-172.28.0.9}
|
||||
|
||||
# openim-admin:
|
||||
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin-front:v3.4.0
|
||||
# # https://github.com/openimsdk/open-im-server/issues/1662
|
||||
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35}
|
||||
# container_name: openim-admin
|
||||
# platform: linux/amd64
|
||||
# restart: always
|
||||
# ports:
|
||||
# - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80"
|
||||
@@ -143,8 +250,8 @@ services:
|
||||
# hostname: prometheus
|
||||
# restart: always
|
||||
# volumes:
|
||||
# - ./config/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
# - ./config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml
|
||||
# - "${DATA_DIR:-./}/config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml"
|
||||
# - "${DATA_DIR:-./}/config/prometheus.yml:/etc/prometheus/prometheus.yml"
|
||||
# ports:
|
||||
# - "${PROMETHEUS_PORT:-19090}:9090"
|
||||
# networks:
|
||||
@@ -157,8 +264,8 @@ services:
|
||||
# hostname: alertmanager
|
||||
# restart: always
|
||||
# volumes:
|
||||
# - ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
||||
# - ./config/email.tmpl:/etc/alertmanager/email.tmpl
|
||||
# - ${DATA_DIR:-./}/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
|
||||
# - ${DATA_DIR:-./}/config/email.tmpl:/etc/alertmanager/email.tmpl
|
||||
# ports:
|
||||
# - "${ALERT_MANAGER_PORT:-19093}:9093"
|
||||
# networks:
|
||||
@@ -174,7 +281,7 @@ services:
|
||||
# ports:
|
||||
# - "${GRAFANA_PORT:-13000}:3000"
|
||||
# volumes:
|
||||
# - ${DATA_DIR:-./}/components/grafana:/var/lib/grafana
|
||||
# - "${DATA_DIR:-./}/components/grafana:/var/lib/grafana"
|
||||
# networks:
|
||||
# server:
|
||||
# ipv4_address: ${GRAFANA_NETWORK_ADDRESS:-172.28.0.11}
|
||||
|
||||
@@ -31,7 +31,7 @@ docs/guide/en-US/cmd/openim/openim-rpc-user_list.md
|
||||
docs/guide/en-US/cmd/openim/openim-rpc-user_update.md
|
||||
docs/guide/en-US/cmd/openim/openim_validate.md
|
||||
docs/guide/en-US/cmd/openim/openim_version.md
|
||||
docs/guide/en-US/yaml/openim/openim.yaml
|
||||
docs/guide/en-US/yaml/openim/config.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_color.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_completion.yaml
|
||||
docs/guide/en-US/yaml/openim/openim_info.yaml
|
||||
|
||||
@@ -89,7 +89,7 @@ While the first two methods will be our main focus, it's worth noting that the t
|
||||
|
||||
### 1.2. <a name='SourceCodeDeployment'></a>Source Code Deployment
|
||||
|
||||
In the source code deployment method, the configuration generation process involves executing `make init`, which fundamentally runs the script `./scripts/init-config.sh`. This script utilizes variables defined in the [`environment.sh`](https://github.com/openimsdk/open-im-server/blob/main/scripts/install/environment.sh) script to render the [`openim.yaml`](https://github.com/openimsdk/open-im-server/blob/main/deployments/templates/openim.yaml) template file, subsequently generating the [`config.yaml`](https://github.com/openimsdk/open-im-server/blob/main/config/config.yaml) configuration file.
|
||||
In the source code deployment method, the configuration generation process involves executing `make init`, which fundamentally runs the script `./scripts/init-config.sh`. This script utilizes variables defined in the [`environment.sh`](https://github.com/openimsdk/open-im-server/blob/main/scripts/install/environment.sh) script to render the [`config.yaml`](https://github.com/openimsdk/open-im-server/blob/main/deployments/templates/config.yaml) template file, subsequently generating the [`config.yaml`](https://github.com/openimsdk/open-im-server/blob/main/config/config.yaml) configuration file.
|
||||
|
||||
### 1.3. <a name='DockerComposeDeployment'></a>Docker Compose Deployment
|
||||
|
||||
@@ -104,16 +104,19 @@ 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:
|
||||
These variables are stored within the [`environment.sh`](https://github.com/OpenIMSDK/open-im-server/blob/main/scripts/install/environment.sh) configuration:
|
||||
|
||||
```bash
|
||||
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. <a name='TencentCloudCOSConfiguration'></a>Tencent Cloud COS Configuration
|
||||
|
||||
@@ -441,7 +448,7 @@ This section involves configuring the log settings, including storage location,
|
||||
|
||||
| Parameter | Example Value | Description |
|
||||
| ------------------------- | ------------------------ | --------------------------------- |
|
||||
| LOG_STORAGE_LOCATION | ""${OPENIM_ROOT}"/logs/" | Location for storing logs |
|
||||
| LOG_STORAGE_LOCATION | "${OPENIM_ROOT}/logs/" | Location for storing logs |
|
||||
| LOG_ROTATION_TIME | "24" | Log rotation time (in hours) |
|
||||
| LOG_REMAIN_ROTATION_COUNT | "2" | Number of log rotations to retain |
|
||||
| LOG_REMAIN_LOG_LEVEL | "6" | Log level to retain |
|
||||
|
||||
@@ -75,7 +75,7 @@ It is critical that our full community is actively engaged on enhancements in th
|
||||
|
||||
- Be aware the cherry pick script assumes you have a git remote called `upstream` that points at the openim-server github org.
|
||||
|
||||
Please see our [recommended Git workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contributors/github-workflow.md#workflow).
|
||||
Please see our [recommended Git workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/github-workflow.md#workflow).
|
||||
|
||||
- You will need to run the cherry pick script separately for each patch release you want to cherry pick to. Cherry picks should be applied to all [active](https://github.com/openimsdk/open-im-server/releases) release branches where the fix is applicable.
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ description: |
|
||||
commit hygiene.
|
||||
---
|
||||
|
||||

|
||||
|
||||
## 1. Fork in the cloud
|
||||
|
||||
@@ -28,10 +27,10 @@ neither `$GOPATH/src/github.com/${your github profile name}/`
|
||||
nor any other pattern will work.
|
||||
|
||||
```sh
|
||||
export working_dir="$(go env GOPATH)/src/k8s.io"
|
||||
export working_dir="$(go env GOPATH)/src/github.com/openimsdk"
|
||||
```
|
||||
|
||||
If you already do Go development on github, the `k8s.io` directory
|
||||
If you already do Go development on github, the `github.com/openimsdk` directory
|
||||
will be a sibling to your existing `github.com` directory.
|
||||
|
||||
Set `user` to match your github profile name:
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -23,8 +23,8 @@ In the `scripts/init_config.sh` file, we defined some template files. These temp
|
||||
```
|
||||
# Defines an associative array where the keys are the template files and the values are the corresponding output files.
|
||||
declare -A TEMPLATES=(
|
||||
[""${OPENIM_ROOT}"/scripts/template/config-tmpl/env.template"]="${OPENIM_OUTPUT_SUBPATH}/bin/.env"
|
||||
[""${OPENIM_ROOT}"/scripts/template/config-tmpl/config.yaml"]="${OPENIM_OUTPUT_SUBPATH}/bin/config.yaml"
|
||||
["${OPENIM_ROOT}/scripts/template/config-tmpl/env.template"]="${OPENIM_OUTPUT_SUBPATH}/bin/.env"
|
||||
["${OPENIM_ROOT}/scripts/template/config-tmpl/config.yaml"]="${OPENIM_OUTPUT_SUBPATH}/bin/config.yaml"
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
258
docs/contrib/mac-developer-deployment-guide.md
Normal file
258
docs/contrib/mac-developer-deployment-guide.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# Mac Developer Deployment Guide for OpenIM
|
||||
|
||||
## Introduction
|
||||
|
||||
This guide aims to assist Mac-based developers in contributing effectively to OpenIM. It covers the setup of a development environment tailored for Mac, including the use of GitHub for development workflow and `devcontainer` for a consistent development experience.
|
||||
|
||||
Before contributing to OpenIM through issues and pull requests, make sure you are familiar with GitHub and the [pull request workflow](https://docs.github.com/en/get-started/quickstart/github-flow).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### System Requirements
|
||||
|
||||
- macOS (latest stable version recommended)
|
||||
- Internet connection
|
||||
- Administrator access
|
||||
|
||||
### Knowledge Requirements
|
||||
|
||||
- Basic understanding of Git and GitHub
|
||||
- Familiarity with Docker and containerization
|
||||
- Experience with Go programming language
|
||||
|
||||
## Setting up the Development Environment
|
||||
|
||||
### Installing Homebrew
|
||||
|
||||
Homebrew is an essential package manager for macOS. Install it using:
|
||||
|
||||
```sh
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
```
|
||||
|
||||
### Installing and Configuring Git
|
||||
|
||||
1. Install Git:
|
||||
|
||||
```sh
|
||||
brew install git
|
||||
```
|
||||
|
||||
2. Configure Git with your user details:
|
||||
|
||||
```sh
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email "your.email@example.com"
|
||||
```
|
||||
|
||||
### Setting Up the Devcontainer
|
||||
|
||||
`Devcontainers` provide a Docker-based isolated development environment.
|
||||
|
||||
Read [README.md](https://github.com/openimsdk/open-im-server/tree/main/.devcontainer) in the `.devcontainer` directory of the project to learn more about the devcontainer.
|
||||
|
||||
To set it up:
|
||||
|
||||
1. Install Docker Desktop for Mac from [Docker Hub](https://docs.docker.com/desktop/install/mac-install/).
|
||||
2. Install Visual Studio Code and the Remote - Containers extension.
|
||||
3. Open the cloned OpenIM repository in VS Code.
|
||||
4. VS Code will prompt to reopen the project in a container. Accept this to set up the environment automatically.
|
||||
|
||||
### Installing Go and Dependencies
|
||||
|
||||
Use Homebrew to install Go:
|
||||
|
||||
```sh
|
||||
brew install go
|
||||
```
|
||||
|
||||
Ensure the version of Go is compatible with the version required by OpenIM (refer to the main documentation for version requirements).
|
||||
|
||||
### Additional Tools
|
||||
|
||||
Install other required tools like Docker, Vagrant, and necessary GNU utils as described in the main documentation.
|
||||
|
||||
## Mac Deployment openim-chat and openim-server
|
||||
|
||||
To integrate the Chinese document into an English document for Linux deployment, we will first translate the content and then adapt it to suit the Linux environment. Here's how the translated and adapted content might look:
|
||||
|
||||
### Ensure a Clean Environment
|
||||
|
||||
- It's recommended to execute in a new directory.
|
||||
- Run `ps -ef | grep openim` to ensure no OpenIM processes are running.
|
||||
- Run `ps -ef | grep chat` to check for absence of chat-related processes.
|
||||
- Execute `docker ps` to verify there are no related containers running.
|
||||
|
||||
### Source Code Deployment
|
||||
|
||||
#### Deploying openim-server
|
||||
|
||||
Source code deployment is slightly more complex because Docker's networking on Linux differs from Mac.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openimsdk/open-im-server
|
||||
cd open-im-server
|
||||
|
||||
export OPENIM_IP="Your IP" # If it's a cloud server, setting might not be needed
|
||||
make init # Generates configuration files
|
||||
```
|
||||
|
||||
Before deploying openim-server, modify the Kafka logic in the docker-compose.yml file. Replace:
|
||||
|
||||
```yaml
|
||||
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}:${KAFKA_PORT:-19094}
|
||||
```
|
||||
|
||||
With:
|
||||
|
||||
```yaml
|
||||
- KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://127.0.0.1:${KAFKA_PORT:-19094}
|
||||
```
|
||||
|
||||
Then start the service:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
Before starting the openim-server source, set `config/config.yaml` by replacing all instances of `172.28.0.1` with `127.0.0.1`:
|
||||
|
||||
```bash
|
||||
vim config/config.yaml -c "%s/172\.28\.0\.1/127.0.0.1/g" -c "wq"
|
||||
```
|
||||
|
||||
Then start openim-server:
|
||||
|
||||
```bash
|
||||
make start
|
||||
```
|
||||
|
||||
To check the startup:
|
||||
|
||||
```bash
|
||||
make check
|
||||
```
|
||||
|
||||
<aside>
|
||||
🚧 To avoid mishaps, it's best to wait five minutes before running `make check` again.
|
||||
|
||||
</aside>
|
||||
|
||||
#### Deploying openim-chat
|
||||
|
||||
There are several ways to deploy openim-chat, either by source code or using Docker.
|
||||
|
||||
Navigate back to the parent directory:
|
||||
|
||||
```bash
|
||||
cd ..
|
||||
```
|
||||
|
||||
First, let's look at deploying chat from source:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openimsdk/chat
|
||||
cd chat
|
||||
make init # Generates configuration files
|
||||
```
|
||||
|
||||
If openim-chat has not deployed MySQL, you will need to deploy it. Note that the official Docker Hub for MySQL does not support architectures like ARM, so you can use the newer version of the open-source edition:
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name mysql \
|
||||
-p 13306:3306 \
|
||||
-p 3306:33060 \
|
||||
-v "$(pwd)/components/mysql/data:/var/lib/mysql" \
|
||||
-v "/etc/localtime:/etc/localtime" \
|
||||
-e MYSQL_ROOT_PASSWORD="openIM123" \
|
||||
--restart always \
|
||||
mariadb:10.6
|
||||
```
|
||||
|
||||
Before starting the source code of openim-chat, set `config/config.yaml` by replacing all instances of `172.28.0.1` with `127.0.0.1`:
|
||||
|
||||
```bash
|
||||
vim config/config.yaml -c "%s/172\.28\.0\.1/127.0.0.1/g" -c "wq"
|
||||
```
|
||||
|
||||
Then start openim-chat from source:
|
||||
|
||||
```bash
|
||||
make start
|
||||
```
|
||||
|
||||
To check, ensure the following four processes start successfully:
|
||||
|
||||
```bash
|
||||
make check
|
||||
```
|
||||
|
||||
### Docker Deployment
|
||||
|
||||
Refer to https://github.com/openimsdk/openim-docker for Docker deployment instructions, which can be followed similarly on Linux.
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openimsdk/openim-docker
|
||||
cd openim-docker
|
||||
export OPENIM_IP="Your IP"
|
||||
make init
|
||||
docker compose up -d
|
||||
docker compose logs -f openim-server
|
||||
docker compose logs -f openim-chat
|
||||
```
|
||||
|
||||
## GitHub Development Workflow
|
||||
|
||||
### Creating a New Branch
|
||||
|
||||
For new features or fixes, create a new branch:
|
||||
|
||||
```sh
|
||||
git checkout -b feat/your-feature-name
|
||||
```
|
||||
|
||||
### Making Changes and Committing
|
||||
|
||||
1. Make your changes in the code.
|
||||
2. Stage your changes:
|
||||
|
||||
```sh
|
||||
git add .
|
||||
```
|
||||
|
||||
3. Commit with a meaningful message:
|
||||
|
||||
```sh
|
||||
git commit -m "Add a brief description of your changes"
|
||||
```
|
||||
|
||||
### Pushing Changes and Creating Pull Requests
|
||||
|
||||
1. Push your branch to GitHub:
|
||||
|
||||
```sh
|
||||
git push origin feat/your-feature-name
|
||||
```
|
||||
|
||||
2. Go to your fork on GitHub and create a pull request to the main OpenIM repository.
|
||||
|
||||
### Keeping Your Fork Updated
|
||||
|
||||
Regularly sync your fork with the main repository:
|
||||
|
||||
```sh
|
||||
git fetch upstream
|
||||
git checkout main
|
||||
git rebase upstream/main
|
||||
```
|
||||
|
||||
More read: [https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)
|
||||
|
||||
## Testing and Quality Assurance
|
||||
|
||||
Run tests as described in the OpenIM documentation to ensure your changes do not break existing functionality.
|
||||
|
||||
## Conclusion
|
||||
|
||||
This guide provides a comprehensive overview for Mac developers to set up and contribute to OpenIM. By following these steps, you can ensure a smooth and efficient development experience. Happy coding!
|
||||
@@ -6,25 +6,57 @@
|
||||
|
||||
Below are the base images and their versions you'll need:
|
||||
|
||||
- wurstmeister/kafka
|
||||
- redis:7.0.0
|
||||
- mongo:6.0.2
|
||||
- mysql:5.7
|
||||
- wurstmeister/zookeeper
|
||||
- minio/minio
|
||||
- [ ] bitnami/kafka:3.5.1
|
||||
- [ ] redis:7.0.0
|
||||
- [ ] mongo:6.0.2
|
||||
- [ ] bitnami/zookeeper:3.8
|
||||
- [ ] minio/minio:latest
|
||||
|
||||
> [!IMPORTANT]
|
||||
> It is important to note that OpenIM removed mysql components from versions v3.5.0 (release-v3.5) and above, so mysql can be deployed without this requirement or above
|
||||
|
||||
**If you need to install more IM components or monitoring products:**
|
||||
|
||||
OpenIM:
|
||||
|
||||
> [!TIP]
|
||||
> If you need to install more IM components or monitoring products [images.md](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md)
|
||||
|
||||
- [ ] ghcr.io/openimsdk/openim-web:latest
|
||||
- [ ] ghcr.io/openimsdk/openim-admin:latest
|
||||
- [ ] ghcr.io/openimsdk/openim-chat:latest
|
||||
- [ ] ghcr.io/openimsdk/openim-server:latest
|
||||
|
||||
|
||||
Monitoring:
|
||||
|
||||
- [ ] prom/prometheus:v2.48.1
|
||||
- [ ] prom/alertmanager:v0.23.0
|
||||
- [ ] grafana/grafana:10.2.2
|
||||
- [ ] bitnami/node-exporter:1.7.0
|
||||
|
||||
|
||||
Use the following commands to pull these base images:
|
||||
|
||||
```
|
||||
docker pull wurstmeister/kafka
|
||||
```bash
|
||||
docker pull bitnami/kafka:3.5.1
|
||||
docker pull redis:7.0.0
|
||||
docker pull mongo:6.0.2
|
||||
docker pull mysql:5.7
|
||||
docker pull wurstmeister/zookeeper
|
||||
docker pull minio/minio
|
||||
docker pull bitnami/zookeeper:3.8
|
||||
docker pull minio/minio:latest
|
||||
```
|
||||
|
||||
## 2. OpenIM & Chat Images
|
||||
If you need to install more IM components or monitoring products:
|
||||
|
||||
```bash
|
||||
docker pull prom/prometheus:v2.48.1
|
||||
docker pull prom/alertmanager:v0.23.0
|
||||
docker pull grafana/grafana:10.2.2
|
||||
docker pull bitnami/node-exporter:1.7.0
|
||||
```
|
||||
|
||||
## 2. OpenIM Images
|
||||
|
||||
**For detailed understanding of version management and storage of OpenIM and Chat**: [version.md](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md)
|
||||
|
||||
@@ -42,9 +74,26 @@ docker pull ghcr.io/openimsdk/openim-server:<version-name>
|
||||
- Execute the following command to pull the image:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/openimsdk/openim-server:<version-name>
|
||||
docker pull ghcr.io/openimsdk/openim-chat:<version-name>
|
||||
```
|
||||
|
||||
### Web Image
|
||||
|
||||
- Execute the following command to pull the image:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/openimsdk/openim-web:<version-name>
|
||||
```
|
||||
|
||||
### Admin Image
|
||||
|
||||
- Execute the following command to pull the image:
|
||||
|
||||
```bash
|
||||
docker pull ghcr.io/openimsdk/openim-admin:<version-name>
|
||||
```
|
||||
|
||||
|
||||
## 3. Image Storage Selection
|
||||
|
||||
**Repositories**:
|
||||
@@ -71,46 +120,61 @@ You can select from the following versions:
|
||||
1. **Pull images**: Execute the above `docker pull` commands to pull all required images locally.
|
||||
2. **Save images**:
|
||||
|
||||
```
|
||||
```bash
|
||||
docker save -o <tar-file-name>.tar <image-name>
|
||||
```
|
||||
|
||||
1. **Fetch code**: Clone the repository:
|
||||
If you want to save all the images, use the following command:
|
||||
|
||||
```
|
||||
git clone https://github.com/OpenIMSDK/openim-docker.git
|
||||
```bash
|
||||
docker save -o <tar-file-name>.tar $(docker images -q)
|
||||
```
|
||||
|
||||
Or download the code from [Releases](https://github.com/OpenIMSDK/openim-docker/releases/).
|
||||
|
||||
1. **Transfer files**: Use `scp` to transfer all images and code to the intranet server.
|
||||
3. **Fetch code**: Clone the repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openimsdk/openim-docker.git
|
||||
```
|
||||
|
||||
Or download the code from [Releases](https://github.com/openimsdk/openim-docker/releases/).
|
||||
|
||||
> Because of the difference between win and linux newlines, please do not clone the repository with win and then synchronize scp to linux.
|
||||
|
||||
4. **Transfer files**: Use `scp` to transfer all images and code to the intranet server.
|
||||
|
||||
```bash
|
||||
scp <tar-file-name>.tar user@remote-ip:/path/on/remote/server
|
||||
```
|
||||
|
||||
Or choose other transfer methods such as a hard drive.
|
||||
|
||||
1. **Import images**: On the intranet server:
|
||||
5. **Import images**: On the intranet server:
|
||||
|
||||
```
|
||||
```bash
|
||||
docker load -i <tar-file-name>.tar
|
||||
```
|
||||
|
||||
1. **Deploy**: Navigate to the `openim-docker` repository directory and follow the README guide for deployment.
|
||||
2. **Deploy using Docker-compose**:
|
||||
Import directly with shortcut commands:
|
||||
|
||||
```bash
|
||||
for i in `ls ./`;do docker load -i $i;done
|
||||
```
|
||||
docker-compose up -d
|
||||
|
||||
# Verify
|
||||
docker-compose ps
|
||||
6. **Deploy**: Navigate to the `openim-docker` repository directory and follow the [README guide](https://github.com/openimsdk/openim-docker) for deployment.
|
||||
|
||||
7. **Deploy using docker compose**:
|
||||
|
||||
```bash
|
||||
export OPENIM_IP="your ip" # Set Ip
|
||||
make init # Init config
|
||||
docker compose up -d # Deployment
|
||||
docker compose ps # Verify
|
||||
```
|
||||
|
||||
> **Note**: If you're using a version of Docker prior to 20, make sure you've installed `docker-compose`.
|
||||
|
||||
## 6. Reference Links
|
||||
|
||||
- [OpenIMSDK Issue #432](https://github.com/openimsdk/open-im-server/issues/432)
|
||||
- [openimsdk Issue #432](https://github.com/openimsdk/open-im-server/issues/432)
|
||||
- [Notion Link](https://nsddd.notion.site/435ee747c0bc44048da9300a2d745ad3?pvs=25)
|
||||
- [OpenIMSDK Issue #474](https://github.com/openimsdk/open-im-server/issues/474)
|
||||
- [openimsdk Issue #474](https://github.com/openimsdk/open-im-server/issues/474)
|
||||
@@ -111,32 +111,35 @@ Importing Grafana Dashboards is a straightforward process and is applicable to O
|
||||
|
||||
To monitor OpenIM in Grafana, you need to focus on three categories of key metrics, each with its specific deployment and configuration steps:
|
||||
|
||||
1. **OpenIM Metrics (`prometheus-dashboard.yaml`)**:
|
||||
+ **Configuration File Path**: Located at `config/prometheus-dashboard.yaml`.
|
||||
+ **Enabling Monitoring**: Set the environment variable `export PROMETHEUS_ENABLE=true` to enable Prometheus monitoring.
|
||||
+ **More Information**: Refer to the [OpenIM Configuration Guide](https://docs.openim.io/configurations/prometheus-integration).
|
||||
2. **Node Exporter**:
|
||||
+ **Container Deployment**: Deploy the `quay.io/prometheus/node-exporter` container for node monitoring.
|
||||
+ **Get Dashboard**: Access the [Node Exporter Full Feature Dashboard](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) and import it using YAML file download or ID import.
|
||||
+ **Deployment Guide**: Refer to the [Node Exporter Deployment Documentation](https://prometheus.io/docs/guides/node-exporter/).
|
||||
3. **Middleware Metrics**: Each middleware requires specific steps and configurations to enable monitoring. Here is a list of common middleware and links to their respective setup guides:
|
||||
+ MySQL:
|
||||
+ **Configuration**: Ensure MySQL has performance monitoring enabled.
|
||||
+ **Link**: Refer to the [MySQL Monitoring Configuration Guide](https://grafana.com/docs/grafana/latest/datasources/mysql/).
|
||||
+ Redis:
|
||||
+ **Configuration**: Configure Redis to allow monitoring data export.
|
||||
+ **Link**: Refer to the [Redis Monitoring Guide](https://grafana.com/docs/grafana/latest/datasources/redis/).
|
||||
+ MongoDB:
|
||||
+ **Configuration**: Set up monitoring metrics for MongoDB.
|
||||
+ **Link**: Refer to the [MongoDB Monitoring Guide](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/).
|
||||
+ Kafka:
|
||||
+ **Configuration**: Integrate Kafka with Prometheus monitoring.
|
||||
+ **Link**: Refer to the [Kafka Monitoring Guide](https://grafana.com/grafana/plugins/grafana-kafka-datasource/).
|
||||
+ Zookeeper:
|
||||
+ **Configuration**: Ensure Zookeeper can be monitored by Prometheus.
|
||||
+ **Link**: Refer to the [Zookeeper Monitoring Configuration](https://grafana.com/docs/grafana/latest/datasources/zookeeper/).
|
||||
**OpenIM Metrics (`prometheus-dashboard.yaml`)**:
|
||||
|
||||
- **Configuration File Path**: Find this at `config/prometheus-dashboard.yaml`.
|
||||
- **Enabling Monitoring**: Activate Prometheus monitoring by setting the environment variable: `export PROMETHEUS_ENABLE=true`.
|
||||
- **More Information**: For detailed instructions, see the [OpenIM Configuration Guide](https://docs.openim.io/configurations/prometheus-integration).
|
||||
|
||||
**Node Exporter**:
|
||||
|
||||
- **Container Deployment**: Use the container `quay.io/prometheus/node-exporter` for effective node monitoring.
|
||||
- **Access Dashboard**: Visit the [Node Exporter Full Feature Dashboard](https://grafana.com/grafana/dashboards/1860-node-exporter-full/) for dashboard integration either through YAML file download or ID.
|
||||
- **Deployment Guide**: For deployment steps, consult the [Node Exporter Deployment Documentation](https://prometheus.io/docs/guides/node-exporter/).
|
||||
|
||||
**Middleware Metrics**: Different middlewares require unique steps and configurations for monitoring:
|
||||
|
||||
- MySQL:
|
||||
- **Configuration**: Make sure MySQL is set up for performance monitoring.
|
||||
- **Guide**: See the [MySQL Monitoring Configuration Guide](https://grafana.com/docs/grafana/latest/datasources/mysql/).
|
||||
- Redis:
|
||||
- **Configuration**: Adjust Redis settings to enable monitoring data export.
|
||||
- **Guide**: Consult the [Redis Monitoring Guide](https://grafana.com/docs/grafana/latest/datasources/redis/).
|
||||
- MongoDB:
|
||||
- **Configuration**: Configure MongoDB for monitoring metrics.
|
||||
- **Guide**: Visit the [MongoDB Monitoring Guide](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/).
|
||||
- Kafka:
|
||||
- **Configuration**: Set up Kafka for Prometheus monitoring integration.
|
||||
- **Guide**: Refer to the [Kafka Monitoring Guide](https://grafana.com/grafana/plugins/grafana-kafka-datasource/).
|
||||
- Zookeeper:
|
||||
- **Configuration**: Ensure Prometheus can monitor Zookeeper.
|
||||
- **Guide**: Check out the [Zookeeper Monitoring Configuration](https://grafana.com/docs/grafana/latest/datasources/zookeeper/).
|
||||
|
||||
**Importing Steps**:
|
||||
|
||||
|
||||
251
docs/contrib/release.md
Normal file
251
docs/contrib/release.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# OpenIM Release Automation Design Document
|
||||
|
||||
This document outlines the automation process for releasing OpenIM. You can use the `make release` command for automated publishing. We will discuss how to use the `make release` command and Github Actions CICD separately, while also providing insight into the design principles involved.
|
||||
|
||||
## Github Actions Automation
|
||||
|
||||
In our CICD pipeline, we have implemented logic for automating the release process using the goreleaser tool. To achieve this, follow these steps on your local machine or server:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/openimsdk/open-im-server
|
||||
cd open-im-server
|
||||
git tag -a v3.6.0 -s -m "release: xxx"
|
||||
# For pre-release versions: git tag -a v3.6.0-rc.0 -s -m "pre-release: xxx"
|
||||
git push origin v3.6.0
|
||||
```
|
||||
|
||||
The remaining tasks are handled by automated processes:
|
||||
|
||||
+ Automatically complete the release publication on Github
|
||||
+ Automatically build the `v3.6.0` version image and push it to aliyun, dockerhub, and github
|
||||
|
||||
Through these automated steps, we achieve rapid and efficient OpenIM version releases, simplifying the release process and enhancing productivity.
|
||||
|
||||
|
||||
Certainly, here is the continuation of the document in English:
|
||||
|
||||
## Local Make Release Design
|
||||
|
||||
There are two primary scenarios for local usage:
|
||||
|
||||
+ Advanced compilation and release, manually executed locally
|
||||
+ Quick compilation verification and version release, manually executed locally
|
||||
|
||||
**These two scenarios can also be combined, for example, by tagging locally and then releasing:**
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -a -s -m "release(v3.6.0): ......"
|
||||
git tag v3.6.0
|
||||
git release
|
||||
git push origin main
|
||||
```
|
||||
|
||||
In a local environment, you can use the `make release` command to complete the release process. The main implementation logic can be found in the `/data/workspaces/open-im-server/scripts/lib/release.sh` file. First, let's explore its usage through the help information.
|
||||
|
||||
### Help Information
|
||||
|
||||
To view the help information, execute the following command:
|
||||
|
||||
```bash
|
||||
$ ./scripts/release.sh --help
|
||||
Usage: release.sh [options]
|
||||
Options:
|
||||
-h, --help Display this help message
|
||||
-se, --setup-env Execute environment setup
|
||||
-vp, --verify-prereqs Execute prerequisite verification
|
||||
-bc, --build-command Execute build command
|
||||
-bi, --build-image Execute build image (default is not executed)
|
||||
-pt, --package-tarballs Execute tarball packaging
|
||||
-ut, --upload-tarballs Execute tarball upload
|
||||
-gr, --github-release Execute GitHub release
|
||||
-gc, --generate-changelog Execute changelog generation
|
||||
```
|
||||
|
||||
### Default Behavior
|
||||
|
||||
If no options are provided, all operations are executed by default:
|
||||
|
||||
```bash
|
||||
# If no options are provided, enable all operations by default
|
||||
if [ "$#" -eq 0 ]; then
|
||||
perform_setup_env=true
|
||||
perform_verify_prereqs=true
|
||||
perform_build_command=true
|
||||
perform_package_tarballs=true
|
||||
perform_upload_tarballs=true
|
||||
perform_github_release=true
|
||||
perform_generate_changelog=true
|
||||
# TODO: Defaultly not enable build_image
|
||||
# perform_build_image=true
|
||||
fi
|
||||
```
|
||||
|
||||
### Environment Variable Setup
|
||||
|
||||
Before starting, you need to set environment variables:
|
||||
|
||||
```bash
|
||||
export TENCENT_SECRET_KEY=OZZ****************************
|
||||
export TENCENT_SECRET_ID=AKI****************************
|
||||
```
|
||||
|
||||
### Modifying COS Account and Password
|
||||
|
||||
If you need to change the COS account, password, and bucket information, please modify the following section in the `/data/workspaces/open-im-server/scripts/lib/release.sh` file:
|
||||
|
||||
```bash
|
||||
readonly BUCKET="openim-1306374445"
|
||||
readonly REGION="ap-guangzhou"
|
||||
readonly COS_RELEASE_DIR="openim-release"
|
||||
```
|
||||
|
||||
### GitHub Release Configuration
|
||||
|
||||
If you intend to use the GitHub Release feature, you also need to set the environment variable:
|
||||
|
||||
```bash
|
||||
export GITHUB_TOKEN="your_github_token"
|
||||
```
|
||||
|
||||
### Modifying GitHub Release Basic Information
|
||||
|
||||
If you need to modify the basic information of GitHub Release, please edit the following section in the `/data/workspaces/open-im-server/scripts/lib/release.sh` file:
|
||||
|
||||
```bash
|
||||
# OpenIM GitHub account information
|
||||
readonly OPENIM_GITHUB_ORG=openimsdk
|
||||
readonly OPENIM_GITHUB_REPO=open-im-server
|
||||
```
|
||||
|
||||
This setup allows you to configure and execute the local release process according to your specific needs.
|
||||
|
||||
|
||||
### GitHub Release Versioning Rules
|
||||
|
||||
Firstly, it's important to note that GitHub Releases should primarily be for pre-release versions. However, goreleaser might provide a `prerelease: auto` option, which automatically marks versions with pre-release indicators like `-rc1`, `-beta`, etc., as pre-releases.
|
||||
|
||||
So, if your most recent tag does not have pre-release indicators such as `-rc1` or `-beta`, even if you use `make release` for pre-release versions, goreleaser might still consider them as formal releases.
|
||||
|
||||
To avoid this issue, I have added the `--draft` flag to github-release. This way, all releases are created as drafts.
|
||||
|
||||
## CICD Release Documentation Design
|
||||
|
||||
The release records still require manual composition for GitHub Release. This is different from github-release.
|
||||
|
||||
This approach ensures that all releases are initially created as drafts, allowing you to manually review and edit the release documentation on GitHub. This manual step provides more control and allows you to curate release notes and other information before making them public.
|
||||
|
||||
|
||||
## Makefile Section
|
||||
|
||||
This document aims to elaborate and explain key sections of the OpenIM Release automation design, including the Makefile section and functions within the code. Below, we will provide a detailed explanation of the logic and functions of each section.
|
||||
|
||||
In the project's root directory, the Makefile imports a subdirectory:
|
||||
|
||||
```makefile
|
||||
include scripts/make-rules/release.mk
|
||||
```
|
||||
|
||||
And defines the `release` target as follows:
|
||||
|
||||
```makefile
|
||||
## release: release the project ✨
|
||||
.PHONY: release release: release.verify release.ensure-tag
|
||||
@scripts/release.sh
|
||||
```
|
||||
|
||||
### Importing Subdirectory
|
||||
|
||||
At the beginning of the Makefile, the `include scripts/make-rules/release.mk` statement imports the `release.mk` file from the subdirectory. This file contains rules and configurations related to releases to be used in subsequent operations.
|
||||
|
||||
### The `release` Target
|
||||
|
||||
The Makefile defines a target named `release`, which is used to execute the project's release operation. This target is marked as a phony target (`.PHONY`), meaning it doesn't represent an actual file or directory but serves as an identifier for executing a series of actions.
|
||||
|
||||
In the `release` target, two dependency targets are executed first: `release.verify` and `release.ensure-tag`. Afterward, the `scripts/release.sh` script is called to perform the actual release operation.
|
||||
|
||||
## Logic of `release.verify` and `release.ensure-tag`
|
||||
|
||||
```makefile
|
||||
## release.verify: Check if a tool is installed and install it
|
||||
.PHONY: release.verify
|
||||
release.verify: tools.verify.git-chglog tools.verify.github-release tools.verify.coscmd tools.verify.coscli
|
||||
|
||||
## release.ensure-tag: ensure tag
|
||||
.PHONY: release.ensure-tag
|
||||
release.ensure-tag: tools.verify.gsemver
|
||||
@scripts/ensure-tag.sh
|
||||
```
|
||||
|
||||
### `release.verify` Target
|
||||
|
||||
The `release.verify` target is used to check and install tools. It depends on four sub-targets: `tools.verify.git-chglog`, `tools.verify.github-release`, `tools.verify.coscmd`, and `tools.verify.coscli`. These sub-targets aim to check if specific tools are installed and attempt to install them if they are not.
|
||||
|
||||
The purpose of this target is to ensure that the necessary tools required for the release process are available so that subsequent operations can be executed smoothly.
|
||||
|
||||
### `release.ensure-tag` Target
|
||||
|
||||
The `release.ensure-tag` target is used to ensure that the project has a version tag. It depends on the sub-target `tools.verify.gsemver`, indicating that it should check if the `gsemver` tool is installed before executing.
|
||||
|
||||
When the `release.ensure-tag` target is executed, it calls the `scripts/ensure-tag.sh` script to ensure that the project has a version tag. Version tags are typically used to identify specific versions of the project for management and release in version control systems.
|
||||
|
||||
## Logic of `release.sh` Script
|
||||
|
||||
```bash
|
||||
openim::golang::setup_env
|
||||
openim::build::verify_prereqs
|
||||
openim::release::verify_prereqs
|
||||
#openim::build::build_image
|
||||
openim::build::build_command
|
||||
openim::release::package_tarballs
|
||||
openim::release::upload_tarballs
|
||||
git push origin ${VERSION}
|
||||
#openim::release::github_release
|
||||
#openim::release::generate_changelog
|
||||
```
|
||||
|
||||
The `release.sh` script is responsible for executing the actual release operations. Below is the logic of this script:
|
||||
|
||||
1. `openim::golang::setup_env`: This function sets up some configurations for the Golang development environment.
|
||||
|
||||
2. `openim::build::verify_prereqs`: This function is used to verify whether the prerequisites for building are met. This includes checking dependencies, environment variables, and more.
|
||||
|
||||
3. `openim::release::verify_prereqs`: Similar to the previous function, this one is used to verify whether the prerequisites for the release are met. It focuses on conditions relevant to the release.
|
||||
|
||||
4. `openim::build::build_command`: This function is responsible for building the project's command, which typically involves compiling the project or performing other build operations.
|
||||
|
||||
5. `openim::release::package_tarballs`: This function is used to package tarball files required for the release. These tarballs are usually used for distribution packages during the release.
|
||||
|
||||
6. `openim::release::upload_tarballs`: This function is used to upload the packaged tarball files, typically to a distribution platform or repository.
|
||||
|
||||
7. `git push origin ${VERSION}`: This line of command pushes the version tag to the remote Git repository's `origin` branch, marking this release in the version control system.
|
||||
|
||||
In the comments, you can see that there are some operations that are commented out, such as `openim::build::build_image`, `openim::release::github_release`, and `openim::release::generate_changelog`. These operations are related to building images, releasing to GitHub, and generating changelogs, and they can be enabled in the release process as needed.
|
||||
|
||||
Let's take a closer look at the function responsible for packaging the tarball files:
|
||||
|
||||
```bash
|
||||
function openim::release::package_tarballs() {
|
||||
# Clean out any old releases
|
||||
rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}"
|
||||
mkdir -p "${RELEASE_TARS}"
|
||||
openim::release::package_src_tarball &
|
||||
openim::release::package_client_tarballs &
|
||||
openim::release::package_openim_manifests_tarball &
|
||||
openim::release::package_server_tarballs &
|
||||
openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; }
|
||||
|
||||
openim::release::package_final_tarball & # _final depends on some of the previous phases
|
||||
openim::util::wait-for-jobs || { openim::log::error "previous tarball phase failed"; return 1; }
|
||||
}
|
||||
```
|
||||
|
||||
The `openim::release::package_tarballs()` function is responsible for packaging the tarball files required for the release. Here is the specific logic of this function:
|
||||
|
||||
1. `rm -rf "${RELEASE_STAGE}" "${RELEASE_TARS}" "${RELEASE_IMAGES}"`: First, the function removes any old release directories and files to ensure a clean starting state.
|
||||
|
||||
2. `mkdir -p "${RELEASE_TARS}"`: Next, it creates a directory `${RELEASE_TARS}` to store the packaged tarball files. If the directory doesn't exist, it will be created.
|
||||
|
||||
3. `openim::release::package_final_tarball &`: This is an asynchronous operation that depends on some of the previous phases. It is likely used to package the final tarball file, which includes the contents of all previous asynchronous operations.
|
||||
|
||||
4. `openim::util::wait-for-jobs`: It waits for all asynchronous operations to complete. If any of the previous asynchronous operations fail, an error will be returned.
|
||||
@@ -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
|
||||
16
go.mod
16
go.mod
@@ -4,8 +4,8 @@ go 1.19
|
||||
|
||||
require (
|
||||
firebase.google.com/go v3.13.0+incompatible
|
||||
github.com/OpenIMSDK/protocol v0.0.44
|
||||
github.com/OpenIMSDK/tools v0.0.21
|
||||
github.com/OpenIMSDK/protocol v0.0.49
|
||||
github.com/OpenIMSDK/tools v0.0.28
|
||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||
github.com/dtm-labs/rockscache v0.1.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
@@ -18,6 +18,7 @@ require (
|
||||
github.com/minio/minio-go/v7 v7.0.63
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
||||
github.com/openimsdk/localcache v0.0.1
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.17.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
@@ -38,6 +39,7 @@ require (
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/redis/go-redis/v9 v9.2.1
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stathat/consistent v1.0.0
|
||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
@@ -81,6 +83,7 @@ require (
|
||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
@@ -116,7 +119,6 @@ require (
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/sergi/go-diff v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/src-d/gcfg v1.4.0 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
@@ -131,8 +133,8 @@ require (
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
golang.org/x/oauth2 v0.13.0 // indirect
|
||||
golang.org/x/sys v0.14.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
golang.org/x/sys v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
@@ -153,6 +155,8 @@ require (
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/openimsdk/localcache => ./pkg/localcache
|
||||
|
||||
24
go.sum
24
go.sum
@@ -18,10 +18,10 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
|
||||
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
|
||||
github.com/OpenIMSDK/protocol v0.0.44 h1:P+9gJ9EW3y+VmzrjPludzn/5r1fjubaC19mKYJ7Oiew=
|
||||
github.com/OpenIMSDK/protocol v0.0.44/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.49 h1:wcqJOMBis7f153zNI7V82Fc4WyqA1GanMgXUQgL618k=
|
||||
github.com/OpenIMSDK/protocol v0.0.49/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
||||
github.com/OpenIMSDK/tools v0.0.27 h1:J/kSRqM+y9U4XK/pQ9RkEB31oQ5BTYD1oA5r1PITPRA=
|
||||
github.com/OpenIMSDK/tools v0.0.27/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=
|
||||
@@ -171,6 +171,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
@@ -371,8 +373,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg=
|
||||
golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
|
||||
@@ -438,12 +440,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
|
||||
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -451,8 +453,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
11
go.work
11
go.work
@@ -4,14 +4,15 @@ use (
|
||||
.
|
||||
./test/typecheck
|
||||
./tools/changelog
|
||||
//./tools/imctl
|
||||
./tools/component
|
||||
./tools/data-conversion
|
||||
./tools/formitychecker
|
||||
./tools/imctl
|
||||
./tools/infra
|
||||
./tools/ncpu
|
||||
./tools/openim-web
|
||||
./tools/up35
|
||||
./tools/url2im
|
||||
./tools/versionchecker
|
||||
./tools/yamlfmt
|
||||
./tools/component
|
||||
./tools/url2im
|
||||
./tools/data-conversion
|
||||
./tools/up35
|
||||
)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
@@ -43,7 +44,7 @@ import (
|
||||
)
|
||||
|
||||
func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient) *gin.Engine {
|
||||
discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials())) // 默认RPC中间件
|
||||
discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) // 默认RPC中间件
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
r := gin.New()
|
||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
@@ -83,6 +84,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 +206,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)
|
||||
|
||||
@@ -201,26 +201,31 @@ func (u *UserApi) GetSubscribeUsersStatus(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.GetSubscribeUsersStatus, u.Client, c)
|
||||
}
|
||||
|
||||
// ProcessUserCommandAdd user general function add
|
||||
// ProcessUserCommandAdd user general function add.
|
||||
func (u *UserApi) ProcessUserCommandAdd(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.ProcessUserCommandAdd, u.Client, c)
|
||||
}
|
||||
|
||||
// ProcessUserCommandDelete user general function delete
|
||||
// ProcessUserCommandDelete user general function delete.
|
||||
func (u *UserApi) ProcessUserCommandDelete(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.ProcessUserCommandDelete, u.Client, c)
|
||||
}
|
||||
|
||||
// ProcessUserCommandUpdate user general function update
|
||||
// ProcessUserCommandUpdate user general function update.
|
||||
func (u *UserApi) ProcessUserCommandUpdate(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.ProcessUserCommandUpdate, u.Client, c)
|
||||
}
|
||||
|
||||
// ProcessUserCommandGet user general function get
|
||||
// ProcessUserCommandGet user general function get.
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err
|
||||
// func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID
|
||||
// string) cbApi.CommonCallbackResp {
|
||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||
// if !config.Config.Callback.CallbackUserOnline.Enable {
|
||||
// if !config.Config.Callback.CallbackUserOnline.WithEnable {
|
||||
// return callbackResp
|
||||
// }
|
||||
// callbackUserOnlineReq := cbApi.CallbackUserOnlineReq{
|
||||
@@ -134,7 +134,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err
|
||||
//}
|
||||
//func callbackUserOffline(operationID, userID string, platformID int, connID string) cbApi.CommonCallbackResp {
|
||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||
// if !config.Config.Callback.CallbackUserOffline.Enable {
|
||||
// if !config.Config.Callback.CallbackUserOffline.WithEnable {
|
||||
// return callbackResp
|
||||
// }
|
||||
// callbackOfflineReq := cbApi.CallbackUserOfflineReq{
|
||||
@@ -161,7 +161,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err
|
||||
//}
|
||||
//func callbackUserKickOff(operationID string, userID string, platformID int) cbApi.CommonCallbackResp {
|
||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||
// if !config.Config.Callback.CallbackUserKickOff.Enable {
|
||||
// if !config.Config.Callback.CallbackUserKickOff.WithEnable {
|
||||
// return callbackResp
|
||||
// }
|
||||
// callbackUserKickOffReq := cbApi.CallbackUserKickOffReq{
|
||||
|
||||
@@ -25,8 +25,6 @@ import (
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
@@ -59,7 +57,7 @@ type Server struct {
|
||||
rpcPort int
|
||||
prometheusPort int
|
||||
LongConnServer LongConnServer
|
||||
pushTerminal []int
|
||||
pushTerminal map[int]struct{}
|
||||
}
|
||||
|
||||
func (s *Server) SetLongConnServer(LongConnServer LongConnServer) {
|
||||
@@ -67,12 +65,15 @@ func (s *Server) SetLongConnServer(LongConnServer LongConnServer) {
|
||||
}
|
||||
|
||||
func NewServer(rpcPort int, proPort int, longConnServer LongConnServer) *Server {
|
||||
return &Server{
|
||||
s := &Server{
|
||||
rpcPort: rpcPort,
|
||||
prometheusPort: proPort,
|
||||
LongConnServer: longConnServer,
|
||||
pushTerminal: []int{constant.IOSPlatformID, constant.AndroidPlatformID},
|
||||
pushTerminal: make(map[int]struct{}),
|
||||
}
|
||||
s.pushTerminal[constant.IOSPlatformID] = struct{}{}
|
||||
s.pushTerminal[constant.AndroidPlatformID] = struct{}{}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Server) OnlinePushMsg(
|
||||
@@ -126,13 +127,9 @@ func (s *Server) OnlineBatchPushOneMsg(
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (s *Server) SuperGroupOnlineBatchPushOneMsg(
|
||||
ctx context.Context,
|
||||
req *msggateway.OnlineBatchPushOneMsgReq,
|
||||
func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq,
|
||||
) (*msggateway.OnlineBatchPushOneMsgResp, error) {
|
||||
|
||||
var singleUserResults []*msggateway.SingleMsgToUserResults
|
||||
|
||||
for _, v := range req.PushToUserIDs {
|
||||
var resp []*msggateway.SingleMsgToUserPlatform
|
||||
results := &msggateway.SingleMsgToUserResults{
|
||||
@@ -153,23 +150,22 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(
|
||||
}
|
||||
|
||||
userPlatform := &msggateway.SingleMsgToUserPlatform{
|
||||
RecvID: v,
|
||||
RecvPlatFormID: int32(client.PlatformID),
|
||||
PlatFormID: int32(client.PlatformID),
|
||||
}
|
||||
if !client.IsBackground ||
|
||||
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
|
||||
err := client.PushMessage(ctx, req.MsgData)
|
||||
if err != nil {
|
||||
userPlatform.ResultCode = -2
|
||||
userPlatform.ResultCode = int64(errs.ErrPushMsgErr.Code())
|
||||
resp = append(resp, userPlatform)
|
||||
} else {
|
||||
if utils.IsContainInt(client.PlatformID, s.pushTerminal) {
|
||||
if _, ok := s.pushTerminal[client.PlatformID]; ok {
|
||||
results.OnlinePush = true
|
||||
resp = append(resp, userPlatform)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userPlatform.ResultCode = -3
|
||||
userPlatform.ResultCode = int64(errs.ErrIOSBackgroundPushErr.Code())
|
||||
resp = append(resp, userPlatform)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -29,6 +28,8 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/apiresp"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
@@ -66,7 +66,7 @@ func StartTransfer(prometheusPort int) error {
|
||||
if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil {
|
||||
return err
|
||||
}
|
||||
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
msgModel := cache.NewMsgCacheModel(rdb)
|
||||
msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase())
|
||||
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel)
|
||||
|
||||
@@ -16,6 +16,7 @@ package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
@@ -37,7 +38,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 +74,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 +108,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{
|
||||
@@ -136,3 +137,14 @@ func callbackBeforeSuperGroupOnlinePush(
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func GetContent(msg *sdkws.MsgData) string {
|
||||
if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd {
|
||||
var notification sdkws.NotificationElem
|
||||
if err := json.Unmarshal(msg.Content, ¬ification); err != nil {
|
||||
return ""
|
||||
}
|
||||
return notification.Detail
|
||||
} else {
|
||||
return string(msg.Content)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
type Consumer struct {
|
||||
pushCh ConsumerHandler
|
||||
successCount uint64
|
||||
}
|
||||
|
||||
func NewConsumer(pusher *Pusher) *Consumer {
|
||||
return &Consumer{
|
||||
pushCh: *NewConsumerHandler(pusher),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Consumer) Start() {
|
||||
// statistics.NewStatistics(&c.successCount, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to
|
||||
// msg_gateway count", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval)
|
||||
go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(&c.pushCh)
|
||||
}
|
||||
@@ -16,17 +16,16 @@ package dummy
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
)
|
||||
|
||||
func NewClient() *Dummy {
|
||||
func NewDummy() *Dummy {
|
||||
return &Dummy{}
|
||||
}
|
||||
|
||||
type Dummy struct {
|
||||
}
|
||||
|
||||
func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package fcm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"path/filepath"
|
||||
|
||||
firebase "firebase.google.com/go"
|
||||
@@ -25,7 +26,6 @@ import (
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
)
|
||||
@@ -39,7 +39,7 @@ type Fcm struct {
|
||||
cache cache.MsgModel
|
||||
}
|
||||
|
||||
func NewClient(cache cache.MsgModel) *Fcm {
|
||||
func NewFcm(cache cache.MsgModel) *Fcm {
|
||||
projectRoot := config.GetProjectRoot()
|
||||
credentialsFilePath := filepath.Join(projectRoot, "config", config.Config.Push.Fcm.ServiceAccount)
|
||||
opt := option.WithCredentialsFile(credentialsFilePath)
|
||||
@@ -56,7 +56,7 @@ func NewClient(cache cache.MsgModel) *Fcm {
|
||||
return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}
|
||||
}
|
||||
|
||||
func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
// accounts->registrationToken
|
||||
allTokens := make(map[string][]string, 0)
|
||||
for _, account := range userIDs {
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -30,7 +31,6 @@ import (
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils/splitter"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
@@ -55,17 +55,17 @@ const (
|
||||
taskIDTTL = 1000 * 60 * 60 * 24
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
type GeTui struct {
|
||||
cache cache.MsgModel
|
||||
tokenExpireTime int64
|
||||
taskIDTTL int64
|
||||
}
|
||||
|
||||
func NewClient(cache cache.MsgModel) *Client {
|
||||
return &Client{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL}
|
||||
func NewGeTui(cache cache.MsgModel) *GeTui {
|
||||
return &GeTui{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL}
|
||||
}
|
||||
|
||||
func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (g *GeTui) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
token, err := g.cache.GetGetuiToken(ctx)
|
||||
if err != nil {
|
||||
if errs.Unwrap(err) == redis.Nil {
|
||||
@@ -111,7 +111,7 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) {
|
||||
func (g *GeTui) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) {
|
||||
h := sha256.New()
|
||||
h.Write(
|
||||
[]byte(config.Config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.GeTui.MasterSecret),
|
||||
@@ -131,7 +131,7 @@ func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expir
|
||||
return respAuth.Token, int64(expire), err
|
||||
}
|
||||
|
||||
func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) (string, error) {
|
||||
func (g *GeTui) GetTaskID(ctx context.Context, token string, pushReq PushReq) (string, error) {
|
||||
respTask := TaskResp{}
|
||||
ttl := int64(1000 * 60 * 5)
|
||||
pushReq.Settings = &Settings{TTL: &ttl}
|
||||
@@ -143,7 +143,7 @@ func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) (
|
||||
}
|
||||
|
||||
// max num is 999.
|
||||
func (g *Client) batchPush(ctx context.Context, token string, userIDs []string, pushReq PushReq) error {
|
||||
func (g *GeTui) batchPush(ctx context.Context, token string, userIDs []string, pushReq PushReq) error {
|
||||
taskID, err := g.GetTaskID(ctx, token, pushReq)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -152,21 +152,21 @@ func (g *Client) batchPush(ctx context.Context, token string, userIDs []string,
|
||||
return g.request(ctx, batchPushURL, pushReq, token, nil)
|
||||
}
|
||||
|
||||
func (g *Client) singlePush(ctx context.Context, token, userID string, pushReq PushReq) error {
|
||||
func (g *GeTui) singlePush(ctx context.Context, token, userID string, pushReq PushReq) error {
|
||||
operationID := mcontext.GetOperationID(ctx)
|
||||
pushReq.RequestID = &operationID
|
||||
pushReq.Audience = &Audience{Alias: []string{userID}}
|
||||
return g.request(ctx, pushURL, pushReq, token, nil)
|
||||
}
|
||||
|
||||
func (g *Client) request(ctx context.Context, url string, input any, token string, output any) error {
|
||||
func (g *GeTui) request(ctx context.Context, url string, input any, token string, output any) error {
|
||||
header := map[string]string{"token": token}
|
||||
resp := &Resp{}
|
||||
resp.Data = output
|
||||
return g.postReturn(ctx, config.Config.Push.GeTui.PushUrl+url, header, input, resp, 3)
|
||||
}
|
||||
|
||||
func (g *Client) postReturn(
|
||||
func (g *GeTui) postReturn(
|
||||
ctx context.Context,
|
||||
url string,
|
||||
header map[string]string,
|
||||
@@ -181,7 +181,7 @@ func (g *Client) postReturn(
|
||||
return output.parseError()
|
||||
}
|
||||
|
||||
func (g *Client) getTokenAndSave2Redis(ctx context.Context) (token string, err error) {
|
||||
func (g *GeTui) getTokenAndSave2Redis(ctx context.Context) (token string, err error) {
|
||||
token, _, err = g.Auth(ctx, time.Now().UnixNano()/1e6)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -193,7 +193,7 @@ func (g *Client) getTokenAndSave2Redis(ctx context.Context) (token string, err e
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (g *Client) GetTaskIDAndSave2Redis(ctx context.Context, token string, pushReq PushReq) (taskID string, err error) {
|
||||
func (g *GeTui) GetTaskIDAndSave2Redis(ctx context.Context, token string, pushReq PushReq) (taskID string, err error) {
|
||||
pushReq.Settings = &Settings{TTL: &g.taskIDTTL}
|
||||
taskID, err = g.GetTaskID(ctx, token, pushReq)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush/body"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||
@@ -27,7 +27,7 @@ import (
|
||||
|
||||
type JPush struct{}
|
||||
|
||||
func NewClient() *JPush {
|
||||
func NewJPush() *JPush {
|
||||
return &JPush{}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func (j *JPush) getAuthorization(appKey string, masterSecret string) string {
|
||||
return Authorization
|
||||
}
|
||||
|
||||
func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
|
||||
func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
|
||||
var pf body.Platform
|
||||
pf.SetAll()
|
||||
var au body.Audience
|
||||
|
||||
52
internal/push/offlinepush/offlinepusher.go
Normal file
52
internal/push/offlinepush/offlinepusher.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package offlinepush
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
GETUI = "getui"
|
||||
FIREBASE = "fcm"
|
||||
JPUSH = "jpush"
|
||||
)
|
||||
|
||||
// OfflinePusher Offline Pusher.
|
||||
type OfflinePusher interface {
|
||||
Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error
|
||||
}
|
||||
|
||||
func NewOfflinePusher(cache cache.MsgModel) OfflinePusher {
|
||||
var offlinePusher OfflinePusher
|
||||
switch config.Config.Push.Enable {
|
||||
case GETUI:
|
||||
offlinePusher = getui.NewGeTui(cache)
|
||||
case FIREBASE:
|
||||
offlinePusher = fcm.NewFcm(cache)
|
||||
case JPUSH:
|
||||
offlinePusher = jpush.NewJPush()
|
||||
default:
|
||||
offlinePusher = dummy.NewDummy()
|
||||
}
|
||||
return offlinePusher
|
||||
}
|
||||
14
internal/push/offlinepush/options/options.go
Normal file
14
internal/push/offlinepush/options/options.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package options
|
||||
|
||||
// Opts opts.
|
||||
type Opts struct {
|
||||
Signal *Signal
|
||||
IOSPushSound string
|
||||
IOSBadgeCount bool
|
||||
Ex string
|
||||
}
|
||||
|
||||
// Signal message id.
|
||||
type Signal struct {
|
||||
ClientMsgID string
|
||||
}
|
||||
211
internal/push/onlinepusher.go
Normal file
211
internal/push/onlinepusher.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"google.golang.org/grpc"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
ENVNAME = "ENVS_DISCOVERY"
|
||||
KUBERNETES = "k8s"
|
||||
ZOOKEEPER = "zookeeper"
|
||||
)
|
||||
|
||||
type OnlinePusher interface {
|
||||
GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error)
|
||||
GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults,
|
||||
pushToUserIDs *[]string) []string
|
||||
}
|
||||
|
||||
type emptyOnlinePUsher struct{}
|
||||
|
||||
func newEmptyOnlinePUsher() *emptyOnlinePUsher {
|
||||
return &emptyOnlinePUsher{}
|
||||
}
|
||||
|
||||
func (emptyOnlinePUsher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
log.ZWarn(ctx, "emptyOnlinePUsher GetConnsAndOnlinePush", nil)
|
||||
return nil, nil
|
||||
}
|
||||
func (u emptyOnlinePUsher) GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData,
|
||||
wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string {
|
||||
log.ZWarn(ctx, "emptyOnlinePUsher GetOnlinePushFailedUserIDs", nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewOnlinePusher(disCov discoveryregistry.SvcDiscoveryRegistry) OnlinePusher {
|
||||
var envType string
|
||||
if value := os.Getenv(ENVNAME); value != "" {
|
||||
envType = os.Getenv(ENVNAME)
|
||||
} else {
|
||||
envType = config.Config.Envs.Discovery
|
||||
}
|
||||
switch envType {
|
||||
case KUBERNETES:
|
||||
return NewK8sStaticConsistentHash(disCov)
|
||||
case ZOOKEEPER:
|
||||
return NewDefaultAllNode(disCov)
|
||||
default:
|
||||
return newEmptyOnlinePUsher()
|
||||
}
|
||||
}
|
||||
|
||||
type DefaultAllNode struct {
|
||||
disCov discoveryregistry.SvcDiscoveryRegistry
|
||||
}
|
||||
|
||||
func NewDefaultAllNode(disCov discoveryregistry.SvcDiscoveryRegistry) *DefaultAllNode {
|
||||
return &DefaultAllNode{disCov: disCov}
|
||||
}
|
||||
|
||||
func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
conns, err := d.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
|
||||
maxWorkers = config.Config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
|
||||
wg.SetLimit(maxWorkers)
|
||||
|
||||
// Online push message
|
||||
for _, conn := range conns {
|
||||
conn := conn // loop var safe
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(conn)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
// always return nil
|
||||
return wsResults, nil
|
||||
}
|
||||
|
||||
func (d *DefaultAllNode) GetOnlinePushFailedUserIDs(_ context.Context, msg *sdkws.MsgData,
|
||||
wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string {
|
||||
|
||||
onlineSuccessUserIDs := []string{msg.SendID}
|
||||
for _, v := range wsResults {
|
||||
//message sender do not need offline push
|
||||
if msg.SendID == v.UserID {
|
||||
continue
|
||||
}
|
||||
// mobile online push success
|
||||
if v.OnlinePush {
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return utils.SliceSub(*pushToUserIDs, onlineSuccessUserIDs)
|
||||
}
|
||||
|
||||
type K8sStaticConsistentHash struct {
|
||||
disCov discoveryregistry.SvcDiscoveryRegistry
|
||||
}
|
||||
|
||||
func NewK8sStaticConsistentHash(disCov discoveryregistry.SvcDiscoveryRegistry) *K8sStaticConsistentHash {
|
||||
return &K8sStaticConsistentHash{disCov: disCov}
|
||||
}
|
||||
|
||||
func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
|
||||
pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
|
||||
var usersHost = make(map[string][]string)
|
||||
for _, v := range pushToUserIDs {
|
||||
tHost, err := k.disCov.GetUserIdHashGatewayHost(ctx, v)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "get msg gateway hash error", err)
|
||||
return nil, err
|
||||
}
|
||||
tUsers, tbl := usersHost[tHost]
|
||||
if tbl {
|
||||
tUsers = append(tUsers, v)
|
||||
usersHost[tHost] = tUsers
|
||||
} else {
|
||||
usersHost[tHost] = []string{v}
|
||||
}
|
||||
}
|
||||
log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
|
||||
var usersConns = make(map[*grpc.ClientConn][]string)
|
||||
for host, userIds := range usersHost {
|
||||
tconn, _ := k.disCov.GetConn(ctx, host)
|
||||
usersConns[tconn] = userIds
|
||||
}
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
maxWorkers = config.Config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
wg.SetLimit(maxWorkers)
|
||||
for conn, userIds := range usersConns {
|
||||
tcon := conn
|
||||
tuserIds := userIds
|
||||
wg.Go(func() error {
|
||||
input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
|
||||
msgClient := msggateway.NewMsgGatewayClient(tcon)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
_ = wg.Wait()
|
||||
return wsResults, nil
|
||||
}
|
||||
func (k *K8sStaticConsistentHash) GetOnlinePushFailedUserIDs(_ context.Context, _ *sdkws.MsgData,
|
||||
wsResults []*msggateway.SingleMsgToUserResults, _ *[]string) []string {
|
||||
var needOfflinePushUserIDs []string
|
||||
for _, v := range wsResults {
|
||||
if !v.OnlinePush {
|
||||
needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
return needOfflinePushUserIDs
|
||||
}
|
||||
51
internal/push/push.go
Normal file
51
internal/push/push.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
pbpush "github.com/OpenIMSDK/protocol/push"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type pushServer struct {
|
||||
database controller.PushDatabase
|
||||
disCov discoveryregistry.SvcDiscoveryRegistry
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
pushCh *ConsumerHandler
|
||||
}
|
||||
|
||||
func (p pushServer) PushMsg(ctx context.Context, req *pbpush.PushMsgReq) (*pbpush.PushMsgResp, error) {
|
||||
//todo reserved Interface
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (p pushServer) DelUserPushToken(ctx context.Context,
|
||||
req *pbpush.DelUserPushTokenReq) (resp *pbpush.DelUserPushTokenResp, err error) {
|
||||
if err = p.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbpush.DelUserPushTokenResp{}, nil
|
||||
}
|
||||
|
||||
func Start(disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheModel := cache.NewMsgCacheModel(rdb)
|
||||
offlinePusher := offlinepush.NewOfflinePusher(cacheModel)
|
||||
database := controller.NewPushDatabase(cacheModel)
|
||||
|
||||
consumer := NewConsumerHandler(offlinePusher, rdb, disCov)
|
||||
pbpush.RegisterPushMsgServiceServer(server, &pushServer{
|
||||
database: database,
|
||||
disCov: disCov,
|
||||
offlinePusher: offlinePusher,
|
||||
pushCh: consumer,
|
||||
})
|
||||
go consumer.pushConsumerGroup.RegisterHandleAndConsumer(consumer)
|
||||
return nil
|
||||
}
|
||||
@@ -16,6 +16,17 @@ package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
"github.com/IBM/sarama"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -31,18 +42,31 @@ import (
|
||||
)
|
||||
|
||||
type ConsumerHandler struct {
|
||||
pushConsumerGroup *kfk.MConsumerGroup
|
||||
pusher *Pusher
|
||||
pushConsumerGroup *kfk.MConsumerGroup
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
onlinePusher OnlinePusher
|
||||
groupLocalCache *rpccache.GroupLocalCache
|
||||
conversationLocalCache *rpccache.ConversationLocalCache
|
||||
msgRpcClient rpcclient.MessageRpcClient
|
||||
conversationRpcClient rpcclient.ConversationRpcClient
|
||||
groupRpcClient rpcclient.GroupRpcClient
|
||||
}
|
||||
|
||||
func NewConsumerHandler(pusher *Pusher) *ConsumerHandler {
|
||||
func NewConsumerHandler(offlinePusher offlinepush.OfflinePusher,
|
||||
rdb redis.UniversalClient, disCov discoveryregistry.SvcDiscoveryRegistry) *ConsumerHandler {
|
||||
var consumerHandler ConsumerHandler
|
||||
consumerHandler.pusher = pusher
|
||||
consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
|
||||
KafkaVersion: sarama.V2_0_0_0,
|
||||
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
|
||||
}, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr,
|
||||
config.Config.Kafka.ConsumerGroupID.MsgToPush)
|
||||
consumerHandler.offlinePusher = offlinePusher
|
||||
consumerHandler.onlinePusher = NewOnlinePusher(disCov)
|
||||
consumerHandler.groupRpcClient = rpcclient.NewGroupRpcClient(disCov)
|
||||
consumerHandler.groupLocalCache = rpccache.NewGroupLocalCache(consumerHandler.groupRpcClient, rdb)
|
||||
consumerHandler.msgRpcClient = rpcclient.NewMessageRpcClient(disCov)
|
||||
consumerHandler.conversationRpcClient = rpcclient.NewConversationRpcClient(disCov)
|
||||
consumerHandler.conversationLocalCache = rpccache.NewConversationLocalCache(consumerHandler.conversationRpcClient, rdb)
|
||||
return &consumerHandler
|
||||
}
|
||||
|
||||
@@ -65,7 +89,7 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
|
||||
var err error
|
||||
switch msgFromMQ.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
err = c.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
default:
|
||||
var pushUserIDList []string
|
||||
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
||||
@@ -74,18 +98,14 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
|
||||
} else {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
|
||||
}
|
||||
err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
|
||||
err = c.Push2User(ctx, pushUserIDList, pbData.MsgData)
|
||||
}
|
||||
if err != nil {
|
||||
if err == errNoOfflinePusher {
|
||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
||||
} else {
|
||||
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
||||
}
|
||||
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
||||
}
|
||||
}
|
||||
func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (*ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (*ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||
func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||
claim sarama.ConsumerGroupClaim,
|
||||
) error {
|
||||
@@ -96,3 +116,243 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Push2User Suitable for two types of conversations, one is SingleChatType and the other is NotificationChatType.
|
||||
func (c *ConsumerHandler) 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())
|
||||
if err := callbackOnlinePush(ctx, userIDs, msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, userIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs)
|
||||
|
||||
if !c.shouldPushOffline(ctx, msg) {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range wsResults {
|
||||
//message sender do not need offline push
|
||||
if msg.SendID == v.UserID {
|
||||
continue
|
||||
}
|
||||
//receiver online push success
|
||||
if v.OnlinePush {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
offlinePUshUserID := []string{msg.RecvID}
|
||||
//receiver offline push
|
||||
if err = callbackOfflinePush(ctx, offlinePUshUserID, msg, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.offlinePushMsg(ctx, msg, offlinePUshUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
|
||||
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
|
||||
var pushToUserIDs []string
|
||||
if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = c.groupMessagesHandler(ctx, groupID, &pushToUserIDs, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "group push result", "result", wsResults, "msg", msg)
|
||||
|
||||
if !c.shouldPushOffline(ctx, msg) {
|
||||
return nil
|
||||
}
|
||||
needOfflinePushUserIDs := c.onlinePusher.GetOnlinePushFailedUserIDs(ctx, msg, wsResults, &pushToUserIDs)
|
||||
|
||||
//filter some user, like don not disturb or don't need offline push etc.
|
||||
needOfflinePushUserIDs, err = c.filterGroupMessageOfflinePush(ctx, groupID, msg, needOfflinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Use offline push messaging
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
|
||||
err = c.offlinePushMsg(ctx, msg, needOfflinePushUserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
|
||||
title, content, opts, err := c.getOfflinePushInfos(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
|
||||
if err != nil {
|
||||
prommetrics.MsgOfflinePushFailedCounter.Inc()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) filterGroupMessageOfflinePush(ctx context.Context, groupID string, msg *sdkws.MsgData,
|
||||
offlinePushUserIDs []string) (userIDs []string, err error) {
|
||||
|
||||
//todo local cache Obtain the difference set through local comparison.
|
||||
needOfflinePushUserIDs, err := c.conversationRpcClient.GetConversationOfflinePushUserIDs(
|
||||
ctx, utils.GenGroupConversationID(groupID), offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return needOfflinePushUserIDs, nil
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *options.Opts, err error) {
|
||||
type AtTextElem struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
AtUserList []string `json:"atUserList,omitempty"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
opts = &options.Opts{Signal: &options.Signal{}}
|
||||
if msg.OfflinePushInfo != nil {
|
||||
opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
|
||||
opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
|
||||
opts.Ex = msg.OfflinePushInfo.Ex
|
||||
}
|
||||
|
||||
if msg.OfflinePushInfo != nil {
|
||||
title = msg.OfflinePushInfo.Title
|
||||
content = msg.OfflinePushInfo.Desc
|
||||
}
|
||||
if title == "" {
|
||||
switch msg.ContentType {
|
||||
case constant.Text:
|
||||
fallthrough
|
||||
case constant.Picture:
|
||||
fallthrough
|
||||
case constant.Voice:
|
||||
fallthrough
|
||||
case constant.Video:
|
||||
fallthrough
|
||||
case constant.File:
|
||||
title = constant.ContentType2PushContent[int64(msg.ContentType)]
|
||||
case constant.AtText:
|
||||
ac := AtTextElem{}
|
||||
_ = utils.JsonStringToStruct(string(msg.Content), &ac)
|
||||
case constant.SignalingNotification:
|
||||
title = constant.ContentType2PushContent[constant.SignalMsg]
|
||||
default:
|
||||
title = constant.ContentType2PushContent[constant.Common]
|
||||
}
|
||||
}
|
||||
if content == "" {
|
||||
content = title
|
||||
}
|
||||
return
|
||||
}
|
||||
func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID string, pushToUserIDs *[]string, msg *sdkws.MsgData) (err error) {
|
||||
if len(*pushToUserIDs) == 0 {
|
||||
*pushToUserIDs, err = c.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch msg.ContentType {
|
||||
case constant.MemberQuitNotification:
|
||||
var tips sdkws.MemberQuitTips
|
||||
if unmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, []string{tips.QuitUser.UserID}); err != nil {
|
||||
log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userID", tips.QuitUser.UserID)
|
||||
}
|
||||
*pushToUserIDs = append(*pushToUserIDs, tips.QuitUser.UserID)
|
||||
case constant.MemberKickedNotification:
|
||||
var tips sdkws.MemberKickedTips
|
||||
if unmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
|
||||
if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, kickedUsers); err != nil {
|
||||
log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", kickedUsers)
|
||||
}
|
||||
|
||||
*pushToUserIDs = append(*pushToUserIDs, kickedUsers...)
|
||||
case constant.GroupDismissedNotification:
|
||||
if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到
|
||||
var tips sdkws.GroupDismissedTips
|
||||
if unmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(*pushToUserIDs), "list", pushToUserIDs)
|
||||
if len(config.Config.Manager.UserID) > 0 {
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0])
|
||||
}
|
||||
defer func(groupID string) {
|
||||
if err = c.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
|
||||
}
|
||||
}(groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
|
||||
conversationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
|
||||
maxSeq, err := c.msgRpcClient.GetConversationMaxSeq(ctx, conversationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conversationID, maxSeq)
|
||||
}
|
||||
|
||||
func unmarshalNotificationElem(bytes []byte, t any) error {
|
||||
var notification sdkws.NotificationElem
|
||||
if err := json.Unmarshal(bytes, ¬ification); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(notification.Detail), t)
|
||||
}
|
||||
|
||||
func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgData) bool {
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
if !isOfflinePush {
|
||||
return false
|
||||
}
|
||||
if msg.ContentType == constant.SignalingNotification {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"sync"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
pbpush "github.com/OpenIMSDK/protocol/push"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
type pushServer struct {
|
||||
pusher *Pusher
|
||||
}
|
||||
|
||||
func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cacheModel := cache.NewMsgCacheModel(rdb)
|
||||
offlinePusher := NewOfflinePusher(cacheModel)
|
||||
database := controller.NewPushDatabase(cacheModel)
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client)
|
||||
conversationRpcClient := rpcclient.NewConversationRpcClient(client)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client)
|
||||
pusher := NewPusher(
|
||||
client,
|
||||
offlinePusher,
|
||||
database,
|
||||
localcache.NewGroupLocalCache(&groupRpcClient),
|
||||
localcache.NewConversationLocalCache(&conversationRpcClient),
|
||||
&conversationRpcClient,
|
||||
&groupRpcClient,
|
||||
&msgRpcClient,
|
||||
)
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
pbpush.RegisterPushMsgServiceServer(server, &pushServer{
|
||||
pusher: pusher,
|
||||
})
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
consumer := NewConsumer(pusher)
|
||||
consumer.Start()
|
||||
}()
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *pushServer) PushMsg(ctx context.Context, pbData *pbpush.PushMsgReq) (resp *pbpush.PushMsgResp, err error) {
|
||||
switch pbData.MsgData.SessionType {
|
||||
case constant.SuperGroupChatType:
|
||||
err = r.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||
default:
|
||||
var pushUserIDList []string
|
||||
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
||||
if !isSenderSync {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID)
|
||||
} else {
|
||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
|
||||
}
|
||||
err = r.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
|
||||
}
|
||||
if err != nil {
|
||||
if err != errNoOfflinePusher {
|
||||
return nil, err
|
||||
} else {
|
||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
||||
}
|
||||
}
|
||||
return &pbpush.PushMsgResp{}, nil
|
||||
}
|
||||
|
||||
func (r *pushServer) DelUserPushToken(
|
||||
ctx context.Context,
|
||||
req *pbpush.DelUserPushTokenReq,
|
||||
) (resp *pbpush.DelUserPushTokenResp, err error) {
|
||||
if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbpush.DelUserPushTokenResp{}, nil
|
||||
}
|
||||
@@ -1,512 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package push
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"google.golang.org/grpc"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/conversation"
|
||||
"github.com/OpenIMSDK/protocol/msggateway"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
|
||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
|
||||
type Pusher struct {
|
||||
database controller.PushDatabase
|
||||
discov discoveryregistry.SvcDiscoveryRegistry
|
||||
offlinePusher offlinepush.OfflinePusher
|
||||
groupLocalCache *localcache.GroupLocalCache
|
||||
conversationLocalCache *localcache.ConversationLocalCache
|
||||
msgRpcClient *rpcclient.MessageRpcClient
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient
|
||||
groupRpcClient *rpcclient.GroupRpcClient
|
||||
}
|
||||
|
||||
var errNoOfflinePusher = errors.New("no offlinePusher is configured")
|
||||
|
||||
func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase,
|
||||
groupLocalCache *localcache.GroupLocalCache, conversationLocalCache *localcache.ConversationLocalCache,
|
||||
conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient,
|
||||
) *Pusher {
|
||||
return &Pusher{
|
||||
discov: discov,
|
||||
database: database,
|
||||
offlinePusher: offlinePusher,
|
||||
groupLocalCache: groupLocalCache,
|
||||
conversationLocalCache: conversationLocalCache,
|
||||
msgRpcClient: msgRpcClient,
|
||||
conversationRpcClient: conversationRpcClient,
|
||||
groupRpcClient: groupRpcClient,
|
||||
}
|
||||
}
|
||||
|
||||
func NewOfflinePusher(cache cache.MsgModel) offlinepush.OfflinePusher {
|
||||
var offlinePusher offlinepush.OfflinePusher
|
||||
switch config.Config.Push.Enable {
|
||||
case "getui":
|
||||
offlinePusher = getui.NewClient(cache)
|
||||
case "fcm":
|
||||
offlinePusher = fcm.NewClient(cache)
|
||||
case "jpush":
|
||||
offlinePusher = jpush.NewClient()
|
||||
default:
|
||||
offlinePusher = dummy.NewClient()
|
||||
}
|
||||
return offlinePusher
|
||||
}
|
||||
|
||||
func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
|
||||
conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
|
||||
maxSeq, err := p.msgRpcClient.GetConversationMaxSeq(ctx, conevrsationID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq)
|
||||
}
|
||||
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs)
|
||||
|
||||
if !isOfflinePush {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, v := range wsResults {
|
||||
if msg.SendID != v.UserID && (!v.OnlinePush) {
|
||||
if err = callbackOfflinePush(ctx, userIDs, msg, &[]string{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = p.offlinePushMsg(ctx, msg.SendID, msg, []string{v.UserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error {
|
||||
var notification sdkws.NotificationElem
|
||||
if err := json.Unmarshal(bytes, ¬ification); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.Unmarshal([]byte(notification.Detail), t)
|
||||
}
|
||||
|
||||
/*
|
||||
k8s deployment,offline push group messages function
|
||||
*/
|
||||
func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error {
|
||||
|
||||
var needOfflinePushUserIDs []string
|
||||
for _, v := range wsResults {
|
||||
if !v.OnlinePush {
|
||||
needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err := callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
if msg.ContentType != constant.SignalingNotification {
|
||||
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
|
||||
ctx,
|
||||
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) > 0 {
|
||||
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
|
||||
log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
|
||||
var pushToUserIDs []string
|
||||
if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(pushToUserIDs) == 0 {
|
||||
pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch msg.ContentType {
|
||||
case constant.MemberQuitNotification:
|
||||
var tips sdkws.MemberQuitTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, []string{tips.QuitUser.UserID})
|
||||
pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID)
|
||||
case constant.MemberKickedNotification:
|
||||
var tips sdkws.MemberKickedTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
|
||||
defer func(groupID string, userIDs []string) {
|
||||
if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
|
||||
log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
|
||||
}
|
||||
}(groupID, kickedUsers)
|
||||
pushToUserIDs = append(pushToUserIDs, kickedUsers...)
|
||||
case constant.GroupDismissedNotification:
|
||||
if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到
|
||||
var tips sdkws.GroupDismissedTips
|
||||
if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
|
||||
return err
|
||||
}
|
||||
log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs)
|
||||
if len(config.Config.Manager.UserID) > 0 {
|
||||
ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0])
|
||||
}
|
||||
defer func(groupID string) {
|
||||
if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
|
||||
log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
|
||||
}
|
||||
}(groupID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg)
|
||||
isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
|
||||
if isOfflinePush && config.Config.Envs.Discovery == "k8s" {
|
||||
return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults)
|
||||
}
|
||||
if isOfflinePush && config.Config.Envs.Discovery == "zookeeper" {
|
||||
var (
|
||||
onlineSuccessUserIDs = []string{msg.SendID}
|
||||
webAndPcBackgroundUserIDs []string
|
||||
)
|
||||
|
||||
for _, v := range wsResults {
|
||||
if v.OnlinePush && v.UserID != msg.SendID {
|
||||
onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
|
||||
}
|
||||
|
||||
if v.OnlinePush {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(v.Resp) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, singleResult := range v.Resp {
|
||||
if singleResult.ResultCode != -2 {
|
||||
continue
|
||||
}
|
||||
|
||||
isPC := constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC
|
||||
isWebID := singleResult.RecvPlatFormID == constant.WebPlatformID
|
||||
|
||||
if isPC || isWebID {
|
||||
webAndPcBackgroundUserIDs = append(webAndPcBackgroundUserIDs, v.UserID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs)
|
||||
|
||||
// Use offline push messaging
|
||||
if len(needOfflinePushUserIDs) > 0 {
|
||||
var offlinePushUserIDs []string
|
||||
err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(offlinePushUserIDs) > 0 {
|
||||
needOfflinePushUserIDs = offlinePushUserIDs
|
||||
}
|
||||
if msg.ContentType != constant.SignalingNotification {
|
||||
resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
|
||||
ctx,
|
||||
&conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resp.UserIDs) > 0 {
|
||||
err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
|
||||
return err
|
||||
}
|
||||
if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil {
|
||||
log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs))
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
var usersHost = make(map[string][]string)
|
||||
for _, v := range pushToUserIDs {
|
||||
tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "get msggateway hash error", err)
|
||||
return nil, err
|
||||
}
|
||||
tUsers, tbl := usersHost[tHost]
|
||||
if tbl {
|
||||
tUsers = append(tUsers, v)
|
||||
usersHost[tHost] = tUsers
|
||||
} else {
|
||||
usersHost[tHost] = []string{v}
|
||||
}
|
||||
}
|
||||
log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
|
||||
var usersConns = make(map[*grpc.ClientConn][]string)
|
||||
for host, userIds := range usersHost {
|
||||
tconn, _ := p.discov.GetConn(ctx, host)
|
||||
usersConns[tconn] = userIds
|
||||
}
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
maxWorkers = config.Config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
wg.SetLimit(maxWorkers)
|
||||
for conn, userIds := range usersConns {
|
||||
tcon := conn
|
||||
tuserIds := userIds
|
||||
wg.Go(func() error {
|
||||
input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
|
||||
msgClient := msggateway.NewMsgGatewayClient(tcon)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
_ = wg.Wait()
|
||||
return wsResults, nil
|
||||
}
|
||||
func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
|
||||
if config.Config.Envs.Discovery == "k8s" {
|
||||
return p.k8sOnlinePush(ctx, msg, pushToUserIDs)
|
||||
}
|
||||
conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName)
|
||||
log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
wg = errgroup.Group{}
|
||||
input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
|
||||
maxWorkers = config.Config.Push.MaxConcurrentWorkers
|
||||
)
|
||||
|
||||
if maxWorkers < 3 {
|
||||
maxWorkers = 3
|
||||
}
|
||||
|
||||
wg.SetLimit(maxWorkers)
|
||||
|
||||
// Online push message
|
||||
for _, conn := range conns {
|
||||
conn := conn // loop var safe
|
||||
wg.Go(func() error {
|
||||
msgClient := msggateway.NewMsgGatewayClient(conn)
|
||||
reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.ZDebug(ctx, "push result", "reply", reply)
|
||||
if reply != nil && reply.SinglePushResult != nil {
|
||||
mu.Lock()
|
||||
wsResults = append(wsResults, reply.SinglePushResult...)
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
_ = wg.Wait()
|
||||
|
||||
// always return nil
|
||||
return wsResults, nil
|
||||
}
|
||||
|
||||
func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
|
||||
title, content, opts, err := p.getOfflinePushInfos(conversationID, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
|
||||
if err != nil {
|
||||
prommetrics.MsgOfflinePushFailedCounter.Inc()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, err error) {
|
||||
opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}}
|
||||
// if msg.ContentType > constant.SignalingNotificationBegin && msg.ContentType < constant.SignalingNotificationEnd {
|
||||
// req := &sdkws.SignalReq{}
|
||||
// if err := proto.Unmarshal(msg.Content, req); err != nil {
|
||||
// return nil, utils.Wrap(err, "")
|
||||
// }
|
||||
// switch req.Payload.(type) {
|
||||
// case *sdkws.SignalReq_Invite, *sdkws.SignalReq_InviteInGroup:
|
||||
// opts.Signal = &offlinepush.Signal{ClientMsgID: msg.ClientMsgID}
|
||||
// }
|
||||
// }
|
||||
if msg.OfflinePushInfo != nil {
|
||||
opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
|
||||
opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
|
||||
opts.Ex = msg.OfflinePushInfo.Ex
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) {
|
||||
if p.offlinePusher == nil {
|
||||
err = errNoOfflinePusher
|
||||
return
|
||||
}
|
||||
|
||||
type atContent struct {
|
||||
Text string `json:"text"`
|
||||
AtUserList []string `json:"atUserList"`
|
||||
IsAtSelf bool `json:"isAtSelf"`
|
||||
}
|
||||
|
||||
opts, err = p.GetOfflinePushOpts(msg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if msg.OfflinePushInfo != nil {
|
||||
title = msg.OfflinePushInfo.Title
|
||||
content = msg.OfflinePushInfo.Desc
|
||||
}
|
||||
if title == "" {
|
||||
switch msg.ContentType {
|
||||
case constant.Text:
|
||||
fallthrough
|
||||
case constant.Picture:
|
||||
fallthrough
|
||||
case constant.Voice:
|
||||
fallthrough
|
||||
case constant.Video:
|
||||
fallthrough
|
||||
case constant.File:
|
||||
title = constant.ContentType2PushContent[int64(msg.ContentType)]
|
||||
case constant.AtText:
|
||||
ac := atContent{}
|
||||
_ = utils.JsonStringToStruct(string(msg.Content), &ac)
|
||||
if utils.IsContain(conversationID, ac.AtUserList) {
|
||||
title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
|
||||
} else {
|
||||
title = constant.ContentType2PushContent[constant.GroupMsg]
|
||||
}
|
||||
case constant.SignalingNotification:
|
||||
title = constant.ContentType2PushContent[constant.SignalMsg]
|
||||
default:
|
||||
title = constant.ContentType2PushContent[constant.Common]
|
||||
}
|
||||
}
|
||||
if content == "" {
|
||||
content = title
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -17,9 +17,10 @@ package conversation
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"sort"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
@@ -89,8 +90,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 +130,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 +433,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,
|
||||
) {
|
||||
|
||||
@@ -16,7 +16,6 @@ package friend
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
@@ -452,22 +451,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{}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 != "" {
|
||||
@@ -1061,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)
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
|
||||
utils2 "github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
@@ -43,7 +44,7 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conversations, err := m.Conversation.GetConversations(ctx, req.UserID, conversationIDs)
|
||||
conversations, err := m.ConversationLocalCache.GetConversations(ctx, req.UserID, conversationIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -106,7 +107,7 @@ func (m *msgServer) MarkMsgsAsRead(
|
||||
if hasReadSeq > maxSeq {
|
||||
return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq")
|
||||
}
|
||||
conversation, err := m.Conversation.GetConversation(ctx, req.UserID, req.ConversationID)
|
||||
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -124,6 +125,17 @@ func (m *msgServer) MarkMsgsAsRead(
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
req_callback := &cbapi.CallbackSingleMsgReadReq{
|
||||
ConversationID: conversation.ConversationID,
|
||||
UserID: req.UserID,
|
||||
Seqs: req.Seqs,
|
||||
ContentType: conversation.ConversationType,
|
||||
}
|
||||
if err = CallbackSingleMsgRead(ctx, req_callback); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
||||
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
|
||||
return
|
||||
@@ -135,7 +147,7 @@ func (m *msgServer) MarkConversationAsRead(
|
||||
ctx context.Context,
|
||||
req *msg.MarkConversationAsReadReq,
|
||||
) (resp *msg.MarkConversationAsReadResp, err error) {
|
||||
conversation, err := m.Conversation.GetConversation(ctx, req.UserID, req.ConversationID)
|
||||
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -47,7 +47,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
||||
if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := m.User.GetUserInfo(ctx, req.UserID)
|
||||
user, err := m.UserLocalCache.GetUserInfo(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -73,12 +73,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
||||
}
|
||||
role = user.AppMangerLevel
|
||||
case constant.SuperGroupChatType:
|
||||
members, err := m.Group.GetGroupMemberInfoMap(
|
||||
ctx,
|
||||
msgs[0].GroupID,
|
||||
utils.Distinct([]string{req.UserID, msgs[0].SendID}),
|
||||
true,
|
||||
)
|
||||
members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, utils.Distinct([]string{req.UserID, msgs[0].SendID}))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -97,7 +98,7 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa
|
||||
}
|
||||
tagAll := utils.IsContain(constant.AtAllString, msg.AtUserIDList)
|
||||
if tagAll {
|
||||
memberUserIDList, err := m.Group.GetGroupMemberIDs(ctx, msg.GroupID)
|
||||
memberUserIDList, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, msg.GroupID)
|
||||
if err != nil {
|
||||
log.ZWarn(ctx, "GetGroupMemberIDs", err)
|
||||
return
|
||||
@@ -143,6 +144,7 @@ func (m *msgServer) sendMsgNotification(
|
||||
}
|
||||
|
||||
func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
|
||||
log.ZDebug(ctx, "sendMsgSingleChat return")
|
||||
if err := m.messageVerification(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -167,6 +169,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
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||
)
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
@@ -26,7 +27,6 @@ import (
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||
)
|
||||
@@ -36,12 +36,11 @@ type (
|
||||
msgServer struct {
|
||||
RegisterCenter discoveryregistry.SvcDiscoveryRegistry
|
||||
MsgDatabase controller.CommonMsgDatabase
|
||||
Group *rpcclient.GroupRpcClient
|
||||
User *rpcclient.UserRpcClient
|
||||
Conversation *rpcclient.ConversationRpcClient
|
||||
friend *rpcclient.FriendRpcClient
|
||||
GroupLocalCache *localcache.GroupLocalCache
|
||||
ConversationLocalCache *localcache.ConversationLocalCache
|
||||
UserLocalCache *rpccache.UserLocalCache
|
||||
FriendLocalCache *rpccache.FriendLocalCache
|
||||
GroupLocalCache *rpccache.GroupLocalCache
|
||||
ConversationLocalCache *rpccache.ConversationLocalCache
|
||||
Handlers MessageInterceptorChain
|
||||
notificationSender *rpcclient.NotificationSender
|
||||
}
|
||||
@@ -81,15 +80,15 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
|
||||
groupRpcClient := rpcclient.NewGroupRpcClient(client)
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client)
|
||||
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel)
|
||||
|
||||
s := &msgServer{
|
||||
Conversation: &conversationClient,
|
||||
User: &userRpcClient,
|
||||
Group: &groupRpcClient,
|
||||
MsgDatabase: msgDatabase,
|
||||
RegisterCenter: client,
|
||||
GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient),
|
||||
ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient),
|
||||
friend: &friendRpcClient,
|
||||
UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, rdb),
|
||||
GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, rdb),
|
||||
ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, rdb),
|
||||
FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, rdb),
|
||||
}
|
||||
s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg))
|
||||
s.addInterceptorHandler(MessageHasReadEnabled)
|
||||
|
||||
@@ -41,7 +41,7 @@ func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq
|
||||
var pbUsers []*msg.ActiveUser
|
||||
if len(users) > 0 {
|
||||
userIDs := utils.Slice(users, func(e *unrelation.UserCount) string { return e.UserID })
|
||||
userMap, err := m.User.GetUsersInfoMap(ctx, userIDs)
|
||||
userMap, err := m.UserLocalCache.GetUsersInfoMap(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -83,7 +83,7 @@ func (m *msgServer) GetActiveGroup(ctx context.Context, req *msg.GetActiveGroupR
|
||||
var pbgroups []*msg.ActiveGroup
|
||||
if len(groups) > 0 {
|
||||
groupIDs := utils.Slice(groups, func(e *unrelation.GroupCount) string { return e.GroupID })
|
||||
resp, err := m.Group.GetGroupInfos(ctx, groupIDs, false)
|
||||
resp, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ func (m *msgServer) PullMessageBySeqs(
|
||||
resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs)
|
||||
for _, seq := range req.SeqRanges {
|
||||
if !msgprocessor.IsNotification(seq.ConversationID) {
|
||||
conversation, err := m.Conversation.GetConversation(ctx, req.UserID, seq.ConversationID)
|
||||
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, seq.ConversationID)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "GetConversation error", err, "conversationID", seq.ConversationID)
|
||||
continue
|
||||
@@ -140,7 +140,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
}
|
||||
}
|
||||
if len(sendIDs) != 0 {
|
||||
sendInfos, err := m.User.GetUsersInfo(ctx, sendIDs)
|
||||
sendInfos, err := m.UserLocalCache.GetUsersInfo(ctx, sendIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -149,7 +149,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
}
|
||||
}
|
||||
if len(recvIDs) != 0 {
|
||||
recvInfos, err := m.User.GetUsersInfo(ctx, recvIDs)
|
||||
recvInfos, err := m.UserLocalCache.GetUsersInfo(ctx, recvIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
}
|
||||
}
|
||||
if len(groupIDs) != 0 {
|
||||
groupInfos, err := m.Group.GetGroupInfos(ctx, groupIDs, true)
|
||||
groupInfos, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ package msg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -58,7 +59,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
data.MsgData.ContentType >= constant.NotificationBegin {
|
||||
return nil
|
||||
}
|
||||
black, err := m.friend.IsBlocked(ctx, data.MsgData.SendID, data.MsgData.RecvID)
|
||||
black, err := m.FriendLocalCache.IsBlack(ctx, data.MsgData.SendID, data.MsgData.RecvID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -66,7 +67,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
return errs.ErrBlockedByPeer.Wrap()
|
||||
}
|
||||
if *config.Config.MessageVerify.FriendVerify {
|
||||
friend, err := m.friend.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID)
|
||||
friend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -77,7 +78,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
}
|
||||
return nil
|
||||
case constant.SuperGroupChatType:
|
||||
groupInfo, err := m.Group.GetGroupInfoCache(ctx, data.MsgData.GroupID)
|
||||
groupInfo, err := m.GroupLocalCache.GetGroupInfo(ctx, data.MsgData.GroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -95,17 +96,17 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
data.MsgData.ContentType >= constant.NotificationBegin {
|
||||
return nil
|
||||
}
|
||||
// memberIDs, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, data.MsgData.GroupID)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if !utils.IsContain(data.MsgData.SendID, memberIDs) {
|
||||
// return errs.ErrNotInGroupYet.Wrap()
|
||||
// }
|
||||
|
||||
groupMemberInfo, err := m.Group.GetGroupMemberCache(ctx, data.MsgData.GroupID, data.MsgData.SendID)
|
||||
memberIDs, err := m.GroupLocalCache.GetGroupMemberIDMap(ctx, data.MsgData.GroupID)
|
||||
if err != nil {
|
||||
if err == errs.ErrRecordNotFound {
|
||||
return err
|
||||
}
|
||||
if _, ok := memberIDs[data.MsgData.SendID]; !ok {
|
||||
return errs.ErrNotInGroupYet.Wrap()
|
||||
}
|
||||
|
||||
groupMemberInfo, err := m.GroupLocalCache.GetGroupMember(ctx, data.MsgData.GroupID, data.MsgData.SendID)
|
||||
if err != nil {
|
||||
if errs.ErrRecordNotFound.Is(err) {
|
||||
return errs.ErrNotInGroupYet.Wrap(err.Error())
|
||||
}
|
||||
return err
|
||||
@@ -186,7 +187,8 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
||||
sessionType int,
|
||||
pb *msg.SendMsgReq,
|
||||
) (bool, error) {
|
||||
opt, err := m.User.GetUserGlobalMsgRecvOpt(ctx, userID)
|
||||
defer log.ZDebug(ctx, "modifyMessageByUserMessageReceiveOpt return")
|
||||
opt, err := m.UserLocalCache.GetUserGlobalMsgRecvOpt(ctx, userID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -202,7 +204,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
||||
return true, nil
|
||||
}
|
||||
// conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType)
|
||||
singleOpt, err := m.Conversation.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID)
|
||||
singleOpt, err := m.ConversationLocalCache.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID)
|
||||
if errs.ErrRecordNotFound.Is(err) {
|
||||
return true, nil
|
||||
} else if err != nil {
|
||||
|
||||
@@ -19,12 +19,14 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"github.com/google/uuid"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
|
||||
@@ -16,6 +16,7 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
|
||||
@@ -17,11 +17,14 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/pagination"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
||||
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||
@@ -58,6 +61,7 @@ type userServer struct {
|
||||
RegisterCenter registry.SvcDiscoveryRegistry
|
||||
}
|
||||
|
||||
|
||||
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
@@ -228,11 +232,21 @@ 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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if req.UserID == "" && req.NickName == "" {
|
||||
total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err
|
||||
} else {
|
||||
total, users, err := s.PageFindUserWithKeyword(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.UserID, req.NickName, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err
|
||||
|
||||
}
|
||||
return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err
|
||||
|
||||
}
|
||||
|
||||
func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterReq) (resp *pbuser.UserRegisterResp, err error) {
|
||||
@@ -377,21 +391,53 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
|
||||
return &pbuser.GetSubscribeUsersStatusResp{StatusList: onlineStatusList}, nil
|
||||
}
|
||||
|
||||
// ProcessUserCommandAdd user general function add
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
@@ -399,18 +445,44 @@ func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.P
|
||||
return &pbuser.ProcessUserCommandDeleteResp{}, nil
|
||||
}
|
||||
|
||||
// ProcessUserCommandUpdate user general function update
|
||||
// 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 +495,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{KVArray: commandInfoSlice}, nil
|
||||
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.ProcessUserCommandGetAllResp{CommandResp: commandInfoSlice}, nil
|
||||
}
|
||||
|
||||
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
|
||||
@@ -438,22 +541,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 err == nil {
|
||||
continue
|
||||
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 {
|
||||
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 +572,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) {
|
||||
@@ -493,34 +606,44 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu
|
||||
}
|
||||
|
||||
func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) {
|
||||
// Check if user is an admin
|
||||
if err := authverify.CheckIMAdmin(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.NickName != "" {
|
||||
users, err := s.UserDatabase.FindByNickname(ctx, req.NickName)
|
||||
var users []*relation.UserModel
|
||||
var err error
|
||||
|
||||
// If a keyword is provided in the request
|
||||
if req.Keyword != "" {
|
||||
// Find users by 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})
|
||||
// Convert users to response format
|
||||
resp := s.userModelToResp(users, req.Pagination)
|
||||
if resp.Total != 0 {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// Find users by nickname if no users found by keyword
|
||||
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
|
||||
}
|
||||
|
||||
users, err := s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
|
||||
// If no keyword, find users with notification settings
|
||||
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 +677,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 +691,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}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ func InitMsgTool() (*MsgTool, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||
userDB, err := mgo.NewUserMongo(mongo.GetDatabase())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -41,7 +41,7 @@ const (
|
||||
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
|
||||
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
|
||||
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
|
||||
CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter"
|
||||
CallbackAfterTransferGroupOwner = "callbackAfterTransferGroupOwner"
|
||||
CallbackBeforeSetFriendRemark = "callbackBeforeSetFriendRemark"
|
||||
CallbackAfterSetFriendRemark = "callbackAfterSetFriendRemark"
|
||||
CallbackSingleMsgRead = "callbackSingleMsgRead"
|
||||
|
||||
@@ -94,9 +94,10 @@ type CallbackGroupMsgReadResp struct {
|
||||
|
||||
type CallbackSingleMsgReadReq struct {
|
||||
CallbackCommand `json:"callbackCommand"`
|
||||
SendID string `json:"sendID"`
|
||||
ReceiveID string `json:"receiveID"`
|
||||
ContentType int64 `json:"contentType"`
|
||||
ConversationID string `json:"conversationID"`
|
||||
UserID string `json:"userID"`
|
||||
Seqs []int64 `json:"Seqs"`
|
||||
ContentType int32 `json:"contentType"`
|
||||
}
|
||||
|
||||
type CallbackSingleMsgReadResp struct {
|
||||
|
||||
15
pkg/common/cachekey/black.go
Normal file
15
pkg/common/cachekey/black.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package cachekey
|
||||
|
||||
const (
|
||||
BlackIDsKey = "BLACK_IDS:"
|
||||
IsBlackKey = "IS_BLACK:" // local cache
|
||||
)
|
||||
|
||||
func GetBlackIDsKey(ownerUserID string) string {
|
||||
return BlackIDsKey + ownerUserID
|
||||
|
||||
}
|
||||
|
||||
func GetIsBlackIDsKey(possibleBlackUserID, userID string) string {
|
||||
return IsBlackKey + userID + "-" + possibleBlackUserID
|
||||
}
|
||||
44
pkg/common/cachekey/conversation.go
Normal file
44
pkg/common/cachekey/conversation.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package cachekey
|
||||
|
||||
const (
|
||||
ConversationKey = "CONVERSATION:"
|
||||
ConversationIDsKey = "CONVERSATION_IDS:"
|
||||
ConversationIDsHashKey = "CONVERSATION_IDS_HASH:"
|
||||
ConversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
|
||||
RecvMsgOptKey = "RECV_MSG_OPT:"
|
||||
SuperGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
|
||||
SuperGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
|
||||
ConversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:"
|
||||
)
|
||||
|
||||
func GetConversationKey(ownerUserID, conversationID string) string {
|
||||
return ConversationKey + ownerUserID + ":" + conversationID
|
||||
}
|
||||
|
||||
func GetConversationIDsKey(ownerUserID string) string {
|
||||
return ConversationIDsKey + ownerUserID
|
||||
}
|
||||
|
||||
func GetSuperGroupRecvNotNotifyUserIDsKey(groupID string) string {
|
||||
return SuperGroupRecvMsgNotNotifyUserIDsKey + groupID
|
||||
}
|
||||
|
||||
func GetRecvMsgOptKey(ownerUserID, conversationID string) string {
|
||||
return RecvMsgOptKey + ownerUserID + ":" + conversationID
|
||||
}
|
||||
|
||||
func GetSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string {
|
||||
return SuperGroupRecvMsgNotNotifyUserIDsHashKey + groupID
|
||||
}
|
||||
|
||||
func GetConversationHasReadSeqKey(ownerUserID, conversationID string) string {
|
||||
return ConversationHasReadSeqKey + ownerUserID + ":" + conversationID
|
||||
}
|
||||
|
||||
func GetConversationNotReceiveMessageUserIDsKey(conversationID string) string {
|
||||
return ConversationNotReceiveMessageUserIDsKey + conversationID
|
||||
}
|
||||
|
||||
func GetUserConversationIDsHashKey(ownerUserID string) string {
|
||||
return ConversationIDsHashKey + ownerUserID
|
||||
}
|
||||
24
pkg/common/cachekey/friend.go
Normal file
24
pkg/common/cachekey/friend.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package cachekey
|
||||
|
||||
const (
|
||||
FriendIDsKey = "FRIEND_IDS:"
|
||||
TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
|
||||
FriendKey = "FRIEND_INFO:"
|
||||
IsFriendKey = "IS_FRIEND:" // local cache key
|
||||
)
|
||||
|
||||
func GetFriendIDsKey(ownerUserID string) string {
|
||||
return FriendIDsKey + ownerUserID
|
||||
}
|
||||
|
||||
func GetTwoWayFriendsIDsKey(ownerUserID string) string {
|
||||
return TwoWayFriendsIDsKey + ownerUserID
|
||||
}
|
||||
|
||||
func GetFriendKey(ownerUserID, friendUserID string) string {
|
||||
return FriendKey + ownerUserID + "-" + friendUserID
|
||||
}
|
||||
|
||||
func GetIsFriendKey(possibleFriendUserID, userID string) string {
|
||||
return IsFriendKey + possibleFriendUserID + "-" + userID
|
||||
}
|
||||
45
pkg/common/cachekey/group.go
Normal file
45
pkg/common/cachekey/group.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package cachekey
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
groupExpireTime = time.Second * 60 * 60 * 12
|
||||
GroupInfoKey = "GROUP_INFO:"
|
||||
GroupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
||||
GroupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
||||
GroupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
||||
JoinedGroupsKey = "JOIN_GROUPS_KEY:"
|
||||
GroupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:"
|
||||
GroupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:"
|
||||
)
|
||||
|
||||
func GetGroupInfoKey(groupID string) string {
|
||||
return GroupInfoKey + groupID
|
||||
}
|
||||
|
||||
func GetJoinedGroupsKey(userID string) string {
|
||||
return JoinedGroupsKey + userID
|
||||
}
|
||||
|
||||
func GetGroupMembersHashKey(groupID string) string {
|
||||
return GroupMembersHashKey + groupID
|
||||
}
|
||||
|
||||
func GetGroupMemberIDsKey(groupID string) string {
|
||||
return GroupMemberIDsKey + groupID
|
||||
}
|
||||
|
||||
func GetGroupMemberInfoKey(groupID, userID string) string {
|
||||
return GroupMemberInfoKey + groupID + "-" + userID
|
||||
}
|
||||
|
||||
func GetGroupMemberNumKey(groupID string) string {
|
||||
return GroupMemberNumKey + groupID
|
||||
}
|
||||
|
||||
func GetGroupRoleLevelMemberIDsKey(groupID string, roleLevel int32) string {
|
||||
return GroupRoleLevelMemberIDsKey + groupID + "-" + strconv.Itoa(int(roleLevel))
|
||||
}
|
||||
14
pkg/common/cachekey/user.go
Normal file
14
pkg/common/cachekey/user.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package cachekey
|
||||
|
||||
const (
|
||||
UserInfoKey = "USER_INFO:"
|
||||
UserGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:"
|
||||
)
|
||||
|
||||
func GetUserInfoKey(userID string) string {
|
||||
return UserInfoKey + userID
|
||||
}
|
||||
|
||||
func GetUserGlobalRecvMsgOptKey(userID string) string {
|
||||
return UserGlobalRecvMsgOptKey + userID
|
||||
}
|
||||
@@ -16,6 +16,7 @@ package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -258,6 +259,8 @@ type configStruct struct {
|
||||
FriendVerify *bool `yaml:"friendVerify"`
|
||||
} `yaml:"messageVerify"`
|
||||
|
||||
LocalCache localCache `yaml:"localCache"`
|
||||
|
||||
IOSPush struct {
|
||||
PushSound string `yaml:"pushSound"`
|
||||
BadgeCount bool `yaml:"badgeCount"`
|
||||
@@ -296,7 +299,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"`
|
||||
@@ -370,6 +373,33 @@ type notification struct {
|
||||
ConversationSetPrivate NotificationConf `yaml:"conversationSetPrivate"`
|
||||
}
|
||||
|
||||
type LocalCache struct {
|
||||
Topic string `yaml:"topic"`
|
||||
SlotNum int `yaml:"slotNum"`
|
||||
SlotSize int `yaml:"slotSize"`
|
||||
SuccessExpire int `yaml:"successExpire"` // second
|
||||
FailedExpire int `yaml:"failedExpire"` // second
|
||||
}
|
||||
|
||||
func (l LocalCache) Failed() time.Duration {
|
||||
return time.Second * time.Duration(l.FailedExpire)
|
||||
}
|
||||
|
||||
func (l LocalCache) Success() time.Duration {
|
||||
return time.Second * time.Duration(l.SuccessExpire)
|
||||
}
|
||||
|
||||
func (l LocalCache) Enable() bool {
|
||||
return l.Topic != "" && l.SlotNum > 0 && l.SlotSize > 0
|
||||
}
|
||||
|
||||
type localCache struct {
|
||||
User LocalCache `yaml:"user"`
|
||||
Group LocalCache `yaml:"group"`
|
||||
Friend LocalCache `yaml:"friend"`
|
||||
Conversation LocalCache `yaml:"conversation"`
|
||||
}
|
||||
|
||||
func (c *configStruct) GetServiceNames() []string {
|
||||
return []string{
|
||||
c.RpcRegisterName.OpenImUserName,
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
|
||||
)
|
||||
|
||||
//go:embed version
|
||||
@@ -37,19 +38,30 @@ const (
|
||||
|
||||
// return absolude path join ../config/, this is k8s container config path.
|
||||
func GetDefaultConfigPath() string {
|
||||
b, err := filepath.Abs(os.Args[0])
|
||||
executablePath, err := os.Executable()
|
||||
if err != nil {
|
||||
fmt.Println("filepath.Abs error,err=", err)
|
||||
fmt.Println("GetDefaultConfigPath error:", err.Error())
|
||||
return ""
|
||||
}
|
||||
return filepath.Join(filepath.Dir(b), "../config/")
|
||||
|
||||
configPath, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../config/"))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return configPath
|
||||
}
|
||||
|
||||
// getProjectRoot returns the absolute path of the project root directory.
|
||||
func GetProjectRoot() string {
|
||||
b, _ := filepath.Abs(os.Args[0])
|
||||
executablePath, _ := os.Executable()
|
||||
|
||||
return filepath.Join(filepath.Dir(b), "../../../../..")
|
||||
projectRoot, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../../../../.."))
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return projectRoot
|
||||
}
|
||||
|
||||
func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options {
|
||||
|
||||
@@ -16,6 +16,8 @@ package config
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -115,3 +117,13 @@ func TestInitConfig(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
Config.LocalCache.Friend.Topic = "friend"
|
||||
Config.LocalCache.Friend.SlotNum = 500
|
||||
Config.LocalCache.Friend.SlotSize = 20000
|
||||
|
||||
data, _ := yaml.Marshal(&Config)
|
||||
|
||||
fmt.Println(string(data))
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
v3.3.0
|
||||
v3.5.0
|
||||
@@ -17,6 +17,7 @@ package convert
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
|
||||
|
||||
15
pkg/common/db/cache/black.go
vendored
15
pkg/common/db/cache/black.go
vendored
@@ -16,6 +16,9 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
@@ -52,11 +55,15 @@ func NewBlackCacheRedis(
|
||||
options rockscache.Options,
|
||||
) BlackCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
|
||||
mc := NewMetaCacheRedis(rcClient)
|
||||
b := config.Config.LocalCache.Friend
|
||||
log.ZDebug(context.Background(), "black local cache init", "Topic", b.Topic, "SlotNum", b.SlotNum, "SlotSize", b.SlotSize, "enable", b.Enable())
|
||||
mc.SetTopic(b.Topic)
|
||||
mc.SetRawRedisClient(rdb)
|
||||
return &BlackCacheRedis{
|
||||
expireTime: blackExpireTime,
|
||||
rcClient: rcClient,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
metaCache: mc,
|
||||
blackDB: blackDB,
|
||||
}
|
||||
}
|
||||
@@ -66,12 +73,12 @@ func (b *BlackCacheRedis) NewCache() BlackCache {
|
||||
expireTime: b.expireTime,
|
||||
rcClient: b.rcClient,
|
||||
blackDB: b.blackDB,
|
||||
metaCache: NewMetaCacheRedis(b.rcClient, b.metaCache.GetPreDelKeys()...),
|
||||
metaCache: b.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *BlackCacheRedis) getBlackIDsKey(ownerUserID string) string {
|
||||
return blackIDsKey + ownerUserID
|
||||
return cachekey.GetBlackIDsKey(ownerUserID)
|
||||
}
|
||||
|
||||
func (b *BlackCacheRedis) GetBlackIDs(ctx context.Context, userID string) (blackIDs []string, err error) {
|
||||
|
||||
66
pkg/common/db/cache/config.go
vendored
Normal file
66
pkg/common/db/cache/config.go
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
subscribe map[string][]string
|
||||
)
|
||||
|
||||
func getPublishKey(topic string, key []string) []string {
|
||||
if topic == "" || len(key) == 0 {
|
||||
return nil
|
||||
}
|
||||
once.Do(func() {
|
||||
list := []struct {
|
||||
Local config.LocalCache
|
||||
Keys []string
|
||||
}{
|
||||
{
|
||||
Local: config.Config.LocalCache.User,
|
||||
Keys: []string{cachekey.UserInfoKey, cachekey.UserGlobalRecvMsgOptKey},
|
||||
},
|
||||
{
|
||||
Local: config.Config.LocalCache.Group,
|
||||
Keys: []string{cachekey.GroupMemberIDsKey, cachekey.GroupInfoKey, cachekey.GroupMemberInfoKey},
|
||||
},
|
||||
{
|
||||
Local: config.Config.LocalCache.Friend,
|
||||
Keys: []string{cachekey.FriendIDsKey, cachekey.BlackIDsKey},
|
||||
},
|
||||
{
|
||||
Local: config.Config.LocalCache.Conversation,
|
||||
Keys: []string{cachekey.ConversationIDsKey, cachekey.ConversationKey},
|
||||
},
|
||||
}
|
||||
subscribe = make(map[string][]string)
|
||||
for _, v := range list {
|
||||
if v.Local.Enable() {
|
||||
subscribe[v.Local.Topic] = v.Keys
|
||||
}
|
||||
}
|
||||
})
|
||||
prefix, ok := subscribe[topic]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
res := make([]string, 0, len(key))
|
||||
for _, k := range key {
|
||||
var exist bool
|
||||
for _, p := range prefix {
|
||||
if strings.HasPrefix(k, p) {
|
||||
exist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if exist {
|
||||
res = append(res, k)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
51
pkg/common/db/cache/conversation.go
vendored
51
pkg/common/db/cache/conversation.go
vendored
@@ -17,6 +17,9 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -30,14 +33,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
conversationKey = "CONVERSATION:"
|
||||
conversationIDsKey = "CONVERSATION_IDS:"
|
||||
conversationIDsHashKey = "CONVERSATION_IDS_HASH:"
|
||||
conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
|
||||
recvMsgOptKey = "RECV_MSG_OPT:"
|
||||
superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
|
||||
superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
|
||||
conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:"
|
||||
//conversationKey = "CONVERSATION:"
|
||||
//conversationIDsKey = "CONVERSATION_IDS:"
|
||||
//conversationIDsHashKey = "CONVERSATION_IDS_HASH:"
|
||||
//conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
|
||||
//recvMsgOptKey = "RECV_MSG_OPT:"
|
||||
//superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
|
||||
//superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
|
||||
//conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:"
|
||||
|
||||
conversationExpireTime = time.Second * 60 * 60 * 12
|
||||
)
|
||||
@@ -84,10 +87,14 @@ type ConversationCache interface {
|
||||
|
||||
func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationtb.ConversationModelInterface) ConversationCache {
|
||||
rcClient := rockscache.NewClient(rdb, opts)
|
||||
|
||||
mc := NewMetaCacheRedis(rcClient)
|
||||
c := config.Config.LocalCache.Conversation
|
||||
log.ZDebug(context.Background(), "black local cache init", "Topic", c.Topic, "SlotNum", c.SlotNum, "SlotSize", c.SlotSize, "enable", c.Enable())
|
||||
mc.SetTopic(c.Topic)
|
||||
mc.SetRawRedisClient(rdb)
|
||||
return &ConversationRedisCache{
|
||||
rcClient: rcClient,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
metaCache: mc,
|
||||
conversationDB: db,
|
||||
expireTime: conversationExpireTime,
|
||||
}
|
||||
@@ -118,38 +125,42 @@ type ConversationRedisCache struct {
|
||||
func (c *ConversationRedisCache) NewCache() ConversationCache {
|
||||
return &ConversationRedisCache{
|
||||
rcClient: c.rcClient,
|
||||
metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...),
|
||||
metaCache: c.Copy(),
|
||||
conversationDB: c.conversationDB,
|
||||
expireTime: c.expireTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getConversationKey(ownerUserID, conversationID string) string {
|
||||
return conversationKey + ownerUserID + ":" + conversationID
|
||||
return cachekey.GetConversationKey(ownerUserID, conversationID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getConversationIDsKey(ownerUserID string) string {
|
||||
return conversationIDsKey + ownerUserID
|
||||
return cachekey.GetConversationIDsKey(ownerUserID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsKey(groupID string) string {
|
||||
return superGroupRecvMsgNotNotifyUserIDsKey + groupID
|
||||
return cachekey.GetSuperGroupRecvNotNotifyUserIDsKey(groupID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getRecvMsgOptKey(ownerUserID, conversationID string) string {
|
||||
return recvMsgOptKey + ownerUserID + ":" + conversationID
|
||||
return cachekey.GetRecvMsgOptKey(ownerUserID, conversationID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string {
|
||||
return superGroupRecvMsgNotNotifyUserIDsHashKey + groupID
|
||||
return cachekey.GetSuperGroupRecvNotNotifyUserIDsHashKey(groupID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getConversationHasReadSeqKey(ownerUserID, conversationID string) string {
|
||||
return conversationHasReadSeqKey + ownerUserID + ":" + conversationID
|
||||
return cachekey.GetConversationHasReadSeqKey(ownerUserID, conversationID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getConversationNotReceiveMessageUserIDsKey(conversationID string) string {
|
||||
return conversationNotReceiveMessageUserIDsKey + conversationID
|
||||
return cachekey.GetConversationNotReceiveMessageUserIDsKey(conversationID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getUserConversationIDsHashKey(ownerUserID string) string {
|
||||
return cachekey.GetUserConversationIDsHashKey(ownerUserID)
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) {
|
||||
@@ -169,10 +180,6 @@ func (c *ConversationRedisCache) DelConversationIDs(userIDs ...string) Conversat
|
||||
return cache
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) getUserConversationIDsHashKey(ownerUserID string) string {
|
||||
return conversationIDsHashKey + ownerUserID
|
||||
}
|
||||
|
||||
func (c *ConversationRedisCache) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
|
||||
40
pkg/common/db/cache/friend.go
vendored
40
pkg/common/db/cache/friend.go
vendored
@@ -16,6 +16,9 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"time"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
@@ -27,10 +30,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
friendExpireTime = time.Second * 60 * 60 * 12
|
||||
friendIDsKey = "FRIEND_IDS:"
|
||||
TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
|
||||
friendKey = "FRIEND_INFO:"
|
||||
friendExpireTime = time.Second * 60 * 60 * 12
|
||||
//friendIDsKey = "FRIEND_IDS:"
|
||||
//TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
|
||||
//friendKey = "FRIEND_INFO:"
|
||||
)
|
||||
|
||||
// FriendCache is an interface for caching friend-related data.
|
||||
@@ -44,6 +47,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.
|
||||
@@ -58,8 +63,13 @@ type FriendCacheRedis struct {
|
||||
func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendModelInterface,
|
||||
options rockscache.Options) FriendCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
mc := NewMetaCacheRedis(rcClient)
|
||||
f := config.Config.LocalCache.Friend
|
||||
log.ZDebug(context.Background(), "friend local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize, "enable", f.Enable())
|
||||
mc.SetTopic(f.Topic)
|
||||
mc.SetRawRedisClient(rdb)
|
||||
return &FriendCacheRedis{
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
metaCache: mc,
|
||||
friendDB: friendDB,
|
||||
expireTime: friendExpireTime,
|
||||
rcClient: rcClient,
|
||||
@@ -70,7 +80,7 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendMo
|
||||
func (f *FriendCacheRedis) NewCache() FriendCache {
|
||||
return &FriendCacheRedis{
|
||||
rcClient: f.rcClient,
|
||||
metaCache: NewMetaCacheRedis(f.rcClient, f.metaCache.GetPreDelKeys()...),
|
||||
metaCache: f.Copy(),
|
||||
friendDB: f.friendDB,
|
||||
expireTime: f.expireTime,
|
||||
}
|
||||
@@ -78,17 +88,17 @@ func (f *FriendCacheRedis) NewCache() FriendCache {
|
||||
|
||||
// getFriendIDsKey returns the key for storing friend IDs in the cache.
|
||||
func (f *FriendCacheRedis) getFriendIDsKey(ownerUserID string) string {
|
||||
return friendIDsKey + ownerUserID
|
||||
return cachekey.GetFriendIDsKey(ownerUserID)
|
||||
}
|
||||
|
||||
// getTwoWayFriendsIDsKey returns the key for storing two-way friend IDs in the cache.
|
||||
func (f *FriendCacheRedis) getTwoWayFriendsIDsKey(ownerUserID string) string {
|
||||
return TwoWayFriendsIDsKey + ownerUserID
|
||||
return cachekey.GetTwoWayFriendsIDsKey(ownerUserID)
|
||||
}
|
||||
|
||||
// getFriendKey returns the key for storing friend info in the cache.
|
||||
func (f *FriendCacheRedis) getFriendKey(ownerUserID, friendUserID string) string {
|
||||
return friendKey + ownerUserID + "-" + friendUserID
|
||||
return cachekey.GetFriendKey(ownerUserID, friendUserID)
|
||||
}
|
||||
|
||||
// GetFriendIDs retrieves friend IDs from the cache or the database if not found.
|
||||
@@ -152,3 +162,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
|
||||
}
|
||||
|
||||
44
pkg/common/db/cache/group.go
vendored
44
pkg/common/db/cache/group.go
vendored
@@ -17,7 +17,8 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
@@ -34,15 +35,14 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
groupExpireTime = time.Second * 60 * 60 * 12
|
||||
groupInfoKey = "GROUP_INFO:"
|
||||
groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
||||
groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
||||
groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
||||
//groupOwnerInfoKey = "GROUP_OWNER_INFO:".
|
||||
joinedGroupsKey = "JOIN_GROUPS_KEY:"
|
||||
groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:"
|
||||
groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:"
|
||||
groupExpireTime = time.Second * 60 * 60 * 12
|
||||
//groupInfoKey = "GROUP_INFO:"
|
||||
//groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
||||
//groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
||||
//groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
||||
//joinedGroupsKey = "JOIN_GROUPS_KEY:"
|
||||
//groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:"
|
||||
//groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:"
|
||||
)
|
||||
|
||||
type GroupHash interface {
|
||||
@@ -105,12 +105,16 @@ func NewGroupCacheRedis(
|
||||
opts rockscache.Options,
|
||||
) GroupCache {
|
||||
rcClient := rockscache.NewClient(rdb, opts)
|
||||
|
||||
mc := NewMetaCacheRedis(rcClient)
|
||||
g := config.Config.LocalCache.Group
|
||||
mc.SetTopic(g.Topic)
|
||||
log.ZDebug(context.Background(), "group local cache init", "Topic", g.Topic, "SlotNum", g.SlotNum, "SlotSize", g.SlotSize, "enable", g.Enable())
|
||||
mc.SetRawRedisClient(rdb)
|
||||
return &GroupCacheRedis{
|
||||
rcClient: rcClient, expireTime: groupExpireTime,
|
||||
groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB,
|
||||
groupHash: hashCode,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
metaCache: mc,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,36 +125,36 @@ func (g *GroupCacheRedis) NewCache() GroupCache {
|
||||
groupDB: g.groupDB,
|
||||
groupMemberDB: g.groupMemberDB,
|
||||
groupRequestDB: g.groupRequestDB,
|
||||
metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...),
|
||||
metaCache: g.Copy(),
|
||||
}
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupInfoKey(groupID string) string {
|
||||
return groupInfoKey + groupID
|
||||
return cachekey.GetGroupInfoKey(groupID)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getJoinedGroupsKey(userID string) string {
|
||||
return joinedGroupsKey + userID
|
||||
return cachekey.GetJoinedGroupsKey(userID)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupMembersHashKey(groupID string) string {
|
||||
return groupMembersHashKey + groupID
|
||||
return cachekey.GetGroupMembersHashKey(groupID)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupMemberIDsKey(groupID string) string {
|
||||
return groupMemberIDsKey + groupID
|
||||
return cachekey.GetGroupMemberIDsKey(groupID)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupMemberInfoKey(groupID, userID string) string {
|
||||
return groupMemberInfoKey + groupID + "-" + userID
|
||||
return cachekey.GetGroupMemberInfoKey(groupID, userID)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupMemberNumKey(groupID string) string {
|
||||
return groupMemberNumKey + groupID
|
||||
return cachekey.GetGroupMemberNumKey(groupID)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) getGroupRoleLevelMemberIDsKey(groupID string, roleLevel int32) string {
|
||||
return groupRoleLevelMemberIDsKey + groupID + "-" + strconv.Itoa(int(roleLevel))
|
||||
return cachekey.GetGroupRoleLevelMemberIDsKey(groupID, roleLevel)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupIndex(group *relationtb.GroupModel, keys []string) (int, error) {
|
||||
|
||||
65
pkg/common/db/cache/meta_cache.go
vendored
65
pkg/common/db/cache/meta_cache.go
vendored
@@ -18,6 +18,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/mw/specialerror"
|
||||
@@ -44,6 +45,9 @@ type metaCache interface {
|
||||
AddKeys(keys ...string)
|
||||
ClearKeys()
|
||||
GetPreDelKeys() []string
|
||||
SetTopic(topic string)
|
||||
SetRawRedisClient(cli redis.UniversalClient)
|
||||
Copy() metaCache
|
||||
}
|
||||
|
||||
func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache {
|
||||
@@ -51,10 +55,36 @@ func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache {
|
||||
}
|
||||
|
||||
type metaCacheRedis struct {
|
||||
topic string
|
||||
rcClient *rockscache.Client
|
||||
keys []string
|
||||
maxRetryTimes int
|
||||
retryInterval time.Duration
|
||||
redisClient redis.UniversalClient
|
||||
}
|
||||
|
||||
func (m *metaCacheRedis) Copy() metaCache {
|
||||
var keys []string
|
||||
if len(m.keys) > 0 {
|
||||
keys = make([]string, 0, len(m.keys)*2)
|
||||
keys = append(keys, m.keys...)
|
||||
}
|
||||
return &metaCacheRedis{
|
||||
topic: m.topic,
|
||||
rcClient: m.rcClient,
|
||||
keys: keys,
|
||||
maxRetryTimes: m.maxRetryTimes,
|
||||
retryInterval: m.retryInterval,
|
||||
redisClient: redisClient,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *metaCacheRedis) SetTopic(topic string) {
|
||||
m.topic = topic
|
||||
}
|
||||
|
||||
func (m *metaCacheRedis) SetRawRedisClient(cli redis.UniversalClient) {
|
||||
m.redisClient = cli
|
||||
}
|
||||
|
||||
func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error {
|
||||
@@ -62,7 +92,7 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error {
|
||||
m.keys = utils.Distinct(m.keys)
|
||||
}
|
||||
if len(m.keys) > 0 {
|
||||
log.ZDebug(ctx, "delete cache", "keys", m.keys)
|
||||
log.ZDebug(ctx, "delete cache", "topic", m.topic, "keys", m.keys)
|
||||
for _, key := range m.keys {
|
||||
for i := 0; i < m.maxRetryTimes; i++ {
|
||||
if err := m.rcClient.TagAsDeleted(key); err != nil {
|
||||
@@ -72,31 +102,18 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error {
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
//retryTimes := 0
|
||||
//for {
|
||||
// m.rcClient.TagAsDeleted()
|
||||
// if err := m.rcClient.TagAsDeletedBatch2(ctx, []string{key}); err != nil {
|
||||
// if retryTimes >= m.maxRetryTimes {
|
||||
// err = errs.ErrInternalServer.Wrap(
|
||||
// fmt.Sprintf(
|
||||
// "delete cache error: %v, keys: %v, retry times %d, please check redis server",
|
||||
// err,
|
||||
// key,
|
||||
// retryTimes,
|
||||
// ),
|
||||
// )
|
||||
// log.ZWarn(ctx, "delete cache failed, please handle keys", err, "keys", key)
|
||||
// return err
|
||||
// }
|
||||
// retryTimes++
|
||||
// } else {
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
}
|
||||
if pk := getPublishKey(m.topic, m.keys); len(pk) > 0 {
|
||||
data, err := json.Marshal(pk)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "keys json marshal failed", err, "topic", m.topic, "keys", pk)
|
||||
} else {
|
||||
if err := m.redisClient.Publish(ctx, m.topic, string(data)).Err(); err != nil {
|
||||
log.ZError(ctx, "redis publish cache delete error", err, "topic", m.topic, "keys", pk)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
24
pkg/common/db/cache/msg.go
vendored
24
pkg/common/db/cache/msg.go
vendored
@@ -17,6 +17,7 @@ package cache
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -44,12 +45,12 @@ const (
|
||||
conversationUserMinSeq = "CON_USER_MIN_SEQ:"
|
||||
hasReadSeq = "HAS_READ_SEQ:"
|
||||
|
||||
appleDeviceToken = "DEVICE_TOKEN"
|
||||
getuiToken = "GETUI_TOKEN"
|
||||
getuiTaskID = "GETUI_TASK_ID"
|
||||
signalCache = "SIGNAL_CACHE:"
|
||||
signalListCache = "SIGNAL_LIST_CACHE:"
|
||||
FCM_TOKEN = "FCM_TOKEN:"
|
||||
//appleDeviceToken = "DEVICE_TOKEN"
|
||||
getuiToken = "GETUI_TOKEN"
|
||||
getuiTaskID = "GETUI_TASK_ID"
|
||||
//signalCache = "SIGNAL_CACHE:"
|
||||
//signalListCache = "SIGNAL_LIST_CACHE:"
|
||||
FCM_TOKEN = "FCM_TOKEN:"
|
||||
|
||||
messageCache = "MESSAGE_CACHE:"
|
||||
messageDelUserList = "MESSAGE_DEL_USER_LIST:"
|
||||
@@ -128,7 +129,8 @@ type MsgModel interface {
|
||||
}
|
||||
|
||||
func NewMsgCacheModel(client redis.UniversalClient) MsgModel {
|
||||
return &msgCache{rdb: client}
|
||||
rcClient := rockscache.NewClient(client, rockscache.NewDefaultOptions())
|
||||
return &msgCache{metaCache: NewMetaCacheRedis(rcClient), rdb: client}
|
||||
}
|
||||
|
||||
type msgCache struct {
|
||||
@@ -148,6 +150,10 @@ func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string
|
||||
return hasReadSeq + userID + ":" + conversationID
|
||||
}
|
||||
|
||||
func (c *msgCache) getConversationUserMinSeqKey(conversationID, userID string) string {
|
||||
return conversationUserMinSeq + conversationID + "u:" + userID
|
||||
}
|
||||
|
||||
func (c *msgCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error {
|
||||
return utils.Wrap1(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err())
|
||||
}
|
||||
@@ -209,10 +215,6 @@ func (c *msgCache) GetMinSeq(ctx context.Context, conversationID string) (int64,
|
||||
return c.getSeq(ctx, conversationID, c.getMinSeqKey)
|
||||
}
|
||||
|
||||
func (c *msgCache) getConversationUserMinSeqKey(conversationID, userID string) string {
|
||||
return conversationUserMinSeq + conversationID + "u:" + userID
|
||||
}
|
||||
|
||||
func (c *msgCache) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
|
||||
return utils.Wrap2(c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64())
|
||||
}
|
||||
|
||||
21
pkg/common/db/cache/user.go
vendored
21
pkg/common/db/cache/user.go
vendored
@@ -18,6 +18,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||
"hash/crc32"
|
||||
"strconv"
|
||||
"time"
|
||||
@@ -36,8 +38,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
userExpireTime = time.Second * 60 * 60 * 12
|
||||
userInfoKey = "USER_INFO:"
|
||||
userExpireTime = time.Second * 60 * 60 * 12
|
||||
//userInfoKey = "USER_INFO:"
|
||||
userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:"
|
||||
olineStatusKey = "ONLINE_STATUS:"
|
||||
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
|
||||
@@ -72,7 +74,11 @@ func NewUserCacheRedis(
|
||||
options rockscache.Options,
|
||||
) UserCache {
|
||||
rcClient := rockscache.NewClient(rdb, options)
|
||||
|
||||
mc := NewMetaCacheRedis(rcClient)
|
||||
u := config.Config.LocalCache.User
|
||||
log.ZDebug(context.Background(), "user local cache init", "Topic", u.Topic, "SlotNum", u.SlotNum, "SlotSize", u.SlotSize, "enable", u.Enable())
|
||||
mc.SetTopic(u.Topic)
|
||||
mc.SetRawRedisClient(rdb)
|
||||
return &UserCacheRedis{
|
||||
rdb: rdb,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
@@ -85,7 +91,7 @@ func NewUserCacheRedis(
|
||||
func (u *UserCacheRedis) NewCache() UserCache {
|
||||
return &UserCacheRedis{
|
||||
rdb: u.rdb,
|
||||
metaCache: NewMetaCacheRedis(u.rcClient, u.metaCache.GetPreDelKeys()...),
|
||||
metaCache: u.Copy(),
|
||||
userDB: u.userDB,
|
||||
expireTime: u.expireTime,
|
||||
rcClient: u.rcClient,
|
||||
@@ -93,18 +99,17 @@ func (u *UserCacheRedis) NewCache() UserCache {
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) getUserInfoKey(userID string) string {
|
||||
return userInfoKey + userID
|
||||
return cachekey.GetUserInfoKey(userID)
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string {
|
||||
return userGlobalRecvMsgOptKey + userID
|
||||
return cachekey.GetUserGlobalRecvMsgOptKey(userID)
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) GetUserInfo(ctx context.Context, userID string) (userInfo *relationtb.UserModel, err error) {
|
||||
return getCache(ctx, u.rcClient, u.getUserInfoKey(userID), u.expireTime, func(ctx context.Context) (*relationtb.UserModel, error) {
|
||||
return u.userDB.Take(ctx, userID)
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]*relationtb.UserModel, error) {
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user