Merge branch 'openimsdk:main' into main

pull/1931/head
chao 8 months ago committed by GitHub
commit d91cde90ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,33 @@
# How do I contribute code to OpenIM
<p align="center">
<a href="./CONTRIBUTING.md">Englist</a> ·
<a href="./CONTRIBUTING-zh_CN.md">中文</a> ·
<a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> ·
<a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> ·
<a href="docs/contributing/CONTRIBUTING-HU.md">Magyar</a> ·
<a href="docs/contributing/CONTRIBUTING-ES.md">Español</a> ·
<a href="docs/contributing/CONTRIBUTING-FA.md">فارسی</a> ·
<a href="docs/contributing/CONTRIBUTING-FR.md">Français</a> ·
<a href="docs/contributing/CONTRIBUTING-DE.md">Deutsch</a> ·
<a href="docs/contributing/CONTRIBUTING-PL.md">Polski</a> ·
<a href="docs/contributing/CONTRIBUTING-ID.md">Indonesian</a> ·
<a href="docs/contributing/CONTRIBUTING-FI.md">Suomi</a> ·
<a href="docs/contributing/CONTRIBUTING-ML.md">മലയാളം</a> ·
<a href="docs/contributing/CONTRIBUTING-JP.md">日本語</a> ·
<a href="docs/contributing/CONTRIBUTING-NL.md">Nederlands</a> ·
<a href="docs/contributing/CONTRIBUTING-IT.md">Italiano</a> ·
<a href="docs/contributing/CONTRIBUTING-RU.md">Русский</a> ·
<a href="docs/contributing/CONTRIBUTING-PTBR.md">Português (Brasil)</a> ·
<a href="docs/contributing/CONTRIBUTING-EO.md">Esperanto</a> ·
<a href="docs/contributing/CONTRIBUTING-KR.md">한국어</a> ·
<a href="docs/contributing/CONTRIBUTING-AR.md">العربي</a> ·
<a href="docs/contributing/CONTRIBUTING-VN.md">Tiếng Việt</a> ·
<a href="docs/contributing/CONTRIBUTING-DA.md">Dansk</a> ·
<a href="docs/contributing/CONTRIBUTING-GR.md">Ελληνικά</a> ·
<a href="docs/contributing/CONTRIBUTING-TR.md">Türkçe</a>
</p>
</div>
</p>

