Compare commits
113 Commits
release-v3
...
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"
|
||||||
|
}
|
||||||
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"
|
echo "Run unit test and get test coverage successfully"
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v2
|
|
||||||
|
|
||||||
- name: OpenIM verify copyright
|
- name: OpenIM verify copyright
|
||||||
run: |
|
run: |
|
||||||
sudo make verify-copyright
|
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.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
||||||
# https://github.com/BetaHuhn/repo-file-sync-action
|
# 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:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
@@ -41,3 +41,4 @@ jobs:
|
|||||||
automerge
|
automerge
|
||||||
ASSIGNEES: |
|
ASSIGNEES: |
|
||||||
kubbot
|
kubbot
|
||||||
|
continue-on-error: true
|
||||||
3
.github/workflows/sync.yml
vendored
3
.github/workflows/sync.yml
vendored
@@ -3,7 +3,7 @@
|
|||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
|
|
||||||
# https://github.com/BetaHuhn/repo-file-sync-action
|
# 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:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
@@ -37,3 +37,4 @@ jobs:
|
|||||||
automerge
|
automerge
|
||||||
ASSIGNEES: |
|
ASSIGNEES: |
|
||||||
kubbot
|
kubbot
|
||||||
|
continue-on-error: true
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,6 +34,7 @@ deployments/charts/generated-configs/
|
|||||||
### OpenIM Config ###
|
### OpenIM Config ###
|
||||||
.env
|
.env
|
||||||
config/config.yaml
|
config/config.yaml
|
||||||
|
config/openim.yaml
|
||||||
config/alertmanager.yml
|
config/alertmanager.yml
|
||||||
config/prometheus.yml
|
config/prometheus.yml
|
||||||
config/email.tmpl
|
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
|
||||||
|
|
||||||
OpenIM 不仅仅是一个开源的即时消息组件,它是你的应用程序生态系统的一个不可或缺的部分。查看下面的图表,了解 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 是如何交互的。
|
OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通话、通知以及AI聊天机器人等通信功能的服务平台。它通过提供一系列强大的API和Webhooks,使开发者可以轻松地在他们的应用中加入这些交互特性。OpenIM 本身并不是一个独立运行的聊天应用,而是作为一个平台,为其他应用提供支持,实现丰富的通信功能。下图展示 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 之间的交互关系来具体说明。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 🚀 关于 OpenIMSDK
|
## 🚀 关于 OpenIMSDK
|
||||||
|
|
||||||
**OpenIMSDK** 无缝集成到您的应用中,提供丰富、实时的消息体验,无需复杂的 UI 集成。它提供:
|
**OpenIMSDK** 是为 **OpenIMServer** 设计的IM SDK,专为嵌入客户端应用而生。其主要功能及模块如下:
|
||||||
|
|
||||||
+ **本地存储**:用于快速数据检索和消息同步。
|
+ 🌟 主要功能:
|
||||||
+ **监听器回调**:确保实时消息交互性。
|
|
||||||
+ **API 封装**:简化开发流程。
|
|
||||||
+ **连接管理**:保证可靠的消息传递。
|
|
||||||
|
|
||||||
它使用 Golang 构建,并支持跨平台部署,确保在所有平台上提供一致的消息体验。
|
- 📦 本地存储
|
||||||
|
- 🔔 监听器回调
|
||||||
|
- 🛡️ API封装
|
||||||
|
- 🌐 连接管理
|
||||||
|
|
||||||
|
## 📚 主要模块:
|
||||||
|
|
||||||
|
1. 🚀 初始化及登录
|
||||||
|
2. 👤 用户管理
|
||||||
|
3. 👫 好友管理
|
||||||
|
4. 🤖 群组功能
|
||||||
|
5. 💬 会话处理
|
||||||
|
|
||||||
|
它使用 Golang 构建,并支持跨平台部署,确保在所有平台上提供一致的接入体验。
|
||||||
|
|
||||||
👉 **[探索 GO SDK](https://github.com/openimsdk/openim-sdk-core)**
|
👉 **[探索 GO SDK](https://github.com/openimsdk/openim-sdk-core)**
|
||||||
|
|
||||||
## 🌐 关于 OpenIMServer
|
## 🌐 关于 OpenIMServer
|
||||||
|
|
||||||
精心用 Golang 开发的 **OpenIMServer** 通过多重方式确保了卓越的即时消息服务器能力:
|
+ **OpenIMServer** 具有以下特点:
|
||||||
|
- 🌐 微服务架构:支持集群模式,包括网关(gateway)和多个rpc服务。
|
||||||
+ **模块组成**:它由多个模块组成,例如网关和多个 RPC 服务,提供一个多功能的消息环境。
|
- 🚀 部署方式多样:支持源代码、kubernetes或docker部署。
|
||||||
+ **微服务架构**:支持集群模式,确保出色的性能和可伸缩性,以有效管理各个实例间的通信。
|
- 海量用户支持:十万超级大群,千万用户,及百亿消息
|
||||||
+ **多样的部署选项**:适应你的操作偏好,通过源代码、Kubernetes 或 Docker 提供部署选项。
|
|
||||||
|
|
||||||
### 增强的业务功能:
|
### 增强的业务功能:
|
||||||
|
|
||||||
+ **REST API**:OpenIMServer 为业务系统提供 REST API,旨在通过后端接口为您的操作提供附加功能,如群组创建和消息推送。
|
+ **REST API**:OpenIMServer 提供了REST API供业务系统使用,旨在赋予业务更多功能,例如通过后台接口建立群组、发送推送消息等。
|
||||||
+ **回调**:为了扩展其在各种业务形式中的实用性,OpenIMServer 提供了回调能力。即,在事件发生之前或之后,它向业务服务器发送请求,比如发送消息,丰富通信过程中的交互和数据交换流。
|
+ **Webhooks**:OpenIMServer提供了回调能力以扩展更多的业务形态,所谓回调,即OpenIMServer会在某一事件发生之前或者之后,向业务服务器发送请求,如发送消息之前或之后的回调。
|
||||||
|
|
||||||
👉 **[了解更多](https://doc.rentsoft.cn/guides/introduction/product)**
|
👉 **[了解更多](https://docs.openim.io/guides/introduction/product)**
|
||||||
|
|
||||||
## :rocket: 快速开始
|
## :rocket: 快速开始
|
||||||
|
|
||||||
你只需要一个简单的命令,就可以快速学习 OpenIM 的工程解决方案:
|
在线体验iOS/Android/H5/PC/Web:
|
||||||
|
|
||||||
```
|
👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)**
|
||||||
bashCopy code
|
|
||||||
$ make demo
|
|
||||||
```
|
|
||||||
|
|
||||||
🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法:
|
🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法:
|
||||||
|
|
||||||
+ **[源代码部署指南](https://doc.rentsoft.cn/guides/gettingStarted/imSourceCodeDeployment)**
|
+ **[源代码部署指南](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)**
|
||||||
+ **[Docker 部署指南](https://doc.rentsoft.cn/guides/gettingStarted/dockerCompose)**
|
+ **[Docker 部署指南](https://docs.openim.io/guides/gettingStarted/dockerCompose)**
|
||||||
+ **[Kubernetes 部署指南](https://github.com/openimsdk/open-im-server/tree/main/deployments)**
|
+ **[Kubernetes 部署指南](https://docs.openim.io/guides/gettingStarted/k8s-deployment)**
|
||||||
|
|
||||||
## :hammer_and_wrench: 开始开发 OpenIM
|
## :hammer_and_wrench: 开始开发 OpenIM
|
||||||
|
|
||||||
|
|||||||
179
README.md
179
README.md
@@ -25,75 +25,48 @@
|
|||||||
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## Ⓜ️ About OpenIM
|
## Ⓜ️ About OpenIM
|
||||||
|
|
||||||
OpenIM isn't just an open-source instant messaging component, it's an integral part of your application ecosystem. Check out this diagram to understand how AppServer, AppClient, OpenIMServer, and OpenIMSDK interact.
|
OpenIM is a service platform specifically designed for integrating chat, audio-video calls, notifications, and AI chatbots into applications. It provides a range of powerful APIs and Webhooks, enabling developers to easily incorporate these interactive features into their applications. OpenIM is not a standalone chat application, but rather serves as a platform to support other applications in achieving rich communication functionalities. The following diagram illustrates the interaction between AppServer, AppClient, OpenIMServer, and OpenIMSDK to explain in detail.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 🚀 About OpenIMSDK
|
## 🚀 About OpenIMSDK
|
||||||
|
|
||||||
**OpenIMSDK** seamlessly integrates into your application, delivering a rich, real-time messaging experience without requiring intricate UI integration. It provides:
|
**OpenIMSDK** is an IM SDK designed for **OpenIMServer**, created specifically for embedding in client applications. Its main features and modules are as follows:
|
||||||
|
|
||||||
+ **Local Storage**: For quick data retrieval and message synchronization.
|
+ 🌟 Main Features:
|
||||||
+ **Listener Callbacks**: Ensuring real-time message interactivity.
|
|
||||||
+ **API Encapsulation**: Streamlining development processes.
|
|
||||||
+ **Connection Management**: Guaranteeing reliable message delivery.
|
|
||||||
|
|
||||||
It's crafted in Golang and supports cross-platform deployment, ensuring a coherent messaging experience across all platforms.
|
- 📦 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)**
|
👉 **[Explore GO SDK](https://github.com/openimsdk/openim-sdk-core)**
|
||||||
|
|
||||||
## 🌐 About OpenIMServer
|
## 🌐 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.
|
### Enhanced Business Functionality:
|
||||||
+ **Microservices Architecture**: Supporting cluster modes, it assures outstanding performance and scalability to manage communication effectively across various instances.
|
|
||||||
+ **Diverse Deployment Options**: Adapts to your operational preferences, offering deployment via source code, Kubernetes, or Docker.
|
|
||||||
|
|
||||||
### Enhanced Business Functionalities:
|
+ **REST API**: OpenIMServer 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.
|
||||||
+ **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.
|
|
||||||
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
👉 **[Learn more](https://docs.openim.io/guides/introduction/product)**
|
||||||
|
|
||||||
## :rocket: Quick Start
|
## :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/)**
|
👉 **[OpenIM online web demo](https://web-enterprise.rentsoft.cn/)**
|
||||||
|
|
||||||
You can quickly learn OpenIM engineering solutions, all it takes is one simple command:
|
🤲 To facilitate user experience, we offer various deployment solutions. You can choose your deployment method from the list below:
|
||||||
|
|
||||||
```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>
|
|
||||||
-->
|
|
||||||
|
|
||||||
+ **[Source Code Deployment Guide](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)**
|
+ **[Source Code Deployment Guide](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)**
|
||||||
+ **[Production deployment of Linux systems](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-openim-linux-system.md)**
|
|
||||||
+ **[Docker Deployment Guide](https://docs.openim.io/guides/gettingStarted/dockerCompose)**
|
+ **[Docker Deployment Guide](https://docs.openim.io/guides/gettingStarted/dockerCompose)**
|
||||||
+ **[Kubernetes Deployment Guide](https://github.com/openimsdk/open-im-server/tree/main/deployments)**
|
+ **[Kubernetes Deployment Guide](https://docs.openim.io/guides/gettingStarted/k8s-deployment)**
|
||||||
|
+ **[Mac Developer Deployment Guide](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)**
|
||||||
|
|
||||||
<!--
|
## :hammer_and_wrench: To Start Developing OpenIM
|
||||||
## :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
|
|
||||||
|
|
||||||
[](https://vscode.dev/github/openimsdk/open-im-server)
|
[](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 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)
|
- [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)
|
- [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
|
## :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).
|
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.
|
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:
|
## 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(席欣).
|
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*.
|
||||||
@@ -1,45 +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.
|
|
||||||
|
|
||||||
# OpenIM base image: https://github.com/openim-sigs/openim-base-image
|
|
||||||
|
|
||||||
# Set go mod installation source and proxy
|
|
||||||
|
|
||||||
FROM golang:1.20 AS builder
|
|
||||||
|
|
||||||
ARG GO111MODULE=on
|
|
||||||
ARG GOPROXY=https://goproxy.cn,direct
|
|
||||||
|
|
||||||
WORKDIR /openim/openim-server
|
|
||||||
|
|
||||||
ENV GO111MODULE=$GO111MODULE
|
|
||||||
ENV GOPROXY=$GOPROXY
|
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN make build BINS=openim-rpc-encryption
|
|
||||||
|
|
||||||
RUN cp /openim/openim-server/_output/bin/platforms/$(go env GOOS)/$(go env GOARCH)/openim-rpc-encryption /usr/bin/openim-rpc-encryption
|
|
||||||
|
|
||||||
# FROM ghcr.io/openim-sigs/openim-bash-image:latest
|
|
||||||
FROM ghcr.io/openim-sigs/openim-bash-image:latest
|
|
||||||
|
|
||||||
WORKDIR /openim/openim-server
|
|
||||||
|
|
||||||
COPY --from=builder /usr/bin/openim-rpc-encryption ./bin/openim-rpc-encryption
|
|
||||||
|
|
||||||
ENTRYPOINT ["./bin/openim-rpc-encryption"]
|
|
||||||
@@ -1,45 +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.
|
|
||||||
|
|
||||||
# OpenIM base image: https://github.com/openim-sigs/openim-base-image
|
|
||||||
|
|
||||||
# Set go mod installation source and proxy
|
|
||||||
|
|
||||||
FROM golang:1.20 AS builder
|
|
||||||
|
|
||||||
ARG GO111MODULE=on
|
|
||||||
ARG GOPROXY=https://goproxy.cn,direct
|
|
||||||
|
|
||||||
WORKDIR /openim/openim-server
|
|
||||||
|
|
||||||
ENV GO111MODULE=$GO111MODULE
|
|
||||||
ENV GOPROXY=$GOPROXY
|
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
RUN make build BINS=openim-rpc-extend-msg
|
|
||||||
|
|
||||||
RUN cp /openim/openim-server/_output/bin/platforms/$(go env GOOS)/$(go env GOARCH)/openim-rpc-extend-msg /usr/bin/openim-rpc-extend-msg
|
|
||||||
|
|
||||||
# FROM ghcr.io/openim-sigs/openim-bash-image:latest
|
|
||||||
FROM ghcr.io/openim-sigs/openim-bash-image:latest
|
|
||||||
|
|
||||||
WORKDIR /openim/openim-server
|
|
||||||
|
|
||||||
COPY --from=builder /usr/bin/openim-rpc-extend-msg ./bin/openim-rpc-extend-msg
|
|
||||||
|
|
||||||
ENTRYPOINT ["./bin/openim-rpc-extend-msg"]
|
|
||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
"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/cache"
|
||||||
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
|
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"
|
"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")
|
log.ZInfo(context.Background(), "api register public config to discov success")
|
||||||
router := api.NewGinRouter(client, rdb)
|
router := api.NewGinRouter(client, rdb)
|
||||||
if config.Config.Prometheus.Enable {
|
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.SetListenAddress(fmt.Sprintf(":%d", proPort))
|
||||||
p.Use(router)
|
p.Use(router)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
# TODO: This config file is the template file
|
# TODO: This config file is the template file
|
||||||
# --| source: deployments/templates/openim.yaml
|
# --| source: deployments/templates/config.yaml
|
||||||
# --| env: scripts/install/environment
|
# --| env: scripts/install/environment
|
||||||
# --| target: config/config.yaml
|
# --| target: config/config.yaml
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
@@ -52,8 +52,8 @@ mongo:
|
|||||||
# Default MongoDB database name
|
# Default MongoDB database name
|
||||||
# Maximum connection pool size
|
# Maximum connection pool size
|
||||||
address: [ 172.28.0.1:37017 ]
|
address: [ 172.28.0.1:37017 ]
|
||||||
database: openIM_v3
|
database: openim_v3
|
||||||
username: root
|
username: openIM
|
||||||
password: openIM123
|
password: openIM123
|
||||||
maxPoolSize: 100
|
maxPoolSize: 100
|
||||||
|
|
||||||
@@ -122,14 +122,14 @@ api:
|
|||||||
# minio.signEndpoint is minio public network address
|
# minio.signEndpoint is minio public network address
|
||||||
object:
|
object:
|
||||||
enable: "minio"
|
enable: "minio"
|
||||||
apiURL: "http://14.155.64.202:10002"
|
apiURL: "http://172.28.0.1:10002"
|
||||||
minio:
|
minio:
|
||||||
bucket: "openim"
|
bucket: "openim"
|
||||||
endpoint: "http://172.28.0.1:10005"
|
endpoint: "http://172.28.0.1:10005"
|
||||||
accessKeyID: "root"
|
accessKeyID: "root"
|
||||||
secretAccessKey: "openIM123"
|
secretAccessKey: "openIM123"
|
||||||
sessionToken: ''
|
sessionToken: ''
|
||||||
signEndpoint: "http://14.155.64.202:10005"
|
signEndpoint: "http://172.28.0.1:10005"
|
||||||
publicRead: false
|
publicRead: false
|
||||||
cos:
|
cos:
|
||||||
bucketURL: https://temp-1252357374.cos.ap-chengdu.myqcloud.com
|
bucketURL: https://temp-1252357374.cos.ap-chengdu.myqcloud.com
|
||||||
@@ -193,7 +193,7 @@ rpcRegisterName:
|
|||||||
# Whether to output in json format
|
# Whether to output in json format
|
||||||
# Whether to include stack trace in logs
|
# Whether to include stack trace in logs
|
||||||
log:
|
log:
|
||||||
storageLocation: /data/workspaces/open-im-server/logs/
|
storageLocation: /workspaces/open-im-server/logs/
|
||||||
rotationTime: 24
|
rotationTime: 24
|
||||||
remainRotationCount: 2
|
remainRotationCount: 2
|
||||||
remainLogLevel: 6
|
remainLogLevel: 6
|
||||||
@@ -247,6 +247,14 @@ manager:
|
|||||||
userID: [ "openIM123456", "openIM654321", "openIMAdmin" ]
|
userID: [ "openIM123456", "openIM654321", "openIMAdmin" ]
|
||||||
nickname: [ "system1", "system2", "system3" ]
|
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
|
# Multi-platform login policy
|
||||||
# For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time
|
# For each platform(Android, iOS, Windows, Mac, web), only one can be online at a time
|
||||||
multiLoginPolicy: 1
|
multiLoginPolicy: 1
|
||||||
@@ -307,21 +315,21 @@ iosPush:
|
|||||||
# Timeout in seconds
|
# Timeout in seconds
|
||||||
# Whether to continue execution if callback fails
|
# Whether to continue execution if callback fails
|
||||||
callback:
|
callback:
|
||||||
url: ""
|
url: "http://127.0.0.1:10008/callbackExample"
|
||||||
beforeSendSingleMsg:
|
beforeSendSingleMsg:
|
||||||
enable: false
|
enable: false
|
||||||
timeout: 5
|
timeout: 5
|
||||||
failedContinue: true
|
failedContinue: true
|
||||||
beforeUpdateUserInfoEx:
|
beforeUpdateUserInfoEx:
|
||||||
enable: false
|
enable: false
|
||||||
timeout: 5
|
timeout: 5
|
||||||
failedContinue: true
|
failedContinue: true
|
||||||
afterUpdateUserInfoEx:
|
afterUpdateUserInfoEx:
|
||||||
enable: false
|
enable: false
|
||||||
timeout: 5
|
timeout: 5
|
||||||
failedContinue: true
|
failedContinue: true
|
||||||
afterSendSingleMsg:
|
afterSendSingleMsg:
|
||||||
enable: false
|
enable: true
|
||||||
timeout: 5
|
timeout: 5
|
||||||
failedContinue: true
|
failedContinue: true
|
||||||
beforeSendGroupMsg:
|
beforeSendGroupMsg:
|
||||||
@@ -505,8 +513,8 @@ callback:
|
|||||||
# The number of Prometheus ports per service needs to correspond to rpcPort
|
# 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
|
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
|
||||||
prometheus:
|
prometheus:
|
||||||
enable: false
|
enable: true
|
||||||
grafanaUrl: 172.28.0.1:13000
|
grafanaUrl: http://172.28.0.1:13000/
|
||||||
apiPrometheusPort: [20100]
|
apiPrometheusPort: [20100]
|
||||||
userPrometheusPort: [ 20110 ]
|
userPrometheusPort: [ 20110 ]
|
||||||
friendPrometheusPort: [ 20120 ]
|
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");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
@@ -12,31 +12,26 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
# ======================================
|
# -----------------------------------------------------------------------------
|
||||||
# ========= 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.
|
# Data storage directory for persistent data.
|
||||||
# Default: OPENIM_USER=root
|
# Example: DATA_DIR=/path/to/data
|
||||||
USER=root
|
DATA_DIR=/workspaces/open-im-server
|
||||||
|
|
||||||
# Password associated with the specified user for authentication.
|
# Docker image registry. Uncomment the preferred one.
|
||||||
# Default: PASSWORD=openIM123
|
# Options: ghcr.io/openimsdk, openim, registry.cn-hangzhou.aliyuncs.com/openimsdk
|
||||||
PASSWORD=openIM123
|
# IMAGE_REGISTRY="ghcr.io/openimsdk"
|
||||||
|
# IMAGE_REGISTRY="openim"
|
||||||
# Base URL for the application programming interface (API).
|
# IMAGE_REGISTRY="registry.cn-hangzhou.aliyuncs.com/openimsdk"
|
||||||
# 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"
|
|
||||||
IMAGE_REGISTRY=ghcr.io/openimsdk
|
IMAGE_REGISTRY=ghcr.io/openimsdk
|
||||||
|
|
||||||
# ======================================
|
# ======================================
|
||||||
@@ -47,10 +42,9 @@ IMAGE_REGISTRY=ghcr.io/openimsdk
|
|||||||
# Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16
|
# Default: DOCKER_BRIDGE_SUBNET=172.28.0.0/16
|
||||||
DOCKER_BRIDGE_SUBNET=172.28.0.0/16
|
DOCKER_BRIDGE_SUBNET=172.28.0.0/16
|
||||||
|
|
||||||
# Gateway for the Docker network.
|
# Set and specify the IP addresses of some containers. Generally speaking,
|
||||||
# Default: DOCKER_BRIDGE_GATEWAY=172.28.0.1
|
# you do not need to modify these configurations to facilitate debugging
|
||||||
DOCKER_BRIDGE_GATEWAY=172.28.0.1
|
DOCKER_BRIDGE_GATEWAY=172.28.0.1
|
||||||
|
|
||||||
MONGO_NETWORK_ADDRESS=172.28.0.2
|
MONGO_NETWORK_ADDRESS=172.28.0.2
|
||||||
REDIS_NETWORK_ADDRESS=172.28.0.3
|
REDIS_NETWORK_ADDRESS=172.28.0.3
|
||||||
KAFKA_NETWORK_ADDRESS=172.28.0.4
|
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
|
OPENIM_ADMIN_FRONT_NETWORK_ADDRESS=172.28.0.13
|
||||||
ALERT_MANAGER_NETWORK_ADDRESS=172.28.0.14
|
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 -----
|
# ----- 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.
|
# Port for ZooKeeper service.
|
||||||
# Default: ZOOKEEPER_PORT=12181
|
# Default: ZOOKEEPER_PORT=12181
|
||||||
ZOOKEEPER_PORT=12181
|
ZOOKEEPER_PORT=12181
|
||||||
|
|
||||||
# ----- MongoDB Configuration -----
|
# MongoDB service port 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.
|
|
||||||
# Default: MONGO_PORT=37017
|
# Default: MONGO_PORT=37017
|
||||||
# MONGO_PORT=37017
|
# MONGO_PORT=37017
|
||||||
|
|
||||||
# Username to authenticate with the MongoDB service.
|
# Password for MongoDB admin user. Used for service authentication.
|
||||||
# Default: MONGO_USERNAME=root
|
|
||||||
# MONGO_USERNAME=root
|
|
||||||
|
|
||||||
# Password to authenticate with the MongoDB service.
|
|
||||||
# Default: MONGO_PASSWORD=openIM123
|
# Default: MONGO_PASSWORD=openIM123
|
||||||
MONGO_PASSWORD=openIM123
|
MONGO_PASSWORD=openIM123
|
||||||
|
|
||||||
# Name of the database in MongoDB to be used.
|
# Username for a regular OpenIM user in MongoDB.
|
||||||
# Default: MONGO_DATABASE=openIM_v3
|
# Default: MONGO_OPENIM_USERNAME=openIM
|
||||||
MONGO_DATABASE=openIM_v3
|
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 -----
|
# ----- 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.
|
# Port on which Redis in-memory data structure store is running.
|
||||||
# Default: REDIS_PORT=16379
|
# Default: REDIS_PORT=16379
|
||||||
@@ -113,11 +128,6 @@ REDIS_PORT=16379
|
|||||||
# Default: REDIS_PASSWORD=openIM123
|
# Default: REDIS_PASSWORD=openIM123
|
||||||
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.
|
# Kakfa username to authenticate with the Kafka service.
|
||||||
# KAFKA_USERNAME=''
|
# KAFKA_USERNAME=''
|
||||||
|
|
||||||
@@ -129,20 +139,13 @@ KAFKA_PORT=19094
|
|||||||
# Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
|
# Default: KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
|
||||||
KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
|
KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
|
||||||
|
|
||||||
# Topic in Kafka for pushing messages (e.g. notifications or updates).
|
# MINIO_PORT
|
||||||
# Default: KAFKA_MSG_PUSH_TOPIC=msgToPush
|
# ----------
|
||||||
KAFKA_MSG_PUSH_TOPIC=msgToPush
|
# 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
|
||||||
# Topic in Kafka for storing offline messages in MongoDB.
|
# to reflect this change. The endpoints include both the 'endpoint' and 'signEndpoint'
|
||||||
# Default: KAFKA_OFFLINEMSG_MONGO_TOPIC=offlineMsgToMongoMysql
|
# under the MinIO configuration.
|
||||||
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.
|
|
||||||
# Default: MINIO_PORT=10005
|
# Default: MINIO_PORT=10005
|
||||||
MINIO_PORT=10005
|
MINIO_PORT=10005
|
||||||
|
|
||||||
@@ -155,19 +158,11 @@ MINIO_PORT=10005
|
|||||||
MINIO_SECRET_KEY=openIM123
|
MINIO_SECRET_KEY=openIM123
|
||||||
|
|
||||||
# ----- Prometheus Configuration -----
|
# ----- 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.
|
# Port on which Prometheus service is running.
|
||||||
# Default: PROMETHEUS_PORT=19090
|
# Default: PROMETHEUS_PORT=19090
|
||||||
PROMETHEUS_PORT=19090
|
PROMETHEUS_PORT=19090
|
||||||
|
|
||||||
# ----- Grafana Configuration -----
|
# ----- 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.
|
# Port on which Grafana service is running.
|
||||||
# Default: GRAFANA_PORT=13000
|
# Default: GRAFANA_PORT=13000
|
||||||
GRAFANA_PORT=13000
|
GRAFANA_PORT=13000
|
||||||
@@ -176,56 +171,41 @@ GRAFANA_PORT=13000
|
|||||||
# ============ OpenIM Web ===============
|
# ============ 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.
|
# Port on which OpenIM web service is running.
|
||||||
# Default: OPENIM_WEB_PORT=11001
|
# Default: OPENIM_WEB_PORT=11001
|
||||||
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 ==============
|
# ========= 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.
|
# Port for the OpenIM WebSockets.
|
||||||
# Default: OPENIM_WS_PORT=10001
|
# Default: OPENIM_WS_PORT=10001
|
||||||
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
|
# Default: API_OPENIM_PORT=10002
|
||||||
API_OPENIM_PORT=10002
|
API_OPENIM_PORT=10002
|
||||||
|
|
||||||
|
|
||||||
# ======================================
|
# ======================================
|
||||||
# ========== OpenIM Chat ===============
|
# ========== OpenIM Chat ===============
|
||||||
# ======================================
|
# ======================================
|
||||||
|
|
||||||
# Branch name for OpenIM chat.
|
# Branch name for OpenIM chat.
|
||||||
# Default: CHAT_IMAGE_VERSION=main
|
# Default: CHAT_IMAGE_VERSION=main
|
||||||
# https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md
|
|
||||||
CHAT_IMAGE_VERSION=main
|
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.
|
# Port for the OpenIM chat API.
|
||||||
# Default: OPENIM_CHAT_API_PORT=10008
|
# Default: OPENIM_CHAT_API_PORT=10008
|
||||||
OPENIM_CHAT_API_PORT=10008
|
OPENIM_CHAT_API_PORT=10008
|
||||||
|
|
||||||
# Directory path for storing data files or related information for OpenIM chat.
|
# Port for the OpenIM admin API.
|
||||||
# Default: OPENIM_CHAT_DATA_DIR=./openim-chat/main
|
# Default: OPENIM_ADMIN_API_PORT=10009
|
||||||
OPENIM_CHAT_DATA_DIR=./openim-chat/main
|
OPENIM_ADMIN_API_PORT=10009
|
||||||
|
|
||||||
|
|
||||||
# ======================================
|
# ======================================
|
||||||
# ========== OpenIM Admin ==============
|
# ========== OpenIM Admin ==============
|
||||||
@@ -235,10 +215,6 @@ OPENIM_CHAT_DATA_DIR=./openim-chat/main
|
|||||||
# Default: SERVER_IMAGE_VERSION=main
|
# Default: SERVER_IMAGE_VERSION=main
|
||||||
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.
|
# Port for the node exporter.
|
||||||
# Default: NODE_EXPORTER_PORT=19100
|
# Default: NODE_EXPORTER_PORT=19100
|
||||||
NODE_EXPORTER_PORT=19100
|
NODE_EXPORTER_PORT=19100
|
||||||
|
|||||||
@@ -527,3 +527,39 @@ prometheus:
|
|||||||
rtcPrometheusPort: [ ${RTC_PROM_PORT} ]
|
rtcPrometheusPort: [ ${RTC_PROM_PORT} ]
|
||||||
thirdPrometheusPort: [ ${THIRD_PROM_PORT} ]
|
thirdPrometheusPort: [ ${THIRD_PROM_PORT} ]
|
||||||
messageTransferPrometheusPort: [ ${MSG_TRANSFER_PROM_PORT} ] # List of ports
|
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
|
||||||
@@ -27,7 +27,7 @@ services:
|
|||||||
- wiredTigerCacheSizeGB=1
|
- wiredTigerCacheSizeGB=1
|
||||||
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
|
- MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
|
||||||
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
|
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
|
||||||
- MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openIM_v3}
|
- MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openim_v3}
|
||||||
- MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username
|
- MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username
|
||||||
- MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password
|
- MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password
|
||||||
restart: always
|
restart: always
|
||||||
@@ -87,6 +87,7 @@ services:
|
|||||||
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@<your_host>:9093
|
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@<your_host>:9093
|
||||||
- KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094
|
- 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://${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_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
|
||||||
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
|
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
|
||||||
networks:
|
networks:
|
||||||
@@ -114,6 +115,7 @@ services:
|
|||||||
openim-web:
|
openim-web:
|
||||||
image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker}
|
image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker}
|
||||||
container_name: openim-web
|
container_name: openim-web
|
||||||
|
platform: linux/amd64
|
||||||
restart: always
|
restart: always
|
||||||
ports:
|
ports:
|
||||||
- "${OPENIM_WEB_PORT:-11001}:80"
|
- "${OPENIM_WEB_PORT:-11001}:80"
|
||||||
@@ -122,7 +124,7 @@ services:
|
|||||||
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7}
|
ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7}
|
||||||
|
|
||||||
### TODO: Uncomment, or deploy using openim docker: https://github.com/openimsdk/openim-docker
|
### TODO: Uncomment, or deploy using openim docker: https://github.com/openimsdk/openim-docker
|
||||||
# Uncomment and configure the following services as needed
|
### Uncomment and configure the following services as needed
|
||||||
|
|
||||||
# openim-server:
|
# openim-server:
|
||||||
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main}
|
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main}
|
||||||
@@ -171,6 +173,23 @@ services:
|
|||||||
# server:
|
# server:
|
||||||
# ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8}
|
# 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:
|
# openim-chat:
|
||||||
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main}
|
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main}
|
||||||
# container_name: openim-chat
|
# container_name: openim-chat
|
||||||
@@ -195,9 +214,15 @@ services:
|
|||||||
# - "${OPENIM_ADMIN_API_PORT:-10009}:10009"
|
# - "${OPENIM_ADMIN_API_PORT:-10009}:10009"
|
||||||
# volumes:
|
# volumes:
|
||||||
# - "${DATA_DIR:-./}/components/openim-chat/logs:/openim/openim-chat/logs"
|
# - "${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"
|
# - "${DATA_DIR:-./}/components/openim-chat/config:/openim/openim-chat/config"
|
||||||
# restart: always
|
# restart: always
|
||||||
# # user: root:root
|
# # user: root:root
|
||||||
|
# depends_on:
|
||||||
|
# - mysql
|
||||||
|
# - kafka
|
||||||
|
# - redis
|
||||||
|
# - zookeeper
|
||||||
# logging:
|
# logging:
|
||||||
# driver: json-file
|
# driver: json-file
|
||||||
# options:
|
# options:
|
||||||
@@ -211,6 +236,7 @@ services:
|
|||||||
# # https://github.com/openimsdk/open-im-server/issues/1662
|
# # 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}
|
# image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35}
|
||||||
# container_name: openim-admin
|
# container_name: openim-admin
|
||||||
|
# platform: linux/amd64
|
||||||
# restart: always
|
# restart: always
|
||||||
# ports:
|
# ports:
|
||||||
# - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80"
|
# - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80"
|
||||||
|
|||||||
12
go.mod
12
go.mod
@@ -4,8 +4,8 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
firebase.google.com/go v3.13.0+incompatible
|
firebase.google.com/go v3.13.0+incompatible
|
||||||
github.com/OpenIMSDK/protocol v0.0.47
|
github.com/OpenIMSDK/protocol v0.0.49
|
||||||
github.com/OpenIMSDK/tools v0.0.29
|
github.com/OpenIMSDK/tools v0.0.28
|
||||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||||
github.com/dtm-labs/rockscache v0.1.1
|
github.com/dtm-labs/rockscache v0.1.1
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
@@ -18,6 +18,7 @@ require (
|
|||||||
github.com/minio/minio-go/v7 v7.0.63
|
github.com/minio/minio-go/v7 v7.0.63
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
|
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/pkg/errors v0.9.1 // indirect
|
||||||
github.com/prometheus/client_golang v1.17.0
|
github.com/prometheus/client_golang v1.17.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
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/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible
|
||||||
github.com/go-redis/redis v6.15.9+incompatible
|
github.com/go-redis/redis v6.15.9+incompatible
|
||||||
github.com/redis/go-redis/v9 v9.2.1
|
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/stathat/consistent v1.0.0
|
||||||
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
|
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
|
||||||
go.uber.org/automaxprocs v1.5.3
|
go.uber.org/automaxprocs v1.5.3
|
||||||
@@ -81,6 +83,7 @@ require (
|
|||||||
github.com/hashicorp/errwrap v1.0.0 // indirect
|
github.com/hashicorp/errwrap v1.0.0 // indirect
|
||||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||||
github.com/hashicorp/go-uuid v1.0.3 // 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/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/jcmturner/aescts/v2 v2.0.0 // 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/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||||
github.com/rs/xid v1.5.0 // indirect
|
github.com/rs/xid v1.5.0 // indirect
|
||||||
github.com/sergi/go-diff v1.0.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/src-d/gcfg v1.4.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/stretchr/objx v0.5.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
@@ -127,7 +129,7 @@ require (
|
|||||||
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/net v0.17.0 // indirect
|
golang.org/x/net v0.17.0 // indirect
|
||||||
golang.org/x/oauth2 v0.13.0 // indirect
|
golang.org/x/oauth2 v0.13.0 // indirect
|
||||||
@@ -156,3 +158,5 @@ require (
|
|||||||
golang.org/x/crypto v0.17.0 // indirect
|
golang.org/x/crypto v0.17.0 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace github.com/openimsdk/localcache => ./pkg/localcache
|
||||||
|
|||||||
14
go.sum
14
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/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
|
github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
|
||||||
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
|
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
|
||||||
github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw=
|
github.com/OpenIMSDK/protocol v0.0.49 h1:wcqJOMBis7f153zNI7V82Fc4WyqA1GanMgXUQgL618k=
|
||||||
github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
github.com/OpenIMSDK/protocol v0.0.49/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
||||||
github.com/OpenIMSDK/tools v0.0.29 h1:NS4PEwYl9sX3SWsMjDOLVxMo3LcTWREMr+2cjzWjcqc=
|
github.com/OpenIMSDK/tools v0.0.27 h1:J/kSRqM+y9U4XK/pQ9RkEB31oQ5BTYD1oA5r1PITPRA=
|
||||||
github.com/OpenIMSDK/tools v0.0.29/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
|
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/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||||
@@ -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.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 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
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/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/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
@@ -356,8 +358,8 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|||||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
|
|||||||
11
go.work
11
go.work
@@ -4,14 +4,15 @@ use (
|
|||||||
.
|
.
|
||||||
./test/typecheck
|
./test/typecheck
|
||||||
./tools/changelog
|
./tools/changelog
|
||||||
//./tools/imctl
|
./tools/component
|
||||||
|
./tools/data-conversion
|
||||||
|
./tools/formitychecker
|
||||||
|
./tools/imctl
|
||||||
./tools/infra
|
./tools/infra
|
||||||
./tools/ncpu
|
./tools/ncpu
|
||||||
./tools/openim-web
|
./tools/openim-web
|
||||||
|
./tools/up35
|
||||||
|
./tools/url2im
|
||||||
./tools/versionchecker
|
./tools/versionchecker
|
||||||
./tools/yamlfmt
|
./tools/yamlfmt
|
||||||
./tools/component
|
|
||||||
./tools/url2im
|
|
||||||
./tools/data-conversion
|
|
||||||
./tools/up35
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/constant"
|
"github.com/OpenIMSDK/protocol/constant"
|
||||||
@@ -43,7 +44,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient) *gin.Engine {
|
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)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
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_delete", ParseToken, u.ProcessUserCommandDelete)
|
||||||
userRouterGroup.POST("/process_user_command_update", ParseToken, u.ProcessUserCommandUpdate)
|
userRouterGroup.POST("/process_user_command_update", ParseToken, u.ProcessUserCommandUpdate)
|
||||||
userRouterGroup.POST("/process_user_command_get", ParseToken, u.ProcessUserCommandGet)
|
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("/add_notification_account", ParseToken, u.AddNotificationAccount)
|
||||||
userRouterGroup.POST("/update_notification_account", ParseToken, u.UpdateNotificationAccountInfo)
|
userRouterGroup.POST("/update_notification_account", ParseToken, u.UpdateNotificationAccountInfo)
|
||||||
|
|||||||
@@ -201,26 +201,31 @@ func (u *UserApi) GetSubscribeUsersStatus(c *gin.Context) {
|
|||||||
a2r.Call(user.UserClient.GetSubscribeUsersStatus, u.Client, c)
|
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) {
|
func (u *UserApi) ProcessUserCommandAdd(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.ProcessUserCommandAdd, u.Client, c)
|
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) {
|
func (u *UserApi) ProcessUserCommandDelete(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.ProcessUserCommandDelete, u.Client, c)
|
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) {
|
func (u *UserApi) ProcessUserCommandUpdate(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.ProcessUserCommandUpdate, u.Client, c)
|
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) {
|
func (u *UserApi) ProcessUserCommandGet(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c)
|
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) {
|
func (u *UserApi) AddNotificationAccount(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.AddNotificationAccount, u.Client, c)
|
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
|
// func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID
|
||||||
// string) cbApi.CommonCallbackResp {
|
// string) cbApi.CommonCallbackResp {
|
||||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||||
// if !config.Config.Callback.CallbackUserOnline.Enable {
|
// if !config.Config.Callback.CallbackUserOnline.WithEnable {
|
||||||
// return callbackResp
|
// return callbackResp
|
||||||
// }
|
// }
|
||||||
// callbackUserOnlineReq := cbApi.CallbackUserOnlineReq{
|
// 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 {
|
//func callbackUserOffline(operationID, userID string, platformID int, connID string) cbApi.CommonCallbackResp {
|
||||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||||
// if !config.Config.Callback.CallbackUserOffline.Enable {
|
// if !config.Config.Callback.CallbackUserOffline.WithEnable {
|
||||||
// return callbackResp
|
// return callbackResp
|
||||||
// }
|
// }
|
||||||
// callbackOfflineReq := cbApi.CallbackUserOfflineReq{
|
// 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 {
|
//func callbackUserKickOff(operationID string, userID string, platformID int) cbApi.CommonCallbackResp {
|
||||||
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
|
||||||
// if !config.Config.Callback.CallbackUserKickOff.Enable {
|
// if !config.Config.Callback.CallbackUserKickOff.WithEnable {
|
||||||
// return callbackResp
|
// return callbackResp
|
||||||
// }
|
// }
|
||||||
// callbackUserKickOffReq := cbApi.CallbackUserKickOffReq{
|
// callbackUserKickOffReq := cbApi.CallbackUserKickOffReq{
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ import (
|
|||||||
"github.com/OpenIMSDK/tools/errs"
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
"github.com/OpenIMSDK/tools/log"
|
"github.com/OpenIMSDK/tools/log"
|
||||||
"github.com/OpenIMSDK/tools/mcontext"
|
"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/authverify"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
"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/cache"
|
||||||
@@ -59,7 +57,7 @@ type Server struct {
|
|||||||
rpcPort int
|
rpcPort int
|
||||||
prometheusPort int
|
prometheusPort int
|
||||||
LongConnServer LongConnServer
|
LongConnServer LongConnServer
|
||||||
pushTerminal []int
|
pushTerminal map[int]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SetLongConnServer(LongConnServer LongConnServer) {
|
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 {
|
func NewServer(rpcPort int, proPort int, longConnServer LongConnServer) *Server {
|
||||||
return &Server{
|
s := &Server{
|
||||||
rpcPort: rpcPort,
|
rpcPort: rpcPort,
|
||||||
prometheusPort: proPort,
|
prometheusPort: proPort,
|
||||||
LongConnServer: longConnServer,
|
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(
|
func (s *Server) OnlinePushMsg(
|
||||||
@@ -126,13 +127,9 @@ func (s *Server) OnlineBatchPushOneMsg(
|
|||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) SuperGroupOnlineBatchPushOneMsg(
|
func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq,
|
||||||
ctx context.Context,
|
|
||||||
req *msggateway.OnlineBatchPushOneMsgReq,
|
|
||||||
) (*msggateway.OnlineBatchPushOneMsgResp, error) {
|
) (*msggateway.OnlineBatchPushOneMsgResp, error) {
|
||||||
|
|
||||||
var singleUserResults []*msggateway.SingleMsgToUserResults
|
var singleUserResults []*msggateway.SingleMsgToUserResults
|
||||||
|
|
||||||
for _, v := range req.PushToUserIDs {
|
for _, v := range req.PushToUserIDs {
|
||||||
var resp []*msggateway.SingleMsgToUserPlatform
|
var resp []*msggateway.SingleMsgToUserPlatform
|
||||||
results := &msggateway.SingleMsgToUserResults{
|
results := &msggateway.SingleMsgToUserResults{
|
||||||
@@ -153,23 +150,22 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(
|
|||||||
}
|
}
|
||||||
|
|
||||||
userPlatform := &msggateway.SingleMsgToUserPlatform{
|
userPlatform := &msggateway.SingleMsgToUserPlatform{
|
||||||
RecvID: v,
|
PlatFormID: int32(client.PlatformID),
|
||||||
RecvPlatFormID: int32(client.PlatformID),
|
|
||||||
}
|
}
|
||||||
if !client.IsBackground ||
|
if !client.IsBackground ||
|
||||||
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
|
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
|
||||||
err := client.PushMessage(ctx, req.MsgData)
|
err := client.PushMessage(ctx, req.MsgData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
userPlatform.ResultCode = -2
|
userPlatform.ResultCode = int64(errs.ErrPushMsgErr.Code())
|
||||||
resp = append(resp, userPlatform)
|
resp = append(resp, userPlatform)
|
||||||
} else {
|
} else {
|
||||||
if utils.IsContainInt(client.PlatformID, s.pushTerminal) {
|
if _, ok := s.pushTerminal[client.PlatformID]; ok {
|
||||||
results.OnlinePush = true
|
results.OnlinePush = true
|
||||||
resp = append(resp, userPlatform)
|
resp = append(resp, userPlatform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
userPlatform.ResultCode = -3
|
userPlatform.ResultCode = int64(errs.ErrIOSBackgroundPushErr.Code())
|
||||||
resp = append(resp, userPlatform)
|
resp = append(resp, userPlatform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/OpenIMSDK/tools/apiresp"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
@@ -29,6 +28,8 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/tools/apiresp"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
"github.com/go-playground/validator/v10"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func StartTransfer(prometheusPort int) error {
|
|||||||
if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil {
|
if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil {
|
||||||
return err
|
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)
|
msgModel := cache.NewMsgCacheModel(rdb)
|
||||||
msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase())
|
msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase())
|
||||||
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel)
|
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel)
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package push
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/constant"
|
"github.com/OpenIMSDK/protocol/constant"
|
||||||
"github.com/OpenIMSDK/protocol/sdkws"
|
"github.com/OpenIMSDK/protocol/sdkws"
|
||||||
@@ -37,7 +38,7 @@ func callbackOfflinePush(
|
|||||||
msg *sdkws.MsgData,
|
msg *sdkws.MsgData,
|
||||||
offlinePushUserIDs *[]string,
|
offlinePushUserIDs *[]string,
|
||||||
) error {
|
) error {
|
||||||
if !config.Config.Callback.CallbackOfflinePush.Enable {
|
if !config.Config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
req := &callbackstruct.CallbackBeforePushReq{
|
req := &callbackstruct.CallbackBeforePushReq{
|
||||||
@@ -73,7 +74,7 @@ func callbackOfflinePush(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
req := callbackstruct.CallbackBeforePushReq{
|
req := callbackstruct.CallbackBeforePushReq{
|
||||||
@@ -107,7 +108,7 @@ func callbackBeforeSuperGroupOnlinePush(
|
|||||||
msg *sdkws.MsgData,
|
msg *sdkws.MsgData,
|
||||||
pushToUserIDs *[]string,
|
pushToUserIDs *[]string,
|
||||||
) error {
|
) error {
|
||||||
if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable {
|
if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable || msg.ContentType == constant.Typing {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
|
req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
|
||||||
@@ -136,3 +137,14 @@ func callbackBeforeSuperGroupOnlinePush(
|
|||||||
}
|
}
|
||||||
return nil
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||||
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewClient() *Dummy {
|
func NewDummy() *Dummy {
|
||||||
return &Dummy{}
|
return &Dummy{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Dummy struct {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package fcm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
firebase "firebase.google.com/go"
|
firebase "firebase.google.com/go"
|
||||||
@@ -25,7 +26,6 @@ import (
|
|||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/constant"
|
"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/config"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||||
)
|
)
|
||||||
@@ -39,7 +39,7 @@ type Fcm struct {
|
|||||||
cache cache.MsgModel
|
cache cache.MsgModel
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(cache cache.MsgModel) *Fcm {
|
func NewFcm(cache cache.MsgModel) *Fcm {
|
||||||
projectRoot := config.GetProjectRoot()
|
projectRoot := config.GetProjectRoot()
|
||||||
credentialsFilePath := filepath.Join(projectRoot, "config", config.Config.Push.Fcm.ServiceAccount)
|
credentialsFilePath := filepath.Join(projectRoot, "config", config.Config.Push.Fcm.ServiceAccount)
|
||||||
opt := option.WithCredentialsFile(credentialsFilePath)
|
opt := option.WithCredentialsFile(credentialsFilePath)
|
||||||
@@ -56,7 +56,7 @@ func NewClient(cache cache.MsgModel) *Fcm {
|
|||||||
return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}
|
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
|
// accounts->registrationToken
|
||||||
allTokens := make(map[string][]string, 0)
|
allTokens := make(map[string][]string, 0)
|
||||||
for _, account := range userIDs {
|
for _, account := range userIDs {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -30,7 +31,6 @@ import (
|
|||||||
"github.com/OpenIMSDK/tools/mcontext"
|
"github.com/OpenIMSDK/tools/mcontext"
|
||||||
"github.com/OpenIMSDK/tools/utils/splitter"
|
"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/config"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
|
||||||
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||||
@@ -55,17 +55,17 @@ const (
|
|||||||
taskIDTTL = 1000 * 60 * 60 * 24
|
taskIDTTL = 1000 * 60 * 60 * 24
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type GeTui struct {
|
||||||
cache cache.MsgModel
|
cache cache.MsgModel
|
||||||
tokenExpireTime int64
|
tokenExpireTime int64
|
||||||
taskIDTTL int64
|
taskIDTTL int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(cache cache.MsgModel) *Client {
|
func NewGeTui(cache cache.MsgModel) *GeTui {
|
||||||
return &Client{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL}
|
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)
|
token, err := g.cache.GetGetuiToken(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errs.Unwrap(err) == redis.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
|
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 := sha256.New()
|
||||||
h.Write(
|
h.Write(
|
||||||
[]byte(config.Config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.GeTui.MasterSecret),
|
[]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
|
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{}
|
respTask := TaskResp{}
|
||||||
ttl := int64(1000 * 60 * 5)
|
ttl := int64(1000 * 60 * 5)
|
||||||
pushReq.Settings = &Settings{TTL: &ttl}
|
pushReq.Settings = &Settings{TTL: &ttl}
|
||||||
@@ -143,7 +143,7 @@ func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// max num is 999.
|
// 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)
|
taskID, err := g.GetTaskID(ctx, token, pushReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
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)
|
operationID := mcontext.GetOperationID(ctx)
|
||||||
pushReq.RequestID = &operationID
|
pushReq.RequestID = &operationID
|
||||||
pushReq.Audience = &Audience{Alias: []string{userID}}
|
pushReq.Audience = &Audience{Alias: []string{userID}}
|
||||||
return g.request(ctx, pushURL, pushReq, token, nil)
|
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}
|
header := map[string]string{"token": token}
|
||||||
resp := &Resp{}
|
resp := &Resp{}
|
||||||
resp.Data = output
|
resp.Data = output
|
||||||
return g.postReturn(ctx, config.Config.Push.GeTui.PushUrl+url, header, input, resp, 3)
|
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,
|
ctx context.Context,
|
||||||
url string,
|
url string,
|
||||||
header map[string]string,
|
header map[string]string,
|
||||||
@@ -181,7 +181,7 @@ func (g *Client) postReturn(
|
|||||||
return output.parseError()
|
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)
|
token, _, err = g.Auth(ctx, time.Now().UnixNano()/1e6)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
@@ -193,7 +193,7 @@ func (g *Client) getTokenAndSave2Redis(ctx context.Context) (token string, err e
|
|||||||
return token, nil
|
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}
|
pushReq.Settings = &Settings{TTL: &g.taskIDTTL}
|
||||||
taskID, err = g.GetTaskID(ctx, token, pushReq)
|
taskID, err = g.GetTaskID(ctx, token, pushReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"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/internal/push/offlinepush/jpush/body"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
|
||||||
@@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
type JPush struct{}
|
type JPush struct{}
|
||||||
|
|
||||||
func NewClient() *JPush {
|
func NewJPush() *JPush {
|
||||||
return &JPush{}
|
return &JPush{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ func (j *JPush) getAuthorization(appKey string, masterSecret string) string {
|
|||||||
return Authorization
|
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
|
var pf body.Platform
|
||||||
pf.SetAll()
|
pf.SetAll()
|
||||||
var au body.Audience
|
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 (
|
import (
|
||||||
"context"
|
"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"
|
"github.com/IBM/sarama"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
@@ -31,18 +42,31 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ConsumerHandler struct {
|
type ConsumerHandler struct {
|
||||||
pushConsumerGroup *kfk.MConsumerGroup
|
pushConsumerGroup *kfk.MConsumerGroup
|
||||||
pusher *Pusher
|
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
|
var consumerHandler ConsumerHandler
|
||||||
consumerHandler.pusher = pusher
|
|
||||||
consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
|
consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
|
||||||
KafkaVersion: sarama.V2_0_0_0,
|
KafkaVersion: sarama.V2_0_0_0,
|
||||||
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
|
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
|
||||||
}, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr,
|
}, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr,
|
||||||
config.Config.Kafka.ConsumerGroupID.MsgToPush)
|
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
|
return &consumerHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +89,7 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
|
|||||||
var err error
|
var err error
|
||||||
switch msgFromMQ.MsgData.SessionType {
|
switch msgFromMQ.MsgData.SessionType {
|
||||||
case constant.SuperGroupChatType:
|
case constant.SuperGroupChatType:
|
||||||
err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
err = c.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
|
||||||
default:
|
default:
|
||||||
var pushUserIDList []string
|
var pushUserIDList []string
|
||||||
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
|
||||||
@@ -74,18 +98,14 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
|
|||||||
} else {
|
} else {
|
||||||
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
|
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 != nil {
|
||||||
if err == errNoOfflinePusher {
|
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
||||||
log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
|
|
||||||
} else {
|
|
||||||
log.ZError(ctx, "push failed", err, "msg", pbData.String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
func (*ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||||
func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
func (*ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
|
||||||
func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
||||||
claim sarama.ConsumerGroupClaim,
|
claim sarama.ConsumerGroupClaim,
|
||||||
) error {
|
) error {
|
||||||
@@ -96,3 +116,243 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
|
|||||||
}
|
}
|
||||||
return nil
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/OpenIMSDK/protocol/sdkws"
|
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/protocol/sdkws"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/tx"
|
"github.com/OpenIMSDK/tools/tx"
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ package friend
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/tx"
|
"github.com/OpenIMSDK/tools/tx"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/sdkws"
|
"github.com/OpenIMSDK/protocol/sdkws"
|
||||||
|
|||||||
@@ -279,20 +279,20 @@ func CallbackApplyJoinGroupBefore(ctx context.Context, req *callbackstruct.Callb
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CallbackTransferGroupOwnerAfter(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (err error) {
|
func CallbackAfterTransferGroupOwner(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (err error) {
|
||||||
if !config.Config.Callback.CallbackTransferGroupOwnerAfter.Enable {
|
if !config.Config.Callback.CallbackAfterTransferGroupOwner.Enable {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{
|
cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{
|
||||||
CallbackCommand: callbackstruct.CallbackTransferGroupOwnerAfter,
|
CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwner,
|
||||||
GroupID: req.GroupID,
|
GroupID: req.GroupID,
|
||||||
OldOwnerUserID: req.OldOwnerUserID,
|
OldOwnerUserID: req.OldOwnerUserID,
|
||||||
NewOwnerUserID: req.NewOwnerUserID,
|
NewOwnerUserID: req.NewOwnerUserID,
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := &callbackstruct.CallbackTransferGroupOwnerResp{}
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1058,7 +1058,7 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := CallbackTransferGroupOwnerAfter(ctx, req); err != nil {
|
if err := CallbackAfterTransferGroupOwner(ctx, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.Notification.GroupOwnerTransferredNotification(ctx, req)
|
s.Notification.GroupOwnerTransferredNotification(ctx, req)
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
utils2 "github.com/OpenIMSDK/tools/utils"
|
utils2 "github.com/OpenIMSDK/tools/utils"
|
||||||
|
|
||||||
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
|
||||||
|
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
@@ -43,7 +44,7 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
conversations, err := m.Conversation.GetConversations(ctx, req.UserID, conversationIDs)
|
conversations, err := m.ConversationLocalCache.GetConversations(ctx, req.UserID, conversationIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -106,7 +107,7 @@ func (m *msgServer) MarkMsgsAsRead(
|
|||||||
if hasReadSeq > maxSeq {
|
if hasReadSeq > maxSeq {
|
||||||
return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than 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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -126,14 +127,15 @@ func (m *msgServer) MarkMsgsAsRead(
|
|||||||
}
|
}
|
||||||
|
|
||||||
req_callback := &cbapi.CallbackSingleMsgReadReq{
|
req_callback := &cbapi.CallbackSingleMsgReadReq{
|
||||||
|
ConversationID: conversation.ConversationID,
|
||||||
UserID: req.UserID,
|
UserID: req.UserID,
|
||||||
ConversationID: req.ConversationID,
|
|
||||||
ContentType: conversation.ConversationType,
|
|
||||||
Seqs: req.Seqs,
|
Seqs: req.Seqs,
|
||||||
|
ContentType: conversation.ConversationType,
|
||||||
}
|
}
|
||||||
if err = CallbackSingleMsgRead(ctx, req_callback); err != nil {
|
if err = CallbackSingleMsgRead(ctx, req_callback); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
|
||||||
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
|
m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
|
||||||
return
|
return
|
||||||
@@ -145,7 +147,7 @@ func (m *msgServer) MarkConversationAsRead(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *msg.MarkConversationAsReadReq,
|
req *msg.MarkConversationAsReadReq,
|
||||||
) (resp *msg.MarkConversationAsReadResp, err error) {
|
) (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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
|||||||
if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil {
|
if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
user, err := m.User.GetUserInfo(ctx, req.UserID)
|
user, err := m.UserLocalCache.GetUserInfo(ctx, req.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -73,12 +73,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
|
|||||||
}
|
}
|
||||||
role = user.AppMangerLevel
|
role = user.AppMangerLevel
|
||||||
case constant.SuperGroupChatType:
|
case constant.SuperGroupChatType:
|
||||||
members, err := m.Group.GetGroupMemberInfoMap(
|
members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, utils.Distinct([]string{req.UserID, msgs[0].SendID}))
|
||||||
ctx,
|
|
||||||
msgs[0].GroupID,
|
|
||||||
utils.Distinct([]string{req.UserID, msgs[0].SendID}),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func (m *msgServer) sendMsgSuperGroupChat(
|
|||||||
if err = callbackBeforeSendGroupMsg(ctx, req); err != nil {
|
if err = callbackBeforeSendGroupMsg(ctx, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := callbackMsgModify(ctx, req); err != nil {
|
if err := callbackMsgModify(ctx, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -97,7 +98,7 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa
|
|||||||
}
|
}
|
||||||
tagAll := utils.IsContain(constant.AtAllString, msg.AtUserIDList)
|
tagAll := utils.IsContain(constant.AtAllString, msg.AtUserIDList)
|
||||||
if tagAll {
|
if tagAll {
|
||||||
memberUserIDList, err := m.Group.GetGroupMemberIDs(ctx, msg.GroupID)
|
memberUserIDList, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, msg.GroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.ZWarn(ctx, "GetGroupMemberIDs", err)
|
log.ZWarn(ctx, "GetGroupMemberIDs", err)
|
||||||
return
|
return
|
||||||
@@ -143,6 +144,7 @@ func (m *msgServer) sendMsgNotification(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
|
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 {
|
if err := m.messageVerification(ctx, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -167,6 +169,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
|
|||||||
if err = callbackBeforeSendSingleMsg(ctx, req); err != nil {
|
if err = callbackBeforeSendSingleMsg(ctx, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := callbackMsgModify(ctx, req); err != nil {
|
if err := callbackMsgModify(ctx, req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package msg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
pbmsg "github.com/OpenIMSDK/protocol/msg"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package msg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"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/cache"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
|
"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/common/db/unrelation"
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
||||||
)
|
)
|
||||||
@@ -36,12 +36,11 @@ type (
|
|||||||
msgServer struct {
|
msgServer struct {
|
||||||
RegisterCenter discoveryregistry.SvcDiscoveryRegistry
|
RegisterCenter discoveryregistry.SvcDiscoveryRegistry
|
||||||
MsgDatabase controller.CommonMsgDatabase
|
MsgDatabase controller.CommonMsgDatabase
|
||||||
Group *rpcclient.GroupRpcClient
|
|
||||||
User *rpcclient.UserRpcClient
|
|
||||||
Conversation *rpcclient.ConversationRpcClient
|
Conversation *rpcclient.ConversationRpcClient
|
||||||
friend *rpcclient.FriendRpcClient
|
UserLocalCache *rpccache.UserLocalCache
|
||||||
GroupLocalCache *localcache.GroupLocalCache
|
FriendLocalCache *rpccache.FriendLocalCache
|
||||||
ConversationLocalCache *localcache.ConversationLocalCache
|
GroupLocalCache *rpccache.GroupLocalCache
|
||||||
|
ConversationLocalCache *rpccache.ConversationLocalCache
|
||||||
Handlers MessageInterceptorChain
|
Handlers MessageInterceptorChain
|
||||||
notificationSender *rpcclient.NotificationSender
|
notificationSender *rpcclient.NotificationSender
|
||||||
}
|
}
|
||||||
@@ -81,15 +80,15 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
|
|||||||
groupRpcClient := rpcclient.NewGroupRpcClient(client)
|
groupRpcClient := rpcclient.NewGroupRpcClient(client)
|
||||||
friendRpcClient := rpcclient.NewFriendRpcClient(client)
|
friendRpcClient := rpcclient.NewFriendRpcClient(client)
|
||||||
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel)
|
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel)
|
||||||
|
|
||||||
s := &msgServer{
|
s := &msgServer{
|
||||||
Conversation: &conversationClient,
|
Conversation: &conversationClient,
|
||||||
User: &userRpcClient,
|
|
||||||
Group: &groupRpcClient,
|
|
||||||
MsgDatabase: msgDatabase,
|
MsgDatabase: msgDatabase,
|
||||||
RegisterCenter: client,
|
RegisterCenter: client,
|
||||||
GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient),
|
UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, rdb),
|
||||||
ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient),
|
GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, rdb),
|
||||||
friend: &friendRpcClient,
|
ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, rdb),
|
||||||
|
FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, rdb),
|
||||||
}
|
}
|
||||||
s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg))
|
s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg))
|
||||||
s.addInterceptorHandler(MessageHasReadEnabled)
|
s.addInterceptorHandler(MessageHasReadEnabled)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq
|
|||||||
var pbUsers []*msg.ActiveUser
|
var pbUsers []*msg.ActiveUser
|
||||||
if len(users) > 0 {
|
if len(users) > 0 {
|
||||||
userIDs := utils.Slice(users, func(e *unrelation.UserCount) string { return e.UserID })
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ func (m *msgServer) GetActiveGroup(ctx context.Context, req *msg.GetActiveGroupR
|
|||||||
var pbgroups []*msg.ActiveGroup
|
var pbgroups []*msg.ActiveGroup
|
||||||
if len(groups) > 0 {
|
if len(groups) > 0 {
|
||||||
groupIDs := utils.Slice(groups, func(e *unrelation.GroupCount) string { return e.GroupID })
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func (m *msgServer) PullMessageBySeqs(
|
|||||||
resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs)
|
resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs)
|
||||||
for _, seq := range req.SeqRanges {
|
for _, seq := range req.SeqRanges {
|
||||||
if !msgprocessor.IsNotification(seq.ConversationID) {
|
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 {
|
if err != nil {
|
||||||
log.ZError(ctx, "GetConversation error", err, "conversationID", seq.ConversationID)
|
log.ZError(ctx, "GetConversation error", err, "conversationID", seq.ConversationID)
|
||||||
continue
|
continue
|
||||||
@@ -140,7 +140,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(sendIDs) != 0 {
|
if len(sendIDs) != 0 {
|
||||||
sendInfos, err := m.User.GetUsersInfo(ctx, sendIDs)
|
sendInfos, err := m.UserLocalCache.GetUsersInfo(ctx, sendIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(recvIDs) != 0 {
|
if len(recvIDs) != 0 {
|
||||||
recvInfos, err := m.User.GetUsersInfo(ctx, recvIDs)
|
recvInfos, err := m.UserLocalCache.GetUsersInfo(ctx, recvIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -158,7 +158,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(groupIDs) != 0 {
|
if len(groupIDs) != 0 {
|
||||||
groupInfos, err := m.Group.GetGroupInfos(ctx, groupIDs, true)
|
groupInfos, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package msg
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/OpenIMSDK/tools/log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -58,7 +59,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
|||||||
data.MsgData.ContentType >= constant.NotificationBegin {
|
data.MsgData.ContentType >= constant.NotificationBegin {
|
||||||
return nil
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -66,7 +67,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
|||||||
return errs.ErrBlockedByPeer.Wrap()
|
return errs.ErrBlockedByPeer.Wrap()
|
||||||
}
|
}
|
||||||
if *config.Config.MessageVerify.FriendVerify {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -77,7 +78,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
case constant.SuperGroupChatType:
|
case constant.SuperGroupChatType:
|
||||||
groupInfo, err := m.Group.GetGroupInfoCache(ctx, data.MsgData.GroupID)
|
groupInfo, err := m.GroupLocalCache.GetGroupInfo(ctx, data.MsgData.GroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -95,17 +96,17 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
|||||||
data.MsgData.ContentType >= constant.NotificationBegin {
|
data.MsgData.ContentType >= constant.NotificationBegin {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// memberIDs, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, data.MsgData.GroupID)
|
memberIDs, err := m.GroupLocalCache.GetGroupMemberIDMap(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)
|
|
||||||
if err != nil {
|
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 errs.ErrNotInGroupYet.Wrap(err.Error())
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@@ -186,7 +187,8 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
|||||||
sessionType int,
|
sessionType int,
|
||||||
pb *msg.SendMsgReq,
|
pb *msg.SendMsgReq,
|
||||||
) (bool, error) {
|
) (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 {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -202,7 +204,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
// conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType)
|
// 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) {
|
if errs.ErrRecordNotFound.Is(err) {
|
||||||
return true, nil
|
return true, nil
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
|||||||
@@ -19,12 +19,14 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"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/open-im-server/v3/pkg/common/db/s3"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/third"
|
"github.com/OpenIMSDK/protocol/third"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||||
"github.com/OpenIMSDK/tools/utils"
|
"github.com/OpenIMSDK/tools/utils"
|
||||||
|
|
||||||
|
|||||||
@@ -17,12 +17,14 @@ package user
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/OpenIMSDK/tools/pagination"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/tools/tx"
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
|
||||||
@@ -59,10 +61,6 @@ type userServer struct {
|
|||||||
RegisterCenter registry.SvcDiscoveryRegistry
|
RegisterCenter registry.SvcDiscoveryRegistry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) {
|
|
||||||
//TODO implement me
|
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||||
rdb, err := cache.NewRedis()
|
rdb, err := cache.NewRedis()
|
||||||
@@ -234,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) {
|
func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) {
|
||||||
total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
|
if req.UserID == "" && req.NickName == "" {
|
||||||
if err != nil {
|
total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
|
||||||
return nil, err
|
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) {
|
func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterReq) (resp *pbuser.UserRegisterResp, err error) {
|
||||||
@@ -383,32 +391,149 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
|
|||||||
return &pbuser.GetSubscribeUsersStatusResp{StatusList: onlineStatusList}, nil
|
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) {
|
func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) {
|
||||||
|
err := authverify.CheckAccessV3(ctx, req.UserID)
|
||||||
return &pbuser.ProcessUserCommandAddResp{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessUserCommandDelete user general function delete
|
|
||||||
func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) {
|
|
||||||
// Assuming you have a method in s.UserDatabase to delete a user command
|
|
||||||
err := s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var value string
|
||||||
|
if req.Value != nil {
|
||||||
|
value = req.Value.Value
|
||||||
|
}
|
||||||
|
var ex string
|
||||||
|
if req.Ex != nil {
|
||||||
|
value = req.Ex.Value
|
||||||
|
}
|
||||||
|
// Assuming you have a method in s.UserDatabase to add a user command
|
||||||
|
err = s.UserDatabase.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, value, ex)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tips := &sdkws.UserCommandAddTips{
|
||||||
|
FromUserID: req.UserID,
|
||||||
|
ToUserID: req.UserID,
|
||||||
|
}
|
||||||
|
err = s.userNotificationSender.UserCommandAddNotification(ctx, tips)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &pbuser.ProcessUserCommandAddResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessUserCommandDelete user general function delete.
|
||||||
|
func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) {
|
||||||
|
err := authverify.CheckAccessV3(ctx, req.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tips := &sdkws.UserCommandDeleteTips{
|
||||||
|
FromUserID: req.UserID,
|
||||||
|
ToUserID: req.UserID,
|
||||||
|
}
|
||||||
|
err = s.userNotificationSender.UserCommandDeleteNotification(ctx, tips)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &pbuser.ProcessUserCommandDeleteResp{}, nil
|
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) {
|
func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) {
|
||||||
|
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
|
return &pbuser.ProcessUserCommandUpdateResp{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) {
|
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 {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize commandInfoSlice as an empty slice
|
||||||
|
commandInfoSlice := make([]*pbuser.CommandInfoResp, 0, len(commands))
|
||||||
|
|
||||||
|
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 the response with the slice
|
||||||
return &pbuser.ProcessUserCommandGetResp{}, 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) {
|
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
|
||||||
@@ -481,31 +606,38 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) {
|
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 {
|
if err := authverify.CheckIMAdmin(ctx); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var users []*relation.UserModel
|
var users []*relation.UserModel
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
// If a keyword is provided in the request
|
||||||
if req.Keyword != "" {
|
if req.Keyword != "" {
|
||||||
|
// Find users by keyword
|
||||||
users, err = s.UserDatabase.Find(ctx, []string{req.Keyword})
|
users, err = s.UserDatabase.Find(ctx, []string{req.Keyword})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert users to response format
|
||||||
resp := s.userModelToResp(users, req.Pagination)
|
resp := s.userModelToResp(users, req.Pagination)
|
||||||
if resp.Total != 0 {
|
if resp.Total != 0 {
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find users by nickname if no users found by keyword
|
||||||
users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword)
|
users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp = s.userModelToResp(users, req.Pagination)
|
resp = s.userModelToResp(users, req.Pagination)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
|
|
||||||
return resp, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If no keyword, find users with notification settings
|
||||||
users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
|
users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ func InitMsgTool() (*MsgTool, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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())
|
userDB, err := mgo.NewUserMongo(mongo.GetDatabase())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const (
|
|||||||
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
|
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
|
||||||
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
|
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
|
||||||
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
|
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
|
||||||
CallbackTransferGroupOwnerAfter = "callbackTransferGroupOwnerAfter"
|
CallbackAfterTransferGroupOwner = "callbackAfterTransferGroupOwner"
|
||||||
CallbackBeforeSetFriendRemark = "callbackBeforeSetFriendRemark"
|
CallbackBeforeSetFriendRemark = "callbackBeforeSetFriendRemark"
|
||||||
CallbackAfterSetFriendRemark = "callbackAfterSetFriendRemark"
|
CallbackAfterSetFriendRemark = "callbackAfterSetFriendRemark"
|
||||||
CallbackSingleMsgRead = "callbackSingleMsgRead"
|
CallbackSingleMsgRead = "callbackSingleMsgRead"
|
||||||
|
|||||||
@@ -94,10 +94,10 @@ type CallbackGroupMsgReadResp struct {
|
|||||||
|
|
||||||
type CallbackSingleMsgReadReq struct {
|
type CallbackSingleMsgReadReq struct {
|
||||||
CallbackCommand `json:"callbackCommand"`
|
CallbackCommand `json:"callbackCommand"`
|
||||||
UserID string `json:"userID"`
|
|
||||||
ConversationID string `json:"conversationID"`
|
ConversationID string `json:"conversationID"`
|
||||||
|
UserID string `json:"userID"`
|
||||||
|
Seqs []int64 `json:"Seqs"`
|
||||||
ContentType int32 `json:"contentType"`
|
ContentType int32 `json:"contentType"`
|
||||||
Seqs []int64 `json:"seqs"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type CallbackSingleMsgReadResp struct {
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
@@ -258,6 +259,8 @@ type configStruct struct {
|
|||||||
FriendVerify *bool `yaml:"friendVerify"`
|
FriendVerify *bool `yaml:"friendVerify"`
|
||||||
} `yaml:"messageVerify"`
|
} `yaml:"messageVerify"`
|
||||||
|
|
||||||
|
LocalCache localCache `yaml:"localCache"`
|
||||||
|
|
||||||
IOSPush struct {
|
IOSPush struct {
|
||||||
PushSound string `yaml:"pushSound"`
|
PushSound string `yaml:"pushSound"`
|
||||||
BadgeCount bool `yaml:"badgeCount"`
|
BadgeCount bool `yaml:"badgeCount"`
|
||||||
@@ -296,7 +299,7 @@ type configStruct struct {
|
|||||||
CallbackKillGroupMember CallBackConfig `yaml:"killGroupMember"`
|
CallbackKillGroupMember CallBackConfig `yaml:"killGroupMember"`
|
||||||
CallbackDismissGroup CallBackConfig `yaml:"dismissGroup"`
|
CallbackDismissGroup CallBackConfig `yaml:"dismissGroup"`
|
||||||
CallbackBeforeJoinGroup CallBackConfig `yaml:"joinGroup"`
|
CallbackBeforeJoinGroup CallBackConfig `yaml:"joinGroup"`
|
||||||
CallbackTransferGroupOwnerAfter CallBackConfig `yaml:"transferGroupOwner"`
|
CallbackAfterTransferGroupOwner CallBackConfig `yaml:"transferGroupOwner"`
|
||||||
CallbackBeforeInviteUserToGroup CallBackConfig `yaml:"beforeInviteUserToGroup"`
|
CallbackBeforeInviteUserToGroup CallBackConfig `yaml:"beforeInviteUserToGroup"`
|
||||||
CallbackAfterJoinGroup CallBackConfig `yaml:"joinGroupAfter"`
|
CallbackAfterJoinGroup CallBackConfig `yaml:"joinGroupAfter"`
|
||||||
CallbackAfterSetGroupInfo CallBackConfig `yaml:"setGroupInfoAfter"`
|
CallbackAfterSetGroupInfo CallBackConfig `yaml:"setGroupInfoAfter"`
|
||||||
@@ -370,6 +373,33 @@ type notification struct {
|
|||||||
ConversationSetPrivate NotificationConf `yaml:"conversationSetPrivate"`
|
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 {
|
func (c *configStruct) GetServiceNames() []string {
|
||||||
return []string{
|
return []string{
|
||||||
c.RpcRegisterName.OpenImUserName,
|
c.RpcRegisterName.OpenImUserName,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed version
|
//go:embed version
|
||||||
@@ -37,19 +38,30 @@ const (
|
|||||||
|
|
||||||
// return absolude path join ../config/, this is k8s container config path.
|
// return absolude path join ../config/, this is k8s container config path.
|
||||||
func GetDefaultConfigPath() string {
|
func GetDefaultConfigPath() string {
|
||||||
b, err := filepath.Abs(os.Args[0])
|
executablePath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("filepath.Abs error,err=", err)
|
fmt.Println("GetDefaultConfigPath error:", err.Error())
|
||||||
return ""
|
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.
|
// getProjectRoot returns the absolute path of the project root directory.
|
||||||
func GetProjectRoot() string {
|
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 {
|
func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options {
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/sdkws"
|
"github.com/OpenIMSDK/protocol/sdkws"
|
||||||
"github.com/OpenIMSDK/tools/utils"
|
"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 (
|
import (
|
||||||
"context"
|
"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"
|
"time"
|
||||||
|
|
||||||
"github.com/dtm-labs/rockscache"
|
"github.com/dtm-labs/rockscache"
|
||||||
@@ -52,11 +55,15 @@ func NewBlackCacheRedis(
|
|||||||
options rockscache.Options,
|
options rockscache.Options,
|
||||||
) BlackCache {
|
) BlackCache {
|
||||||
rcClient := rockscache.NewClient(rdb, options)
|
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{
|
return &BlackCacheRedis{
|
||||||
expireTime: blackExpireTime,
|
expireTime: blackExpireTime,
|
||||||
rcClient: rcClient,
|
rcClient: rcClient,
|
||||||
metaCache: NewMetaCacheRedis(rcClient),
|
metaCache: mc,
|
||||||
blackDB: blackDB,
|
blackDB: blackDB,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,12 +73,12 @@ func (b *BlackCacheRedis) NewCache() BlackCache {
|
|||||||
expireTime: b.expireTime,
|
expireTime: b.expireTime,
|
||||||
rcClient: b.rcClient,
|
rcClient: b.rcClient,
|
||||||
blackDB: b.blackDB,
|
blackDB: b.blackDB,
|
||||||
metaCache: NewMetaCacheRedis(b.rcClient, b.metaCache.GetPreDelKeys()...),
|
metaCache: b.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BlackCacheRedis) getBlackIDsKey(ownerUserID string) string {
|
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) {
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"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"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -30,14 +33,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
conversationKey = "CONVERSATION:"
|
//conversationKey = "CONVERSATION:"
|
||||||
conversationIDsKey = "CONVERSATION_IDS:"
|
//conversationIDsKey = "CONVERSATION_IDS:"
|
||||||
conversationIDsHashKey = "CONVERSATION_IDS_HASH:"
|
//conversationIDsHashKey = "CONVERSATION_IDS_HASH:"
|
||||||
conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
|
//conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
|
||||||
recvMsgOptKey = "RECV_MSG_OPT:"
|
//recvMsgOptKey = "RECV_MSG_OPT:"
|
||||||
superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
|
//superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
|
||||||
superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
|
//superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
|
||||||
conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:"
|
//conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:"
|
||||||
|
|
||||||
conversationExpireTime = time.Second * 60 * 60 * 12
|
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 {
|
func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationtb.ConversationModelInterface) ConversationCache {
|
||||||
rcClient := rockscache.NewClient(rdb, opts)
|
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{
|
return &ConversationRedisCache{
|
||||||
rcClient: rcClient,
|
rcClient: rcClient,
|
||||||
metaCache: NewMetaCacheRedis(rcClient),
|
metaCache: mc,
|
||||||
conversationDB: db,
|
conversationDB: db,
|
||||||
expireTime: conversationExpireTime,
|
expireTime: conversationExpireTime,
|
||||||
}
|
}
|
||||||
@@ -118,38 +125,42 @@ type ConversationRedisCache struct {
|
|||||||
func (c *ConversationRedisCache) NewCache() ConversationCache {
|
func (c *ConversationRedisCache) NewCache() ConversationCache {
|
||||||
return &ConversationRedisCache{
|
return &ConversationRedisCache{
|
||||||
rcClient: c.rcClient,
|
rcClient: c.rcClient,
|
||||||
metaCache: NewMetaCacheRedis(c.rcClient, c.metaCache.GetPreDelKeys()...),
|
metaCache: c.Copy(),
|
||||||
conversationDB: c.conversationDB,
|
conversationDB: c.conversationDB,
|
||||||
expireTime: c.expireTime,
|
expireTime: c.expireTime,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getConversationKey(ownerUserID, conversationID string) string {
|
func (c *ConversationRedisCache) getConversationKey(ownerUserID, conversationID string) string {
|
||||||
return conversationKey + ownerUserID + ":" + conversationID
|
return cachekey.GetConversationKey(ownerUserID, conversationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getConversationIDsKey(ownerUserID string) string {
|
func (c *ConversationRedisCache) getConversationIDsKey(ownerUserID string) string {
|
||||||
return conversationIDsKey + ownerUserID
|
return cachekey.GetConversationIDsKey(ownerUserID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsKey(groupID string) string {
|
func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsKey(groupID string) string {
|
||||||
return superGroupRecvMsgNotNotifyUserIDsKey + groupID
|
return cachekey.GetSuperGroupRecvNotNotifyUserIDsKey(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getRecvMsgOptKey(ownerUserID, conversationID string) string {
|
func (c *ConversationRedisCache) getRecvMsgOptKey(ownerUserID, conversationID string) string {
|
||||||
return recvMsgOptKey + ownerUserID + ":" + conversationID
|
return cachekey.GetRecvMsgOptKey(ownerUserID, conversationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string {
|
func (c *ConversationRedisCache) getSuperGroupRecvNotNotifyUserIDsHashKey(groupID string) string {
|
||||||
return superGroupRecvMsgNotNotifyUserIDsHashKey + groupID
|
return cachekey.GetSuperGroupRecvNotNotifyUserIDsHashKey(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getConversationHasReadSeqKey(ownerUserID, conversationID string) string {
|
func (c *ConversationRedisCache) getConversationHasReadSeqKey(ownerUserID, conversationID string) string {
|
||||||
return conversationHasReadSeqKey + ownerUserID + ":" + conversationID
|
return cachekey.GetConversationHasReadSeqKey(ownerUserID, conversationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ConversationRedisCache) getConversationNotReceiveMessageUserIDsKey(conversationID string) string {
|
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) {
|
func (c *ConversationRedisCache) GetUserConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) {
|
||||||
@@ -169,10 +180,6 @@ func (c *ConversationRedisCache) DelConversationIDs(userIDs ...string) Conversat
|
|||||||
return cache
|
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) {
|
func (c *ConversationRedisCache) GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) {
|
||||||
return getCache(
|
return getCache(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
26
pkg/common/db/cache/friend.go
vendored
26
pkg/common/db/cache/friend.go
vendored
@@ -16,6 +16,9 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"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"
|
"time"
|
||||||
|
|
||||||
"github.com/dtm-labs/rockscache"
|
"github.com/dtm-labs/rockscache"
|
||||||
@@ -27,10 +30,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
friendExpireTime = time.Second * 60 * 60 * 12
|
friendExpireTime = time.Second * 60 * 60 * 12
|
||||||
friendIDsKey = "FRIEND_IDS:"
|
//friendIDsKey = "FRIEND_IDS:"
|
||||||
TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
|
//TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
|
||||||
friendKey = "FRIEND_INFO:"
|
//friendKey = "FRIEND_INFO:"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FriendCache is an interface for caching friend-related data.
|
// FriendCache is an interface for caching friend-related data.
|
||||||
@@ -60,8 +63,13 @@ type FriendCacheRedis struct {
|
|||||||
func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendModelInterface,
|
func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendModelInterface,
|
||||||
options rockscache.Options) FriendCache {
|
options rockscache.Options) FriendCache {
|
||||||
rcClient := rockscache.NewClient(rdb, options)
|
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{
|
return &FriendCacheRedis{
|
||||||
metaCache: NewMetaCacheRedis(rcClient),
|
metaCache: mc,
|
||||||
friendDB: friendDB,
|
friendDB: friendDB,
|
||||||
expireTime: friendExpireTime,
|
expireTime: friendExpireTime,
|
||||||
rcClient: rcClient,
|
rcClient: rcClient,
|
||||||
@@ -72,7 +80,7 @@ func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendMo
|
|||||||
func (f *FriendCacheRedis) NewCache() FriendCache {
|
func (f *FriendCacheRedis) NewCache() FriendCache {
|
||||||
return &FriendCacheRedis{
|
return &FriendCacheRedis{
|
||||||
rcClient: f.rcClient,
|
rcClient: f.rcClient,
|
||||||
metaCache: NewMetaCacheRedis(f.rcClient, f.metaCache.GetPreDelKeys()...),
|
metaCache: f.Copy(),
|
||||||
friendDB: f.friendDB,
|
friendDB: f.friendDB,
|
||||||
expireTime: f.expireTime,
|
expireTime: f.expireTime,
|
||||||
}
|
}
|
||||||
@@ -80,17 +88,17 @@ func (f *FriendCacheRedis) NewCache() FriendCache {
|
|||||||
|
|
||||||
// getFriendIDsKey returns the key for storing friend IDs in the cache.
|
// getFriendIDsKey returns the key for storing friend IDs in the cache.
|
||||||
func (f *FriendCacheRedis) getFriendIDsKey(ownerUserID string) string {
|
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.
|
// getTwoWayFriendsIDsKey returns the key for storing two-way friend IDs in the cache.
|
||||||
func (f *FriendCacheRedis) getTwoWayFriendsIDsKey(ownerUserID string) string {
|
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.
|
// getFriendKey returns the key for storing friend info in the cache.
|
||||||
func (f *FriendCacheRedis) getFriendKey(ownerUserID, friendUserID string) string {
|
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.
|
// GetFriendIDs retrieves friend IDs from the cache or the database if not found.
|
||||||
|
|||||||
44
pkg/common/db/cache/group.go
vendored
44
pkg/common/db/cache/group.go
vendored
@@ -17,7 +17,8 @@ package cache
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/constant"
|
"github.com/OpenIMSDK/protocol/constant"
|
||||||
@@ -34,15 +35,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
groupExpireTime = time.Second * 60 * 60 * 12
|
groupExpireTime = time.Second * 60 * 60 * 12
|
||||||
groupInfoKey = "GROUP_INFO:"
|
//groupInfoKey = "GROUP_INFO:"
|
||||||
groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
//groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
||||||
groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
//groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
||||||
groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
//groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
||||||
//groupOwnerInfoKey = "GROUP_OWNER_INFO:".
|
//joinedGroupsKey = "JOIN_GROUPS_KEY:"
|
||||||
joinedGroupsKey = "JOIN_GROUPS_KEY:"
|
//groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:"
|
||||||
groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:"
|
//groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:"
|
||||||
groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type GroupHash interface {
|
type GroupHash interface {
|
||||||
@@ -105,12 +105,16 @@ func NewGroupCacheRedis(
|
|||||||
opts rockscache.Options,
|
opts rockscache.Options,
|
||||||
) GroupCache {
|
) GroupCache {
|
||||||
rcClient := rockscache.NewClient(rdb, opts)
|
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{
|
return &GroupCacheRedis{
|
||||||
rcClient: rcClient, expireTime: groupExpireTime,
|
rcClient: rcClient, expireTime: groupExpireTime,
|
||||||
groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB,
|
groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB,
|
||||||
groupHash: hashCode,
|
groupHash: hashCode,
|
||||||
metaCache: NewMetaCacheRedis(rcClient),
|
metaCache: mc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,36 +125,36 @@ func (g *GroupCacheRedis) NewCache() GroupCache {
|
|||||||
groupDB: g.groupDB,
|
groupDB: g.groupDB,
|
||||||
groupMemberDB: g.groupMemberDB,
|
groupMemberDB: g.groupMemberDB,
|
||||||
groupRequestDB: g.groupRequestDB,
|
groupRequestDB: g.groupRequestDB,
|
||||||
metaCache: NewMetaCacheRedis(g.rcClient, g.metaCache.GetPreDelKeys()...),
|
metaCache: g.Copy(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getGroupInfoKey(groupID string) string {
|
func (g *GroupCacheRedis) getGroupInfoKey(groupID string) string {
|
||||||
return groupInfoKey + groupID
|
return cachekey.GetGroupInfoKey(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getJoinedGroupsKey(userID string) string {
|
func (g *GroupCacheRedis) getJoinedGroupsKey(userID string) string {
|
||||||
return joinedGroupsKey + userID
|
return cachekey.GetJoinedGroupsKey(userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getGroupMembersHashKey(groupID string) string {
|
func (g *GroupCacheRedis) getGroupMembersHashKey(groupID string) string {
|
||||||
return groupMembersHashKey + groupID
|
return cachekey.GetGroupMembersHashKey(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getGroupMemberIDsKey(groupID string) string {
|
func (g *GroupCacheRedis) getGroupMemberIDsKey(groupID string) string {
|
||||||
return groupMemberIDsKey + groupID
|
return cachekey.GetGroupMemberIDsKey(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getGroupMemberInfoKey(groupID, userID string) string {
|
func (g *GroupCacheRedis) getGroupMemberInfoKey(groupID, userID string) string {
|
||||||
return groupMemberInfoKey + groupID + "-" + userID
|
return cachekey.GetGroupMemberInfoKey(groupID, userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getGroupMemberNumKey(groupID string) string {
|
func (g *GroupCacheRedis) getGroupMemberNumKey(groupID string) string {
|
||||||
return groupMemberNumKey + groupID
|
return cachekey.GetGroupMemberNumKey(groupID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupCacheRedis) getGroupRoleLevelMemberIDsKey(groupID string, roleLevel int32) string {
|
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) {
|
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"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/mw/specialerror"
|
"github.com/OpenIMSDK/tools/mw/specialerror"
|
||||||
@@ -44,6 +45,9 @@ type metaCache interface {
|
|||||||
AddKeys(keys ...string)
|
AddKeys(keys ...string)
|
||||||
ClearKeys()
|
ClearKeys()
|
||||||
GetPreDelKeys() []string
|
GetPreDelKeys() []string
|
||||||
|
SetTopic(topic string)
|
||||||
|
SetRawRedisClient(cli redis.UniversalClient)
|
||||||
|
Copy() metaCache
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache {
|
func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache {
|
||||||
@@ -51,10 +55,36 @@ func NewMetaCacheRedis(rcClient *rockscache.Client, keys ...string) metaCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type metaCacheRedis struct {
|
type metaCacheRedis struct {
|
||||||
|
topic string
|
||||||
rcClient *rockscache.Client
|
rcClient *rockscache.Client
|
||||||
keys []string
|
keys []string
|
||||||
maxRetryTimes int
|
maxRetryTimes int
|
||||||
retryInterval time.Duration
|
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 {
|
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)
|
m.keys = utils.Distinct(m.keys)
|
||||||
}
|
}
|
||||||
if len(m.keys) > 0 {
|
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 _, key := range m.keys {
|
||||||
for i := 0; i < m.maxRetryTimes; i++ {
|
for i := 0; i < m.maxRetryTimes; i++ {
|
||||||
if err := m.rcClient.TagAsDeleted(key); err != nil {
|
if err := m.rcClient.TagAsDeleted(key); err != nil {
|
||||||
@@ -72,31 +102,18 @@ func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//retryTimes := 0
|
if pk := getPublishKey(m.topic, m.keys); len(pk) > 0 {
|
||||||
//for {
|
data, err := json.Marshal(pk)
|
||||||
// m.rcClient.TagAsDeleted()
|
if err != nil {
|
||||||
// if err := m.rcClient.TagAsDeletedBatch2(ctx, []string{key}); err != nil {
|
log.ZError(ctx, "keys json marshal failed", err, "topic", m.topic, "keys", pk)
|
||||||
// if retryTimes >= m.maxRetryTimes {
|
} else {
|
||||||
// err = errs.ErrInternalServer.Wrap(
|
if err := m.redisClient.Publish(ctx, m.topic, string(data)).Err(); err != nil {
|
||||||
// fmt.Sprintf(
|
log.ZError(ctx, "redis publish cache delete error", err, "topic", m.topic, "keys", pk)
|
||||||
// "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
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/dtm-labs/rockscache"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -44,12 +45,12 @@ const (
|
|||||||
conversationUserMinSeq = "CON_USER_MIN_SEQ:"
|
conversationUserMinSeq = "CON_USER_MIN_SEQ:"
|
||||||
hasReadSeq = "HAS_READ_SEQ:"
|
hasReadSeq = "HAS_READ_SEQ:"
|
||||||
|
|
||||||
appleDeviceToken = "DEVICE_TOKEN"
|
//appleDeviceToken = "DEVICE_TOKEN"
|
||||||
getuiToken = "GETUI_TOKEN"
|
getuiToken = "GETUI_TOKEN"
|
||||||
getuiTaskID = "GETUI_TASK_ID"
|
getuiTaskID = "GETUI_TASK_ID"
|
||||||
signalCache = "SIGNAL_CACHE:"
|
//signalCache = "SIGNAL_CACHE:"
|
||||||
signalListCache = "SIGNAL_LIST_CACHE:"
|
//signalListCache = "SIGNAL_LIST_CACHE:"
|
||||||
FCM_TOKEN = "FCM_TOKEN:"
|
FCM_TOKEN = "FCM_TOKEN:"
|
||||||
|
|
||||||
messageCache = "MESSAGE_CACHE:"
|
messageCache = "MESSAGE_CACHE:"
|
||||||
messageDelUserList = "MESSAGE_DEL_USER_LIST:"
|
messageDelUserList = "MESSAGE_DEL_USER_LIST:"
|
||||||
@@ -128,7 +129,8 @@ type MsgModel interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewMsgCacheModel(client redis.UniversalClient) MsgModel {
|
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 {
|
type msgCache struct {
|
||||||
@@ -148,6 +150,10 @@ func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string
|
|||||||
return hasReadSeq + userID + ":" + conversationID
|
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 {
|
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())
|
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)
|
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) {
|
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())
|
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"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
"hash/crc32"
|
"hash/crc32"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@@ -36,8 +38,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
userExpireTime = time.Second * 60 * 60 * 12
|
userExpireTime = time.Second * 60 * 60 * 12
|
||||||
userInfoKey = "USER_INFO:"
|
//userInfoKey = "USER_INFO:"
|
||||||
userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:"
|
userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:"
|
||||||
olineStatusKey = "ONLINE_STATUS:"
|
olineStatusKey = "ONLINE_STATUS:"
|
||||||
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
|
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
|
||||||
@@ -72,7 +74,11 @@ func NewUserCacheRedis(
|
|||||||
options rockscache.Options,
|
options rockscache.Options,
|
||||||
) UserCache {
|
) UserCache {
|
||||||
rcClient := rockscache.NewClient(rdb, options)
|
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{
|
return &UserCacheRedis{
|
||||||
rdb: rdb,
|
rdb: rdb,
|
||||||
metaCache: NewMetaCacheRedis(rcClient),
|
metaCache: NewMetaCacheRedis(rcClient),
|
||||||
@@ -85,7 +91,7 @@ func NewUserCacheRedis(
|
|||||||
func (u *UserCacheRedis) NewCache() UserCache {
|
func (u *UserCacheRedis) NewCache() UserCache {
|
||||||
return &UserCacheRedis{
|
return &UserCacheRedis{
|
||||||
rdb: u.rdb,
|
rdb: u.rdb,
|
||||||
metaCache: NewMetaCacheRedis(u.rcClient, u.metaCache.GetPreDelKeys()...),
|
metaCache: u.Copy(),
|
||||||
userDB: u.userDB,
|
userDB: u.userDB,
|
||||||
expireTime: u.expireTime,
|
expireTime: u.expireTime,
|
||||||
rcClient: u.rcClient,
|
rcClient: u.rcClient,
|
||||||
@@ -93,18 +99,17 @@ func (u *UserCacheRedis) NewCache() UserCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserCacheRedis) getUserInfoKey(userID string) string {
|
func (u *UserCacheRedis) getUserInfoKey(userID string) string {
|
||||||
return userInfoKey + userID
|
return cachekey.GetUserInfoKey(userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string {
|
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) {
|
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 getCache(ctx, u.rcClient, u.getUserInfoKey(userID), u.expireTime, func(ctx context.Context) (*relationtb.UserModel, error) {
|
||||||
return u.userDB.Take(ctx, userID)
|
return u.userDB.Take(ctx, userID)
|
||||||
},
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]*relationtb.UserModel, error) {
|
func (u *UserCacheRedis) GetUsersInfo(ctx context.Context, userIDs []string) ([]*relationtb.UserModel, error) {
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ func Test_BatchInsertChat2DB(t *testing.T) {
|
|||||||
func GetDB() *commonMsgDatabase {
|
func GetDB() *commonMsgDatabase {
|
||||||
config.Config.Mongo.Address = []string{"203.56.175.233:37017"}
|
config.Config.Mongo.Address = []string{"203.56.175.233:37017"}
|
||||||
// config.Config.Mongo.Timeout = 60
|
// config.Config.Mongo.Timeout = 60
|
||||||
config.Config.Mongo.Database = "openIM_v3"
|
config.Config.Mongo.Database = "openim_v3"
|
||||||
// config.Config.Mongo.Source = "admin"
|
// config.Config.Mongo.Source = "admin"
|
||||||
config.Config.Mongo.Username = "root"
|
config.Config.Mongo.Username = "root"
|
||||||
config.Config.Mongo.Password = "openIM123"
|
config.Config.Mongo.Password = "openIM123"
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ type UserDatabase interface {
|
|||||||
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
|
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
|
||||||
// FindUser
|
// FindUser
|
||||||
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
|
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
|
||||||
|
//FindUser with keyword
|
||||||
|
PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID string, nickName string, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
|
||||||
// Page If not found, no error is returned
|
// Page If not found, no error is returned
|
||||||
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
|
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
|
||||||
// IsExist true as long as one exists
|
// IsExist true as long as one exists
|
||||||
@@ -78,10 +80,11 @@ type UserDatabase interface {
|
|||||||
SetUserStatus(ctx context.Context, userID string, status, platformID int32) error
|
SetUserStatus(ctx context.Context, userID string, status, platformID int32) error
|
||||||
|
|
||||||
//CRUD user command
|
//CRUD user command
|
||||||
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
|
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error
|
||||||
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
|
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
|
||||||
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
|
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error
|
||||||
GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
|
GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
|
||||||
|
GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userDatabase struct {
|
type userDatabase struct {
|
||||||
@@ -139,12 +142,12 @@ func (u *userDatabase) Find(ctx context.Context, userIDs []string) (users []*rel
|
|||||||
return u.cache.GetUsersInfo(ctx, userIDs)
|
return u.cache.GetUsersInfo(ctx, userIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find userInfo By Nickname
|
// Find userInfo By Nickname.
|
||||||
func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) {
|
func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) {
|
||||||
return u.userDB.TakeByNickname(ctx, nickname)
|
return u.userDB.TakeByNickname(ctx, nickname)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find notificationAccouts
|
// Find notificationAccouts.
|
||||||
func (u *userDatabase) FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error) {
|
func (u *userDatabase) FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error) {
|
||||||
return u.userDB.TakeNotification(ctx, level)
|
return u.userDB.TakeNotification(ctx, level)
|
||||||
}
|
}
|
||||||
@@ -188,6 +191,16 @@ func (u *userDatabase) PageFindUser(ctx context.Context, level1 int64, level2 in
|
|||||||
return u.userDB.PageFindUser(ctx, level1, level2, pagination)
|
return u.userDB.PageFindUser(ctx, level1, level2, pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *userDatabase) PageFindUserWithKeyword(
|
||||||
|
ctx context.Context,
|
||||||
|
level1 int64,
|
||||||
|
level2 int64,
|
||||||
|
userID, nickName string,
|
||||||
|
pagination pagination.Pagination,
|
||||||
|
) (count int64, users []*relation.UserModel, err error) {
|
||||||
|
return u.userDB.PageFindUserWithKeyword(ctx, level1, level2, userID, nickName, pagination)
|
||||||
|
}
|
||||||
|
|
||||||
// IsExist Does userIDs exist? As long as there is one, it will be true.
|
// IsExist Does userIDs exist? As long as there is one, it will be true.
|
||||||
func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) {
|
func (u *userDatabase) IsExist(ctx context.Context, userIDs []string) (exist bool, err error) {
|
||||||
users, err := u.userDB.Find(ctx, userIDs)
|
users, err := u.userDB.Find(ctx, userIDs)
|
||||||
@@ -259,16 +272,20 @@ func (u *userDatabase) GetUserStatus(ctx context.Context, userIDs []string) ([]*
|
|||||||
func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error {
|
func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error {
|
||||||
return u.cache.SetUserStatus(ctx, userID, status, platformID)
|
return u.cache.SetUserStatus(ctx, userID, status, platformID)
|
||||||
}
|
}
|
||||||
func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
|
func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error {
|
||||||
return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value)
|
return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value, ex)
|
||||||
}
|
}
|
||||||
func (u *userDatabase) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
|
func (u *userDatabase) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
|
||||||
return u.userDB.DeleteUserCommand(ctx, userID, Type, UUID)
|
return u.userDB.DeleteUserCommand(ctx, userID, Type, UUID)
|
||||||
}
|
}
|
||||||
func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
|
func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error {
|
||||||
return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, value)
|
return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, val)
|
||||||
}
|
}
|
||||||
func (u *userDatabase) GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
|
func (u *userDatabase) GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
|
||||||
commands, err := u.userDB.GetUserCommand(ctx, userID, Type)
|
commands, err := u.userDB.GetUserCommand(ctx, userID, Type)
|
||||||
return commands, err
|
return commands, err
|
||||||
}
|
}
|
||||||
|
func (u *userDatabase) GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) {
|
||||||
|
commands, err := u.userDB.GetAllUserCommand(ctx, userID)
|
||||||
|
return commands, err
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,87 +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 localcache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/conversation"
|
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConversationLocalCache struct {
|
|
||||||
lock sync.Mutex
|
|
||||||
superGroupRecvMsgNotNotifyUserIDs map[string]Hash
|
|
||||||
conversationIDs map[string]Hash
|
|
||||||
client *rpcclient.ConversationRpcClient
|
|
||||||
}
|
|
||||||
|
|
||||||
type Hash struct {
|
|
||||||
hash uint64
|
|
||||||
ids []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConversationLocalCache(client *rpcclient.ConversationRpcClient) *ConversationLocalCache {
|
|
||||||
return &ConversationLocalCache{
|
|
||||||
superGroupRecvMsgNotNotifyUserIDs: make(map[string]Hash),
|
|
||||||
conversationIDs: make(map[string]Hash),
|
|
||||||
client: client,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *ConversationLocalCache) GetRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) {
|
|
||||||
resp, err := g.client.Client.GetRecvMsgNotNotifyUserIDs(ctx, &conversation.GetRecvMsgNotNotifyUserIDsReq{
|
|
||||||
GroupID: groupID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return resp.UserIDs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *ConversationLocalCache) GetConversationIDs(ctx context.Context, userID string) ([]string, error) {
|
|
||||||
resp, err := g.client.Client.GetUserConversationIDsHash(ctx, &conversation.GetUserConversationIDsHashReq{
|
|
||||||
OwnerUserID: userID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
g.lock.Lock()
|
|
||||||
hash, ok := g.conversationIDs[userID]
|
|
||||||
g.lock.Unlock()
|
|
||||||
|
|
||||||
if !ok || hash.hash != resp.Hash {
|
|
||||||
conversationIDsResp, err := g.client.Client.GetConversationIDs(ctx, &conversation.GetConversationIDsReq{
|
|
||||||
UserID: userID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
g.lock.Lock()
|
|
||||||
defer g.lock.Unlock()
|
|
||||||
g.conversationIDs[userID] = Hash{
|
|
||||||
hash: resp.Hash,
|
|
||||||
ids: conversationIDsResp.ConversationIDs,
|
|
||||||
}
|
|
||||||
|
|
||||||
return conversationIDsResp.ConversationIDs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return hash.ids, nil
|
|
||||||
}
|
|
||||||
@@ -1,15 +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 localcache // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache"
|
|
||||||
@@ -1,78 +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 localcache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/group"
|
|
||||||
"github.com/OpenIMSDK/tools/errs"
|
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GroupLocalCache struct {
|
|
||||||
lock sync.Mutex
|
|
||||||
cache map[string]GroupMemberIDsHash
|
|
||||||
client *rpcclient.GroupRpcClient
|
|
||||||
}
|
|
||||||
|
|
||||||
type GroupMemberIDsHash struct {
|
|
||||||
memberListHash uint64
|
|
||||||
userIDs []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGroupLocalCache(client *rpcclient.GroupRpcClient) *GroupLocalCache {
|
|
||||||
return &GroupLocalCache{
|
|
||||||
cache: make(map[string]GroupMemberIDsHash, 0),
|
|
||||||
client: client,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GroupLocalCache) GetGroupMemberIDs(ctx context.Context, groupID string) ([]string, error) {
|
|
||||||
resp, err := g.client.Client.GetGroupAbstractInfo(ctx, &group.GetGroupAbstractInfoReq{
|
|
||||||
GroupIDs: []string{groupID},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(resp.GroupAbstractInfos) < 1 {
|
|
||||||
return nil, errs.ErrGroupIDNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
g.lock.Lock()
|
|
||||||
localHashInfo, ok := g.cache[groupID]
|
|
||||||
if ok && localHashInfo.memberListHash == resp.GroupAbstractInfos[0].GroupMemberListHash {
|
|
||||||
g.lock.Unlock()
|
|
||||||
return localHashInfo.userIDs, nil
|
|
||||||
}
|
|
||||||
g.lock.Unlock()
|
|
||||||
|
|
||||||
groupMembersResp, err := g.client.Client.GetGroupMemberUserIDs(ctx, &group.GetGroupMemberUserIDsReq{
|
|
||||||
GroupID: groupID,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
g.lock.Lock()
|
|
||||||
defer g.lock.Unlock()
|
|
||||||
g.cache[groupID] = GroupMemberIDsHash{
|
|
||||||
memberListHash: resp.GroupAbstractInfos[0].GroupMemberListHash,
|
|
||||||
userIDs: groupMembersResp.UserIDs,
|
|
||||||
}
|
|
||||||
return g.cache[groupID].userIDs, nil
|
|
||||||
}
|
|
||||||
@@ -114,8 +114,7 @@ func (c *ConversationMgo) GetAllConversationIDs(ctx context.Context) ([]string,
|
|||||||
func (c *ConversationMgo) GetAllConversationIDsNumber(ctx context.Context) (int64, error) {
|
func (c *ConversationMgo) GetAllConversationIDsNumber(ctx context.Context) (int64, error) {
|
||||||
counts, err := mgoutil.Aggregate[int64](ctx, c.coll, []bson.M{
|
counts, err := mgoutil.Aggregate[int64](ctx, c.coll, []bson.M{
|
||||||
{"$group": bson.M{"_id": "$conversation_id"}},
|
{"$group": bson.M{"_id": "$conversation_id"}},
|
||||||
{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": 1}}},
|
{"$project": bson.M{"_id": 0, "conversation_id": "$_id"}},
|
||||||
{"$project": bson.M{"_id": 0}},
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ package mgo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/mgoutil"
|
"github.com/OpenIMSDK/tools/mgoutil"
|
||||||
"github.com/OpenIMSDK/tools/pagination"
|
"github.com/OpenIMSDK/tools/pagination"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
|||||||
@@ -16,9 +16,12 @@ package mgo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/OpenIMSDK/protocol/user"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/protocol/user"
|
||||||
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/mgoutil"
|
"github.com/OpenIMSDK/tools/mgoutil"
|
||||||
"github.com/OpenIMSDK/tools/pagination"
|
"github.com/OpenIMSDK/tools/pagination"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
@@ -88,6 +91,41 @@ func (u *UserMgo) PageFindUser(ctx context.Context, level1 int64, level2 int64,
|
|||||||
return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
|
return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *UserMgo) PageFindUserWithKeyword(
|
||||||
|
ctx context.Context,
|
||||||
|
level1 int64,
|
||||||
|
level2 int64,
|
||||||
|
userID string,
|
||||||
|
nickName string,
|
||||||
|
pagination pagination.Pagination,
|
||||||
|
) (count int64, users []*relation.UserModel, err error) {
|
||||||
|
// Initialize the base query with level conditions
|
||||||
|
query := bson.M{
|
||||||
|
"$and": []bson.M{
|
||||||
|
{"app_manger_level": bson.M{"$in": []int64{level1, level2}}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add userID and userName conditions to the query if they are provided
|
||||||
|
if userID != "" || nickName != "" {
|
||||||
|
userConditions := []bson.M{}
|
||||||
|
if userID != "" {
|
||||||
|
// Use regex for userID
|
||||||
|
regexPattern := primitive.Regex{Pattern: userID, Options: "i"} // 'i' for case-insensitive matching
|
||||||
|
userConditions = append(userConditions, bson.M{"user_id": regexPattern})
|
||||||
|
}
|
||||||
|
if nickName != "" {
|
||||||
|
// Use regex for userName
|
||||||
|
regexPattern := primitive.Regex{Pattern: nickName, Options: "i"} // 'i' for case-insensitive matching
|
||||||
|
userConditions = append(userConditions, bson.M{"nickname": regexPattern})
|
||||||
|
}
|
||||||
|
query["$and"] = append(query["$and"].([]bson.M), bson.M{"$or": userConditions})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the paginated search
|
||||||
|
return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *UserMgo) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) {
|
func (u *UserMgo) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) {
|
||||||
return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
|
return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
|
||||||
}
|
}
|
||||||
@@ -107,7 +145,7 @@ func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int6
|
|||||||
return mgoutil.Count(ctx, u.coll, bson.M{"create_time": bson.M{"$lt": before}})
|
return mgoutil.Count(ctx, u.coll, bson.M{"create_time": bson.M{"$lt": before}})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
|
func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error {
|
||||||
collection := u.coll.Database().Collection("userCommands")
|
collection := u.coll.Database().Collection("userCommands")
|
||||||
|
|
||||||
// Create a new document instead of updating an existing one
|
// Create a new document instead of updating an existing one
|
||||||
@@ -117,28 +155,48 @@ func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32,
|
|||||||
"uuid": UUID,
|
"uuid": UUID,
|
||||||
"createTime": time.Now().Unix(), // assuming you want the creation time in Unix timestamp
|
"createTime": time.Now().Unix(), // assuming you want the creation time in Unix timestamp
|
||||||
"value": value,
|
"value": value,
|
||||||
|
"ex": ex,
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := collection.InsertOne(ctx, doc)
|
_, err := collection.InsertOne(ctx, doc)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserMgo) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
|
func (u *UserMgo) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
|
||||||
collection := u.coll.Database().Collection("userCommands")
|
collection := u.coll.Database().Collection("userCommands")
|
||||||
|
|
||||||
filter := bson.M{"userID": userID, "type": Type, "uuid": UUID}
|
filter := bson.M{"userID": userID, "type": Type, "uuid": UUID}
|
||||||
|
|
||||||
_, err := collection.DeleteOne(ctx, filter)
|
result, err := collection.DeleteOne(ctx, filter)
|
||||||
|
if result.DeletedCount == 0 {
|
||||||
|
// No records found to update
|
||||||
|
return errs.Wrap(errs.ErrRecordNotFound)
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error {
|
func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error {
|
||||||
|
if len(val) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
collection := u.coll.Database().Collection("userCommands")
|
collection := u.coll.Database().Collection("userCommands")
|
||||||
|
|
||||||
filter := bson.M{"userID": userID, "type": Type, "uuid": UUID}
|
filter := bson.M{"userID": userID, "type": Type, "uuid": UUID}
|
||||||
update := bson.M{"$set": bson.M{"value": value}}
|
update := bson.M{"$set": val}
|
||||||
|
|
||||||
_, err := collection.UpdateOne(ctx, filter, update)
|
result, err := collection.UpdateOne(ctx, filter, update)
|
||||||
return err
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.MatchedCount == 0 {
|
||||||
|
// No records found to update
|
||||||
|
return errs.Wrap(errs.ErrRecordNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
|
func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
|
||||||
collection := u.coll.Database().Collection("userCommands")
|
collection := u.coll.Database().Collection("userCommands")
|
||||||
filter := bson.M{"userID": userID, "type": Type}
|
filter := bson.M{"userID": userID, "type": Type}
|
||||||
@@ -154,19 +212,23 @@ func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32)
|
|||||||
|
|
||||||
for cursor.Next(ctx) {
|
for cursor.Next(ctx) {
|
||||||
var document struct {
|
var document struct {
|
||||||
|
Type int32 `bson:"type"`
|
||||||
UUID string `bson:"uuid"`
|
UUID string `bson:"uuid"`
|
||||||
Value string `bson:"value"`
|
Value string `bson:"value"`
|
||||||
CreateTime int64 `bson:"createTime"`
|
CreateTime int64 `bson:"createTime"`
|
||||||
|
Ex string `bson:"ex"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := cursor.Decode(&document); err != nil {
|
if err := cursor.Decode(&document); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
commandInfo := &user.CommandInfoResp{ // Change here: use a pointer to the struct
|
commandInfo := &user.CommandInfoResp{
|
||||||
|
Type: document.Type,
|
||||||
Uuid: document.UUID,
|
Uuid: document.UUID,
|
||||||
Value: document.Value,
|
Value: document.Value,
|
||||||
CreateTime: document.CreateTime,
|
CreateTime: document.CreateTime,
|
||||||
|
Ex: document.Ex,
|
||||||
}
|
}
|
||||||
|
|
||||||
commands = append(commands, commandInfo)
|
commands = append(commands, commandInfo)
|
||||||
@@ -178,7 +240,48 @@ func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32)
|
|||||||
|
|
||||||
return commands, nil
|
return commands, nil
|
||||||
}
|
}
|
||||||
|
func (u *UserMgo) GetAllUserCommand(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) {
|
||||||
|
collection := u.coll.Database().Collection("userCommands")
|
||||||
|
filter := bson.M{"userID": userID}
|
||||||
|
|
||||||
|
cursor, err := collection.Find(ctx, filter)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer cursor.Close(ctx)
|
||||||
|
|
||||||
|
// Initialize commands as a slice of pointers
|
||||||
|
commands := []*user.AllCommandInfoResp{}
|
||||||
|
|
||||||
|
for cursor.Next(ctx) {
|
||||||
|
var document struct {
|
||||||
|
Type int32 `bson:"type"`
|
||||||
|
UUID string `bson:"uuid"`
|
||||||
|
Value string `bson:"value"`
|
||||||
|
CreateTime int64 `bson:"createTime"`
|
||||||
|
Ex string `bson:"ex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cursor.Decode(&document); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
commandInfo := &user.AllCommandInfoResp{
|
||||||
|
Type: document.Type,
|
||||||
|
Uuid: document.UUID,
|
||||||
|
Value: document.Value,
|
||||||
|
CreateTime: document.CreateTime,
|
||||||
|
Ex: document.Ex,
|
||||||
|
}
|
||||||
|
|
||||||
|
commands = append(commands, commandInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := cursor.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return commands, nil
|
||||||
|
}
|
||||||
func (u *UserMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
func (u *UserMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
|
||||||
pipeline := bson.A{
|
pipeline := bson.A{
|
||||||
bson.M{
|
bson.M{
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minPartSize = 1024 * 1024 * 1 // 1MB
|
minPartSize int64 = 1024 * 1024 * 1 // 1MB
|
||||||
maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB
|
maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB
|
||||||
maxNumSize = 1000
|
maxNumSize int64 = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -133,7 +133,7 @@ func (c *Cos) PartSize(ctx context.Context, size int64) (int64, error) {
|
|||||||
return 0, errors.New("size must be greater than 0")
|
return 0, errors.New("size must be greater than 0")
|
||||||
}
|
}
|
||||||
if size > maxPartSize*maxNumSize {
|
if size > maxPartSize*maxNumSize {
|
||||||
return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize)
|
return 0, fmt.Errorf("COS size must be less than the maximum allowed limit")
|
||||||
}
|
}
|
||||||
if size <= minPartSize*maxNumSize {
|
if size <= minPartSize*maxNumSize {
|
||||||
return minPartSize, nil
|
return minPartSize, nil
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minPartSize = 1024 * 1024 * 5 // 1MB
|
minPartSize int64 = 1024 * 1024 * 5 // 1MB
|
||||||
maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB
|
maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB
|
||||||
maxNumSize = 10000
|
maxNumSize int64 = 10000
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -240,7 +240,7 @@ func (m *Minio) PartSize(ctx context.Context, size int64) (int64, error) {
|
|||||||
return 0, errors.New("size must be greater than 0")
|
return 0, errors.New("size must be greater than 0")
|
||||||
}
|
}
|
||||||
if size > maxPartSize*maxNumSize {
|
if size > maxPartSize*maxNumSize {
|
||||||
return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize)
|
return 0, fmt.Errorf("MINIO size must be less than the maximum allowed limit")
|
||||||
}
|
}
|
||||||
if size <= minPartSize*maxNumSize {
|
if size <= minPartSize*maxNumSize {
|
||||||
return minPartSize, nil
|
return minPartSize, nil
|
||||||
|
|||||||
@@ -37,9 +37,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
minPartSize = 1024 * 1024 * 1 // 1MB
|
minPartSize int64 = 1024 * 1024 * 1 // 1MB
|
||||||
maxPartSize = 1024 * 1024 * 1024 * 5 // 5GB
|
maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB
|
||||||
maxNumSize = 10000
|
maxNumSize int64 = 10000
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -141,7 +141,7 @@ func (o *OSS) PartSize(ctx context.Context, size int64) (int64, error) {
|
|||||||
return 0, errors.New("size must be greater than 0")
|
return 0, errors.New("size must be greater than 0")
|
||||||
}
|
}
|
||||||
if size > maxPartSize*maxNumSize {
|
if size > maxPartSize*maxNumSize {
|
||||||
return 0, fmt.Errorf("size must be less than %db", maxPartSize*maxNumSize)
|
return 0, fmt.Errorf("OSS size must be less than the maximum allowed limit")
|
||||||
}
|
}
|
||||||
if size <= minPartSize*maxNumSize {
|
if size <= minPartSize*maxNumSize {
|
||||||
return minPartSize, nil
|
return minPartSize, nil
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import (
|
|||||||
type PartLimit struct {
|
type PartLimit struct {
|
||||||
MinPartSize int64 `json:"minPartSize"`
|
MinPartSize int64 `json:"minPartSize"`
|
||||||
MaxPartSize int64 `json:"maxPartSize"`
|
MaxPartSize int64 `json:"maxPartSize"`
|
||||||
MaxNumSize int `json:"maxNumSize"`
|
MaxNumSize int64 `json:"maxNumSize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type InitiateMultipartUploadResult struct {
|
type InitiateMultipartUploadResult struct {
|
||||||
|
|||||||
@@ -16,9 +16,10 @@ package relation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/OpenIMSDK/protocol/user"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/protocol/user"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/pagination"
|
"github.com/OpenIMSDK/tools/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -57,6 +58,7 @@ type UserModelInterface interface {
|
|||||||
TakeByNickname(ctx context.Context, nickname string) (user []*UserModel, err error)
|
TakeByNickname(ctx context.Context, nickname string) (user []*UserModel, err error)
|
||||||
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
|
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
|
||||||
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
|
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
|
||||||
|
PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID, nickName string, pagination pagination.Pagination) (count int64, users []*UserModel, err error)
|
||||||
Exist(ctx context.Context, userID string) (exist bool, err error)
|
Exist(ctx context.Context, userID string) (exist bool, err error)
|
||||||
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, err error)
|
GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, err error)
|
||||||
GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error)
|
GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error)
|
||||||
@@ -65,8 +67,9 @@ type UserModelInterface interface {
|
|||||||
// 获取范围内用户增量
|
// 获取范围内用户增量
|
||||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||||
//CRUD user command
|
//CRUD user command
|
||||||
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
|
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error
|
||||||
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
|
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
|
||||||
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string) error
|
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error
|
||||||
GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
|
GetUserCommand(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error)
|
||||||
|
GetAllUserCommand(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error)
|
||||||
}
|
}
|
||||||
|
|||||||
81
pkg/common/discoveryregister/direct/directResolver.go
Normal file
81
pkg/common/discoveryregister/direct/directResolver.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package direct
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/OpenIMSDK/tools/log"
|
||||||
|
"google.golang.org/grpc/resolver"
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
slashSeparator = "/"
|
||||||
|
// EndpointSepChar is the separator char in endpoints.
|
||||||
|
EndpointSepChar = ','
|
||||||
|
|
||||||
|
subsetSize = 32
|
||||||
|
scheme = "direct"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResolverDirect struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResolverDirect() *ResolverDirect {
|
||||||
|
return &ResolverDirect{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rd *ResolverDirect) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (
|
||||||
|
resolver.Resolver, error) {
|
||||||
|
log.ZDebug(context.Background(), "Build", "target", target)
|
||||||
|
endpoints := strings.FieldsFunc(GetEndpoints(target), func(r rune) bool {
|
||||||
|
return r == EndpointSepChar
|
||||||
|
})
|
||||||
|
endpoints = subset(endpoints, subsetSize)
|
||||||
|
addrs := make([]resolver.Address, 0, len(endpoints))
|
||||||
|
|
||||||
|
for _, val := range endpoints {
|
||||||
|
addrs = append(addrs, resolver.Address{
|
||||||
|
Addr: val,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := cc.UpdateState(resolver.State{
|
||||||
|
Addresses: addrs,
|
||||||
|
}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &nopResolver{cc: cc}, nil
|
||||||
|
}
|
||||||
|
func init() {
|
||||||
|
resolver.Register(&ResolverDirect{})
|
||||||
|
}
|
||||||
|
func (rd *ResolverDirect) Scheme() string {
|
||||||
|
return scheme // return your custom scheme name
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEndpoints returns the endpoints from the given target.
|
||||||
|
func GetEndpoints(target resolver.Target) string {
|
||||||
|
return strings.Trim(target.URL.Path, slashSeparator)
|
||||||
|
}
|
||||||
|
func subset(set []string, sub int) []string {
|
||||||
|
rand.Shuffle(len(set), func(i, j int) {
|
||||||
|
set[i], set[j] = set[j], set[i]
|
||||||
|
})
|
||||||
|
if len(set) <= sub {
|
||||||
|
return set
|
||||||
|
}
|
||||||
|
|
||||||
|
return set[:sub]
|
||||||
|
}
|
||||||
|
|
||||||
|
type nopResolver struct {
|
||||||
|
cc resolver.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nopResolver) ResolveNow(options resolver.ResolveNowOptions) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nopResolver) Close() {
|
||||||
|
|
||||||
|
}
|
||||||
154
pkg/common/discoveryregister/direct/directconn.go
Normal file
154
pkg/common/discoveryregister/direct/directconn.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package direct
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
|
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/insecure"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServiceAddresses map[string][]int
|
||||||
|
|
||||||
|
func getServiceAddresses() ServiceAddresses {
|
||||||
|
return ServiceAddresses{
|
||||||
|
config2.Config.RpcRegisterName.OpenImUserName: config2.Config.RpcPort.OpenImUserPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImFriendName: config2.Config.RpcPort.OpenImFriendPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImMsgName: config2.Config.RpcPort.OpenImMessagePort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImMessageGatewayName: config2.Config.LongConnSvr.OpenImMessageGatewayPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImGroupName: config2.Config.RpcPort.OpenImGroupPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImAuthName: config2.Config.RpcPort.OpenImAuthPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImPushName: config2.Config.RpcPort.OpenImPushPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImConversationName: config2.Config.RpcPort.OpenImConversationPort,
|
||||||
|
config2.Config.RpcRegisterName.OpenImThirdName: config2.Config.RpcPort.OpenImThirdPort,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConnDirect struct {
|
||||||
|
additionalOpts []grpc.DialOption
|
||||||
|
currentServiceAddress string
|
||||||
|
conns map[string][]*grpc.ClientConn
|
||||||
|
resolverDirect *ResolverDirect
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) GetClientLocalConns() map[string][]*grpc.ClientConn {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) Register(serviceName, host string, port int, opts ...grpc.DialOption) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) UnRegister() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) CreateRpcRootNodes(serviceNames []string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) RegisterConf2Registry(key string, conf []byte) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) GetConfFromRegistry(key string) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) Close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConnDirect() (*ConnDirect, error) {
|
||||||
|
return &ConnDirect{
|
||||||
|
conns: make(map[string][]*grpc.ClientConn),
|
||||||
|
resolverDirect: NewResolverDirect(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) GetConns(ctx context.Context,
|
||||||
|
serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
|
||||||
|
|
||||||
|
if conns, exists := cd.conns[serviceName]; exists {
|
||||||
|
return conns, nil
|
||||||
|
}
|
||||||
|
ports := getServiceAddresses()[serviceName]
|
||||||
|
var connections []*grpc.ClientConn
|
||||||
|
for _, port := range ports {
|
||||||
|
conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(config2.Config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("connect to port %d failed,serviceName %s, IP %s\n", port, serviceName, config2.Config.Rpc.ListenIP)
|
||||||
|
}
|
||||||
|
connections = append(connections, conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(connections) == 0 {
|
||||||
|
return nil, fmt.Errorf("no connections found for service: %s", serviceName)
|
||||||
|
}
|
||||||
|
return connections, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
// Get service addresses
|
||||||
|
addresses := getServiceAddresses()
|
||||||
|
address, ok := addresses[serviceName]
|
||||||
|
if !ok {
|
||||||
|
return nil, errs.Wrap(errors.New("unknown service name"), "serviceName", serviceName)
|
||||||
|
}
|
||||||
|
var result string
|
||||||
|
for _, addr := range address {
|
||||||
|
if result != "" {
|
||||||
|
result = result + "," + fmt.Sprintf(config2.Config.Rpc.ListenIP+":%d", addr)
|
||||||
|
} else {
|
||||||
|
result = fmt.Sprintf(config2.Config.Rpc.ListenIP+":%d", addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try to dial a new connection
|
||||||
|
conn, err := cd.dialService(ctx, result, append(cd.additionalOpts, opts...)...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errs.Wrap(err, "address", result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the new connection
|
||||||
|
cd.conns[serviceName] = append(cd.conns[serviceName], conn)
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) GetSelfConnTarget() string {
|
||||||
|
return cd.currentServiceAddress
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) AddOption(opts ...grpc.DialOption) {
|
||||||
|
cd.additionalOpts = append(cd.additionalOpts, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) CloseConn(conn *grpc.ClientConn) {
|
||||||
|
if conn != nil {
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cd *ConnDirect) dialService(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
conn, err := grpc.DialContext(ctx, cd.resolverDirect.Scheme()+":///"+address, options...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
|
func (cd *ConnDirect) dialServiceWithoutResolver(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||||
|
conn, err := grpc.DialContext(ctx, address, options...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ package discoveryregister
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes"
|
||||||
@@ -36,6 +37,8 @@ func NewDiscoveryRegister(envType string) (discoveryregistry.SvcDiscoveryRegistr
|
|||||||
return zookeeper.NewZookeeperDiscoveryRegister()
|
return zookeeper.NewZookeeperDiscoveryRegister()
|
||||||
case "k8s":
|
case "k8s":
|
||||||
return kubernetes.NewK8sDiscoveryRegister()
|
return kubernetes.NewK8sDiscoveryRegister()
|
||||||
|
case "direct":
|
||||||
|
return direct.NewConnDirect()
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("envType not correct")
|
return nil, errors.New("envType not correct")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ func TestNewDiscoveryRegister(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{"zookeeper", false, true},
|
{"zookeeper", false, true},
|
||||||
{"k8s", false, true}, // 假设 k8s 配置也已正确设置
|
{"k8s", false, true}, // 假设 k8s 配置也已正确设置
|
||||||
|
{"direct", false, true},
|
||||||
{"invalid", true, false},
|
{"invalid", true, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,15 +18,17 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/stathat/consistent"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/stathat/consistent"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/discoveryregistry"
|
"github.com/OpenIMSDK/tools/discoveryregistry"
|
||||||
"github.com/OpenIMSDK/tools/log"
|
"github.com/OpenIMSDK/tools/log"
|
||||||
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ func getSelfHost(ctx context.Context) string {
|
|||||||
return host
|
return host
|
||||||
}
|
}
|
||||||
|
|
||||||
// like openimserver-openim-msggateway-0.openimserver-openim-msggateway-headless.openim-lin.svc.cluster.local:88
|
// like openimserver-openim-msggateway-0.openimserver-openim-msggateway-headless.openim-lin.svc.cluster.local:88.
|
||||||
func getMsgGatewayHost(ctx context.Context) []string {
|
func getMsgGatewayHost(ctx context.Context) []string {
|
||||||
port := 88
|
port := 88
|
||||||
instance := "openimserver"
|
instance := "openimserver"
|
||||||
@@ -130,20 +132,40 @@ func getMsgGatewayHost(ctx context.Context) []string {
|
|||||||
// GetConns returns the gRPC client connections to the specified service.
|
// GetConns returns the gRPC client connections to the specified service.
|
||||||
func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
|
func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
|
||||||
|
|
||||||
|
// This conditional checks if the serviceName is not the OpenImMessageGatewayName.
|
||||||
|
// It seems to handle a special case for the OpenImMessageGateway.
|
||||||
if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName {
|
if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName {
|
||||||
|
// DialContext creates a client connection to the given target (serviceName) using the specified context.
|
||||||
|
// 'cli.options' are likely default or common options for all connections in this struct.
|
||||||
|
// 'opts...' allows for additional gRPC dial options to be passed and used.
|
||||||
conn, err := grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...)
|
conn, err := grpc.DialContext(ctx, serviceName, append(cli.options, opts...)...)
|
||||||
|
|
||||||
|
// The function returns a slice of client connections with the new connection, or an error if occurred.
|
||||||
return []*grpc.ClientConn{conn}, err
|
return []*grpc.ClientConn{conn}, err
|
||||||
} else {
|
} else {
|
||||||
|
// This block is executed if the serviceName is OpenImMessageGatewayName.
|
||||||
|
// 'ret' will accumulate the connections to return.
|
||||||
var ret []*grpc.ClientConn
|
var ret []*grpc.ClientConn
|
||||||
|
|
||||||
|
// getMsgGatewayHost presumably retrieves hosts for the message gateway service.
|
||||||
|
// The context is passed, likely for cancellation and timeout control.
|
||||||
gatewayHosts := getMsgGatewayHost(ctx)
|
gatewayHosts := getMsgGatewayHost(ctx)
|
||||||
|
|
||||||
|
// Iterating over the retrieved gateway hosts.
|
||||||
for _, host := range gatewayHosts {
|
for _, host := range gatewayHosts {
|
||||||
|
// Establishes a connection to each host.
|
||||||
|
// Again, appending cli.options with any additional opts provided.
|
||||||
conn, err := grpc.DialContext(ctx, host, append(cli.options, opts...)...)
|
conn, err := grpc.DialContext(ctx, host, append(cli.options, opts...)...)
|
||||||
|
|
||||||
|
// If there's an error while dialing any host, the function returns immediately with the error.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else {
|
} else {
|
||||||
|
// If the connection is successful, it is added to the 'ret' slice.
|
||||||
ret = append(ret, conn)
|
ret = append(ret, conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// After all hosts are processed, the slice of connections is returned.
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,7 +188,7 @@ func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) {
|
|||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// do not use this method for call rpc
|
// do not use this method for call rpc.
|
||||||
func (cli *K8sDR) GetClientLocalConns() map[string][]*grpc.ClientConn {
|
func (cli *K8sDR) GetClientLocalConns() map[string][]*grpc.ClientConn {
|
||||||
fmt.Println("should not call this function!!!!!!!!!!!!!!!!!!!!!!!!!")
|
fmt.Println("should not call this function!!!!!!!!!!!!!!!!!!!!!!!!!")
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -14,14 +14,14 @@
|
|||||||
|
|
||||||
package prommetrics
|
package prommetrics
|
||||||
|
|
||||||
import ginProm "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
import ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
labels := prometheus.Labels{"label_one": "any", "label_two": "value"}
|
labels := prometheus.Labels{"label_one": "any", "label_two": "value"}
|
||||||
ApiCustomCnt.MetricCollector.(*prometheus.CounterVec).With(labels).Inc().
|
ApiCustomCnt.MetricCollector.(*prometheus.CounterVec).With(labels).Inc().
|
||||||
*/
|
*/
|
||||||
var (
|
var (
|
||||||
ApiCustomCnt = &ginProm.Metric{
|
ApiCustomCnt = &ginprom.Metric{
|
||||||
Name: "custom_total",
|
Name: "custom_total",
|
||||||
Description: "Custom counter events.",
|
Description: "Custom counter events.",
|
||||||
Type: "counter_vec",
|
Type: "counter_vec",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
package prommetrics
|
package prommetrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
|
gp "github.com/grpc-ecosystem/go-grpc-prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||||
|
|
||||||
@@ -23,10 +23,10 @@ import (
|
|||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
"github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *grpc_prometheus.ServerMetrics, error) {
|
func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *gp.ServerMetrics, error) {
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
reg := prometheus.NewRegistry()
|
reg := prometheus.NewRegistry()
|
||||||
grpcMetrics := grpc_prometheus.NewServerMetrics()
|
grpcMetrics := gp.NewServerMetrics()
|
||||||
grpcMetrics.EnableHandlingTimeHistogram()
|
grpcMetrics.EnableHandlingTimeHistogram()
|
||||||
cusMetrics = append(cusMetrics, grpcMetrics, collectors.NewGoCollector())
|
cusMetrics = append(cusMetrics, grpcMetrics, collectors.NewGoCollector())
|
||||||
reg.MustRegister(cusMetrics...)
|
reg.MustRegister(cusMetrics...)
|
||||||
|
|||||||
16
pkg/common/redispubsub/redispubliser.go
Normal file
16
pkg/common/redispubsub/redispubliser.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package redispubsub
|
||||||
|
|
||||||
|
import "github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
|
type Publisher struct {
|
||||||
|
client redis.UniversalClient
|
||||||
|
channel string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPublisher(client redis.UniversalClient, channel string) *Publisher {
|
||||||
|
return &Publisher{client: client, channel: channel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Publisher) Publish(message string) error {
|
||||||
|
return p.client.Publish(ctx, p.channel, message).Err()
|
||||||
|
}
|
||||||
34
pkg/common/redispubsub/redissubscriber.go
Normal file
34
pkg/common/redispubsub/redissubscriber.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package redispubsub
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/redis/go-redis/v9"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ctx = context.Background()
|
||||||
|
|
||||||
|
type Subscriber struct {
|
||||||
|
client redis.UniversalClient
|
||||||
|
channel string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubscriber(client redis.UniversalClient, channel string) *Subscriber {
|
||||||
|
return &Subscriber{client: client, channel: channel}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Subscriber) OnMessage(ctx context.Context, callback func(string)) error {
|
||||||
|
messageChannel := s.client.Subscribe(ctx, s.channel).Channel()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
case msg := <-messageChannel:
|
||||||
|
callback(msg.Payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -71,7 +71,7 @@ func Start(
|
|||||||
}
|
}
|
||||||
|
|
||||||
defer client.Close()
|
defer client.Close()
|
||||||
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()))
|
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
|
||||||
registerIP, err := network.GetRpcRegisterIP(config.Config.Rpc.RegisterIP)
|
registerIP, err := network.GetRpcRegisterIP(config.Config.Rpc.RegisterIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user