@ -1,6 +1,38 @@
# Contributing to Open-IM-Server
So, you want to hack on Open-IM-Server? Yay!
# How do I contribute code to OpenIM
<p align="center">
<a href="./CONTRIBUTING.md">Englist</a> ·
<a href="./CONTRIBUTING-zh_CN.md">中文</a> ·
<a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> ·
<a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> ·
<a href="docs/contributing/CONTRIBUTING-HU.md">Magyar</a> ·
<a href="docs/contributing/CONTRIBUTING-ES.md">Español</a> ·
<a href="docs/contributing/CONTRIBUTING-FA.md">فارسی</a> ·
<a href="docs/contributing/CONTRIBUTING-FR.md">Français</a> ·
<a href="docs/contributing/CONTRIBUTING-DE.md">Deutsch</a> ·
<a href="docs/contributing/CONTRIBUTING-PL.md">Polski</a> ·
<a href="docs/contributing/CONTRIBUTING-ID.md">Indonesian</a> ·
<a href="docs/contributing/CONTRIBUTING-FI.md">Suomi</a> ·
<a href="docs/contributing/CONTRIBUTING-ML.md">മലയാളം</a> ·
<a href="docs/contributing/CONTRIBUTING-JP.md">日本語</a> ·
<a href="docs/contributing/CONTRIBUTING-NL.md">Nederlands</a> ·
<a href="docs/contributing/CONTRIBUTING-IT.md">Italiano</a> ·
<a href="docs/contributing/CONTRIBUTING-RU.md">Русский</a> ·
<a href="docs/contributing/CONTRIBUTING-PTBR.md">Português (Brasil)</a> ·
<a href="docs/contributing/CONTRIBUTING-EO.md">Esperanto</a> ·
<a href="docs/contributing/CONTRIBUTING-KR.md">한국어</a> ·
<a href="docs/contributing/CONTRIBUTING-AR.md">العربي</a> ·
<a href="docs/contributing/CONTRIBUTING-VN.md">Tiếng Việt</a> ·
<a href="docs/contributing/CONTRIBUTING-DA.md">Dansk</a> ·
<a href="docs/contributing/CONTRIBUTING-GR.md">Ελληνικά</a> ·
<a href="docs/contributing/CONTRIBUTING-TR.md">Türkçe</a>
</p>
</div>
</p>
So, you want to hack on open-im-server? Yay!
First of all, thank you for considering contributing to our project! We appreciate your time and effort, and we value any contribution, whether it's reporting a bug, suggesting a new feature, or submitting a pull request.
@ -12,7 +44,7 @@ This document provides guidelines and best practices to help you contribute effe
## 📇Topics
- [Contributing to Open-IM-Server](#contributing-to-open-im-server)
- [How do I contribute code to OpenIM](#how-do-i-contribute-code-to-openim)
- [📇Topics](#topics)
- [What we expect of you](#what-we-expect-of-you)
- [Code of ConductCode of Conduct](#code-of-conductcode-of-conduct)
@ -32,13 +64,13 @@ This document provides guidelines and best practices to help you contribute effe
## What we expect of you
We hope that anyone can join Open-IM-Server , even if you are a student, writer, translator
We hope that anyone can join open-im-server , even if you are a student, writer, translator
Please meet the minimum version of the Go language published in [go.mod](./go.mod). If you want to manage the Go language version, we provide tools to install [gvm](https://github.com/moovweb/gvm) in our [Makefile](./Makefile)
Please meet the minimum version of the Go language published in [go.mod](./go.mod). If you want to manage the Go language version, we provide tools tHow do I contribute code to OpenIMo install [gvm](https://github.com/moovweb/gvm) in our [Makefile](./Makefile)
You'd better use Linux OR WSL as the development environment, Linux with [Makefile](./Makefile) can help you quickly build and test Open-IM-Server project.
You'd better use Linux OR WSL as the development environment, Linux with [Makefile](./Makefile) can help you quickly build and test open-im-server project.
If you are familiar with [Makefile](./Makefile) , you can easily see the clever design of the Open-IM-Server Makefile. Storing the necessary tools such as golangci in the `/tools` directory can avoid some tool version issues.
If you are familiar with [Makefile](./Makefile) , you can easily see the clever design of the open-im-server Makefile. Storing the necessary tools such as golangci in the `/tools` directory can avoid some tool version issues.
The [Makefile](./Makefile) is for every developer, even if you don't know how to use the Makefile tool, don't worry, we provide two great commands to get you up to speed with the Makefile architecture, `make help` and `make help-all`, it can reduce problems of the developing environment.
@ -52,7 +84,7 @@ In accordance with the naming conventions adopted by OpenIM and drawing referenc
#### Code and doc contribution
Every action to make project Open-IM-Server better is encouraged. On GitHub, every improvement for Open-IM-Server could be via a [PR](https://github.com/openimsdk/open-im-server/pulls) (short for pull request).
Every action to make project open-im-server better is encouraged. On GitHub, every improvement for open-im-server could be via a [PR](https://github.com/openimsdk/open-im-server/pulls) (short for pull request).
+ If you find a typo, try to fix it!
+ If you find a bug, try to fix it!
@ -67,8 +99,8 @@ Every action to make project Open-IM-Server better is encouraged. On GitHub, eve
#### Where should I start?
+ If you are new to the project, don't know how to contribute Open-IM-Server, please check out the [good first issue](https://github.com/openimsdk/open-im-server/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label.
+ You should be good at filtering the Open-IM-Server issue tags and finding the ones you like, such as [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Abug+) fixes.
+ If you are new to the project, don't know how to contribute open-im-server, please check out the [good first issue](https://github.com/openimsdk/open-im-server/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label.
+ You should be good at filtering the open-im-server issue tags and finding the ones you like, such as [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Abug+) fixes.
+ If you are looking for something to work on, check out our [open issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
+ If you have an idea for a new feature, please [open an issue](https://github.com/openimsdk/open-im-server/issues/new/choose), and we can discuss it.
@ -85,7 +117,7 @@ When documenting a new design, we recommend a 2-step approach:
1. Use the short-form RFC template to outline your ideas and get early feedback.
2. Once you have received sufficient feedback and consensus, you may use the longer-form design doc template to specify and discuss your design in more details.
In order to contribute a feature to Open-IM-Server you'll need to go through the following steps:
In order to contribute a feature to open-im-server you'll need to go through the following steps:
+ Discuss your idea with the appropriate [working groups](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) on the working group's Slack channel.
+ Once there is general agreement that the feature is useful, create a GitHub issue to track the discussion. The issue should include information about the requirements and use cases that it is trying to address.
@ -95,13 +127,27 @@ But keep in mind that there is no guarantee of it being accepted and so it is us
## Getting Started
To propose PR for the Open-IM-Server item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps:
To propose PR for the open-im-server item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps:
1. Fork the repository(open-im-server)
2. **CLONE** your own repository to main locally. Use `git clone https://github.com/<your-username>/open-im-server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make.
3. **Initialize Git Hooks with `make init-githooks`**
After cloning the repository, it's recommended to set up Git hooks to streamline your workflow and ensure your contributions adhere to OpenIM's community standards. Git hooks are scripts that run automatically every time a particular event occurs in a Git repository, such as before a commit or push. To initialize Git hooks for the OpenIM server repository, use the `make init-githooks` command.
- **Enabling Git Hooks Mode**: By running `make init-githooks` and entering `1` when prompted, you enable Git hooks mode. This action will generate a series of hooks within the `.git/hooks/` directory. These hooks impose certain checks on your commits and pushes, ensuring they meet the quality and standards expected by the OpenIM community. For instance, commit hooks might enforce a specific commit message format, while push hooks could check for code style or linting issues.
1. Fork the repository(Open-IM-Server)
- **Benefits for First-Time Contributors**: This setup is especially beneficial for new contributors. It guides you to make professional, community-standard-compliant Pull Requests (PRs) and PR descriptions right from your first contribution. By automating checks and balances, it reduces the chances of common mistakes and speeds up the review process.
2. **CLONE** your own repository to main locally. Use `git clone https://github.com/<your-username>/Open-IM-Server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make.
- **Disabling Git Hooks**: If for any reason you wish to remove the Git hooks, simply run `make init-githooks` again and enter `2` when prompted. This will delete the existing Git hooks, removing the automatic checks and constraints from your Git operations. However, keep in mind that manually ensuring your contributions adhere to community standards without the aid of Git hooks requires diligence.
3. **Set Remote** upstream to be `https://github.com/openimsdk/open-im-server.git` using the following two commands:
> [!NOTE] Utilizing Git hooks through the `make init-githooks` command is a straightforward yet powerful way to ensure your contributions are consistent and high-quality. It's a step towards fostering a professional and efficient development environment in the OpenIM project.
4. **Set Remote** upstream to be `https://github.com/openimsdk/open-im-server.git` using the following two commands:
```bash
git remote add upstream https://github.com/openimsdk/open-im-server.git
@ -112,18 +158,18 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu
```bash
git remote -v
origin https://github.com/<your-username>/Open-IM-Server.git (fetch)
origin https://github.com/<your-username>/Open-IM-Server.git (push)
origin https://github.com/<your-username>/open-im-server.git (fetch)
origin https://github.com/<your-username>/open-im-server.git (push)
upstream https://github.com/openimsdk/open-im-server.git (fetch)
upstream no-pushing (push)
```
Adding this, we can easily synchronize local branches with upstream branches.
4. Create a new branch for your changes (use a descriptive name, such as `fix-bug-123` or `add-new-feature`).
5. Create a new branch for your changes (use a descriptive name, such as `fix-bug-123` or `add-new-feature`).
```bash
cd Open-IM-Server
cd open-im-server
git fetch upstream
git checkout upstream/main
```
@ -136,7 +182,8 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu
Make any change on the `new-branch` then use [Makefile](./Makefile) build and test your codes.
5. **Commit your changes** to your local branch, lint before committing and commit with sign-off
6. **Commit your changes** to your local branch, lint before committing and commit with sign-off
```bash
git rebase upstream/main
@ -145,7 +192,7 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu
git commit -a -s -m "message for your changes" # -s adds a Signed-off-by trailer
```
6. **Push your branch** to your forked repository, it is recommended to have only one commit for a PR.
7. **Push your branch** to your forked repository, it is recommended to have only one commit for a PR.
```bash
# sync up with upstream
@ -177,28 +224,94 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu
git rebase upstream/main # rebase the current branch to upstream/main branch
git add -A
git commit -m -s "feat: feature two"
# then create pull request, and merge
```
7. **Open a pull request** to `openimsdk/open-im-server:main`
**Verifying Your Pull Request with `make all` Command**
Before verifying, you may need to complete the basic deployment of OpenIM to get familiar with the deployment status of OpenIM. Please read [this deployment document](https://docs.openim.io/zh-Hans/guides/gettingStarted/imSourceCodeDeployment), which will tell you how to deploy OpenIM middleware and OpenIM services in detail
Before submitting your Pull Request (PR), it's crucial to ensure that it passes all the necessary checks and verifications to maintain the quality and integrity of the OpenIM server project. To facilitate this process, we have encapsulated a series of validation steps into the `make all` command.
- **Purpose of `make all` Command**: The `make all` command serves as a comprehensive pre-PR verification tool. It sequentially executes a variety of tasks designed to scrutinize your changes from multiple angles, ensuring they are ready for submission.
- **Included Commands**:
- `tidy`: Cleans up the module by removing unused dependencies.
- `gen`: Generates necessary files from templates or specifications, ensuring that your codebase is up-to-date with its dependencies.
- `add-copyright`: Checks for and adds copyright notices to files, ensuring compliance with legal requirements.
- `verify`: Verifies the integrity and consistency of the code, dependencies, and various checks.
- `test-api`: Runs API tests to ensure that your changes do not break any existing functionality and adhere to the expected behaviors.
- `lint`: Analyzes the code for potential stylistic or programming errors, enforcing the project's coding standards.
- `cover`: Measures the code coverage of tests, helping you understand how much of the code is being tested.
- `restart`: (Optionally) restarts services or applications to ensure that changes are correctly applied and functioning in a live environment.
- **Executing the Command**: To run the `make all` command, simply navigate to the root directory of your cloned repository in your terminal and execute:
```bash
make all
```
This command will sequentially perform all the listed actions, outputting any warnings or errors encountered during the process. It's a vital step to catch any issues early and ensure your contribution meets the quality standards set by the OpenIM community.
- **Benefits**: By using `make all` for pre-PR verification, you significantly increase the likelihood of your PR being accepted on the first review. It not only demonstrates your commitment to quality but also streamlines the review process by minimizing back-and-forth due to common issues that can be caught automatically.
**Troubleshooting Git Push Failures**
When working with Git, encountering errors during push operations is not uncommon. Two primary reasons you might face push failures are due to firewall restrictions or authentication issues. Heres how you can troubleshoot and resolve these problems.
**Firewall Errors**
If you're behind a corporate firewall or your network restricts certain types of traffic, you might encounter issues when trying to push your changes via HTTPS. This is because firewalls can block the ports used by the HTTPS protocol. To resolve this issue, you can configure Git to use a proxy.
If you have a local proxy server set up, you can direct Git to use it by setting the `https_proxy` and `http_proxy` environment variables. Open your terminal or command prompt and run the following commands:
```bash
export https_proxy="http://127.0.0.1:7890"
export http_proxy="http://127.0.0.1:7890"
```
Replace `127.0.0.1:7890` with the address and port of your proxy server. These commands set the proxy for the current session. If you want to make these changes permanent, add them to your `.bashrc`, `.bash_profile`, or equivalent shell configuration file.
**Using SSH Instead of HTTPS**
An alternative to using HTTPS is to set up an SSH connection for Git operations. SSH connections are often not blocked by firewalls and do not require proxy settings. Additionally, SSH provides a secure channel and can simplify the authentication process since it relies on SSH keys rather than username and password credentials.
To use SSH with Git, you first need to generate an SSH key pair and add the public key to your GitHub account (or another Git hosting service).
1. **Generate SSH Key Pair**: Open your terminal and run `ssh-keygen -t rsa -b 4096 -C "your_email@example.com"`, replacing `your_email@example.com` with your email. Press enter to accept the default file location and passphrase prompts.
2. **Add SSH Key to SSH-Agent**: Ensure the ssh-agent is running with `eval "$(ssh-agent -s)"` and then add your SSH private key to the ssh-agent using `ssh-add ~/.ssh/id_rsa`.
3. **Add SSH Key to GitHub**: Copy your SSH public key to your clipboard with `cat ~/.ssh/id_rsa.pub | clip` (Windows) or `pbcopy < ~/.ssh/id_rsa.pub` (Mac). Go to GitHub, navigate to Settings > SSH and GPG keys, and add a new SSH key, pasting your key into the field provided.
4. **Switch to SSH in Your Repository**: Change your repository's remote URL from HTTPS to SSH. You can find the SSH URL in your repository settings on GitHub and use `git remote set-url origin git@github.com:username/repository.git` to switch.
**Authentication Errors**
If you're experiencing authentication errors, it might be due to missing or incorrect credentials. Ensure you have added your SSH key to your Git hosting service. You can test your SSH connection with `ssh -T git@github.com` (replace `github.com` with your Git hosting service's domain). If successful, you'll receive a welcome message.
For HTTPS users, check that your username and password (or personal access token for services like GitHub that no longer accept password authentication for Git operations) are correct.
8. **Open a pull request** to `openimsdk/open-im-server:main`
It is recommended to review your changes before filing a pull request. Check if your code doesn't conflict with the main branch and no redundant code is included.
> [!TIP] There is a [good blog post documenting](https://nsddd.top/posts/participating-in-this-project/) the entire push contribution process.
## Style and Specification
We divide the problem into security and general problems:
#### Reporting security issues
Security issues are always treated seriously. As our usual principle, we discourage anyone to spread security issues. If you find a security issue of Open-IM-Server, please do not discuss it in public and even do not open a public issue.
Security issues are always treated seriously. As our usual principle, we discourage anyone to spread security issues. If you find a security issue of open-im-server, please do not discuss it in public and even do not open a public issue.
Instead we encourage you to send us a private email to info@openim.io to report this.
#### Reporting general issues
To be honest, we regard every user of Open-IM-Serveras a very kind contributor. After experiencing Open-IM-Server, you may have some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/openimsdk/open-im-server/issues/new/choose).
To be honest, we regard every user of open-im-serveras a very kind contributor. After experiencing open-im-server, you may have some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/openimsdk/open-im-server/issues/new/choose).
Since we collaborate project Open-IM-Server in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one.
Since we collaborate project open-im-server in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one.
To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](https://github.com/OpenIMSDK/.github/tree/main/.github/ISSUE_TEMPLATE) for issue reporters. You can find three kinds of issue templates there: question, bug report and feature request. Please **BE SURE** to follow the instructions to fill fields in template.
@ -206,20 +319,20 @@ To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](
+ bug report
+ feature request
+ Open-IM-Server performance issues
+ open-im-server performance issues
+ feature proposal
+ feature design
+ help wanted
+ doc incomplete
+ test improvement
+ any questions on Open-IM-Server project
+ any questions on open-im-server project
+ and so on
Also, we must be reminded when submitting a new question about Open-IM-Server, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on.
Also, we must be reminded when submitting a new question about open-im-server, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on.
#### Commit Rules
Actually in Open-IM-Server, we take two rules serious when committing:
Actually in open-im-server, we take two rules serious when committing:
**🥇 Commit Message:**
@ -262,7 +375,7 @@ An example for this could be:
#### PR Description
PR is the only way to make change to Open-IM-Server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/.github/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request.
PR is the only way to make change to open-im-server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/.github/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request.
You can find some very formal PR in [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) issues and learn about them.
@ -311,11 +424,11 @@ git() {
#### Docs Contribution
The documentation for Open-IM-Server includes:
The documentation for open-im-server includes:
+ [README.md](https://github.com/openimsdk/open-im-server/blob/main/README.md): This file includes the basic information and instructions for getting started with Open-IM-Server.
+ [CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to Open-IM-Server's codebase, such as how to submit issues, pull requests, and code reviews.
+ [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for Open-IM-Server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips.
+ [README.md](https://github.com/openimsdk/open-im-server/blob/main/README.md): This file includes the basic information and instructions for getting started with open-im-server.
+ [CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to open-im-server's codebase, such as how to submit issues, pull requests, and code reviews.
+ [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for open-im-server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips.
Please obey the following rules to better format the docs, which would greatly improve the reading experience.
@ -328,20 +441,20 @@ Please obey the following rules to better format the docs, which would greatly i
## Engage to help anything
We choose GitHub as the primary place for Open-IM-Server to collaborate. So the latest updates of Open-IM-Server are always here. Although contributions via PR is an explicit way to help, we still call for any other ways.
We choose GitHub as the primary place for open-im-server to collaborate. So the latest updates of open-im-server are always here. Although contributions via PR is an explicit way to help, we still call for any other ways.
+ reply to other's [issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) if you could;
+ help solve other user's problems;
+ help review other's [PR](https://github.com/openimsdk/open-im-server/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc) design;
+ discuss about Open-IM-Server to make things clearer;
+ advocate [Open-IM-Server](https://google.com/search?q=Open-IM-Server) technology beyond GitHub;
+ write blogs on Open-IM-Server and so on.
+ discuss about open-im-server to make things clearer;
+ advocate [open-im-server](https://google.com/search?q=open-im-server) technology beyond GitHub;
+ write blogs on open-im-server and so on.
In a word, **ANY HELP IS CONTRIBUTION.**
## Release version
Releases of Open-IM-Server are done using [Release Please](https://github.com/googleapis/release-please) and [GoReleaser](https://goreleaser.com/). The workflow looks like this:
Releases of open-im-server are done using [Release Please](https://github.com/googleapis/release-please) and [GoReleaser](https://goreleaser.com/). The workflow looks like this:
🎯 A PR is merged to the `main` branch:
@ -366,17 +479,23 @@ Such a commit can get produced as follows:
git commit --allow-empty -m "chore: release 0.0.3" -m "Release-As: 0.0.3
````
For the complex release process, in fact, and encapsulation as CICD, you only need to tag locally, and then publish the tag to OpenIM github to complete the entire OpenIM release process.
In addition to CICD, we also do a complex release command locally, which can help you complete the full platform compilation, testing, and release to Minio, just by using the `make release` command.
Please [read the detailed documents](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/release.md)
## Contact Us
We value close connections with our users, developers, and contributors here at Open-IM-Server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
We value close connections with our users, developers, and contributors here at open-im-server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of Open-IM-Server. You can ask technical questions, seek help, or share your experiences with other users of Open-IM-Server.
Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of open-im-server. You can ask technical questions, seek help, or share your experiences with other users of open-im-server.
In addition to Slack, we also offer the following ways to get in touch:
+ <a href="https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q" target="_blank"><img src="https://img.shields.io/badge/slack-%40OpenIMSDKCore-informational?logo=slack&style=flat-square"></a>: We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 Open-IM-Server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) team channel.
+ <a href="https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q" target="_blank"><img src="https://img.shields.io/badge/slack-%40OpenIMSDKCore-informational?logo=slack&style=flat-square"></a>: We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 open-im-server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) team channel.
+ <a href="https://mail.google.com/mail/u/0/?fs=1&tf=cm&to=4closetool3@gmail.com" target="_blank"><img src="https://img.shields.io/badge/gmail-%40OOpenIMSDKCore?style=social&logo=gmail"></a>: Get in touch with us on [Gmail](info@openim.io). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email.
+ <a href="https://doc.rentsoft.cn/" target="_blank"><img src="https://img.shields.io/badge/%E5%8D%9A%E5%AE%A2-%40OpenIMSDKCore-blue?style=social&logo=Octopus%20Deploy"></a>: Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with Open-IM-Server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
+ <a href="https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg" target="_blank"><img src="https://img.shields.io/badge/%E5%BE%AE%E4%BF%A1-OpenIMSDKCore-brightgreen?logo=wechat&style=flat-square"></a>: Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of Open-IM-Server. We will process your request as soon as possible.
+ <a href="https://doc.rentsoft.cn/" target="_blank"><img src="https://img.shields.io/badge/%E5%8D%9A%E5%AE%A2-%40OpenIMSDKCore-blue?style=social&logo=Octopus%20Deploy"></a>: Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with open-im-server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
+ <a href="https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg" target="_blank"><img src="https://img.shields.io/badge/%E5%BE%AE%E4%BF%A1-OpenIMSDKCore-brightgreen?logo=wechat&style=flat-square"></a>: Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of open-im-server. We will process your request as soon as possible.
Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.

@ -4,29 +4,51 @@
</a>
</p>
<h3 align="center" style="border-bottom: none">
⭐️ Open source Instant Messaging Server ⭐️ <br>
<h3>
<p align=center>
<a href="https://goreportcard.com/report/github.com/openimsdk/open-im-server"><img src="https://goreportcard.com/badge/github.com/openimsdk/open-im-server" alt="A+"></a>
<a href="https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22"><img src="https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?logo=%22github%22" alt="good first"></a>
<a href="https://github.com/openimsdk/open-im-server"><img src="https://img.shields.io/github/stars/openimsdk/open-im-server.svg?style=flat&logo=github&colorB=deeppink&label=stars"></a>
<a href="https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q"><img src="https://img.shields.io/badge/Slack-300%2B-blueviolet?logo=slack&amp;logoColor=white"></a>
<a href="https://github.com/openimsdk/open-im-server/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green"></a>
<a href="https://golang.org/"><img src="https://img.shields.io/badge/Language-Go-blue.svg"></a>
</p>
<div align="center">
[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers)
[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members)
[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server)
[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server)
[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3)
[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE)
[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)
[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045)
[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/)
</p>
<p align="center">
<a href="./README.md"><b> English </b></a>
<a href="./README-zh_CN.md"><b> 简体中文 </b></a>
<a href="https://openim.io/en"><b> Docs </b></a>
<a href="./README.md">Englist</a> ·
<a href="./README-zh_CN.md">中文</a> ·
<a href="docs/readme/README-UA.md">Українська</a> ·
<a href="docs/readme/README-CS.md">Česky</a> ·
<a href="docs/readme/README-HU.md">Magyar</a> ·
<a href="docs/readme/README-ES.md">Español</a> ·
<a href="docs/readme/README-FA.md">فارسی</a> ·
<a href="docs/readme/README-FR.md">Français</a> ·
<a href="docs/readme/README-DE.md">Deutsch</a> ·
<a href="docs/readme/README-PL.md">Polski</a> ·
<a href="docs/readme/README-ID.md">Indonesian</a> ·
<a href="docs/readme/README-FI.md">Suomi</a> ·
<a href="docs/readme/README-ML.md">മലയാളം</a> ·
<a href="docs/readme/README-JP.md">日本語</a> ·
<a href="docs/readme/README-NL.md">Nederlands</a> ·
<a href="docs/readme/README-IT.md">Italiano</a> ·
<a href="docs/readme/README-RU.md">Русский</a> ·
<a href="docs/readme/README-PTBR.md">Português (Brasil)</a> ·
<a href="docs/readme/README-EO.md">Esperanto</a> ·
<a href="docs/readme/README-KR.md">한국어</a> ·
<a href="docs/readme/README-AR.md">العربي</a> ·
<a href="docs/readme/README-VN.md">Tiếng Việt</a> ·
<a href="docs/readme/README-DA.md">Dansk</a> ·
<a href="docs/readme/README-GR.md">Ελληνικά</a> ·
<a href="docs/readme/README-TR.md">Türkçe</a>
</p>
</div>
</p>
## 🟢 扫描微信进群交流
@ -39,8 +61,6 @@ OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通
![App-OpenIM 关系](./docs/images/oepnim-design.png)
## 🚀 关于 OpenIMSDK

@ -17,9 +17,35 @@
[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22)
[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/)
[**English**](./README.md) •
[**简体中文**](./README-zh_CN.md) •
[**Docs**](https://openim.io/en)
<p align="center">
<a href="./README.md">Englist</a> ·
<a href="./README-zh_CN.md">中文</a> ·
<a href="docs/readme/README-UA.md">Українська</a> ·
<a href="docs/readme/README-CS.md">Česky</a> ·
<a href="docs/readme/README-HU.md">Magyar</a> ·
<a href="docs/readme/README-ES.md">Español</a> ·
<a href="docs/readme/README-FA.md">فارسی</a> ·
<a href="docs/readme/README-FR.md">Français</a> ·
<a href="docs/readme/README-DE.md">Deutsch</a> ·
<a href="docs/readme/README-PL.md">Polski</a> ·
<a href="docs/readme/README-ID.md">Indonesian</a> ·
<a href="docs/readme/README-FI.md">Suomi</a> ·
<a href="docs/readme/README-ML.md">മലയാളം</a> ·
<a href="docs/readme/README-JP.md">日本語</a> ·
<a href="docs/readme/README-NL.md">Nederlands</a> ·
<a href="docs/readme/README-IT.md">Italiano</a> ·
<a href="docs/readme/README-RU.md">Русский</a> ·
<a href="docs/readme/README-PTBR.md">Português (Brasil)</a> ·
<a href="docs/readme/README-EO.md">Esperanto</a> ·
<a href="docs/readme/README-KR.md">한국어</a> ·
<a href="docs/readme/README-AR.md">العربي</a> ·
<a href="docs/readme/README-VN.md">Tiếng Việt</a> ·
<a href="docs/readme/README-DA.md">Dansk</a> ·
<a href="docs/readme/README-GR.md">Ελληνικά</a> ·
<a href="docs/readme/README-TR.md">Türkçe</a>
</p>
</div>
@ -68,6 +94,13 @@ It is built using Golang and supports cross-platform deployment, ensuring a cons
👉 **[Learn more](https://docs.openim.io/guides/introduction/product)**
## :building_construction: Overall Architecture
Delve into the heart of Open-IM-Server's functionality with our architecture diagram.
![Overall Architecture](./docs/images/architecture-layers.png)
## :rocket: Quick Start
We support many platforms. Here are the addresses for quick experience on the web side

@ -26,9 +26,10 @@ import (
"syscall"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/tools/discoveryregistry"
"github.com/OpenIMSDK/tools/log"
"github.com/openimsdk/open-im-server/v3/internal/api"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
@ -44,55 +45,43 @@ func main() {
apiCmd.AddPortFlag()
apiCmd.AddApi(run)
if err := apiCmd.Execute(); err != nil {
log.ZError(context.Background(), "API command execution failed", err)
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}
func run(port int, proPort int) error {
log.ZInfo(context.Background(), "Openim api port:", "port", port, "proPort", proPort)
if port == 0 || proPort == 0 {
err := "port or proPort is empty:" + strconv.Itoa(port) + "," + strconv.Itoa(proPort)
log.ZError(context.Background(), err, nil)
return fmt.Errorf(err)
return errs.Wrap(fmt.Errorf(err))
}
rdb, err := cache.NewRedis()
if err != nil {
log.ZError(context.Background(), "Failed to initialize Redis", err)
return err
}
log.ZInfo(context.Background(), "api start init discov client")
var client discoveryregistry.SvcDiscoveryRegistry
// Determine whether zk is passed according to whether it is a clustered deployment
client, err = kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery)
if err != nil {
log.ZError(context.Background(), "Failed to initialize discovery register", err)
return err
return errs.Wrap(err, "register discovery err")
}
if err = client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil {
log.ZError(context.Background(), "Failed to create RPC root nodes", err)
return err
return errs.Wrap(err, "create rpc root nodes error")
}
log.ZInfo(context.Background(), "api register public config to discov")
if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.Config.EncodeConfig()); err != nil {
log.ZError(context.Background(), "Failed to register public config to discov", err)
return err
}
log.ZInfo(context.Background(), "api register public config to discov success")
router := api.NewGinRouter(client, rdb)
if config.Config.Prometheus.Enable {
p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
p.SetListenAddress(fmt.Sprintf(":%d", proPort))
p.Use(router)
}
log.ZInfo(context.Background(), "api init router success")
var address string
if config.Config.Api.ListenIP != "" {
@ -100,13 +89,11 @@ func run(port int, proPort int) error {
} else {
address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port))
}
log.ZInfo(context.Background(), "start api server", "address", address, "OpenIM version", config.Version)
server := http.Server{Addr: address, Handler: router}
go func() {
err = server.ListenAndServe()
if err != nil && err != http.ErrServerClosed {
log.ZError(context.Background(), "api run failed", err, "address", address)
os.Exit(1)
}
}()
@ -120,7 +107,6 @@ func run(port int, proPort int) error {
// graceful shutdown operation.
if err := server.Shutdown(ctx); err != nil {
log.ZError(context.Background(), "failed to api-server shutdown", err)
return err
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
)
@ -54,6 +57,7 @@ func main() {
// openIM clear msg --clearAll
msgUtilsCmd.AddCommand(&getCmd.Command, &fixCmd.Command, &clearCmd.Command)
if err := msgUtilsCmd.Execute(); err != nil {
panic(err)
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/tools"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
)
@ -22,6 +25,7 @@ import (
func main() {
cronTaskCmd := cmd.NewCronTaskCmd()
if err := cronTaskCmd.Exec(tools.StartTask); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
)
@ -25,6 +28,7 @@ func main() {
msgGatewayCmd.AddPrometheusPortFlag()
if err := msgGatewayCmd.Exec(); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
)
@ -23,6 +26,7 @@ func main() {
msgTransferCmd.AddPrometheusPortFlag()
msgTransferCmd.AddTransferProgressFlag()
if err := msgTransferCmd.Exec(); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/push"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/auth"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/conversation"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/friend"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/group"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/msg"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/third"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -15,6 +15,9 @@
package main
import (
"fmt"
"os"
"github.com/openimsdk/open-im-server/v3/internal/rpc/user"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
@ -28,6 +31,7 @@ func main() {
panic(err.Error())
}
if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil {
panic(err.Error())
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err)
os.Exit(-1)
}
}

@ -7,7 +7,6 @@ Welcome to the OpenIM Documentation hub! This center provides a comprehensive ra
1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Guidance on contributing and configurations for developers
2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Coding conventions, logging policies, and other transformation tools
------
## Contrib

@ -305,13 +305,14 @@ Feel free to explore the MinIO documentation for more advanced configurations an
This section involves setting up MongoDB, including its port, address, and credentials.
| Parameter | Example Value | Description |
| -------------- | -------------- | ----------------------- |
| MONGO_PORT | "27017" | Port used by MongoDB. |
| MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. |
| MONGO_USERNAME | [User Defined] | Admin Username for MongoDB. |
| MONGO_PASSWORD | [User Defined] | Admin Password for MongoDB. |
| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Username for MongoDB. |
| MONGO_OPENIM_USERNAME | [User Defined] | OpenIM Username for MongoDB. |
| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Password for MongoDB. |
### 2.8. <a name='TencentCloudCOSConfiguration'></a>Tencent Cloud COS Configuration

@ -0,0 +1,33 @@
# How do I contribute code to OpenIM
<p align="center">
<a href="./CONTRIBUTING.md">Englist</a> ·
<a href="./CONTRIBUTING-zh_CN.md">中文</a> ·
<a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> ·
<a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> ·
<a href="docs/contributing/CONTRIBUTING-HU.md">Magyar</a> ·
<a href="docs/contributing/CONTRIBUTING-ES.md">Español</a> ·
<a href="docs/contributing/CONTRIBUTING-FA.md">فارسی</a> ·
<a href="docs/contributing/CONTRIBUTING-FR.md">Français</a> ·
<a href="docs/contributing/CONTRIBUTING-DE.md">Deutsch</a> ·
<a href="docs/contributing/CONTRIBUTING-PL.md">Polski</a> ·
<a href="docs/contributing/CONTRIBUTING-ID.md">Indonesian</a> ·
<a href="docs/contributing/CONTRIBUTING-FI.md">Suomi</a> ·
<a href="docs/contributing/CONTRIBUTING-ML.md">മലയാളം</a> ·
<a href="docs/contributing/CONTRIBUTING-JP.md">日本語</a> ·
<a href="docs/contributing/CONTRIBUTING-NL.md">Nederlands</a> ·
<a href="docs/contributing/CONTRIBUTING-IT.md">Italiano</a> ·
<a href="docs/contributing/CONTRIBUTING-RU.md">Русский</a> ·
<a href="docs/contributing/CONTRIBUTING-PTBR.md">Português (Brasil)</a> ·
<a href="docs/contributing/CONTRIBUTING-EO.md">Esperanto</a> ·
<a href="docs/contributing/CONTRIBUTING-KR.md">한국어</a> ·
<a href="docs/contributing/CONTRIBUTING-AR.md">العربي</a> ·
<a href="docs/contributing/CONTRIBUTING-VN.md">Tiếng Việt</a> ·
<a href="docs/contributing/CONTRIBUTING-DA.md">Dansk</a> ·
<a href="docs/contributing/CONTRIBUTING-GR.md">Ελληνικά</a> ·
<a href="docs/contributing/CONTRIBUTING-TR.md">Türkçe</a>
</p>
</div>
</p>

@ -0,0 +1,33 @@
# How do I contribute code to OpenIM
<p align="center">
<a href="./CONTRIBUTING.md">Englist</a> ·
<a href="./CONTRIBUTING-zh_CN.md">中文</a> ·
<a href="docs/contributing/CONTRIBUTING-UA.md">Українська</a> ·
<a href="docs/contributing/CONTRIBUTING-CS.md">Česky</a> ·
<a href="docs/contributing/CONTRIBUTING-HU.md">Magyar</a> ·
<a href="docs/contributing/CONTRIBUTING-ES.md">Español</a> ·
<a href="docs/contributing/CONTRIBUTING-FA.md">فارسی</a> ·
<a href="docs/contributing/CONTRIBUTING-FR.md">Français</a> ·
<a href="docs/contributing/CONTRIBUTING-DE.md">Deutsch</a> ·
<a href="docs/contributing/CONTRIBUTING-PL.md">Polski</a> ·
<a href="docs/contributing/CONTRIBUTING-ID.md">Indonesian</a> ·
<a href="docs/contributing/CONTRIBUTING-FI.md">Suomi</a> ·
<a href="docs/contributing/CONTRIBUTING-ML.md">മലയാളം</a> ·
<a href="docs/contributing/CONTRIBUTING-JP.md">日本語</a> ·
<a href="docs/contributing/CONTRIBUTING-NL.md">Nederlands</a> ·
<a href="docs/contributing/CONTRIBUTING-IT.md">Italiano</a> ·
<a href="docs/contributing/CONTRIBUTING-RU.md">Русский</a> ·
<a href="docs/contributing/CONTRIBUTING-PTBR.md">Português (Brasil)</a> ·
<a href="docs/contributing/CONTRIBUTING-EO.md">Esperanto</a> ·
<a href="docs/contributing/CONTRIBUTING-KR.md">한국어</a> ·
<a href="docs/contributing/CONTRIBUTING-AR.md">العربي</a> ·
<a href="docs/contributing/CONTRIBUTING-VN.md">Tiếng Việt</a> ·
<a href="docs/contributing/CONTRIBUTING-DA.md">Dansk</a> ·
<a href="docs/contributing/CONTRIBUTING-GR.md">Ελληνικά</a> ·
<a href="docs/contributing/CONTRIBUTING-TR.md">Türkçe</a>
</p>
</div>
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

@ -0,0 +1,7 @@
<p align="center">
<a href="https://openim.io">
<img src="./assets/logo-gif/openim-logo.gif" width="60%" height="30%"/>
</a>
</p>
<div align="center">

@ -4,8 +4,8 @@ go 1.19
require (
firebase.google.com/go v3.13.0+incompatible
github.com/OpenIMSDK/protocol v0.0.48
github.com/OpenIMSDK/tools v0.0.29
github.com/OpenIMSDK/protocol v0.0.55
github.com/OpenIMSDK/tools v0.0.33
github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/dtm-labs/rockscache v0.1.1
github.com/gin-gonic/gin v1.9.1
@ -31,7 +31,7 @@ require (
gopkg.in/yaml.v3 v3.0.1
)
require github.com/google/uuid v1.3.1
require github.com/google/uuid v1.5.0
require (
github.com/IBM/sarama v1.41.3
@ -41,7 +41,6 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stathat/consistent v1.0.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.45
go.uber.org/automaxprocs v1.5.3
golang.org/x/sync v0.4.0
gopkg.in/src-d/go-git.v4 v4.13.1
gotest.tools v2.2.0+incompatible
@ -94,8 +93,8 @@ require (
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
github.com/klauspost/compress v1.16.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/klauspost/compress v1.17.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lithammer/shortuuid v3.0.0+incompatible // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
@ -129,7 +128,7 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.13.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
@ -141,7 +140,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect
gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gorm.io/gorm v1.23.8 // indirect
gorm.io/gorm v1.25.4 // indirect
stathat.com/c/consistent v1.0.0 // indirect
)
@ -156,5 +155,3 @@ require (
golang.org/x/crypto v0.17.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
replace github.com/OpenIMSDK/protocol v0.0.47 => github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94

@ -18,10 +18,10 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c=
github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ=
github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw=
github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/OpenIMSDK/tools v0.0.29 h1:NS4PEwYl9sX3SWsMjDOLVxMo3LcTWREMr+2cjzWjcqc=
github.com/OpenIMSDK/tools v0.0.29/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
github.com/OpenIMSDK/protocol v0.0.55 h1:eBjg8DyuhxGmuCUjpoZjg6MJJJXU/xJ3xJwFhrn34yA=
github.com/OpenIMSDK/protocol v0.0.55/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
github.com/OpenIMSDK/tools v0.0.33 h1:rvFCxXaXxLv1MJFC4qcoWRGwKBnV+hR68UN2N0/zZhE=
github.com/OpenIMSDK/tools v0.0.33/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
@ -152,8 +152,8 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ=
github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas=
@ -194,7 +194,6 @@ github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
@ -205,12 +204,12 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@ -278,7 +277,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@ -353,8 +351,6 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
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/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
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.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
@ -402,8 +398,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY=
golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0=
@ -531,8 +527,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

@ -33,6 +33,10 @@ func (o *AuthApi) UserToken(c *gin.Context) {
a2r.Call(auth.AuthClient.UserToken, o.Client, c)
}
func (o *AuthApi) GetUserToken(c *gin.Context) {
a2r.Call(auth.AuthClient.GetUserToken, o.Client, c)
}
func (o *AuthApi) ParseToken(c *gin.Context) {
a2r.Call(auth.AuthClient.ParseToken, o.Client, c)
}

@ -150,6 +150,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
{
a := NewAuthApi(*authRpc)
authRouterGroup.POST("/user_token", a.UserToken)
authRouterGroup.POST("/get_user_token", ParseToken, a.GetUserToken)
authRouterGroup.POST("/parse_token", a.ParseToken)
authRouterGroup.POST("/force_logout", ParseToken, a.ForceLogout)
}

@ -15,19 +15,25 @@
package msgtransfer
import (
"context"
"errors"
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"net/http"
"sync"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/mw"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/OpenIMSDK/tools/log"
"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"
@ -48,11 +54,13 @@ func StartTransfer(prometheusPort int) error {
if err != nil {
return err
}
mongo, err := unrelation.NewMongo()
if err != nil {
return err
}
if err := mongo.CreateMsgIndex(); err != nil {
if err = mongo.CreateMsgIndex(); err != nil {
return err
}
client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery)
@ -63,56 +71,100 @@ func StartTransfer(prometheusPort int) error {
if err != nil {
return err
}
if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil {
return err
}
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
msgModel := cache.NewMsgCacheModel(rdb)
msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase())
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel)
msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel)
if err != nil {
return err
}
conversationRpcClient := rpcclient.NewConversationRpcClient(client)
groupRpcClient := rpcclient.NewGroupRpcClient(client)
msgTransfer := NewMsgTransfer(msgDatabase, &conversationRpcClient, &groupRpcClient)
msgTransfer, err := NewMsgTransfer(msgDatabase, &conversationRpcClient, &groupRpcClient)
if err != nil {
return err
}
return msgTransfer.Start(prometheusPort)
}
func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) *MsgTransfer {
return &MsgTransfer{
historyCH: NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient),
historyMongoCH: NewOnlineHistoryMongoConsumerHandler(msgDatabase),
func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) {
historyCH, err := NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient)
if err != nil {
return nil, err
}
historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(msgDatabase)
if err != nil {
return nil, err
}
return &MsgTransfer{
historyCH: historyCH,
historyMongoCH: historyMongoCH,
}, nil
}
func (m *MsgTransfer) Start(prometheusPort int) error {
var wg sync.WaitGroup
wg.Add(1)
ctx := context.Background()
fmt.Println("start msg transfer", "prometheusPort:", prometheusPort)
if prometheusPort <= 0 {
return errors.New("prometheusPort not correct")
return errs.Wrap(errors.New("prometheusPort not correct"))
}
if config.Config.ChatPersistenceMysql {
// go m.persistentCH.persistentConsumerGroup.RegisterHandleAndConsumer(m.persistentCH)
} else {
fmt.Println("msg transfer not start mysql consumer")
}
go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.historyCH)
go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.historyMongoCH)
// go m.modifyCH.modifyMsgConsumerGroup.RegisterHandleAndConsumer(m.modifyCH)
/*err := prome.StartPrometheusSrv(prometheusPort)
if err != nil {
return err
}*/
////////////////////////////
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyCH)
}()
wg.Add(1)
go func() {
defer wg.Done()
m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyMongoCH)
}()
if config.Config.Prometheus.Enable {
reg := prometheus.NewRegistry()
reg.MustRegister(
go func() {
proreg := prometheus.NewRegistry()
proreg.MustRegister(
collectors.NewGoCollector(),
)
reg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer")...)
http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg}))
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil))
proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer")...)
http.Handle("/metrics", promhttp.HandlerFor(proreg, promhttp.HandlerOpts{Registry: proreg}))
err := http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil)
if err != nil && err != http.ErrServerClosed {
panic(err)
}
}()
}
////////////////////////////////////////
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
<-sigs
// graceful close kafka client.
go m.historyCH.historyConsumerGroup.Close()
go m.historyMongoCH.historyConsumerGroup.Close()
done := make(chan struct{}, 1)
go func() {
wg.Wait()
close(done)
}()
select {
case <-done:
log.ZInfo(context.Background(), "msgtrasfer exit successfully")
case <-time.After(15 * time.Second):
log.ZError(context.Background(), "msgtransfer force to exit, timeout 15s", nil)
}
return nil
}

@ -19,6 +19,7 @@ import (
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
@ -87,7 +88,7 @@ func NewOnlineHistoryRedisConsumerHandler(
database controller.CommonMsgDatabase,
conversationRpcClient *rpcclient.ConversationRpcClient,
groupRpcClient *rpcclient.GroupRpcClient,
) *OnlineHistoryRedisConsumerHandler {
) (*OnlineHistoryRedisConsumerHandler, error) {
var och OnlineHistoryRedisConsumerHandler
och.msgDatabase = database
och.msgDistributionCh = make(chan Cmd2Value) // no buffer channel
@ -98,14 +99,15 @@ func NewOnlineHistoryRedisConsumerHandler(
}
och.conversationRpcClient = conversationRpcClient
och.groupRpcClient = groupRpcClient
och.historyConsumerGroup = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
var err error
och.historyConsumerGroup, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
KafkaVersion: sarama.V2_0_0_0,
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
}, []string{config.Config.Kafka.LatestMsgToRedis.Topic},
config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis)
// statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d
// second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval)
return &och
return &och, err
}
func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) {
@ -430,16 +432,29 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset",
claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition())
split := 1000
rwLock := new(sync.RWMutex)
messages := make([]*sarama.ConsumerMessage, 0, 1000)
ticker := time.NewTicker(time.Millisecond * 100)
var (
split = 1000
rwLock = new(sync.RWMutex)
messages = make([]*sarama.ConsumerMessage, 0, 1000)
ticker = time.NewTicker(time.Millisecond * 100)
wg = sync.WaitGroup{}
running = new(atomic.Bool)
)
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case <-ticker.C:
// if the buffer is empty and running is false, return loop.
if len(messages) == 0 {
if !running.Load() {
return
}
continue
}
@ -472,7 +487,18 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
}
}()
for msg := range claim.Messages() {
wg.Add(1)
go func() {
defer wg.Done()
for running.Load() {
select {
case msg, ok := <-claim.Messages():
if !ok {
running.Store(false)
return
}
if len(msg.Value) == 0 {
continue
}
@ -482,7 +508,14 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
rwLock.Unlock()
sess.MarkMessage(msg, "")
case <-sess.Context().Done():
running.Store(false)
return
}
}
}()
wg.Wait()
return nil
}

@ -34,16 +34,21 @@ type OnlineHistoryMongoConsumerHandler struct {
msgDatabase controller.CommonMsgDatabase
}
func NewOnlineHistoryMongoConsumerHandler(database controller.CommonMsgDatabase) *OnlineHistoryMongoConsumerHandler {
mc := &OnlineHistoryMongoConsumerHandler{
historyConsumerGroup: kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
func NewOnlineHistoryMongoConsumerHandler(database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) {
historyConsumerGroup, err := kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
KafkaVersion: sarama.V2_0_0_0,
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
}, []string{config.Config.Kafka.MsgToMongo.Topic},
config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo),
config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo)
if err != nil {
return nil, err
}
mc := &OnlineHistoryMongoConsumerHandler{
historyConsumerGroup: historyConsumerGroup,
msgDatabase: database,
}
return mc
return mc, nil
}
func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(

@ -14,19 +14,24 @@
package push
import "context"
type Consumer struct {
pushCh ConsumerHandler
successCount uint64
}
func NewConsumer(pusher *Pusher) *Consumer {
return &Consumer{
pushCh: *NewConsumerHandler(pusher),
func NewConsumer(pusher *Pusher) (*Consumer, error) {
c, err := NewConsumerHandler(pusher)
if err != nil {
return nil, err
}
return &Consumer{
pushCh: *c,
}, nil
}
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)
go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh)
}

@ -35,15 +35,19 @@ type ConsumerHandler struct {
pusher *Pusher
}
func NewConsumerHandler(pusher *Pusher) *ConsumerHandler {
func NewConsumerHandler(pusher *Pusher) (*ConsumerHandler, error) {
var consumerHandler ConsumerHandler
consumerHandler.pusher = pusher
consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
var err error
consumerHandler.pushConsumerGroup, err = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
KafkaVersion: sarama.V2_0_0_0,
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
}, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr,
config.Config.Kafka.ConsumerGroupID.MsgToPush)
return &consumerHandler
if err != nil {
return nil, err
}
return &consumerHandler, nil
}
func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {

@ -66,9 +66,12 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
pusher: pusher,
})
}()
consumer, err := NewConsumer(pusher)
if err != nil {
return err
}
go func() {
defer wg.Done()
consumer := NewConsumer(pusher)
consumer.Start()
}()
wg.Wait()

@ -80,6 +80,28 @@ func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*
return &resp, nil
}
func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenReq) (*pbauth.GetUserTokenResp, error) {
if err := authverify.CheckAdmin(ctx); err != nil {
return nil, err
}
resp := pbauth.GetUserTokenResp{}
if authverify.IsManagerUserID(req.UserID) {
return nil, errs.ErrNoPermission.Wrap("don't get Admin token")
}
if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil {
return nil, err
}
token, err := s.authDatabase.CreateToken(ctx, req.UserID, int(req.PlatformID))
if err != nil {
return nil, err
}
resp.Token = token
resp.ExpireTimeSeconds = config.Config.TokenPolicy.Expire * 24 * 60 * 60
return &resp, nil
}
func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) {
claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret())
if err != nil {

@ -51,6 +51,11 @@ type conversationServer struct {
conversationNotificationSender *notification.ConversationNotificationSender
}
func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) {
//TODO implement me
panic("implement me")
}
func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
rdb, err := cache.NewRedis()
if err != nil {

@ -108,6 +108,11 @@ type groupServer struct {
msgRpcClient rpcclient.MessageRpcClient
}
func (s *groupServer) GetJoinedGroupIDs(ctx context.Context, req *pbgroup.GetJoinedGroupIDsReq) (*pbgroup.GetJoinedGroupIDsResp, error) {
//TODO implement me
panic("implement me")
}
func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) {
defer log.ZDebug(ctx, "NotificationUserInfoUpdate return")
members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID)

@ -80,7 +80,10 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
userRpcClient := rpcclient.NewUserRpcClient(client)
groupRpcClient := rpcclient.NewGroupRpcClient(client)
friendRpcClient := rpcclient.NewFriendRpcClient(client)
msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel)
msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, cacheModel)
if err != nil {
return err
}
s := &msgServer{
Conversation: &conversationClient,
User: &userRpcClient,

@ -61,6 +61,11 @@ type userServer struct {
RegisterCenter registry.SvcDiscoveryRegistry
}
func (s *userServer) GetGroupOnlineUser(ctx context.Context, req *pbuser.GetGroupOnlineUserReq) (*pbuser.GetGroupOnlineUserResp, error) {
//TODO implement me
panic("implement me")
}
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
rdb, err := cache.NewRedis()
if err != nil {

@ -22,17 +22,18 @@ import (
"syscall"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/redis/go-redis/v9"
"github.com/robfig/cron/v3"
"github.com/OpenIMSDK/tools/log"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
)
func StartTask() error {
fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime)
msgTool, err := InitMsgTool()
if err != nil {
return err
@ -47,18 +48,16 @@ func StartTask() error {
// register cron tasks
var crontab = cron.New()
log.ZInfo(context.Background(), "start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime)
fmt.Println("start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime)
_, err = crontab.AddFunc(config.Config.ChatRecordsClearTime, cronWrapFunc(rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq))
if err != nil {
log.ZError(context.Background(), "start allConversationClearMsgAndFixSeq cron failed", err)
panic(err)
return errs.Wrap(err)
}
log.ZInfo(context.Background(), "start msgDestruct cron task", "cron config", config.Config.MsgDestructTime)
fmt.Println("start msgDestruct cron task", "cron config", config.Config.MsgDestructTime)
_, err = crontab.AddFunc(config.Config.MsgDestructTime, cronWrapFunc(rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs))
if err != nil {
log.ZError(context.Background(), "start conversationsDestructMsgs cron failed", err)
panic(err)
return errs.Wrap(err)
}
// start crontab

@ -84,7 +84,10 @@ func InitMsgTool() (*MsgTool, error) {
if err != nil {
return nil, err
}
msgDatabase := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase())
msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase())
if err != nil {
return nil, err
}
userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase())
ctxTx := tx.NewMongo(mongo.GetClient())
userDatabase := controller.NewUserDatabase(

@ -48,8 +48,7 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) {
}
func IsAppManagerUid(ctx context.Context) bool {
return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) ||
utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID)
return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID)
}
func CheckAdmin(ctx context.Context) error {

@ -20,7 +20,6 @@ import (
config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/spf13/cobra"
_ "go.uber.org/automaxprocs"
"github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/tools/log"

@ -49,7 +49,7 @@ func NewRedis() (redis.UniversalClient, error) {
overrideConfigFromEnv()
if len(config.Config.Redis.Address) == 0 {
return nil, errors.New("redis address is empty")
return nil, errs.Wrap(errors.New("redis address is empty"))
}
specialerror.AddReplace(redis.Nil, errs.ErrRecordNotFound)
var rdb redis.UniversalClient
@ -77,9 +77,10 @@ func NewRedis() (redis.UniversalClient, error) {
defer cancel()
err = rdb.Ping(ctx).Err()
if err != nil {
return nil, fmt.Errorf("redis ping %w", err)
uriFormat := "address:%s, username:%s, password:%s, clusterMode:%t, enablePipeline:%t"
errMsg := fmt.Sprintf(uriFormat, config.Config.Redis.Address, config.Config.Redis.Username, config.Config.Redis.Password, config.Config.Redis.ClusterMode, config.Config.Redis.EnablePipeline)
return nil, errs.Wrap(err, errMsg)
}
redisClient = rdb
return rdb, err
}

@ -126,21 +126,32 @@ type CommonMsgDatabase interface {
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)
}
func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel) CommonMsgDatabase {
func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel) (CommonMsgDatabase, error) {
producerToRedis, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic)
if err != nil {
return nil, err
}
producerToMongo, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic)
if err != nil {
return nil, err
}
producerToPush, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic)
if err != nil {
return nil, err
}
return &commonMsgDatabase{
msgDocDatabase: msgDocModel,
cache: cacheModel,
producer: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic),
producerToMongo: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic),
producerToPush: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic),
}
producer: producerToRedis,
producerToMongo: producerToMongo,
producerToPush: producerToPush,
}, nil
}
func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database) CommonMsgDatabase {
func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database) (CommonMsgDatabase, error) {
cacheModel := cache.NewMsgCacheModel(rdb)
msgDocModel := unrelation.NewMsgMongoDriver(database)
CommonMsgDatabase := NewCommonMsgDatabase(msgDocModel, cacheModel)
return CommonMsgDatabase
return NewCommonMsgDatabase(msgDocModel, cacheModel)
}
type commonMsgDatabase struct {

@ -18,6 +18,8 @@ import (
"context"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/tools/mgoutil"
"github.com/OpenIMSDK/tools/pagination"
@ -38,7 +40,7 @@ func NewConversationMongo(db *mongo.Database) (*ConversationMgo, error) {
Options: options.Index().SetUnique(true),
})
if err != nil {
return nil, err
return nil, errs.Wrap(err)
}
return &ConversationMgo{coll: coll}, nil
}

@ -18,6 +18,8 @@ import (
"context"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/mgoutil"
"github.com/OpenIMSDK/tools/pagination"
"go.mongodb.org/mongo-driver/bson"
@ -36,7 +38,7 @@ func NewGroupMongo(db *mongo.Database) (relation.GroupModelInterface, error) {
Options: options.Index().SetUnique(true),
})
if err != nil {
return nil, err
return nil, errs.Wrap(err)
}
return &GroupMgo{coll: coll}, nil
}

@ -17,6 +17,8 @@ package mgo
import (
"context"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/tools/mgoutil"
"github.com/OpenIMSDK/tools/pagination"
@ -37,7 +39,7 @@ func NewGroupMember(db *mongo.Database) (relation.GroupMemberModelInterface, err
Options: options.Index().SetUnique(true),
})
if err != nil {
return nil, err
return nil, errs.Wrap(err)
}
return &GroupMemberMgo{coll: coll}, nil
}

@ -17,6 +17,8 @@ package mgo
import (
"context"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/mgoutil"
"github.com/OpenIMSDK/tools/pagination"
"go.mongodb.org/mongo-driver/bson"
@ -36,7 +38,7 @@ func NewGroupRequestMgo(db *mongo.Database) (relation.GroupRequestModelInterface
Options: options.Index().SetUnique(true),
})
if err != nil {
return nil, err
return nil, errs.Wrap(err)
}
return &GroupRequestMgo{coll: coll}, nil
}

@ -40,7 +40,7 @@ func NewUserMongo(db *mongo.Database) (relation.UserModelInterface, error) {
Options: options.Index().SetUnique(true),
})
if err != nil {
return nil, err
return nil, errs.Wrap(err)
}
return &UserMgo{coll: coll}, nil
}

@ -27,7 +27,6 @@ import (
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/mw/specialerror"
"github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
@ -56,16 +55,17 @@ func NewMongo() (*Mongo, error) {
defer cancel()
mongoClient, err = mongo.Connect(ctx, options.Client().ApplyURI(uri))
if err == nil {
if err = mongoClient.Ping(ctx, nil); err != nil {
return nil, errs.Wrap(err, uri)
}
return &Mongo{db: mongoClient}, nil
}
if shouldRetry(err) {
fmt.Printf("Failed to connect to MongoDB, retrying: %s\n", err)
time.Sleep(time.Second) // exponential backoff could be implemented here
continue
}
return nil, err
}
return nil, err
return nil, errs.Wrap(err, uri)
}
func buildMongoURI() string {
@ -150,7 +150,7 @@ func (m *Mongo) createMongoIndex(collection string, isUnique bool, keys ...strin
_, err := indexView.CreateOne(context.Background(), index, opts)
if err != nil {
return utils.Wrap(err, "CreateIndex")
return errs.Wrap(err, "CreateIndex")
}
return nil
}

@ -15,10 +15,13 @@
package zookeeper
import (
"fmt"
"os"
"strings"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/discoveryregistry"
openkeeper "github.com/OpenIMSDK/tools/discoveryregistry/zookeeper"
"github.com/OpenIMSDK/tools/log"
@ -33,7 +36,7 @@ func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, er
username := getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username)
password := getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password)
return openkeeper.NewClient(
zk, err := openkeeper.NewClient(
zkAddr,
schema,
openkeeper.WithFreq(time.Hour),
@ -42,6 +45,16 @@ func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, er
openkeeper.WithTimeout(10),
openkeeper.WithLogger(log.NewZkLogger()),
)
if err != nil {
uriFormat := "address:%s, username:%s, password:%s, schema:%s."
errInfo := fmt.Sprintf(uriFormat,
config.Config.Zookeeper.ZkAddr,
config.Config.Zookeeper.Username,
config.Config.Zookeeper.Password,
config.Config.Zookeeper.Schema)
return nil, errs.Wrap(err, errInfo)
}
return zk, nil
}
// getEnv returns the value of an environment variable if it exists, otherwise it returns the fallback value.

@ -16,15 +16,18 @@ package kafka
import (
"context"
"errors"
"github.com/IBM/sarama"
"strings"
"github.com/OpenIMSDK/tools/errs"
"github.com/OpenIMSDK/tools/log"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/IBM/sarama"
)
type MConsumerGroup struct {
ctx context.Context
cancel context.CancelFunc
sarama.ConsumerGroup
groupID string
topics []string
@ -36,7 +39,7 @@ type MConsumerGroupConfig struct {
IsReturnErr bool
}
func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string) *MConsumerGroup {
func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string) (*MConsumerGroup, error) {
consumerGroupConfig := sarama.NewConfig()
consumerGroupConfig.Version = consumerConfig.KafkaVersion
consumerGroupConfig.Consumer.Offsets.Initial = consumerConfig.OffsetsInitial
@ -49,26 +52,43 @@ func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []str
SetupTLSConfig(consumerGroupConfig)
consumerGroup, err := sarama.NewConsumerGroup(addrs, groupID, consumerGroupConfig)
if err != nil {
panic(err.Error())
return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, config.Config.Kafka.Username, config.Config.Kafka.Password)
}
ctx, cancel := context.WithCancel(context.Background())
return &MConsumerGroup{
ctx, cancel,
consumerGroup,
groupID,
topics,
}
}, nil
}
func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) context.Context {
return GetContextWithMQHeader(cMsg.Headers)
}
func (mc *MConsumerGroup) RegisterHandleAndConsumer(handler sarama.ConsumerGroupHandler) {
func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) {
log.ZDebug(context.Background(), "register consumer group", "groupID", mc.groupID)
ctx := context.Background()
for {
err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler)
err := mc.ConsumerGroup.Consume(mc.ctx, mc.topics, handler)
if errors.Is(err, sarama.ErrClosedConsumerGroup) {
return
}
if mc.ctx.Err() != nil {
return
}
if err != nil {
panic(err.Error())
log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID)
}
if ctx.Err() != nil {
return
}
}
}
func (mc *MConsumerGroup) Close() {
mc.cancel()
mc.ConsumerGroup.Close()
}

@ -21,6 +21,8 @@ import (
"strings"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/IBM/sarama"
"github.com/OpenIMSDK/protocol/constant"
"github.com/OpenIMSDK/tools/log"
@ -44,7 +46,7 @@ type Producer struct {
}
// NewKafkaProducer initializes a new Kafka producer.
func NewKafkaProducer(addr []string, topic string) *Producer {
func NewKafkaProducer(addr []string, topic string) (*Producer, error) {
p := Producer{
addr: addr,
topic: topic,
@ -87,17 +89,17 @@ func NewKafkaProducer(addr []string, topic string) *Producer {
for i := 0; i <= maxRetry; i++ {
p.producer, err = sarama.NewSyncProducer(p.addr, p.config)
if err == nil {
return &p
return &p, nil
}
time.Sleep(1 * time.Second) // Wait before retrying
}
// Panic if unable to create producer after retries
if err != nil {
panic("Failed to create Kafka producer: " + err.Error())
return nil, errs.Wrap(errors.New("failed to create Kafka producer: " + err.Error()))
}
return &p
return &p, nil
}
// configureProducerAck configures the producer's acknowledgement level.

@ -17,7 +17,6 @@ package startrpc
import (
"errors"
"fmt"
"log"
"net"
"net/http"
"os"
@ -27,6 +26,8 @@ import (
"syscall"
"time"
"github.com/OpenIMSDK/tools/errs"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"golang.org/x/sync/errgroup"
@ -43,7 +44,6 @@ import (
"github.com/OpenIMSDK/tools/discoveryregistry"
"github.com/OpenIMSDK/tools/mw"
"github.com/OpenIMSDK/tools/network"
"github.com/OpenIMSDK/tools/utils"
)
// Start rpc server.
@ -61,20 +61,20 @@ func Start(
net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)),
)
if err != nil {
return err
return errs.Wrap(err, network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort))
}
defer listener.Close()
client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery)
if err != nil {
return utils.Wrap1(err)
return errs.Wrap(err)
}
defer client.Close()
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
registerIP, err := network.GetRpcRegisterIP(config.Config.Rpc.RegisterIP)
if err != nil {
return err
return errs.Wrap(err)
}
var reg *prometheus.Registry
@ -96,7 +96,7 @@ func Start(
err = rpcFn(client, srv)
if err != nil {
return utils.Wrap1(err)
return err
}
err = client.Register(
rpcRegisterName,
@ -105,7 +105,7 @@ func Start(
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
return utils.Wrap1(err)
return errs.Wrap(err)
}
var wg errgroup.Group
@ -116,14 +116,15 @@ func Start(
// Create a HTTP server for prometheus.
httpServer := &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)}
if err := httpServer.ListenAndServe(); err != nil {
log.Fatal("Unable to start a http server.")
fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v PrometheusPort: %d \n\n", err, prometheusPort)
os.Exit(-1)
}
}
return nil
})
wg.Go(func() error {
return utils.Wrap1(srv.Serve(listener))
return errs.Wrap(srv.Serve(listener))
})
sigs := make(chan os.Signal, 1)
@ -146,7 +147,7 @@ func Start(
return gerr
case <-time.After(15 * time.Second):
return utils.Wrap1(errors.New("timeout exit"))
return errs.Wrap(errors.New("timeout exit"))
}
}

@ -20,7 +20,6 @@ import (
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"google.golang.org/grpc"
"github.com/OpenIMSDK/protocol/third"

@ -49,6 +49,14 @@ print_services_and_ports() {
echo "+-------------------------+----------+"
}
handle_error() {
echo "An error occurred. Printing ${STDERR_LOG_FILE} contents:"
cat "${STDERR_LOG_FILE}"
exit 1
}
trap handle_error ERR
# Assuming OPENIM_SERVER_NAME_TARGETS and OPENIM_SERVER_PORT_TARGETS are defined
# Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS
@ -94,3 +102,5 @@ else
fi
set -e
trap - ERR

@ -34,6 +34,9 @@ readonly OPENIM_API_SERVICE_TARGETS=(
readonly OPENIM_API_SERVICE_LISTARIES=("${OPENIM_API_SERVICE_TARGETS[@]##*/}")
function openim::api::start() {
rm -rf "$TMP_LOG_FILE"
echo "++ OPENIM_API_SERVICE_LISTARIES: ${OPENIM_API_SERVICE_LISTARIES[@]}"
echo "++ OPENIM_API_PORT_LISTARIES: ${OPENIM_API_PORT_LISTARIES[@]}"
echo "++ OpenIM API config path: ${OPENIM_API_CONFIG}"
@ -80,8 +83,7 @@ function openim::api::start_service() {
local prometheus_port="$3"
local cmd="${OPENIM_OUTPUT_HOSTBIN}/${binary_name} --port ${service_port} -c ${OPENIM_API_CONFIG}"
nohup ${cmd} >> "${LOG_FILE}" 2>&1 &
nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") &
if [ $? -ne 0 ]; then
openim::log::error_exit "Failed to start ${binary_name} on port ${service_port}."

@ -44,14 +44,19 @@ OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P)
SERVER_NAME="openim-crontask"
function openim::crontask::start() {
rm -rf "$TMP_LOG_FILE"
openim::log::info "Start OpenIM Cron, binary root: ${SERVER_NAME}"
openim::log::status "Start OpenIM Cron, path: ${OPENIM_CRONTASK_BINARY}"
openim::util::stop_services_with_name ${OPENIM_CRONTASK_BINARY}
openim::log::status "start cron_task process, path: ${OPENIM_CRONTASK_BINARY}"
nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2>&1 &
nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") &
openim::util::check_process_names ${SERVER_NAME}
}
###################################### Linux Systemd ######################################

@ -26,6 +26,9 @@ openim::util::set_max_fd 200000
SERVER_NAME="openim-msggateway"
function openim::msggateway::start() {
rm -rf "$TMP_LOG_FILE"
openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}"
openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGGATEWAY_BINARY}"
@ -61,7 +64,7 @@ function openim::msggateway::start() {
PROMETHEUS_PORT_OPTION="--prometheus_port ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}"
fi
nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2>&1 &
nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") &
done
openim::util::check_process_names ${SERVER_NAME}

@ -28,6 +28,9 @@ openim::util::set_max_fd 200000
SERVER_NAME="openim-msgtransfer"
function openim::msgtransfer::start() {
rm -rf "$TMP_LOG_FILE"
openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}"
openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGTRANSFER_BINARY}"
@ -56,7 +59,7 @@ function openim::msgtransfer::start() {
if [[ -n "${OPENIM_PROMETHEUS_PORTS[$i]}" ]]; then
PROMETHEUS_PORT_OPTION="--prometheus_port ${OPENIM_PROMETHEUS_PORTS[$i]}"
fi
nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i}>> ${LOG_FILE} 2>&1 &
nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") &
done
openim::util::check_process_names "${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME}"

@ -50,6 +50,9 @@ OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P)
SERVER_NAME="openim-push"
function openim::push::start() {
rm -rf "$TMP_LOG_FILE"
openim::log::status "Start OpenIM Push, binary root: ${SERVER_NAME}"
openim::log::info "Start OpenIM Push, path: ${OPENIM_PUSH_BINARY}"
@ -70,7 +73,7 @@ function openim::push::start() {
for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do
openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}"
nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2>&1 &
nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") &
done
openim::util::check_process_names ${SERVER_NAME}

@ -102,6 +102,8 @@ readonly OPENIM_RPC_PROM_PORT_TARGETS
readonly OPENIM_RPC_PROM_PORT_LISTARIES=("${OPENIM_RPC_PROM_PORT_TARGETS[@]##*/}")
function openim::rpc::start() {
rm -rf "$TMP_LOG_FILE"
echo "OPENIM_RPC_SERVICE_LISTARIES: ${OPENIM_RPC_SERVICE_LISTARIES[@]}"
echo "OPENIM_RPC_PROM_PORT_LISTARIES: ${OPENIM_RPC_PROM_PORT_LISTARIES[@]}"
echo "OPENIM_RPC_PORT_LISTARIES: ${OPENIM_RPC_PORT_LISTARIES[@]}"
@ -123,6 +125,7 @@ function openim::rpc::start() {
for ((i = 0; i < ${#OPENIM_RPC_SERVICE_LISTARIES[*]}; i++)); do
# openim::util::stop_services_with_name ${OPENIM_RPC_SERVICE_LISTARIES
openim::util::stop_services_on_ports ${OPENIM_RPC_PORT_LISTARIES[$i]}
openim::util::stop_services_on_ports ${OPENIM_RPC_PROM_PORT_LISTARIES[$i]}
openim::log::info "OpenIM ${OPENIM_RPC_SERVICE_LISTARIES[$i]} config path: ${OPENIM_RPC_CONFIG}"
@ -157,7 +160,7 @@ function openim::rpc::start_service() {
printf "Specifying prometheus port: %s\n" "${prometheus_port}"
cmd="${cmd} --prometheus_port ${prometheus_port}"
fi
nohup ${cmd} >> "${LOG_FILE}" 2>&1 &
nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") &
}
###################################### Linux Systemd ######################################

@ -26,10 +26,14 @@ fi
# Set the log file path
LOG_FILE="${OPENIM_OUTPUT}/logs/openim_$(date '+%Y%m%d').log"
STDERR_LOG_FILE="${OPENIM_OUTPUT}/logs/openim_error_$(date '+%Y%m%d').log"
TMP_LOG_FILE="${OPENIM_OUTPUT}/logs/openim_tmp_$(date '+%Y%m%d').log"
if [[ ! -d "${OPENIM_OUTPUT}/logs" ]]; then
mkdir -p "${OPENIM_OUTPUT}/logs"
touch "$LOG_FILE"
touch "$STDERR_LOG_FILE"
touch "$TMP_LOG_FILE"
fi
# Define the logging function
@ -129,14 +133,22 @@ openim::log::error_exit() {
# Log an error but keep going. Don't dump the stack or exit.
openim::log::error() {
# Define red color
red='\033[0;31m'
# No color (reset)
nc='\033[0m' # No Color
timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]")
echo_log "!!! ${timestamp} ${1-}" >&2
# Apply red color for error message
echo_log "${red}!!! ${timestamp} ${1-}${nc}" >&2
shift
for message; do
echo_log " ${message}" >&2
# Apply red color for subsequent lines of the error message
echo_log "${red} ${message}${nc}" >&2
done
}
# Print an usage message to stderr. The arguments are printed directly.
openim::log::usage() {
echo_log >&2

@ -360,7 +360,9 @@ openim::util::check_ports() {
# If any of the processes is not running, return a status of 1.
if [[ ${#not_started[@]} -ne 0 ]]; then
echo "++++ OpenIM Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}'
return 1
else
openim::log::success "All specified processes are running."
@ -444,9 +446,12 @@ openim::util::check_process_names() {
# Return status
if [[ ${#not_started[@]} -ne 0 ]]; then
echo "++++ OpenIM Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}'
return 1
else
echo ""
openim::log::success "All processes are running."
return 0
fi
@ -481,7 +486,7 @@ openim::util::stop_services_on_ports() {
local pid=$(echo $line | awk '{print $2}')
# Try to stop the service by killing its process.
if kill -TERM $pid; then
if kill -10 $pid; then
stopped+=($port)
else
not_stopped+=($port)
@ -558,7 +563,7 @@ openim::util::stop_services_with_name() {
# If there's a Process ID, it means the service with the name is running.
if [[ -n $pid ]]; then
# Try to stop the service by killing its process.
if kill -TERM $pid 2>/dev/null; then
if kill -10 $pid 2>/dev/null; then
stopped_this_time=true
fi
fi
@ -1536,12 +1541,8 @@ openim::util::check_ports() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
if command -v ss > /dev/null 2>&1; then
info=$(ss -ltnp | grep ":$port" || true)
echo "!!!!!!!!!!! port=$port"
echo "!!!!!!!!!!! info=$info"
else
info=$(netstat -ltnp | grep ":$port" || true)
echo "!!!!!!!!!!! port=$port"
echo "!!!!!!!!!!! info=$info"
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
# For macOS, use lsof
@ -1594,7 +1595,10 @@ openim::util::check_ports() {
# If any of the processes is not running, return a status of 1.
if [[ ${#not_started[@]} -ne 0 ]]; then
echo "++++ OpenIM Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
echo ""
cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}'
return 1
else
openim::log::success "All specified processes are running."
@ -1678,9 +1682,12 @@ openim::util::check_process_names() {
# Return status
if [[ ${#not_started[@]} -ne 0 ]]; then
echo "++++ OpenIM Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}"
openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}'
return 1
else
echo ""
openim::log::success "All processes are running."
return 0
fi
@ -1715,7 +1722,7 @@ openim::util::stop_services_on_ports() {
local pid=$(echo $line | awk '{print $2}')
# Try to stop the service by killing its process.
if kill -TERM $pid; then
if kill -10 $pid; then
stopped+=($port)
else
not_stopped+=($port)
@ -1792,7 +1799,7 @@ openim::util::stop_services_with_name() {
# If there's a Process ID, it means the service with the name is running.
if [[ -n $pid ]]; then
# Try to stop the service by killing its process.
if kill -TERM $pid 2>/dev/null; then
if kill -10 $pid 2>/dev/null; then
stopped_this_time=true
fi
fi

@ -82,4 +82,4 @@ execute_scripts
openim::log::info "\n## Post Starting OpenIM services"
${TOOLS_START_SCRIPTS_PATH} openim::tools::post-start
openim::log::success "✨ All OpenIM services have been successfully started!"
openim::color::echo $COLOR_BLUE "✨ All OpenIM services have been successfully started!"

@ -15,50 +15,36 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
"net"
"net/url"
"os"
"strings"
"time"
"github.com/IBM/sarama"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
"github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper"
"github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
"github.com/OpenIMSDK/tools/component"
"github.com/OpenIMSDK/tools/errs"
"github.com/go-zookeeper/zk"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/redis/go-redis/v9"
"gopkg.in/yaml.v3"
)
const (
// defaultCfgPath is the default path of the configuration file.
defaultCfgPath = "../../../../../config/config.yaml"
minioHealthCheckDuration = 1
maxRetry = 300
componentStartErrCode = 6000
configErrCode = 6001
mongoConnTimeout = 30 * time.Second
)
const (
colorRed = 31
colorGreen = 32
colorYellow = 33
)
var (
cfgPath = flag.String("c", defaultCfgPath, "Path to the configuration file")
ErrComponentStart = errs.NewCodeError(componentStartErrCode, "ComponentStartErr")
ErrConfig = errs.NewCodeError(configErrCode, "Config file is incorrect")
)
func initCfg() error {
@ -72,7 +58,8 @@ func initCfg() error {
type checkFunc struct {
name string
function func() (string, error)
function func() error
flag bool
}
func main() {
@ -84,11 +71,13 @@ func main() {
return
}
configGetEnv()
checks := []checkFunc{
//{name: "Mysql", function: checkMysql},
{name: "Mongo", function: checkMongo},
{name: "Minio", function: checkMinio},
{name: "Redis", function: checkRedis},
{name: "Minio", function: checkMinio},
{name: "Zookeeper", function: checkZookeeper},
{name: "Kafka", function: checkKafka},
}
@ -99,259 +88,85 @@ func main() {
}
fmt.Printf("Checking components Round %v...\n", i+1)
var err error
allSuccess := true
for _, check := range checks {
str, err := check.function()
for index, check := range checks {
if !check.flag {
err = check.function()
if err != nil {
errorPrint(fmt.Sprintf("Starting %s failed, %v", check.name, err))
component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, err))
allSuccess = false
break
} else {
successPrint(fmt.Sprintf("%s connected successfully, %s", check.name, str))
checks[index].flag = true
component.SuccessPrint(fmt.Sprintf("%s connected successfully", check.name))
}
}
}
if allSuccess {
successPrint("All components started successfully!")
component.SuccessPrint("All components started successfully!")
return
}
}
os.Exit(1)
}
func exactIP(urll string) string {
u, _ := url.Parse(urll)
host, _, err := net.SplitHostPort(u.Host)
if err != nil {
host = u.Host
}
if strings.HasSuffix(host, ":") {
host = host[0 : len(host)-1]
}
return host
}
// Helper function to get environment variable or default value
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
return fallback
}
// checkMongo checks the MongoDB connection without retries
func checkMongo() (string, error) {
uri := getEnv("MONGO_URI", buildMongoURI())
ctx, cancel := context.WithTimeout(context.Background(), mongoConnTimeout)
defer cancel()
str := "ths addr is:" + strings.Join(config.Config.Mongo.Address, ",")
client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri))
if err != nil {
return "", errs.Wrap(errStr(err, str))
}
defer client.Disconnect(context.Background())
ctx, cancel = context.WithTimeout(context.Background(), mongoConnTimeout)
defer cancel()
if err = client.Ping(ctx, nil); err != nil {
return "", errs.Wrap(errStr(err, str))
}
return str, nil
func checkMongo() error {
_, err := unrelation.NewMongo()
return err
}
// buildMongoURI constructs the MongoDB URI using configuration settings
func buildMongoURI() string {
// Fallback to config if environment variables are not set
username := config.Config.Mongo.Username
password := config.Config.Mongo.Password
database := config.Config.Mongo.Database
maxPoolSize := config.Config.Mongo.MaxPoolSize
mongodbHosts := strings.Join(config.Config.Mongo.Address, ",")
if username != "" && password != "" {
return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d",
username, password, mongodbHosts, database, maxPoolSize)
}
return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d",
mongodbHosts, database, maxPoolSize)
// checkRedis checks the Redis connection
func checkRedis() error {
_, err := cache.NewRedis()
return err
}
// checkMinio checks the MinIO connection
func checkMinio() (string, error) {
func checkMinio() error {
// Check if MinIO is enabled
if config.Config.Object.Enable != "minio" {
return "", nil
return errs.Wrap(errors.New("minio.Enable is empty"))
}
// Prioritize environment variables
endpoint := getEnv("MINIO_ENDPOINT", config.Config.Object.Minio.Endpoint)
accessKeyID := getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID)
secretAccessKey := getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey)
useSSL := getEnv("MINIO_USE_SSL", "false") // Assuming SSL is not used by default
if endpoint == "" || accessKeyID == "" || secretAccessKey == "" {
return "", ErrConfig.Wrap("MinIO configuration missing")
minio := &component.Minio{
ApiURL: config.Config.Object.ApiURL,
Endpoint: config.Config.Object.Minio.Endpoint,
AccessKeyID: config.Config.Object.Minio.AccessKeyID,
SecretAccessKey: config.Config.Object.Minio.SecretAccessKey,
SignEndpoint: config.Config.Object.Minio.SignEndpoint,
UseSSL: getEnv("MINIO_USE_SSL", "false"),
}
// Parse endpoint URL to determine if SSL is enabled
u, err := url.Parse(endpoint)
if err != nil {
str := "the endpoint is:" + endpoint
return "", errs.Wrap(errStr(err, str))
}
secure := u.Scheme == "https" || useSSL == "true"
// Initialize MinIO client
minioClient, err := minio.New(u.Host, &minio.Options{
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
Secure: secure,
})
str := "ths addr is:" + u.Host
if err != nil {
strs := fmt.Sprintf("%v;host:%s,accessKeyID:%s,secretAccessKey:%s,Secure:%v", err, u.Host, accessKeyID, secretAccessKey, secure)
return "", errs.Wrap(err, strs)
}
// Perform health check
cancel, err := minioClient.HealthCheck(time.Duration(minioHealthCheckDuration) * time.Second)
if err != nil {
return "", errs.Wrap(errStr(err, str))
}
defer cancel()
if minioClient.IsOffline() {
str := fmt.Sprintf("Minio server is offline;%s", str)
return "", ErrComponentStart.Wrap(str)
}
// Check for localhost in API URL and Minio SignEndpoint
if exactIP(config.Config.Object.ApiURL) == "127.0.0.1" || exactIP(config.Config.Object.Minio.SignEndpoint) == "127.0.0.1" {
return "", ErrConfig.Wrap("apiURL or Minio SignEndpoint endpoint contain 127.0.0.1")
}
return str, nil
}
// checkRedis checks the Redis connection
func checkRedis() (string, error) {
// Prioritize environment variables
address := getEnv("REDIS_ADDRESS", strings.Join(config.Config.Redis.Address, ","))
username := getEnv("REDIS_USERNAME", config.Config.Redis.Username)
password := getEnv("REDIS_PASSWORD", config.Config.Redis.Password)
// Split address to handle multiple addresses for cluster setup
redisAddresses := strings.Split(address, ",")
var redisClient redis.UniversalClient
if len(redisAddresses) > 1 {
// Use cluster client for multiple addresses
redisClient = redis.NewClusterClient(&redis.ClusterOptions{
Addrs: redisAddresses,
Username: username,
Password: password,
})
} else {
// Use regular client for single address
redisClient = redis.NewClient(&redis.Options{
Addr: redisAddresses[0],
Username: username,
Password: password,
})
}
defer redisClient.Close()
// Ping Redis to check connectivity
_, err := redisClient.Ping(context.Background()).Result()
str := "the addr is:" + strings.Join(redisAddresses, ",")
if err != nil {
return "", errs.Wrap(errStr(err, str))
}
return str, nil
err := component.CheckMinio(minio)
return err
}
// checkZookeeper checks the Zookeeper connection
func checkZookeeper() (string, error) {
// Prioritize environment variables
schema := getEnv("ZOOKEEPER_SCHEMA", "digest")
address := getEnv("ZOOKEEPER_ADDRESS", strings.Join(config.Config.Zookeeper.ZkAddr, ","))
username := getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username)
password := getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password)
// Split addresses to handle multiple Zookeeper nodes
zookeeperAddresses := strings.Split(address, ",")
// Connect to Zookeeper
str := "the addr is:" + address
c, eventChan, err := zk.Connect(zookeeperAddresses, time.Second) // Adjust the timeout as necessary
if err != nil {
return "", errs.Wrap(errStr(err, str))
}
timeout := time.After(5 * time.Second)
for {
select {
case event := <-eventChan:
if event.State == zk.StateConnected {
fmt.Println("Connected to Zookeeper")
goto Connected
}
case <-timeout:
return "", errs.Wrap(errors.New("timeout waiting for Zookeeper connection"), "Zookeeper Addr: "+strings.Join(config.Config.Zookeeper.ZkAddr, " "))
}
}
Connected:
defer c.Close()
// Set authentication if username and password are provided
if username != "" && password != "" {
if err := c.AddAuth(schema, []byte(username+":"+password)); err != nil {
return "", errs.Wrap(errStr(err, str))
}
}
return str, nil
func checkZookeeper() error {
_, err := zookeeper.NewZookeeperDiscoveryRegister()
return err
}
// checkKafka checks the Kafka connection
func checkKafka() (string, error) {
func checkKafka() error {
// Prioritize environment variables
username := getEnv("KAFKA_USERNAME", config.Config.Kafka.Username)
password := getEnv("KAFKA_PASSWORD", config.Config.Kafka.Password)
address := getEnv("KAFKA_ADDRESS", strings.Join(config.Config.Kafka.Addr, ","))
// Split addresses to handle multiple Kafka brokers
kafkaAddresses := strings.Split(address, ",")
// Configure Kafka client
cfg := sarama.NewConfig()
if username != "" && password != "" {
cfg.Net.SASL.Enable = true
cfg.Net.SASL.User = username
cfg.Net.SASL.Password = password
}
// Additional Kafka setup (e.g., TLS configuration) can be added here
// kafka.SetupTLSConfig(cfg)
// Create Kafka client
str := "the addr is:" + address
kafkaClient, err := sarama.NewClient(kafkaAddresses, cfg)
kafkaStu := &component.Kafka{
Username: config.Config.Kafka.Username,
Password: config.Config.Kafka.Password,
Addr: config.Config.Kafka.Addr,
}
kafkaClient, err := component.CheckKafka(kafkaStu)
if err != nil {
return "", errs.Wrap(errStr(err, str))
return err
}
defer kafkaClient.Close()
// Verify if necessary topics exist
topics, err := kafkaClient.Topics()
if err != nil {
return "", errs.Wrap(err)
return errs.Wrap(err)
}
requiredTopics := []string{
@ -362,11 +177,38 @@ func checkKafka() (string, error) {
for _, requiredTopic := range requiredTopics {
if !isTopicPresent(requiredTopic, topics) {
return "", ErrComponentStart.Wrap(fmt.Sprintf("Kafka doesn't contain topic: %v", requiredTopic))
return errs.Wrap(err, fmt.Sprintf("Kafka doesn't contain topic: %v", requiredTopic))
}
}
return str, nil
_, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
KafkaVersion: sarama.V2_0_0_0,
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
}, []string{config.Config.Kafka.LatestMsgToRedis.Topic},
config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis)
if err != nil {
return err
}
_, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
KafkaVersion: sarama.V2_0_0_0,
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
}, []string{config.Config.Kafka.MsgToPush.Topic},
config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo)
if err != nil {
return err
}
kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
KafkaVersion: sarama.V2_0_0_0,
OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
}, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr,
config.Config.Kafka.ConsumerGroupID.MsgToPush)
if err != nil {
return err
}
return nil
}
// isTopicPresent checks if a topic is present in the list of topics
@ -379,22 +221,34 @@ func isTopicPresent(topic string, topics []string) bool {
return false
}
func colorPrint(colorCode int, format string, a ...interface{}) {
fmt.Printf("\x1b[%dm%s\x1b[0m\n", colorCode, fmt.Sprintf(format, a...))
func configGetEnv() {
config.Config.Object.Minio.AccessKeyID = getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID)
config.Config.Object.Minio.SecretAccessKey = getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey)
config.Config.Mongo.Uri = getEnv("MONGO_URI", config.Config.Mongo.Uri)
config.Config.Mongo.Username = getEnv("MONGO_OPENIM_USERNAME", config.Config.Mongo.Username)
config.Config.Mongo.Password = getEnv("MONGO_OPENIM_PASSWORD", config.Config.Mongo.Password)
config.Config.Kafka.Username = getEnv("KAFKA_USERNAME", config.Config.Kafka.Username)
config.Config.Kafka.Password = getEnv("KAFKA_PASSWORD", config.Config.Kafka.Password)
config.Config.Kafka.Addr = strings.Split(getEnv("KAFKA_ADDRESS", strings.Join(config.Config.Kafka.Addr, ",")), ",")
config.Config.Object.Minio.Endpoint = getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Config.Object.Minio.Endpoint)
}
func errorPrint(s string) {
colorPrint(colorRed, "%v", s)
func getMinioAddr(key1, key2, key3, fallback string) string {
// Prioritize environment variables
endpoint := getEnv(key1, fallback)
address, addressExist := os.LookupEnv(key2)
port, portExist := os.LookupEnv(key3)
if portExist && addressExist {
endpoint = "http://" + address + ":" + port
return endpoint
}
func successPrint(s string) {
colorPrint(colorGreen, "%v", s)
return endpoint
}
func warningPrint(s string) {
colorPrint(colorYellow, "Warning: But %v", s)
// Helper function to get environment variable or default value
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
return value
}
func errStr(err error, str string) error {
return fmt.Errorf("%v;%s", err, str)
return fallback
}

@ -1,4 +1,4 @@
// Copyright © 2023 OpenIM. All rights reserved.
// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -22,6 +22,11 @@ import (
)
func main() {
maxprocs.Set()
fmt.Print(runtime.GOMAXPROCS(0))
// Set maxprocs with a custom logger that does nothing to ignore logs.
maxprocs.Set(maxprocs.Logger(func(string, ...interface{}) {
// Intentionally left blank to suppress all log output from automaxprocs.
}))
// Now this will print the GOMAXPROCS value without printing the automaxprocs log message.
fmt.Println(runtime.GOMAXPROCS(0))
}

Loading…
Cancel
Save