mirror of
https://github.com/openimsdk/open-im-server.git
synced 2026-04-28 22:39:18 +08:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 958afd6115 | |||
| 4f1dacabfa | |||
| 0c11880326 | |||
| b0d940945f | |||
| 6f9f7c767c | |||
| dd5f2d0476 | |||
| 84c60cd6b6 | |||
| 7000755e15 | |||
| b3899f559c | |||
| 03906a89cd | |||
| 51e747119c | |||
| 5353e5fa66 | |||
| 57ce0bd29d | |||
| c5d66914fa | |||
| 50a63adf5e | |||
| 71393da390 | |||
| 825622fd99 | |||
| 415d40f8c5 | |||
| bde3d98d0f | |||
| d37796a58f | |||
| 4fcea57df0 | |||
| 1d98a9959c | |||
| 3ecd33aded | |||
| 5615e47d99 | |||
| 04a97ac154 | |||
| 598938c13c | |||
| d97c44ef99 | |||
| e706620f12 | |||
| bd518365ea | |||
| 73effdf644 | |||
| c8971de833 | |||
| b26a979378 | |||
| 3836bf19d6 | |||
| f2f2448fe2 | |||
| 969a9d8782 | |||
| 6f64b0a236 | |||
| 4d04c76cd7 | |||
| 7a448b35d4 | |||
| 594b16374b | |||
| 39275c0110 | |||
| 4ad81c363c | |||
| 10b56142d5 | |||
| 5ee54c9c2a | |||
| 0b75c52a6e | |||
| 375105997f | |||
| 4e9179f309 | |||
| 863e925b30 | |||
| 8cb05d50c9 | |||
| 5d34e3f081 | |||
| d8fd4bfcf1 |
@@ -40,10 +40,13 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Download Docker Compose
|
||||
run: |
|
||||
curl -o docker-compose.yaml https://gist.githubusercontent.com/cubxxw/b1d5cbd2edfa23fee911118aa3e8249e/raw/openim-server.sh
|
||||
shell: bash
|
||||
|
||||
- name: Start Docker Compose
|
||||
run: |
|
||||
sudo docker compose stop
|
||||
sudo sleep 30
|
||||
sudo docker compose up -d
|
||||
sudo sleep 60
|
||||
continue-on-error: true
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ flycheck_*.el
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
# go.work
|
||||
|
||||
### JetBrains ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||
|
||||
+6
-7
@@ -10,14 +10,14 @@ ENV GOPROXY=$GOPROXY
|
||||
# Set up the working directory
|
||||
WORKDIR /openim/openim-server
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
COPY go.mod go.sum go.work go.work.sum ./
|
||||
#RUN go mod download
|
||||
|
||||
# Copy all files to the container
|
||||
ADD . .
|
||||
|
||||
RUN /bin/sh -c "make clean"
|
||||
RUN /bin/sh -c "make build"
|
||||
RUN make clean
|
||||
RUN make build
|
||||
|
||||
FROM ghcr.io/openim-sigs/openim-bash-image:latest
|
||||
|
||||
@@ -27,7 +27,6 @@ WORKDIR ${SERVER_WORKDIR}
|
||||
COPY --from=builder ${OPENIM_SERVER_CMDDIR} /openim/openim-server/scripts
|
||||
COPY --from=builder ${SERVER_WORKDIR}/config /openim/openim-server/config
|
||||
COPY --from=builder ${SERVER_WORKDIR}/_output/bin/platforms /openim/openim-server/_output/bin/platforms
|
||||
COPY --from=builder ${SERVER_WORKDIR}/_output/bin-tools/platforms /openim/openim-server/_output/bin-tools/platforms
|
||||
|
||||
VOLUME ["/openim/openim-server/logs","/openim/openim-server/config","/openim/openim-server/scripts"]
|
||||
|
||||
CMD ["bash","-c","${OPENIM_SERVER_CMDDIR}/docker_start_all.sh"]
|
||||
CMD ["bash","-c","${OPENIM_SERVER_CMDDIR}/docker_start_all.sh"]
|
||||
|
||||
@@ -22,42 +22,109 @@
|
||||
|
||||
<p align="center">
|
||||
<a href="./README.md"><b> English </b></a> •
|
||||
<a href="./README-zh_CN.md"><b> 中文 </b></a>
|
||||
<a href="./README-zh_CN.md"><b> 简体中文 </b></a> •
|
||||
<a href="https://www.openim.online/en"><b> Docs </b></a>
|
||||
</p>
|
||||
|
||||
|
||||
</p>
|
||||
|
||||
## What is Open-IM-Server
|
||||
## ✨ About OpenIM
|
||||
|
||||
Open-IM-Server is an instant messaging server developed using pure Golang, adopting JSON over WebSocket as the communication protocol. In Open-IM-Server, everything is a message, so you can easily extend custom messages without modifying the server code. With a microservice architecture, Open-IM-Server can be deployed using clusters. By deploying Open-IM-Server on a server, developers can quickly integrate instant messaging and real-time networking features into their applications, ensuring the security and privacy of business data.
|
||||
Open-IM-Server, crafted meticulously using pure Golang, stands as a robust instant messaging server. Its unique approach to communication, via JSON over WebSocket, treats every interaction as a message. This simplifies customization and eliminates the need for tinkering with server code. Harnessing the power of microservice architecture, the server can be deployed via clusters, promising impressive performance and scalability.
|
||||
|
||||
Open-IM-Server is not a standalone product and does not include account registration and login services. For your convenience, we have open-sourced the [chat repository](https://github.com/OpenIMSDK/chat) which includes login and registration functionality. By deploying the chat business server alongside Open-IM-Server, a chat product can be set up.
|
||||
Open-IM-Server is more than an instant messaging server; it's a powerful tool for incorporating real-time networking into your applications, positioning itself as your premier choice for integration! 🚀
|
||||
|
||||
## Features
|
||||
Please be aware that Open-IM-Server does not function as a standalone product and does not offer built-in account registration or login services. To ease your implementation process, we've open-sourced the [chat repository](https://github.com/OpenIMSDK/chat), which comprises these features. Deploying this chat business server in conjunction with Open-IM-Server expedites the setup of a comprehensive chat product. 👥
|
||||
|
||||
- Open source
|
||||
- Easy to integrate
|
||||
- Excellent scalability
|
||||
- High performance
|
||||
- Lightweight
|
||||
- Supports multiple protocols
|
||||
Further enhancing your experience, we also provide an SDK client, wherein most complex logics are implemented. The [SDK repository](https://github.com/OpenIMSDK/openim-sdk-core) can be found at [this link](https://github.com/OpenIMSDK/openim-sdk-core). The [chat repository](https://github.com/OpenIMSDK/chat) is our business server while the 'core' represents the high-level encapsulation of the SDK, synergistically working together to deliver superior results. ✨
|
||||
|
||||
## Community
|
||||
- Visit the official website: [OpenIM Developer Documentation](https://www.openim.online/)
|
||||
## :star2: Why OpenIM
|
||||
|
||||
## Quick Start
|
||||
**🔍 Function screenshot display**
|
||||
|
||||
### Deploying with docker-compose
|
||||
<div align="center">
|
||||
|
||||
| 💻🔄📱 Multi Terminal Synchronization 🔄🖥️ | 📅⚡ Efficient Meetings 🚀💼 |
|
||||
| :----------------------------------------------------------: | :---------------------------------------------------------: |
|
||||
|  |  |
|
||||
| 📲🔄 **One-to-one and Group Chats** 👥🗣️ | 🎁💻 **Special Features - Custom Messages** ✉️🎨|
|
||||
|  |  |
|
||||
|
||||
</div>
|
||||
|
||||
1. **Comprehensive Message Type Support :speech_balloon:**
|
||||
|
||||
✅ Supports almost all types of messages, including text, images, emojis, voice, video, geographical location, files, quotes, business cards, system notifications, custom messages and more
|
||||
|
||||
✅ Supports one-on-one and multi-person audio and video calls
|
||||
|
||||
✅ Provides terminal support for multiple platforms such as iOS, Android, Flutter, uni-app, ReactNative, Electron, Web, H5
|
||||
|
||||
2. **Efficient Meetings Anytime, Anywhere :earth_americas:**
|
||||
|
||||
✅ Based on IM (Instant Messaging) with 100% reliable forced signaling capabilities, it paves the way for IM systems, deeply integrated with chat applications
|
||||
|
||||
✅ Supports hundreds of people in a single meeting, with subscription numbers reaching thousands, and server-side audio and video recording
|
||||
|
||||
3. **One-on-one and Group Chats for Various Social Scenarios :busts_in_silhouette:**
|
||||
|
||||
✅ OpenIM has four roles: application administrator, group owner, group administrator, and regular member
|
||||
|
||||
✅ Powerful group features such as muting, group announcements, group validation, unlimited group members, and loading group messages as needed
|
||||
|
||||
4. **Unique Features :star2:**
|
||||
|
||||
✅ Supports read-and-burn private chats, customizable duration
|
||||
|
||||
✅ Message editing function broadens social scenarios, making instant communication more diverse and interesting
|
||||
|
||||
5. **Open Source :open_hands:**
|
||||
|
||||
✅ The code of OpenIM is open source, self-controlled data, aimed at building a globally leading IM open source community, including client SDK and server
|
||||
|
||||
✅ Based on open source Server, many excellent open source projects have been developed, such as [OpenKF](https://github.com/OpenIMSDK/OpenKF) (Open source AI customer service system)
|
||||
|
||||
6. **Easy to Expand :wrench:**
|
||||
|
||||
✅ The OpenIM server is implemented in Golang, introducing an innovative "everything is a message" communication model, simplifying the implementation of custom messages and extended features
|
||||
|
||||
7. **High Performance :racing_car:**
|
||||
|
||||
✅ OpenIM supports a hierarchical governance architecture in the cluster, tested by a large number of users, and abstracts the storage model of online messages, offline messages, and historical messages
|
||||
|
||||
8. **Full Platform Support :tv:**
|
||||
|
||||
✅ Supports native iOS, Android; cross-platform Flutter, uni-app, ReactNative; major web front-end frameworks such as React, Vue; applets; and PC platforms supported by Electron
|
||||
|
||||
9. **The ultimate deployment experience 🤖**
|
||||
|
||||
✅ Supports [cluster deployment](https://github.com/OpenIMSDK/Open-IM-Server/edit/main/deployments/README.md)
|
||||
|
||||
✅ Supports multi-architecture mirroring, our Docker images are hosted not only on GitHub but also on Alibaba Cloud and Docker Hub supporting multiple architectures. Visit [our GitHub packages](https://github.com/orgs/OpenIMSDK/packages?repo_name=Open-IM-Server) and read our [version management document](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/version.md) for more information.
|
||||
|
||||
11. **A large ecosystem of open source communities 🤲**
|
||||
|
||||
✅ We have tens of thousands of users and many solutions to problems.
|
||||
|
||||
✅ We have a large open source community called [OpenIMSDK](https://github.com/OpenIMSDK) that runs the core modules, we have an open source community called [openim-sigs](https://github.com/openim-sigs) to explore more IM-based infrastructure products.
|
||||
|
||||
## :rocket: Quick Start
|
||||
|
||||
<details> <summary>Deploying with Docker Compose</summary>
|
||||
|
||||
1. Clone the project
|
||||
|
||||
```bash
|
||||
# choose what you need
|
||||
BRANCH=release-v3.0
|
||||
BRANCH=release-v3.1
|
||||
git clone -b $BRANCH https://github.com/OpenIMSDK/Open-IM-Server openim && export openim=$(pwd)/openim && cd $openim && make build
|
||||
```
|
||||
|
||||
2. Modify .env
|
||||
> **Note**
|
||||
> Read our release policy: https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/version.md
|
||||
|
||||
2. Modify `.env`
|
||||
|
||||
```bash
|
||||
USER=root #no need to modify
|
||||
@@ -70,9 +137,9 @@ DATA_DIR=./ #designate large disk directory
|
||||
3. Deploy and start
|
||||
|
||||
> **Note**
|
||||
> This command can only be executed once. It will modify the component passwords in docker-compose based on the PASSWORD variable in .env, and modify the component passwords in config/config.yaml. If the password in .env changes, you need to first execute docker-compose down; rm components -rf and then execute this command.
|
||||
> This command can only be executed once. It will modify the component passwords in docker-compose based on the `PASSWORD` variable in `.env`, and modify the component passwords in `config/config.yaml`. If the password in `.env` changes, you need to first execute `docker-compose down`; `rm components -rf` and then execute this command.
|
||||
|
||||
```
|
||||
```bash
|
||||
make install
|
||||
```
|
||||
|
||||
@@ -84,9 +151,9 @@ make check
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
|
||||
### Compile from source
|
||||
<details> <summary>Compile from Source</summary>
|
||||
|
||||
Ur need `Go 1.18` or higher version, and `make`.
|
||||
|
||||
@@ -97,6 +164,7 @@ Version Details: https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conv
|
||||
BRANCH=release-v3.1
|
||||
git clone -b $BRANCH https://github.com/OpenIMSDK/Open-IM-Server openim && export openim=$(pwd)/openim && cd $openim && make build
|
||||
```
|
||||
|
||||
Read about the [OpenIM Version Policy](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/version.md)
|
||||
|
||||
`make help` to help you see the instructions supported by OpenIM.
|
||||
@@ -105,7 +173,9 @@ All services have been successfully built as shown in the figure
|
||||
|
||||

|
||||
|
||||
### Component Configuration Instructions
|
||||
</details>
|
||||
|
||||
<details> <summary>Component Configuration Instructions</summary>
|
||||
|
||||
The config/config.yaml file has detailed configuration instructions for the storage components.
|
||||
|
||||
@@ -113,7 +183,7 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
||||
|
||||
- Used for RPC service discovery and registration, cluster support.
|
||||
|
||||
```
|
||||
```bash
|
||||
zookeeper:
|
||||
schema: openim #Not recommended to modify
|
||||
address: [ 127.0.0.1:2181 ] #address
|
||||
@@ -125,7 +195,7 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
||||
|
||||
- Used for storing users, relationships, and groups, supports master-slave database.
|
||||
|
||||
```
|
||||
```bash
|
||||
mysql:
|
||||
address: [ 127.0.0.1:13306 ] #address
|
||||
username: root #username
|
||||
@@ -142,7 +212,7 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
||||
|
||||
- Used for storing offline messages, supports mongo sharded clusters.
|
||||
|
||||
```
|
||||
```bash
|
||||
mongo:
|
||||
uri: #Use this value directly if not empty
|
||||
address: [ 127.0.0.1:37017 ] #address
|
||||
@@ -156,7 +226,7 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
||||
|
||||
- Used for storing message sequence numbers, latest messages, user tokens, and mysql cache, supports cluster deployment.
|
||||
|
||||
```
|
||||
```bash
|
||||
redis:
|
||||
address: [ 127.0.0.1:16379 ] #address
|
||||
username: #username
|
||||
@@ -167,7 +237,7 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
||||
|
||||
- Used for message queues, for message decoupling, supports cluster deployment.
|
||||
|
||||
```
|
||||
```bash
|
||||
kafka:
|
||||
username: #username
|
||||
password: #password
|
||||
@@ -188,27 +258,31 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
||||
msgToModify: modify
|
||||
```
|
||||
|
||||
### Start and Stop Services
|
||||
</details>
|
||||
|
||||
<details> <summary>Start and Stop Services</summary>
|
||||
|
||||
Start services
|
||||
|
||||
```
|
||||
./start_all.sh;
|
||||
./scripts/start_all.sh;
|
||||
```
|
||||
|
||||
Check services
|
||||
|
||||
```
|
||||
./check_all.sh
|
||||
./scripts/check_all.sh
|
||||
```
|
||||
|
||||
Stop services
|
||||
|
||||
```
|
||||
./stop_all.sh
|
||||
./scripts/stop_all.sh
|
||||
```
|
||||
|
||||
### Open IM Ports
|
||||
</details>
|
||||
|
||||
<details> <summary>Open IM Ports</summary>
|
||||
|
||||
| TCP Port | Description | Operation |
|
||||
| --------- | ------------------------------------------------------------ | ----------------------------------------------------- |
|
||||
@@ -216,42 +290,75 @@ Stop services
|
||||
| TCP:10002 | api port, such as user, friend, group, message interfaces. | Port release or nginx reverse proxy, and firewall off |
|
||||
| TCP:10005 | Required when choosing minio storage (openIM uses minio storage by default) | Port release or nginx reverse proxy, and firewall off |
|
||||
|
||||
### Open Chat Ports
|
||||
</details>
|
||||
|
||||
<details> <summary>Open Chat Ports</summary>
|
||||
|
||||
+ chat warehouse: https://github.com/OpenIMSDK/chat
|
||||
|
||||
| TCP Port | Description | Operation |
|
||||
| --------- | --------------------------------------------------- | ----------------------------------------------------- |
|
||||
| TCP:10008 | Business system, such as registration, login etc | Port release or nginx reverse proxy, and firewall off |
|
||||
| TCP:10009 | Management backend, such as statistics, banning etc | Port release or nginx reverse proxy, and firewall off |
|
||||
|
||||
## Relationship Between APP and OpenIM
|
||||
</details>
|
||||
|
||||
OpenIM is an open source instant messaging component, it is not an independent product. This image shows the relationship between AppServer, AppClient, Open-IM-Server and Open-IM-SDK.
|
||||
## :link: Relationship Between APP and OpenIM
|
||||
|
||||

|
||||
OpenIM isn't just an open-source instant messaging component, it's an integral part of your application ecosystem. Check out this diagram to understand how AppServer, AppClient, Open-IM-Server, and Open-IM-SDK interact.
|
||||
|
||||
## Overall Architecture
|
||||

|
||||
|
||||

|
||||
## :building_construction: Overall Architecture
|
||||
|
||||
## To start developing OpenIM
|
||||
The [community repository](https://github.com/OpenIMSDK/community) hosts all information about building Kubernetes from source, how to contribute code and documentation, who to contact about what, etc.
|
||||
Delve into the heart of Open-IM-Server's functionality with our architecture diagram.
|
||||
|
||||

|
||||
|
||||
## Contributing
|
||||
## :hammer_and_wrench: To start developing OpenIM
|
||||
|
||||
Contributions to this project are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
||||
OpenIM Our goal is to build a top-level open source community. We have a set of standards, in the [Community repository](https://github.com/OpenIMSDK/community).
|
||||
|
||||
## Community Meetings
|
||||
We want anyone to get involved in our community, we offer gifts and rewards, and we welcome you to join us every Thursday night.
|
||||
If you'd like to contribute to this Open-IM-Server repository, please read our [contributor documentation](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/CONTRIBUTING.md).
|
||||
|
||||
We take notes of each [biweekly meeting](https://github.com/OpenIMSDK/Open-IM-Server/issues/381) in [GitHub discussions](https://github.com/OpenIMSDK/Open-IM-Server/discussions/categories/meeting), and our minutes are written in [Google Docs](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing).
|
||||
Before you start, please make sure your changes are in demand. The best for that is to create a [new discussion](https://github.com/OpenIMSDK/Open-IM-Server/discussions/new/choose) OR [Slack Communication](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg), or if you find an issue, [report it](https://github.com/OpenIMSDK/Open-IM-Server/issues/new/choose) first.
|
||||
|
||||
- [Code Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/go_code.md)
|
||||
- [Docker Images Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/images.md)
|
||||
- [Directory Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/directory.md)
|
||||
- [Commit Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/commit.md)
|
||||
- [Versioning Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/version.md)
|
||||
- [Interface Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/api.md)
|
||||
- [Log Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/log.md)
|
||||
- [Error Code Standards](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/error_code.md)
|
||||
|
||||
## Who are using Open-IM-Server
|
||||
The [user case studies](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) page includes the user list of the project. You can leave a [📝comment](https://github.com/OpenIMSDK/Open-IM-Server/issues/379) to let us know your use case.
|
||||
## :busts_in_silhouette: Community
|
||||
|
||||

|
||||
+ 📚 [OpenIM Community](https://github.com/OpenIMSDK/community)
|
||||
+ 💕 [OpenIM Interest Group](https://github.com/Openim-sigs)
|
||||
+ 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg)
|
||||
+ :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg)
|
||||
|
||||
## License
|
||||
## :calendar: Community Meetings
|
||||
|
||||
Open-IM-Server is under the Apache 2.0 license. See the [LICENSE](./LICENSE) file for details
|
||||
We want anyone to get involved in our community and contributing code, we offer gifts and rewards, and we welcome you to join us every Thursday night.
|
||||
|
||||
Our conference is in the [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-1tmoj26uf-_FDy3dowVHBiGvLk9e5Xkg) 🎯, then you can search the Open-IM-Server pipeline to join
|
||||
|
||||
We take notes of each [biweekly meeting](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) in [GitHub discussions](https://github.com/OpenIMSDK/Open-IM-Server/discussions/categories/meeting), Our historical meeting notes, as well as replays of the meetings are available at [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing).
|
||||
|
||||
## :eyes: Who are using OpenIM
|
||||
|
||||
Check out our [user case studies](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) page for a list of the project users. Don't hesitate to leave a [📝comment](https://github.com/OpenIMSDK/Open-IM-Server/issues/379) and share your use case.
|
||||
|
||||
## :page_facing_up: License
|
||||
|
||||
OpenIM is licensed under the Apache 2.0 license. See [LICENSE](https://github.com/OpenIMSDK/Open-IM-Server/tree/main/LICENSE) for the full license text.
|
||||
|
||||
The OpenIM logo, including its variations and animated versions, displayed in this repository [OpenIM](https://github.com/OpenIMSDK/Open-IM-Server) under the [assets/logo](./assets/logo) and [assets/logo-gif](assets/logo-gif) directories, are protected by copyright laws.
|
||||
|
||||
## 🔮 Thanks to our contributors!
|
||||
|
||||
<a href="https://github.com/OpenIMSDK/Open-IM-Server/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=OpenIMSDK/Open-IM-Server" />
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
## :star2: Why OpenIM
|
||||
|
||||
**🔍 Function screenshot display**
|
||||
|
||||
<div align="center">
|
||||
|
||||
|
||||
| multiple message | Efficient meetings |
|
||||
| :---------------------------------------: | :---------------------------------------------: |
|
||||
|  |  |
|
||||
| **One-to-one and group chats** | **Special features - Custom messages** |
|
||||
|  |  |
|
||||
|
||||
</div>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 418 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 931 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 318 KiB |
@@ -43,4 +43,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
|
||||
EXPOSE ${10002}
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-api --port 10002 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-api --port 10002 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -42,4 +42,4 @@ WORKDIR /openim/openim-server
|
||||
COPY --from=builder ${SERVER_WORKDIR}/_output/bin/platforms /openim/openim-server/_output/bin/platforms
|
||||
COPY --from=builder ${SERVER_WORKDIR}/config /openim/openim-server/config
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-cmdutils"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-cmdutils"]
|
||||
|
||||
@@ -41,4 +41,4 @@ WORKDIR /openim/openim-server
|
||||
COPY --from=builder /openim/openim-server/_output/bin/platforms /openim/openim-server/_output/bin/platforms
|
||||
COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-crontask"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-crontask"]
|
||||
|
||||
@@ -49,4 +49,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
|
||||
EXPOSE 10160
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-auth --port 10160 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-auth --port 10160 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -56,4 +56,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
EXPOSE 10230
|
||||
EXPOSE 20230
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-conversation --port 10230 --prometheus_port 20230 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-conversation --port 10230 --prometheus_port 20230 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -56,4 +56,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
EXPOSE 10120
|
||||
EXPOSE 20120
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-friend --port 10120 --prometheus_port 20120 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-friend --port 10120 --prometheus_port 20120 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -56,4 +56,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
EXPOSE 10150
|
||||
EXPOSE 20150
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-group --port 10150 --prometheus_port 20150 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-group --port 10150 --prometheus_port 20150 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -56,4 +56,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
EXPOSE 10130
|
||||
EXPOSE 20130
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-msg --port 10130 --prometheus_port 20130 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-msg --port 10130 --prometheus_port 20130 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -55,4 +55,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
|
||||
EXPOSE 10200
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-third --port 10200 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-third --port 10200 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -55,4 +55,4 @@ COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
||||
|
||||
EXPOSE 10110
|
||||
|
||||
CMD ["sh","-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-user --port 10110 -c ${SERVER_WORKDIR}/config"]
|
||||
CMD ["bash", "-c","${OPENIM_SERVER_BINDIR}/platforms/${OS}/${ARCH}/openim-rpc-user --port 10110 -c ${SERVER_WORKDIR}/config"]
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
|
||||
func main() {
|
||||
cronTaskCmd := cmd.NewCronTaskCmd()
|
||||
if err := cronTaskCmd.Exec(tools.StartCronTask); err != nil {
|
||||
if err := cronTaskCmd.Exec(tools.StartTask); err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
+43
-39
@@ -24,10 +24,10 @@
|
||||
# Zookeeper username
|
||||
# Zookeeper password
|
||||
zookeeper:
|
||||
schema: openim
|
||||
address: [ 127.0.0.1:2181 ]
|
||||
username:
|
||||
password:
|
||||
schema: openim
|
||||
address: [ 127.0.0.1:2181 ]
|
||||
username:
|
||||
password:
|
||||
|
||||
###################### Mysql ######################
|
||||
# MySQL configuration
|
||||
@@ -42,12 +42,12 @@ mysql:
|
||||
address: [ 127.0.0.1:13306 ]
|
||||
username: root
|
||||
password: openIM123
|
||||
database: openIM_v3
|
||||
maxOpenConn: 1000
|
||||
maxIdleConn: 100
|
||||
maxLifeTime: 60
|
||||
logLevel: 4
|
||||
slowThreshold: 500
|
||||
database: openIM_v3
|
||||
maxOpenConn: 1000
|
||||
maxIdleConn: 100
|
||||
maxLifeTime: 60
|
||||
logLevel: 4
|
||||
slowThreshold: 500
|
||||
|
||||
###################### Mongo ######################
|
||||
# MongoDB configuration
|
||||
@@ -62,7 +62,7 @@ mongo:
|
||||
database: openIM_v3
|
||||
username: root
|
||||
password: openIM123
|
||||
maxPoolSize: 100
|
||||
maxPoolSize: 100
|
||||
|
||||
###################### Redis ######################
|
||||
# Redis configuration
|
||||
@@ -70,7 +70,7 @@ mongo:
|
||||
# Username is required only for Redis version 6.0+
|
||||
redis:
|
||||
address: [ 127.0.0.1:16379 ]
|
||||
username:
|
||||
username:
|
||||
password: openIM123
|
||||
|
||||
###################### Kafka ######################
|
||||
@@ -81,13 +81,13 @@ redis:
|
||||
# It's not recommended to modify this topic name
|
||||
# Consumer group ID, it's not recommended to modify
|
||||
kafka:
|
||||
username:
|
||||
password:
|
||||
username:
|
||||
password:
|
||||
addr: [ 127.0.0.1:9092 ]
|
||||
latestMsgToRedis:
|
||||
topic: "latestMsgToRedis"
|
||||
topic: "latestMsgToRedis"
|
||||
offlineMsgToMongo:
|
||||
topic: "offlineMsgToMongoMysql"
|
||||
topic: "offlineMsgToMongoMysql"
|
||||
msgToPush:
|
||||
topic: "msgToPush"
|
||||
consumerGroupID:
|
||||
@@ -111,8 +111,8 @@ rpc:
|
||||
# API service port
|
||||
# Default listen IP is 0.0.0.0
|
||||
api:
|
||||
openImApiPort: [ 10002 ]
|
||||
listenIP: 0.0.0.0
|
||||
openImApiPort: [ 10002 ]
|
||||
listenIP: 0.0.0.0
|
||||
|
||||
###################### Gateway ######################
|
||||
# Object storage configuration
|
||||
@@ -124,25 +124,29 @@ api:
|
||||
# Session token
|
||||
# Configuration for Tencent COS
|
||||
# Configuration for Aliyun OSS
|
||||
# apiURL is the address of the api, the access address of the app, use s3 must be configured
|
||||
# minio.endpoint can be configured as an intranet address,
|
||||
# minio.signEndpoint is minio public network address
|
||||
object:
|
||||
enable: "minio"
|
||||
apiURL: http://127.0.0.1:10002/object/
|
||||
enable: "minio"
|
||||
apiURL: "http://127.0.0.1:10002"
|
||||
minio:
|
||||
bucket: "openim"
|
||||
endpoint: http://127.0.0.1:10005
|
||||
accessKeyID: root
|
||||
secretAccessKey: openIM123
|
||||
sessionToken: ""
|
||||
cos:
|
||||
bucket: "openim"
|
||||
endpoint: "http://127.0.0.1:10005"
|
||||
accessKeyID: "root"
|
||||
secretAccessKey: "openIM123"
|
||||
sessionToken: ""
|
||||
signEndpoint: "http://127.0.0.1:10005"
|
||||
cos:
|
||||
bucketURL: "https://temp-1252357374.cos.ap-chengdu.myqcloud.com"
|
||||
secretID: ""
|
||||
secretKey: ""
|
||||
sessionToken: ""
|
||||
oss:
|
||||
oss:
|
||||
endpoint: "https://oss-cn-chengdu.aliyuncs.com"
|
||||
bucket: "demo-9999999"
|
||||
bucketURL: "https://demo-9999999.oss-cn-chengdu.aliyuncs.com"
|
||||
accessKeyID: root
|
||||
accessKeyID: ""
|
||||
accessKeySecret: ""
|
||||
sessionToken: ""
|
||||
|
||||
@@ -150,7 +154,7 @@ object:
|
||||
# These ports are passed into the program by the script and are not recommended to modify
|
||||
# For launching multiple programs, just fill in multiple ports separated by commas
|
||||
# For example, [10110, 10111]
|
||||
rpcPort:
|
||||
rpcPort:
|
||||
openImUserPort: [ 10110 ]
|
||||
openImFriendPort: [ 10120 ]
|
||||
openImMessagePort: [ 10130 ]
|
||||
@@ -183,12 +187,12 @@ rpcRegisterName:
|
||||
# Whether to output in json format
|
||||
# Whether to include stack trace in logs
|
||||
log:
|
||||
storageLocation: ../../../../../logs/
|
||||
rotationTime: 24
|
||||
remainRotationCount: 2
|
||||
remainLogLevel: 6
|
||||
isStdout: false
|
||||
isJson: false
|
||||
storageLocation: ../../../../../logs/
|
||||
rotationTime: 24
|
||||
remainRotationCount: 2
|
||||
remainLogLevel: 6
|
||||
isStdout: false
|
||||
isJson: false
|
||||
withStack: false
|
||||
|
||||
# Long connection server configuration
|
||||
@@ -198,10 +202,10 @@ log:
|
||||
# Maximum length of websocket request package
|
||||
# Websocket connection handshake timeout
|
||||
longConnSvr:
|
||||
openImWsPort: [ 10001 ]
|
||||
websocketMaxConnNum: 100000
|
||||
websocketMaxMsgLen: 4096
|
||||
websocketTimeout: 10
|
||||
openImWsPort: [ 10001 ]
|
||||
websocketMaxConnNum: 100000
|
||||
websocketMaxMsgLen: 4096
|
||||
websocketTimeout: 10
|
||||
|
||||
# Push notification service configuration
|
||||
#
|
||||
|
||||
@@ -304,6 +304,16 @@ userInfoUpdated:
|
||||
desc: "Remove a blocked user"
|
||||
ext: "Remove a blocked user"
|
||||
|
||||
userStatusChanged:
|
||||
isSendMsg: false
|
||||
reliabilityLevel: 1
|
||||
unreadCount: false
|
||||
offlinePush:
|
||||
enable: false
|
||||
title: "user status changed"
|
||||
desc: "user status changed"
|
||||
ext: "user status changed"
|
||||
|
||||
#####################conversation#########################
|
||||
conversationChanged:
|
||||
isSendMsg: false
|
||||
|
||||
+12
-1
@@ -2,6 +2,17 @@
|
||||
|
||||
OpenIM 支持很多种集群化部署方式,包括但不限于 helm, sealos, kubeam, kubesphere, kubeflow, kuboard, kubespray, k3s, k3d, k3c, k3sup, k3v, k3x
|
||||
|
||||
**目前还在开发这个模块,预计 v3.2.0 之前会有一个集群方案。**
|
||||
|
||||
目前各个贡献者,以及之前的官方有出过一些可以参考的方案:
|
||||
|
||||
- https://github.com/OpenIMSDK/k8s-jenkins
|
||||
- https://github.com/OpenIMSDK/Open-IM-Server-k8s-deploy
|
||||
- https://github.com/OpenIMSDK/openim-charts
|
||||
- https://github.com/showurl/deploy-openim
|
||||
|
||||
|
||||
|
||||
### 依赖检查
|
||||
|
||||
```bash
|
||||
@@ -23,4 +34,4 @@ $ cd ${OpenIM_ROOT}
|
||||
|
||||
|
||||
|
||||
### Helm安装
|
||||
### Helm安装
|
||||
|
||||
+10
-10
@@ -52,7 +52,6 @@ services:
|
||||
net.core.somaxconn: 1024
|
||||
command: redis-server --requirepass ${PASSWORD} --appendonly yes
|
||||
|
||||
|
||||
zookeeper:
|
||||
image: wurstmeister/zookeeper
|
||||
ports:
|
||||
@@ -63,7 +62,7 @@ services:
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
restart: always
|
||||
|
||||
network_mode: "host"
|
||||
|
||||
kafka:
|
||||
image: wurstmeister/kafka
|
||||
@@ -74,7 +73,7 @@ services:
|
||||
environment:
|
||||
TZ: Asia/Shanghai
|
||||
KAFKA_BROKER_ID: 0
|
||||
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
||||
KAFKA_ZOOKEEPER_CONNECT: 127.0.0.1:2181
|
||||
KAFKA_CREATE_TOPICS: "latestMsgToRedis:8:1,msgToPush:8:1,offlineMsgToMongoMysql:8:1"
|
||||
KAFKA_ADVERTISED_LISTENERS: INSIDE://127.0.0.1:9092,OUTSIDE://103.116.45.174:9092
|
||||
KAFKA_LISTENERS: INSIDE://:9092,OUTSIDE://:9093
|
||||
@@ -82,6 +81,7 @@ services:
|
||||
KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
|
||||
depends_on:
|
||||
- zookeeper
|
||||
network_mode: "host"
|
||||
|
||||
minio:
|
||||
image: minio/minio
|
||||
@@ -98,9 +98,9 @@ services:
|
||||
restart: always
|
||||
command: minio server /data --console-address ':9090'
|
||||
|
||||
|
||||
openim_server:
|
||||
image: ghcr.io/openim-sigs/openim-server:v1.0.0-debug.11 #ghcr.io/openimsdk/openim-server:main
|
||||
openim-server:
|
||||
image: ghcr.io/openimsdk/openim-server:latest
|
||||
# build: .
|
||||
container_name: openim-server
|
||||
volumes:
|
||||
- ./logs:/openim/openim-server/logs
|
||||
@@ -122,7 +122,7 @@ services:
|
||||
max-file: "2"
|
||||
|
||||
openim-chat:
|
||||
image: ghcr.io/openim-sigs/openim-chat:v1.0.0-debug.11 # ghcr.io/openimsdk/openim-chat:main
|
||||
image: ghcr.io/openimsdk/openim-chat:latest
|
||||
container_name: openim-chat
|
||||
volumes:
|
||||
- ./_output/openim/openim-chat/logs:/openim/openim-chat/logs
|
||||
@@ -134,7 +134,7 @@ services:
|
||||
- mongodb
|
||||
- redis
|
||||
- minio
|
||||
- openim_server
|
||||
- openim-server
|
||||
network_mode: "host"
|
||||
logging:
|
||||
driver: json-file
|
||||
@@ -150,7 +150,7 @@ services:
|
||||
# ports:
|
||||
# - 9091:9091
|
||||
depends_on:
|
||||
- openim_server
|
||||
- openim-server
|
||||
command: --web.listen-address=:9091 --config.file="/etc/prometheus/prometheus.yml"
|
||||
network_mode: "host"
|
||||
|
||||
@@ -170,4 +170,4 @@ services:
|
||||
# container_name: node-exporter
|
||||
# restart: always
|
||||
# ports:
|
||||
# - "9100:9100"
|
||||
# - "9100:9100"
|
||||
@@ -1,8 +1,5 @@
|
||||
docs/.generated_docs
|
||||
docs/guide/en-US/cmd/iam-apiserver.md
|
||||
docs/guide/en-US/cmd/iam-authz-server.md
|
||||
docs/guide/en-US/cmd/iam-pump.md
|
||||
docs/guide/en-US/cmd/iam-watcher.md
|
||||
|
||||
docs/guide/en-US/cmd/openim/openim.md
|
||||
docs/guide/en-US/cmd/openim/openim_color.md
|
||||
docs/guide/en-US/cmd/openim/openim_completion.md
|
||||
|
||||
+401
-409
File diff suppressed because it is too large
Load Diff
+65
-42
@@ -1,76 +1,88 @@
|
||||
# OpenIM Branch Management and Versioning
|
||||
# OpenIM Branch Management and Versioning: A Blueprint for High-Grade Software Development
|
||||
|
||||
Our project, OpenIM, follows the [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/) standards.
|
||||
[📚 **OpenIM TOC**](#openim-branch-management-and-versioning-a-blueprint-for-high-grade-software-development)
|
||||
- [Unfolding the Mechanism of OpenIM Version Maintenance](#unfolding-the-mechanism-of-openim-version-maintenance)
|
||||
- [Main Branch: The Heart of OpenIM Development](#main-branch-the-heart-of-openim-development)
|
||||
- [Release Branch: The Beacon of Stability](#release-branch-the-beacon-of-stability)
|
||||
- [Tag Management: The Cornerstone of Version Control](#tag-management-the-cornerstone-of-version-control)
|
||||
- [Release Management: A Guided Tour](#release-management-a-guided-tour)
|
||||
- [Milestones, Branching, and Addressing Major Bugs](#milestones-branching-and-addressing-major-bugs)
|
||||
- [Applying Principles: A Git Workflow Example](#applying-principles-a-git-workflow-example)
|
||||
- [Docker Images Version Management](#docker-images-version-management)
|
||||
|
||||
OpenIM, the open source project, employs a comprehensive version management system to ensure the reliability and traceability of our software. Our version management consists of three main components: the `main` branch, the `release` branch, and `tag` management.
|
||||
|
||||
## Main Branch
|
||||
At OpenIM, we acknowledge the profound impact of implementing a robust and efficient version management system, hence we abide by the established standards of [Semantic Versioning 2.0.0](https://semver.org/lang/zh-CN/).
|
||||
|
||||
The `main` branch is where all the latest code resides. It's the hub of activity, embodying all the cutting-edge features that are currently being developed or updated. However, since it's subject to frequent changes and updates, it may not always represent the most stable version of the software. Access the `main` branch [here](https://github.com/OpenIMSDK/Open-IM-Server/tree/main).
|
||||
Our software blueprint orchestrates a tripartite version management system that integrates the `main` branch, the `release` branch, and `tag` management. These constituents operate in synchrony to preserve the reliability and traceability of our software across various stages of development.
|
||||
|
||||
## Release Branch
|
||||
## Unfolding the Mechanism of OpenIM Version Maintenance
|
||||
|
||||
On the other hand, we have the `release` branch. For instance, in the context of version 3.1, we maintain a `release-v3.1` branch. Unlike the `main` branch, the release branch is designed to be a continuously stable and updated version of the software. This provides a reliable option for users who prefer stability over the latest, but potentially unstable, features. Access the `release-v3.1` branch [here](https://github.com/OpenIMSDK/Open-IM-Server/tree/release-v3.1).
|
||||
Our version maintenance protocol revolves around two primary branches, namely: `main` and `release`. We resort to Semantic Versioning 2.0.0 for marking distinctive versions of our software, representing substantial milestones in its evolution.
|
||||
|
||||
## Tag Management
|
||||
In the OpenIM repository, version identification strictly complies with the `MAJOR.MINOR.PATCH` protocol. Herein:
|
||||
|
||||
In addition to the `main` and `release` branches, `tag` also plays a pivotal role in version control. Tags are immutable, meaning once they're created, they remain unchanged. Therefore, if you need a specific version of the software, you can use the corresponding tag. All of our available tags can be viewed [here](https://github.com/OpenIMSDK/Open-IM-Server/tags).
|
||||
- The `MAJOR` version indicates a shift arising from incompatible changes to the API.
|
||||
- The `MINOR` version suggests the addition of features in a backward-compatible manner.
|
||||
- The `PATCH` version flags backward-compatible bug fixes.
|
||||
|
||||
Moreover, our Docker image versions are closely tied with these three components. For example, a tag might correspond to the Docker image `ghcr.io/openimsdk/openim-server:v3.1.0`, a release might be represented as `ghcr.io/openimsdk/openim-server:release-v3.0`, and the main branch could be represented as `ghcr.io/openimsdk/openim-server:main` or `ghcr.io/openimsdk/openim-server:latest`.
|
||||
## Main Branch: The Heart of OpenIM Development
|
||||
|
||||
Here is the specification of our version numbers:
|
||||
The `main` branch is the operational heart of our development process. Housing the most recent and advanced features, this branch serves as the nerve center for all enhancements and updates. It encapsulates the freshest, though possibly unstable, facets of the software. Visit our `main` branch [here](https://github.com/OpenIMSDK/Open-IM-Server/tree/main).
|
||||
|
||||
- **Revision version number**: The third digit of the version number, representing bug fixes or code optimizations, usually no new features are added and it is backward compatible with older versions.
|
||||
## Release Branch: The Beacon of Stability
|
||||
|
||||
- **Build version number**: Usually automatically generated by the system, every code submission will result in an automatic increment by 1.
|
||||
For every major release, we curate a corresponding `release` branch, e.g., `release-v3.1`. This branch symbolizes an embodiment of stability and ensures an updated version of the software, providing a dependable option for users favoring stability over nascent, yet possibly unstable, features. Visit the `release-v3.1` branch [here](https://github.com/OpenIMSDK/Open-IM-Server/tree/release-v3.1).
|
||||
|
||||
- Version modifiers
|
||||
## Tag Management: The Cornerstone of Version Control
|
||||
|
||||
: These can represent the development stage and stability of the software. Common ones include:
|
||||
In OpenIM's version control system, the role of `tags` stands paramount. Owing to their immutable nature, tags can be effectively utilized to retrieve a specific version of the software. Explore our library of tags [here](https://github.com/OpenIMSDK/Open-IM-Server/tags).
|
||||
|
||||
- `alpha`: An internal testing version with many bugs, generally used for communication among developers.
|
||||
- `beta`: A test version with many bugs, generally used for testing by eager community members, who provide feedback to the developers.
|
||||
- `rc`: Release candidate, to be released as the official version, it's the last test version before the official version.
|
||||
Our Docker image versions are intimately entwined with these tripartite components. For instance, a Docker image tag may correspond to `ghcr.io/openimsdk/openim-server:v3.1.0`, a release to `ghcr.io/openimsdk/openim-server:release-v3.0`, and the main branch to `ghcr.io/openimsdk/openim-server:main` or `ghcr.io/openimsdk/openim-server:latest`.
|
||||
|
||||
To further clarify, the semantics of our version numbers are as follows:
|
||||
|
||||
- **Revision version number**: This represents bug fixes or code optimizations. Typically, it entails no new feature additions and ensures backward compatibility.
|
||||
- **Build version number**: Auto-generated by the system, each code submission prompts an automatic increment by 1.
|
||||
- **Version modifiers**: These hint at the software's development stage and stability. Some commonly used modifiers are `alpha`, `beta`, `rc`, `ga`, `r/release/or nothing`, and `lts`.
|
||||
- `alpha`: An internal testing version with numerous bugs, typically used for communication among developers.
|
||||
- `beta`: A test version with numerous bugs, generally used for testing by eager community members, who provide feedback to the developers.
|
||||
- `rc`: Release candidate, which is to be released as the official version. It's the last test version before the official version.
|
||||
- `ga`: General Availability, the first stable release.
|
||||
- `r/release/or nothing`: The final release version, intended for general users.
|
||||
- `lts`: Long Term Support, the official will specify the maintenance year for this version and will fix all bugs found in this version.
|
||||
- `lts`: Long Term Support, the official will specify the maintenance year for this version and will fix all bugs discovered in this version.
|
||||
|
||||
When adding partial functions to the project, the minor version number increases by 1, and the revision version number resets to 0. When there are major changes in the project, the major version number increases by 1. The build number is generally automatically generated by the compiler during the compilation process, only the format needs to be defined, and it does not need to be manually controlled.
|
||||
Whenever a project undergoes a partial functional addition, the minor version number increments by 1, resetting the revision version number to 0. In contrast, any major project overhaul results in an increment by 1 in the major version number. The build number, typically auto-generated during the compilation process, only requires format definition, thereby eliminating manual control.
|
||||
|
||||
## OpenIM version
|
||||
## Release Management: A Guided Tour
|
||||
|
||||
OpenIM manages two primary branches: `main` and `release`. The project uses Semantic Versioning 2.0.0 to tag different versions of the software, each indicating a significant milestone in the software's development.
|
||||
Our GitHub repository at https://github.com/OpenIMSDK/Open-IM-Server/releases associates a release with each tag, with a distinction between Pre-release and Latest, determined by the branch source. Every significant feature launch prompts the issue of a `release` branch, such as `release-v3.2`, as a beacon of stability and Latest release.
|
||||
|
||||
In the OpenIM repository, the versioning adheres to the `MAJOR.MINOR.PATCH` format, where:
|
||||
Pre-releases correspond to releases from the `main` branch, denoting tags with Version modifiers such as `v3.2.1-beta.0`, `v3.2.1-rc.1`, etc. If you are seeking the most recent, albeit possibly unstable, release with new features, these tags, originating from the latest `main` branch code, are your go-to.
|
||||
|
||||
- `MAJOR` version changes when there are incompatible changes to the API,
|
||||
- `MINOR` version changes when features are added in a backward-compatible manner, and
|
||||
- `PATCH` version changes when backward-compatible bugs are fixed.
|
||||
Conversely, if stability is your primary concern, you should opt for the release tagged Latest, denoted by tags without Version modifiers, such as `v3.2.1`, `v3.2.2` etc. These tags are linked to the latest stable maintenance branch, like `release-v3.2`.
|
||||
|
||||
## Milestones and Branching
|
||||
## Milestones, Branching, and Addressing Major Bugs
|
||||
|
||||
**About:**
|
||||
|
||||
+ [OpenIM Milestones](https://github.com/OpenIMSDK/Open-IM-Server/milestones)
|
||||
+ [OpenIM Tags](https://github.com/OpenIMSDK/Open-IM-Server/tags)
|
||||
+ [OpenIM Branches](https://github.com/OpenIMSDK/Open-IM-Server/branches)
|
||||
|
||||
When a significant milestone like v3.1.0 is achieved, a new branch `release-v3.1` is created. This branch contains all the code pertaining to this stable release. All bug fixes and features intended for the next version, v3.2.0, are merged into this branch.
|
||||
We create a new branch, such as `release-v3.1`, for each significant milestone (e.g., v3.1.0), housing all relevant code for that release. All enhancements and bug fixes targeting the subsequent version (e.g., v3.2.0) are integrated into this branch.
|
||||
|
||||
The release of `PATCH` versions (Z in `X.Y.Z`) are driven by bug fixes, and these can be rolled out depending on the bug's priority or over a scheduled time. On the other hand, `MINOR` versions (Y in `X.Y.Z`) are released based on the project's roadmap, milestone completion, or on a scheduled timeline. Importantly, the API of minor versions is always backward-compatible.
|
||||
`PATCH` versions (represented by Z in `X.Y.Z`) are primarily propelled by bug fixes, and their release may be either priority-driven or scheduled. In contrast, `MINOR` versions (represented by Y in `X.Y.Z`) are contingent upon the project's roadmap, milestone completion, or a pre-established timeline, always maintaining backward-compatible APIs.
|
||||
|
||||
## Dealing with Major Bugs
|
||||
When dealing with major bugs, we selectively merge the fix into the affected version (e.g., v3.1 or the `release-v3.1` branch), as well as the `main` branch. This dual pronged strategy ensures that users on older versions receive crucial bug fixes, while also keeping the `main` branch updated.
|
||||
|
||||
In the event of a major bug discovery, the fix would selectively be merged into the previous version (e.g., v3.1 or the `release-v3.1` branch), as well as into the `main` branch. This is to ensure that users relying on the older version can still receive important bug fixes, while also keeping the main branch updated.
|
||||
We reinforce our approach to branch management and versioning with stringent testing protocols. Automated tests and code review sessions form vital components of maintaining a robust and reliable codebase.
|
||||
|
||||
It's worth noting that a robust testing regime should be in place to ensure the integrity of all branches at any given time. Automated tests and code review sessions are crucial components of maintaining a healthy codebase.
|
||||
## Applying Principles: A Git Workflow Example
|
||||
|
||||
To summarize, OpenIM's approach to branch management and versioning ensures a balance between introducing new features, fixing bugs, and maintaining backward compatibility. This strategy is vital for managing user expectations, supporting older versions, and paving the way for the project's continuous growth.
|
||||
The workflow to address a bug fix might follow these steps:
|
||||
|
||||
## Git Workflow Example
|
||||
|
||||
To put the above principles into practice, here's a Git workflow example that you might follow when working on a bug fix:
|
||||
|
||||
```
|
||||
bashCopy code# Checkout the branch for the version that needs the bug fix
|
||||
```bash
|
||||
bashCopy codebashCopy code# Checkout the branch for the version that needs the bug fix
|
||||
git checkout release-v3.1
|
||||
|
||||
# Create a new branch for the bug fix
|
||||
@@ -92,10 +104,21 @@ git merge release-v3.1
|
||||
# Push the updates to the main branch
|
||||
git push origin main
|
||||
```
|
||||
## Release Process
|
||||
|
||||
Remember, communication with your team is key throughout this process, keeping everyone up-to-date with the changes being made.
|
||||
```
|
||||
Publishing v3.2.0: A Step-by-Step Guide
|
||||
(1) Create the tag v3.2.0-alpha.0 from the main branch.
|
||||
(2) Bugs are fixed on the main branch. Once the bugs are resolved, tag the main branch as v3.2.0-rc.0.
|
||||
(3) After further testing, if v3.2.0-rc.0 is deemed stable, create a branch named release-v3.2 from the tag v3.2.0-rc.0.
|
||||
(4) From the release-v3.2 branch, create the tag v3.2.0. At this point, the official release of v3.2.0 is complete.
|
||||
|
||||
After the release of v3.2.0, if urgent bugs are discovered, fix them on the release-v3.2 branch. Then, submit two pull requests (PRs) to both the main and release-v3.2 branches. Tag the release-v3.2 branch as v3.2.1.
|
||||
```
|
||||
|
||||
## Docker images version management
|
||||
Throughout this process, active communication within the team is pivotal to maintaining transparency and consensus on changes.
|
||||
|
||||
## Docker Images Version Management
|
||||
|
||||
For more details on managing Docker image versions, visit [OpenIM Docker Images Administration](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/images.md).
|
||||
|
||||
+ [OpenIM Docker Images Administration](https://github.com/OpenIMSDK/Open-IM-Server/blob/main/docs/conversions/images.md)
|
||||
|
||||
@@ -8,7 +8,7 @@ require (
|
||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||
github.com/dtm-labs/rockscache v0.1.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-playground/validator/v10 v10.14.1
|
||||
github.com/go-playground/validator/v10 v10.15.0
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/protobuf v1.5.3
|
||||
@@ -25,19 +25,19 @@ require (
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.mongodb.org/mongo-driver v1.12.1
|
||||
golang.org/x/image v0.9.0 // indirect
|
||||
google.golang.org/api v0.134.0
|
||||
golang.org/x/image v0.11.0
|
||||
google.golang.org/api v0.136.0
|
||||
google.golang.org/grpc v1.57.0
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gorm.io/driver/mysql v1.5.1
|
||||
gorm.io/gorm v1.25.2
|
||||
gorm.io/gorm v1.25.3
|
||||
)
|
||||
|
||||
require github.com/google/uuid v1.3.0
|
||||
|
||||
require (
|
||||
github.com/OpenIMSDK/protocol v0.0.4
|
||||
github.com/OpenIMSDK/protocol v0.0.12
|
||||
github.com/OpenIMSDK/tools v0.0.13
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
@@ -47,11 +47,11 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.110.4 // indirect
|
||||
cloud.google.com/go/compute v1.20.1 // indirect
|
||||
cloud.google.com/go v0.110.6 // indirect
|
||||
cloud.google.com/go/compute v1.23.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/firestore v1.11.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.0 // indirect
|
||||
cloud.google.com/go/iam v1.1.1 // indirect
|
||||
cloud.google.com/go/longrunning v0.5.1 // indirect
|
||||
cloud.google.com/go/storage v1.30.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
@@ -117,16 +117,17 @@ require (
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/net v0.12.0 // indirect
|
||||
golang.org/x/oauth2 v0.10.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/oauth2 v0.11.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -137,7 +138,6 @@ require (
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||
golang.org/x/crypto v0.12.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
|
||||
cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
||||
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
|
||||
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q=
|
||||
cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
||||
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||
cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/firestore v1.11.0 h1:PPgtwcYUOXV2jFe1bV3nda3RCrOa8cvBjTOn2MQVfW8=
|
||||
cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4=
|
||||
cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94=
|
||||
cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
|
||||
cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
|
||||
cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU=
|
||||
cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
|
||||
cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
|
||||
cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM=
|
||||
@@ -17,8 +17,8 @@ cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7Biccwk
|
||||
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
|
||||
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OpenIMSDK/protocol v0.0.4 h1:zEEAi677nog+k4u3e5h36nvYeb1XAwcKQ3Uc2tzxHYs=
|
||||
github.com/OpenIMSDK/protocol v0.0.4/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
||||
github.com/OpenIMSDK/protocol v0.0.12 h1:79rBgpKBF4TMy5tkLH3DQNiZFVmxKMP7VyX+kmJh4d8=
|
||||
github.com/OpenIMSDK/protocol v0.0.12/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
||||
github.com/OpenIMSDK/tools v0.0.13 h1:rcw4HS8S2DPZR9UOBxD8/ol9UBMzXBypzOVEytDRIMo=
|
||||
github.com/OpenIMSDK/tools v0.0.13/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
|
||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
|
||||
@@ -100,8 +100,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.15.0 h1:nDU5XeOKtB3GEa+uB7GNYwhVKsgjAR7VgKoNB6ryXfw=
|
||||
github.com/go-playground/validator/v10 v10.15.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
@@ -361,11 +361,11 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.9.0 h1:QrzfX26snvCM20hIhBwuHI/ThTg18b/+kcKdXHvnR+g=
|
||||
golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
||||
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
|
||||
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
@@ -394,12 +394,12 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
|
||||
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -430,8 +430,8 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
@@ -442,8 +442,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -463,8 +463,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
google.golang.org/api v0.134.0 h1:ktL4Goua+UBgoP1eL1/60LwZJqa1sIzkLmvoR3hR6Gw=
|
||||
google.golang.org/api v0.134.0/go.mod h1:sjRL3UnjTx5UqNQS9EWr9N8p7xbHpy1k0XGRLCf3Spk=
|
||||
google.golang.org/api v0.136.0 h1:e/6enzUE1s4tGPa6Q3ZYShKTtvRc+1Jq0rrafhppmOs=
|
||||
google.golang.org/api v0.136.0/go.mod h1:XtJfF+V2zgUxelOn5Zs3kECtluMxneJG8ZxUTlLNTPA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
@@ -473,12 +473,12 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 h1:Au6te5hbKUV8pIYWHqOUZ1pva5qK/rwbIhoXEUB9Lu8=
|
||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 h1:Z8qdAF9GFsmcUuWQ5KVYIpP3PCKydn/YKORnghIalu4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
|
||||
google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
@@ -523,8 +523,8 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
||||
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
||||
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
gorm.io/gorm v1.25.3 h1:zi4rHZj1anhZS2EuEODMhDisGy+Daq9jtPrNGgbQYD8=
|
||||
gorm.io/gorm v1.25.3/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
||||
+310
@@ -0,0 +1,310 @@
|
||||
cloud.google.com/go/accessapproval v1.7.1 h1:/5YjNhR6lzCvmJZAnByYkfEgWjfAKwYP6nkuTk6nKFE=
|
||||
cloud.google.com/go/accessapproval v1.7.1/go.mod h1:JYczztsHRMK7NTXb6Xw+dwbs/WnOJxbo/2mTI+Kgg68=
|
||||
cloud.google.com/go/accesscontextmanager v1.8.1 h1:WIAt9lW9AXtqw/bnvrEUaE8VG/7bAAeMzRCBGMkc4+w=
|
||||
cloud.google.com/go/accesscontextmanager v1.8.1/go.mod h1:JFJHfvuaTC+++1iL1coPiG1eu5D24db2wXCDWDjIrxo=
|
||||
cloud.google.com/go/aiplatform v1.48.0 h1:M5davZWCTzE043rJCn+ZLW6hSxfG1KAx4vJTtas2/ec=
|
||||
cloud.google.com/go/aiplatform v1.48.0/go.mod h1:Iu2Q7sC7QGhXUeOhAj/oCK9a+ULz1O4AotZiqjQ8MYA=
|
||||
cloud.google.com/go/analytics v0.21.3 h1:TFBC1ZAqX9/jL56GEXdLrVe5vT3I22bDVWyDwZX4IEg=
|
||||
cloud.google.com/go/analytics v0.21.3/go.mod h1:U8dcUtmDmjrmUTnnnRnI4m6zKn/yaA5N9RlEkYFHpQo=
|
||||
cloud.google.com/go/apigateway v1.6.1 h1:aBSwCQPcp9rZ0zVEUeJbR623palnqtvxJlUyvzsKGQc=
|
||||
cloud.google.com/go/apigateway v1.6.1/go.mod h1:ufAS3wpbRjqfZrzpvLC2oh0MFlpRJm2E/ts25yyqmXA=
|
||||
cloud.google.com/go/apigeeconnect v1.6.1 h1:6u/jj0P2c3Mcm+H9qLsXI7gYcTiG9ueyQL3n6vCmFJM=
|
||||
cloud.google.com/go/apigeeconnect v1.6.1/go.mod h1:C4awq7x0JpLtrlQCr8AzVIzAaYgngRqWf9S5Uhg+wWs=
|
||||
cloud.google.com/go/apigeeregistry v0.7.1 h1:hgq0ANLDx7t2FDZDJQrCMtCtddR/pjCqVuvQWGrQbXw=
|
||||
cloud.google.com/go/apigeeregistry v0.7.1/go.mod h1:1XgyjZye4Mqtw7T9TsY4NW10U7BojBvG4RMD+vRDrIw=
|
||||
cloud.google.com/go/appengine v1.8.1 h1:J+aaUZ6IbTpBegXbmEsh8qZZy864ZVnOoWyfa1XSNbI=
|
||||
cloud.google.com/go/appengine v1.8.1/go.mod h1:6NJXGLVhZCN9aQ/AEDvmfzKEfoYBlfB80/BHiKVputY=
|
||||
cloud.google.com/go/area120 v0.8.1 h1:wiOq3KDpdqXmaHzvZwKdpoM+3lDcqsI2Lwhyac7stss=
|
||||
cloud.google.com/go/area120 v0.8.1/go.mod h1:BVfZpGpB7KFVNxPiQBuHkX6Ed0rS51xIgmGyjrAfzsg=
|
||||
cloud.google.com/go/artifactregistry v1.14.1 h1:k6hNqab2CubhWlGcSzunJ7kfxC7UzpAfQ1UPb9PDCKI=
|
||||
cloud.google.com/go/artifactregistry v1.14.1/go.mod h1:nxVdG19jTaSTu7yA7+VbWL346r3rIdkZ142BSQqhn5E=
|
||||
cloud.google.com/go/asset v1.14.1 h1:vlHdznX70eYW4V1y1PxocvF6tEwxJTTarwIGwOhFF3U=
|
||||
cloud.google.com/go/asset v1.14.1/go.mod h1:4bEJ3dnHCqWCDbWJ/6Vn7GVI9LerSi7Rfdi03hd+WTQ=
|
||||
cloud.google.com/go/assuredworkloads v1.11.1 h1:yaO0kwS+SnhVSTF7BqTyVGt3DTocI6Jqo+S3hHmCwNk=
|
||||
cloud.google.com/go/assuredworkloads v1.11.1/go.mod h1:+F04I52Pgn5nmPG36CWFtxmav6+7Q+c5QyJoL18Lry0=
|
||||
cloud.google.com/go/automl v1.13.1 h1:iP9iQurb0qbz+YOOMfKSEjhONA/WcoOIjt6/m+6pIgo=
|
||||
cloud.google.com/go/automl v1.13.1/go.mod h1:1aowgAHWYZU27MybSCFiukPO7xnyawv7pt3zK4bheQE=
|
||||
cloud.google.com/go/baremetalsolution v1.1.1 h1:0Ge9PQAy6cZ1tRrkc44UVgYV15nw2TVnzJzYsMHXF+E=
|
||||
cloud.google.com/go/baremetalsolution v1.1.1/go.mod h1:D1AV6xwOksJMV4OSlWHtWuFNZZYujJknMAP4Qa27QIA=
|
||||
cloud.google.com/go/batch v1.3.1 h1:uE0Q//W7FOGPjf7nuPiP0zoE8wOT3ngoIO2HIet0ilY=
|
||||
cloud.google.com/go/batch v1.3.1/go.mod h1:VguXeQKXIYaeeIYbuozUmBR13AfL4SJP7IltNPS+A4A=
|
||||
cloud.google.com/go/beyondcorp v1.0.0 h1:VPg+fZXULQjs8LiMeWdLaB5oe8G9sEoZ0I0j6IMiG1Q=
|
||||
cloud.google.com/go/beyondcorp v1.0.0/go.mod h1:YhxDWw946SCbmcWo3fAhw3V4XZMSpQ/VYfcKGAEU8/4=
|
||||
cloud.google.com/go/bigquery v1.53.0 h1:K3wLbjbnSlxhuG5q4pntHv5AEbQM1QqHKGYgwFIqOTg=
|
||||
cloud.google.com/go/bigquery v1.53.0/go.mod h1:3b/iXjRQGU4nKa87cXeg6/gogLjO8C6PmuM8i5Bi/u4=
|
||||
cloud.google.com/go/billing v1.16.0 h1:1iktEAIZ2uA6KpebC235zi/rCXDdDYQ0bTXTNetSL80=
|
||||
cloud.google.com/go/billing v1.16.0/go.mod h1:y8vx09JSSJG02k5QxbycNRrN7FGZB6F3CAcgum7jvGA=
|
||||
cloud.google.com/go/binaryauthorization v1.6.1 h1:cAkOhf1ic92zEN4U1zRoSupTmwmxHfklcp1X7CCBKvE=
|
||||
cloud.google.com/go/binaryauthorization v1.6.1/go.mod h1:TKt4pa8xhowwffiBmbrbcxijJRZED4zrqnwZ1lKH51U=
|
||||
cloud.google.com/go/certificatemanager v1.7.1 h1:uKsohpE0hiobx1Eak9jNcPCznwfB6gvyQCcS28Ah9E8=
|
||||
cloud.google.com/go/certificatemanager v1.7.1/go.mod h1:iW8J3nG6SaRYImIa+wXQ0g8IgoofDFRp5UMzaNk1UqI=
|
||||
cloud.google.com/go/channel v1.16.0 h1:dqRkK2k7Ll/HHeYGxv18RrfhozNxuTJRkspW0iaFZoY=
|
||||
cloud.google.com/go/channel v1.16.0/go.mod h1:eN/q1PFSl5gyu0dYdmxNXscY/4Fi7ABmeHCJNf/oHmc=
|
||||
cloud.google.com/go/cloudbuild v1.13.0 h1:YBbAWcvE4x6xPWTyS+OU4eiUpz5rCS3VCM/aqmfddPA=
|
||||
cloud.google.com/go/cloudbuild v1.13.0/go.mod h1:lyJg7v97SUIPq4RC2sGsz/9tNczhyv2AjML/ci4ulzU=
|
||||
cloud.google.com/go/clouddms v1.6.1 h1:rjR1nV6oVf2aNNB7B5uz1PDIlBjlOiBgR+q5n7bbB7M=
|
||||
cloud.google.com/go/clouddms v1.6.1/go.mod h1:Ygo1vL52Ov4TBZQquhz5fiw2CQ58gvu+PlS6PVXCpZI=
|
||||
cloud.google.com/go/cloudtasks v1.12.1 h1:cMh9Q6dkvh+Ry5LAPbD/U2aw6KAqdiU6FttwhbTo69w=
|
||||
cloud.google.com/go/cloudtasks v1.12.1/go.mod h1:a9udmnou9KO2iulGscKR0qBYjreuX8oHwpmFsKspEvM=
|
||||
cloud.google.com/go/contactcenterinsights v1.10.0 h1:YR2aPedGVQPpFBZXJnPkqRj8M//8veIZZH5ZvICoXnI=
|
||||
cloud.google.com/go/contactcenterinsights v1.10.0/go.mod h1:bsg/R7zGLYMVxFFzfh9ooLTruLRCG9fnzhH9KznHhbM=
|
||||
cloud.google.com/go/container v1.24.0 h1:N51t/cgQJFqDD/W7Mb+IvmAPHrf8AbPx7Bb7aF4lROE=
|
||||
cloud.google.com/go/container v1.24.0/go.mod h1:lTNExE2R7f+DLbAN+rJiKTisauFCaoDq6NURZ83eVH4=
|
||||
cloud.google.com/go/containeranalysis v0.10.1 h1:SM/ibWHWp4TYyJMwrILtcBtYKObyupwOVeceI9pNblw=
|
||||
cloud.google.com/go/containeranalysis v0.10.1/go.mod h1:Ya2jiILITMY68ZLPaogjmOMNkwsDrWBSTyBubGXO7j0=
|
||||
cloud.google.com/go/datacatalog v1.16.0 h1:qVeQcw1Cz93/cGu2E7TYUPh8Lz5dn5Ws2siIuQ17Vng=
|
||||
cloud.google.com/go/datacatalog v1.16.0/go.mod h1:d2CevwTG4yedZilwe+v3E3ZBDRMobQfSG/a6cCCN5R4=
|
||||
cloud.google.com/go/dataflow v0.9.1 h1:VzG2tqsk/HbmOtq/XSfdF4cBvUWRK+S+oL9k4eWkENQ=
|
||||
cloud.google.com/go/dataflow v0.9.1/go.mod h1:Wp7s32QjYuQDWqJPFFlnBKhkAtiFpMTdg00qGbnIHVw=
|
||||
cloud.google.com/go/dataform v0.8.1 h1:xcWso0hKOoxeW72AjBSIp/UfkvpqHNzzS0/oygHlcqY=
|
||||
cloud.google.com/go/dataform v0.8.1/go.mod h1:3BhPSiw8xmppbgzeBbmDvmSWlwouuJkXsXsb8UBih9M=
|
||||
cloud.google.com/go/datafusion v1.7.1 h1:eX9CZoyhKQW6g1Xj7+RONeDj1mV8KQDKEB9KLELX9/8=
|
||||
cloud.google.com/go/datafusion v1.7.1/go.mod h1:KpoTBbFmoToDExJUso/fcCiguGDk7MEzOWXUsJo0wsI=
|
||||
cloud.google.com/go/datalabeling v0.8.1 h1:zxsCD/BLKXhNuRssen8lVXChUj8VxF3ofN06JfdWOXw=
|
||||
cloud.google.com/go/datalabeling v0.8.1/go.mod h1:XS62LBSVPbYR54GfYQsPXZjTW8UxCK2fkDciSrpRFdY=
|
||||
cloud.google.com/go/dataplex v1.9.0 h1:yoBWuuUZklYp7nx26evIhzq8+i/nvKYuZr1jka9EqLs=
|
||||
cloud.google.com/go/dataplex v1.9.0/go.mod h1:7TyrDT6BCdI8/38Uvp0/ZxBslOslP2X2MPDucliyvSE=
|
||||
cloud.google.com/go/dataproc/v2 v2.0.1 h1:4OpSiPMMGV3XmtPqskBU/RwYpj3yMFjtMLj/exi425Q=
|
||||
cloud.google.com/go/dataproc/v2 v2.0.1/go.mod h1:7Ez3KRHdFGcfY7GcevBbvozX+zyWGcwLJvvAMwCaoZ4=
|
||||
cloud.google.com/go/dataqna v0.8.1 h1:ITpUJep04hC9V7C+gcK390HO++xesQFSUJ7S4nSnF3U=
|
||||
cloud.google.com/go/dataqna v0.8.1/go.mod h1:zxZM0Bl6liMePWsHA8RMGAfmTG34vJMapbHAxQ5+WA8=
|
||||
cloud.google.com/go/datastore v1.13.0 h1:ktbC66bOQB3HJPQe8qNI1/aiQ77PMu7hD4mzE6uxe3w=
|
||||
cloud.google.com/go/datastore v1.13.0/go.mod h1:KjdB88W897MRITkvWWJrg2OUtrR5XVj1EoLgSp6/N70=
|
||||
cloud.google.com/go/datastream v1.10.0 h1:ra/+jMv36zTAGPfi8TRne1hXme+UsKtdcK4j6bnqQiw=
|
||||
cloud.google.com/go/datastream v1.10.0/go.mod h1:hqnmr8kdUBmrnk65k5wNRoHSCYksvpdZIcZIEl8h43Q=
|
||||
cloud.google.com/go/deploy v1.13.0 h1:A+w/xpWgz99EYzB6e31gMGAI/P5jTZ2UO7veQK5jQ8o=
|
||||
cloud.google.com/go/deploy v1.13.0/go.mod h1:tKuSUV5pXbn67KiubiUNUejqLs4f5cxxiCNCeyl0F2g=
|
||||
cloud.google.com/go/dialogflow v1.40.0 h1:sCJbaXt6ogSbxWQnERKAzos57f02PP6WkGbOZvXUdwc=
|
||||
cloud.google.com/go/dialogflow v1.40.0/go.mod h1:L7jnH+JL2mtmdChzAIcXQHXMvQkE3U4hTaNltEuxXn4=
|
||||
cloud.google.com/go/dlp v1.10.1 h1:tF3wsJ2QulRhRLWPzWVkeDz3FkOGVoMl6cmDUHtfYxw=
|
||||
cloud.google.com/go/dlp v1.10.1/go.mod h1:IM8BWz1iJd8njcNcG0+Kyd9OPnqnRNkDV8j42VT5KOI=
|
||||
cloud.google.com/go/documentai v1.22.0 h1:dW8ex9yb3oT9s1yD2+yLcU8Zq15AquRZ+wd0U+TkxFw=
|
||||
cloud.google.com/go/documentai v1.22.0/go.mod h1:yJkInoMcK0qNAEdRnqY/D5asy73tnPe88I1YTZT+a8E=
|
||||
cloud.google.com/go/domains v0.9.1 h1:rqz6KY7mEg7Zs/69U6m6LMbB7PxFDWmT3QWNXIqhHm0=
|
||||
cloud.google.com/go/domains v0.9.1/go.mod h1:aOp1c0MbejQQ2Pjf1iJvnVyT+z6R6s8pX66KaCSDYfE=
|
||||
cloud.google.com/go/edgecontainer v1.1.1 h1:zhHWnLzg6AqzE+I3gzJqiIwHfjEBhWctNQEzqb+FaRo=
|
||||
cloud.google.com/go/edgecontainer v1.1.1/go.mod h1:O5bYcS//7MELQZs3+7mabRqoWQhXCzenBu0R8bz2rwk=
|
||||
cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0=
|
||||
cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU=
|
||||
cloud.google.com/go/essentialcontacts v1.6.2 h1:OEJ0MLXXCW/tX1fkxzEZOsv/wRfyFsvDVNaHWBAvoV0=
|
||||
cloud.google.com/go/essentialcontacts v1.6.2/go.mod h1:T2tB6tX+TRak7i88Fb2N9Ok3PvY3UNbUsMag9/BARh4=
|
||||
cloud.google.com/go/eventarc v1.13.0 h1:xIP3XZi0Xawx8DEfh++mE2lrIi5kQmCr/KcWhJ1q0J4=
|
||||
cloud.google.com/go/eventarc v1.13.0/go.mod h1:mAFCW6lukH5+IZjkvrEss+jmt2kOdYlN8aMx3sRJiAI=
|
||||
cloud.google.com/go/filestore v1.7.1 h1:Eiz8xZzMJc5ppBWkuaod/PUdUZGCFR8ku0uS+Ah2fRw=
|
||||
cloud.google.com/go/filestore v1.7.1/go.mod h1:y10jsorq40JJnjR/lQ8AfFbbcGlw3g+Dp8oN7i7FjV4=
|
||||
cloud.google.com/go/functions v1.15.1 h1:LtAyqvO1TFmNLcROzHZhV0agEJfBi+zfMZsF4RT/a7U=
|
||||
cloud.google.com/go/functions v1.15.1/go.mod h1:P5yNWUTkyU+LvW/S9O6V+V423VZooALQlqoXdoPz5AE=
|
||||
cloud.google.com/go/gkebackup v1.3.0 h1:lgyrpdhtJKV7l1GM15YFt+OCyHMxsQZuSydyNmS0Pxo=
|
||||
cloud.google.com/go/gkebackup v1.3.0/go.mod h1:vUDOu++N0U5qs4IhG1pcOnD1Mac79xWy6GoBFlWCWBU=
|
||||
cloud.google.com/go/gkeconnect v0.8.1 h1:a1ckRvVznnuvDWESM2zZDzSVFvggeBaVY5+BVB8tbT0=
|
||||
cloud.google.com/go/gkeconnect v0.8.1/go.mod h1:KWiK1g9sDLZqhxB2xEuPV8V9NYzrqTUmQR9shJHpOZw=
|
||||
cloud.google.com/go/gkehub v0.14.1 h1:2BLSb8i+Co1P05IYCKATXy5yaaIw/ZqGvVSBTLdzCQo=
|
||||
cloud.google.com/go/gkehub v0.14.1/go.mod h1:VEXKIJZ2avzrbd7u+zeMtW00Y8ddk/4V9511C9CQGTY=
|
||||
cloud.google.com/go/gkemulticloud v1.0.0 h1:MluqhtPVZReoriP5+adGIw+ij/RIeRik8KApCW2WMTw=
|
||||
cloud.google.com/go/gkemulticloud v1.0.0/go.mod h1:kbZ3HKyTsiwqKX7Yw56+wUGwwNZViRnxWK2DVknXWfw=
|
||||
cloud.google.com/go/gsuiteaddons v1.6.1 h1:mi9jxZpzVjLQibTS/XfPZvl+Jr6D5Bs8pGqUjllRb00=
|
||||
cloud.google.com/go/gsuiteaddons v1.6.1/go.mod h1:CodrdOqRZcLp5WOwejHWYBjZvfY0kOphkAKpF/3qdZY=
|
||||
cloud.google.com/go/iap v1.8.1 h1:X1tcp+EoJ/LGX6cUPt3W2D4H2Kbqq0pLAsldnsCjLlE=
|
||||
cloud.google.com/go/iap v1.8.1/go.mod h1:sJCbeqg3mvWLqjZNsI6dfAtbbV1DL2Rl7e1mTyXYREQ=
|
||||
cloud.google.com/go/ids v1.4.1 h1:khXYmSoDDhWGEVxHl4c4IgbwSRR+qE/L4hzP3vaU9Hc=
|
||||
cloud.google.com/go/ids v1.4.1/go.mod h1:np41ed8YMU8zOgv53MMMoCntLTn2lF+SUzlM+O3u/jw=
|
||||
cloud.google.com/go/iot v1.7.1 h1:yrH0OSmicD5bqGBoMlWG8UltzdLkYzNUwNVUVz7OT54=
|
||||
cloud.google.com/go/iot v1.7.1/go.mod h1:46Mgw7ev1k9KqK1ao0ayW9h0lI+3hxeanz+L1zmbbbk=
|
||||
cloud.google.com/go/kms v1.15.0 h1:xYl5WEaSekKYN5gGRyhjvZKM22GVBBCzegGNVPy+aIs=
|
||||
cloud.google.com/go/kms v1.15.0/go.mod h1:c9J991h5DTl+kg7gi3MYomh12YEENGrf48ee/N/2CDM=
|
||||
cloud.google.com/go/language v1.10.1 h1:3MXeGEv8AlX+O2LyV4pO4NGpodanc26AmXwOuipEym0=
|
||||
cloud.google.com/go/language v1.10.1/go.mod h1:CPp94nsdVNiQEt1CNjF5WkTcisLiHPyIbMhvR8H2AW0=
|
||||
cloud.google.com/go/lifesciences v0.9.1 h1:axkANGx1wiBXHiPcJZAE+TDjjYoJRIDzbHC/WYllCBU=
|
||||
cloud.google.com/go/lifesciences v0.9.1/go.mod h1:hACAOd1fFbCGLr/+weUKRAJas82Y4vrL3O5326N//Wc=
|
||||
cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I=
|
||||
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
|
||||
cloud.google.com/go/managedidentities v1.6.1 h1:2/qZuOeLgUHorSdxSQGtnOu9xQkBn37+j+oZQv/KHJY=
|
||||
cloud.google.com/go/managedidentities v1.6.1/go.mod h1:h/irGhTN2SkZ64F43tfGPMbHnypMbu4RB3yl8YcuEak=
|
||||
cloud.google.com/go/maps v1.4.0 h1:PdfgpBLhAoSzZrQXP+/zBc78fIPLZSJp5y8+qSMn2UU=
|
||||
cloud.google.com/go/maps v1.4.0/go.mod h1:6mWTUv+WhnOwAgjVsSW2QPPECmW+s3PcRyOa9vgG/5s=
|
||||
cloud.google.com/go/mediatranslation v0.8.1 h1:50cF7c1l3BanfKrpnTCaTvhf+Fo6kdF21DG0byG7gYU=
|
||||
cloud.google.com/go/mediatranslation v0.8.1/go.mod h1:L/7hBdEYbYHQJhX2sldtTO5SZZ1C1vkapubj0T2aGig=
|
||||
cloud.google.com/go/memcache v1.10.1 h1:7lkLsF0QF+Mre0O/NvkD9Q5utUNwtzvIYjrOLOs0HO0=
|
||||
cloud.google.com/go/memcache v1.10.1/go.mod h1:47YRQIarv4I3QS5+hoETgKO40InqzLP6kpNLvyXuyaA=
|
||||
cloud.google.com/go/metastore v1.12.0 h1:+9DsxUOHvsqvC0ylrRc/JwzbXJaaBpfIK3tX0Lx8Tcc=
|
||||
cloud.google.com/go/metastore v1.12.0/go.mod h1:uZuSo80U3Wd4zi6C22ZZliOUJ3XeM/MlYi/z5OAOWRA=
|
||||
cloud.google.com/go/monitoring v1.15.1 h1:65JhLMd+JiYnXr6j5Z63dUYCuOg770p8a/VC+gil/58=
|
||||
cloud.google.com/go/monitoring v1.15.1/go.mod h1:lADlSAlFdbqQuwwpaImhsJXu1QSdd3ojypXrFSMr2rM=
|
||||
cloud.google.com/go/networkconnectivity v1.12.1 h1:LnrYM6lBEeTq+9f2lR4DjBhv31EROSAQi/P5W4Q0AEc=
|
||||
cloud.google.com/go/networkconnectivity v1.12.1/go.mod h1:PelxSWYM7Sh9/guf8CFhi6vIqf19Ir/sbfZRUwXh92E=
|
||||
cloud.google.com/go/networkmanagement v1.8.0 h1:/3xP37eMxnyvkfLrsm1nv1b2FbMMSAEAOlECTvoeCq4=
|
||||
cloud.google.com/go/networkmanagement v1.8.0/go.mod h1:Ho/BUGmtyEqrttTgWEe7m+8vDdK74ibQc+Be0q7Fof0=
|
||||
cloud.google.com/go/networksecurity v0.9.1 h1:TBLEkMp3AE+6IV/wbIGRNTxnqLXHCTEQWoxRVC18TzY=
|
||||
cloud.google.com/go/networksecurity v0.9.1/go.mod h1:MCMdxOKQ30wsBI1eI659f9kEp4wuuAueoC9AJKSPWZQ=
|
||||
cloud.google.com/go/notebooks v1.9.1 h1:CUqMNEtv4EHFnbogV+yGHQH5iAQLmijOx191innpOcs=
|
||||
cloud.google.com/go/notebooks v1.9.1/go.mod h1:zqG9/gk05JrzgBt4ghLzEepPHNwE5jgPcHZRKhlC1A8=
|
||||
cloud.google.com/go/optimization v1.4.1 h1:pEwOAmO00mxdbesCRSsfj8Sd4rKY9kBrYW7Vd3Pq7cA=
|
||||
cloud.google.com/go/optimization v1.4.1/go.mod h1:j64vZQP7h9bO49m2rVaTVoNM0vEBEN5eKPUPbZyXOrk=
|
||||
cloud.google.com/go/orchestration v1.8.1 h1:KmN18kE/xa1n91cM5jhCh7s1/UfIguSCisw7nTMUzgE=
|
||||
cloud.google.com/go/orchestration v1.8.1/go.mod h1:4sluRF3wgbYVRqz7zJ1/EUNc90TTprliq9477fGobD8=
|
||||
cloud.google.com/go/orgpolicy v1.11.1 h1:I/7dHICQkNwym9erHqmlb50LRU588NPCvkfIY0Bx9jI=
|
||||
cloud.google.com/go/orgpolicy v1.11.1/go.mod h1:8+E3jQcpZJQliP+zaFfayC2Pg5bmhuLK755wKhIIUCE=
|
||||
cloud.google.com/go/osconfig v1.12.1 h1:dgyEHdfqML6cUW6/MkihNdTVc0INQst0qSE8Ou1ub9c=
|
||||
cloud.google.com/go/osconfig v1.12.1/go.mod h1:4CjBxND0gswz2gfYRCUoUzCm9zCABp91EeTtWXyz0tE=
|
||||
cloud.google.com/go/oslogin v1.10.1 h1:LdSuG3xBYu2Sgr3jTUULL1XCl5QBx6xwzGqzoDUw1j0=
|
||||
cloud.google.com/go/oslogin v1.10.1/go.mod h1:x692z7yAue5nE7CsSnoG0aaMbNoRJRXO4sn73R+ZqAs=
|
||||
cloud.google.com/go/phishingprotection v0.8.1 h1:aK/lNmSd1vtbft/vLe2g7edXK72sIQbqr2QyrZN/iME=
|
||||
cloud.google.com/go/phishingprotection v0.8.1/go.mod h1:AxonW7GovcA8qdEk13NfHq9hNx5KPtfxXNeUxTDxB6I=
|
||||
cloud.google.com/go/policytroubleshooter v1.8.0 h1:XTMHy31yFmXgQg57CB3w9YQX8US7irxDX0Fl0VwlZyY=
|
||||
cloud.google.com/go/policytroubleshooter v1.8.0/go.mod h1:tmn5Ir5EToWe384EuboTcVQT7nTag2+DuH3uHmKd1HU=
|
||||
cloud.google.com/go/privatecatalog v0.9.1 h1:B/18xGo+E0EMS9LOEQ0zXz7F2asMgmVgTYGSI89MHOA=
|
||||
cloud.google.com/go/privatecatalog v0.9.1/go.mod h1:0XlDXW2unJXdf9zFz968Hp35gl/bhF4twwpXZAW50JA=
|
||||
cloud.google.com/go/pubsub v1.33.0 h1:6SPCPvWav64tj0sVX/+npCBKhUi/UjJehy9op/V3p2g=
|
||||
cloud.google.com/go/pubsub v1.33.0/go.mod h1:f+w71I33OMyxf9VpMVcZbnG5KSUkCOUHYpFd5U1GdRc=
|
||||
cloud.google.com/go/pubsublite v1.8.1 h1:pX+idpWMIH30/K7c0epN6V703xpIcMXWRjKJsz0tYGY=
|
||||
cloud.google.com/go/pubsublite v1.8.1/go.mod h1:fOLdU4f5xldK4RGJrBMm+J7zMWNj/k4PxwEZXy39QS0=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.7.2 h1:IGkbudobsTXAwmkEYOzPCQPApUCsN4Gbq3ndGVhHQpI=
|
||||
cloud.google.com/go/recaptchaenterprise/v2 v2.7.2/go.mod h1:kR0KjsJS7Jt1YSyWFkseQ756D45kaYNTlDPPaRAvDBU=
|
||||
cloud.google.com/go/recommendationengine v0.8.1 h1:nMr1OEVHuDambRn+/y4RmNAmnR/pXCuHtH0Y4tCgGRQ=
|
||||
cloud.google.com/go/recommendationengine v0.8.1/go.mod h1:MrZihWwtFYWDzE6Hz5nKcNz3gLizXVIDI/o3G1DLcrE=
|
||||
cloud.google.com/go/recommender v1.10.1 h1:UKp94UH5/Lv2WXSQe9+FttqV07x/2p1hFTMMYVFtilg=
|
||||
cloud.google.com/go/recommender v1.10.1/go.mod h1:XFvrE4Suqn5Cq0Lf+mCP6oBHD/yRMA8XxP5sb7Q7gpA=
|
||||
cloud.google.com/go/redis v1.13.1 h1:YrjQnCC7ydk+k30op7DSjSHw1yAYhqYXFcOq1bSXRYA=
|
||||
cloud.google.com/go/redis v1.13.1/go.mod h1:VP7DGLpE91M6bcsDdMuyCm2hIpB6Vp2hI090Mfd1tcg=
|
||||
cloud.google.com/go/resourcemanager v1.9.1 h1:QIAMfndPOHR6yTmMUB0ZN+HSeRmPjR/21Smq5/xwghI=
|
||||
cloud.google.com/go/resourcemanager v1.9.1/go.mod h1:dVCuosgrh1tINZ/RwBufr8lULmWGOkPS8gL5gqyjdT8=
|
||||
cloud.google.com/go/resourcesettings v1.6.1 h1:Fdyq418U69LhvNPFdlEO29w+DRRjwDA4/pFamm4ksAg=
|
||||
cloud.google.com/go/resourcesettings v1.6.1/go.mod h1:M7mk9PIZrC5Fgsu1kZJci6mpgN8o0IUzVx3eJU3y4Jw=
|
||||
cloud.google.com/go/retail v1.14.1 h1:gYBrb9u/Hc5s5lUTFXX1Vsbc/9BEvgtioY6ZKaK0DK8=
|
||||
cloud.google.com/go/retail v1.14.1/go.mod h1:y3Wv3Vr2k54dLNIrCzenyKG8g8dhvhncT2NcNjb/6gE=
|
||||
cloud.google.com/go/run v1.2.0 h1:kHeIG8q+N6Zv0nDkBjSOYfK2eWqa5FnaiDPH/7/HirE=
|
||||
cloud.google.com/go/run v1.2.0/go.mod h1:36V1IlDzQ0XxbQjUx6IYbw8H3TJnWvhii963WW3B/bo=
|
||||
cloud.google.com/go/scheduler v1.10.1 h1:yoZbZR8880KgPGLmACOMCiY2tPk+iX4V/dkxqTirlz8=
|
||||
cloud.google.com/go/scheduler v1.10.1/go.mod h1:R63Ldltd47Bs4gnhQkmNDse5w8gBRrhObZ54PxgR2Oo=
|
||||
cloud.google.com/go/secretmanager v1.11.1 h1:cLTCwAjFh9fKvU6F13Y4L9vPcx9yiWPyWXE4+zkuEQs=
|
||||
cloud.google.com/go/secretmanager v1.11.1/go.mod h1:znq9JlXgTNdBeQk9TBW/FnR/W4uChEKGeqQWAJ8SXFw=
|
||||
cloud.google.com/go/security v1.15.1 h1:jR3itwycg/TgGA0uIgTItcVhA55hKWiNJxaNNpQJaZE=
|
||||
cloud.google.com/go/security v1.15.1/go.mod h1:MvTnnbsWnehoizHi09zoiZob0iCHVcL4AUBj76h9fXA=
|
||||
cloud.google.com/go/securitycenter v1.23.0 h1:XOGJ9OpnDtqg8izd7gYk/XUhj8ytjIalyjjsR6oyG0M=
|
||||
cloud.google.com/go/securitycenter v1.23.0/go.mod h1:8pwQ4n+Y9WCWM278R8W3nF65QtY172h4S8aXyI9/hsQ=
|
||||
cloud.google.com/go/servicedirectory v1.11.0 h1:pBWpjCFVGWkzVTkqN3TBBIqNSoSHY86/6RL0soSQ4z8=
|
||||
cloud.google.com/go/servicedirectory v1.11.0/go.mod h1:Xv0YVH8s4pVOwfM/1eMTl0XJ6bzIOSLDt8f8eLaGOxQ=
|
||||
cloud.google.com/go/shell v1.7.1 h1:aHbwH9LSqs4r2rbay9f6fKEls61TAjT63jSyglsw7sI=
|
||||
cloud.google.com/go/shell v1.7.1/go.mod h1:u1RaM+huXFaTojTbW4g9P5emOrrmLE69KrxqQahKn4g=
|
||||
cloud.google.com/go/spanner v1.47.0 h1:aqiMP8dhsEXgn9K5EZBWxPG7dxIiyM2VaikqeU4iteg=
|
||||
cloud.google.com/go/spanner v1.47.0/go.mod h1:IXsJwVW2j4UKs0eYDqodab6HgGuA1bViSqW4uH9lfUI=
|
||||
cloud.google.com/go/speech v1.19.0 h1:MCagaq8ObV2tr1kZJcJYgXYbIn8Ai5rp42tyGYw9rls=
|
||||
cloud.google.com/go/speech v1.19.0/go.mod h1:8rVNzU43tQvxDaGvqOhpDqgkJTFowBpDvCJ14kGlJYo=
|
||||
cloud.google.com/go/storagetransfer v1.10.0 h1:+ZLkeXx0K0Pk5XdDmG0MnUVqIR18lllsihU/yq39I8Q=
|
||||
cloud.google.com/go/storagetransfer v1.10.0/go.mod h1:DM4sTlSmGiNczmV6iZyceIh2dbs+7z2Ayg6YAiQlYfA=
|
||||
cloud.google.com/go/talent v1.6.2 h1:j46ZgD6N2YdpFPux9mc7OAf4YK3tiBCsbLKc8rQx+bU=
|
||||
cloud.google.com/go/talent v1.6.2/go.mod h1:CbGvmKCG61mkdjcqTcLOkb2ZN1SrQI8MDyma2l7VD24=
|
||||
cloud.google.com/go/texttospeech v1.7.1 h1:S/pR/GZT9p15R7Y2dk2OXD/3AufTct/NSxT4a7nxByw=
|
||||
cloud.google.com/go/texttospeech v1.7.1/go.mod h1:m7QfG5IXxeneGqTapXNxv2ItxP/FS0hCZBwXYqucgSk=
|
||||
cloud.google.com/go/tpu v1.6.1 h1:kQf1jgPY04UJBYYjNUO+3GrZtIb57MfGAW2bwgLbR3A=
|
||||
cloud.google.com/go/tpu v1.6.1/go.mod h1:sOdcHVIgDEEOKuqUoi6Fq53MKHJAtOwtz0GuKsWSH3E=
|
||||
cloud.google.com/go/trace v1.10.1 h1:EwGdOLCNfYOOPtgqo+D2sDLZmRCEO1AagRTJCU6ztdg=
|
||||
cloud.google.com/go/trace v1.10.1/go.mod h1:gbtL94KE5AJLH3y+WVpfWILmqgc6dXcqgNXdOPAQTYk=
|
||||
cloud.google.com/go/translate v1.8.2 h1:PQHamiOzlehqLBJMnM72lXk/OsMQewZB12BKJ8zXrU0=
|
||||
cloud.google.com/go/translate v1.8.2/go.mod h1:d1ZH5aaOA0CNhWeXeC8ujd4tdCFw8XoNWRljklu5RHs=
|
||||
cloud.google.com/go/video v1.19.0 h1:BRyyS+wU+Do6VOXnb8WfPr42ZXti9hzmLKLUCkggeK4=
|
||||
cloud.google.com/go/video v1.19.0/go.mod h1:9qmqPqw/Ib2tLqaeHgtakU+l5TcJxCJbhFXM7UJjVzU=
|
||||
cloud.google.com/go/videointelligence v1.11.1 h1:MBMWnkQ78GQnRz5lfdTAbBq/8QMCF3wahgtHh3s/J+k=
|
||||
cloud.google.com/go/videointelligence v1.11.1/go.mod h1:76xn/8InyQHarjTWsBR058SmlPCwQjgcvoW0aZykOvo=
|
||||
cloud.google.com/go/vision/v2 v2.7.2 h1:ccK6/YgPfGHR/CyESz1mvIbsht5Y2xRsWCPqmTNydEw=
|
||||
cloud.google.com/go/vision/v2 v2.7.2/go.mod h1:jKa8oSYBWhYiXarHPvP4USxYANYUEdEsQrloLjrSwJU=
|
||||
cloud.google.com/go/vmmigration v1.7.1 h1:gnjIclgqbEMc+cF5IJuPxp53wjBIlqZ8h9hE8Rkwp7A=
|
||||
cloud.google.com/go/vmmigration v1.7.1/go.mod h1:WD+5z7a/IpZ5bKK//YmT9E047AD+rjycCAvyMxGJbro=
|
||||
cloud.google.com/go/vmwareengine v1.0.0 h1:qsJ0CPlOQu/3MFBGklu752v3AkD+Pdu091UmXJ+EjTA=
|
||||
cloud.google.com/go/vmwareengine v1.0.0/go.mod h1:Px64x+BvjPZwWuc4HdmVhoygcXqEkGHXoa7uyfTgSI0=
|
||||
cloud.google.com/go/vpcaccess v1.7.1 h1:ram0GzjNWElmbxXMIzeOZUkQ9J8ZAahD6V8ilPGqX0Y=
|
||||
cloud.google.com/go/vpcaccess v1.7.1/go.mod h1:FogoD46/ZU+JUBX9D606X21EnxiszYi2tArQwLY4SXs=
|
||||
cloud.google.com/go/webrisk v1.9.1 h1:Ssy3MkOMOnyRV5H2bkMQ13Umv7CwB/kugo3qkAX83Fk=
|
||||
cloud.google.com/go/webrisk v1.9.1/go.mod h1:4GCmXKcOa2BZcZPn6DCEvE7HypmEJcJkr4mtM+sqYPc=
|
||||
cloud.google.com/go/websecurityscanner v1.6.1 h1:CfEF/vZ+xXyAR3zC9iaC/QRdf1MEgS20r5UR17Q4gOg=
|
||||
cloud.google.com/go/websecurityscanner v1.6.1/go.mod h1:Njgaw3rttgRHXzwCB8kgCYqv5/rGpFCsBOvPbYgszpg=
|
||||
cloud.google.com/go/workflows v1.11.1 h1:2akeQ/PgtRhrNuD/n1WvJd5zb7YyuDZrlOanBj2ihPg=
|
||||
cloud.google.com/go/workflows v1.11.1/go.mod h1:Z+t10G1wF7h8LgdY/EmRcQY8ptBD/nvofaL6FqlET6g=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/OpenIMSDK/protocol v0.0.12/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
||||
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409 h1:DTQ/38ao/CfXsrK0cSAL+h4R/u0VVvfWLZEOlLwEROI=
|
||||
github.com/alecthomas/kingpin/v2 v2.3.1 h1:ANLJcKmQm4nIaog7xdr/id6FM6zm5hHnfZrvtKPxqGg=
|
||||
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
|
||||
github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
|
||||
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
|
||||
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
|
||||
github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
|
||||
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
|
||||
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk=
|
||||
github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
|
||||
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA=
|
||||
github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f/go.mod h1:sfYdkwUW4BA3PbKjySwjJy+O4Pu0h62rlqCMHNk+K+Q=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
|
||||
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
|
||||
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
|
||||
github.com/google/go-pkcs11 v0.2.0 h1:5meDPB26aJ98f+K9G21f0AqZwo/S5BJMJh8nuhMbdsI=
|
||||
github.com/google/go-pkcs11 v0.2.0/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
|
||||
github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
|
||||
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563 h1:2VDxTtn9dAqI2DnnvB9fXpPE4DblOmquyzmN2zxTD8A=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563 h1:FoX+MK4vHThvPO6FbP5q98zD8S3n+d5+DbtK7skl++c=
|
||||
github.com/xdg/scram v1.0.3 h1:nTadYh2Fs4BK2xdldEa2g5bbaZp0/+1nJMMPtPxS/to=
|
||||
github.com/xdg/stringprep v1.0.3 h1:cmL5Enob4W83ti/ZHuZLuKD/xqJfus4fVPwE+/BDm+4=
|
||||
github.com/xhit/go-str2duration v1.2.0 h1:BcV5u025cITWxEQKGWr1URRzrcXtu7uk8+luz3Yuhwc=
|
||||
github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
|
||||
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0 h1:rwOQPCuKAKmwGKq2aVNnYIibI6wnV7EvzgfTCzcdGg8=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
|
||||
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
|
||||
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577 h1:ZX0eQu2J+jOO87sq8fQG8J/Nfp7D7BhHpixIE5EYK/k=
|
||||
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230807174057-1744710a1577/go.mod h1:NjCQG/D8JandXxM57PZbAJL1DCNL6EypA0vPPwfsc7c=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
|
||||
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
@@ -363,3 +363,6 @@ func (m *MessageApi) GetActiveGroup(c *gin.Context) {
|
||||
func (m *MessageApi) SearchMsg(c *gin.Context) {
|
||||
a2r.Call(msg.MsgClient.SearchMessage, m.Client, c)
|
||||
}
|
||||
func (m *MessageApi) GetServerTime(c *gin.Context) {
|
||||
a2r.Call(msg.MsgClient.GetServerTime, m.Client, c)
|
||||
}
|
||||
|
||||
@@ -82,9 +82,10 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
|
||||
userRouterGroup.POST("/get_users", ParseToken, u.GetUsers)
|
||||
userRouterGroup.POST("/get_users_online_status", ParseToken, u.GetUsersOnlineStatus)
|
||||
userRouterGroup.POST("/get_users_online_token_detail", ParseToken, u.GetUsersOnlineTokenDetail)
|
||||
userRouterGroup.POST("/subscribe_users_status", ParseToken, u.UnSubscriberStatus)
|
||||
userRouterGroup.POST("/subscribe_users_status", ParseToken, u.SubscriberStatus)
|
||||
userRouterGroup.POST("/unsubscribe_users_status", ParseToken, u.UnSubscriberStatus)
|
||||
userRouterGroup.POST("/get_users_status", ParseToken, u.GetUserStatus)
|
||||
userRouterGroup.POST("/get_subscribe_users_status", ParseToken, u.GetSubscribeUsersStatus)
|
||||
}
|
||||
// friend routing group
|
||||
friendRouterGroup := r.Group("/friend", ParseToken)
|
||||
@@ -186,6 +187,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
|
||||
|
||||
msgGroup.POST("/batch_send_msg", m.BatchSendMsg)
|
||||
msgGroup.POST("/check_msg_is_send_success", m.CheckMsgIsSendSuccess)
|
||||
msgGroup.POST("/get_server_time", m.GetServerTime)
|
||||
}
|
||||
// Conversation
|
||||
conversationGroup := r.Group("/conversation", ParseToken)
|
||||
|
||||
@@ -83,7 +83,14 @@ func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
|
||||
operationID = strconv.Itoa(rand.Int())
|
||||
}
|
||||
ctx := mcontext.SetOperationID(c, operationID)
|
||||
resp, err := o.Client.AccessURL(ctx, &third.AccessURLReq{Name: name})
|
||||
query := make(map[string]string)
|
||||
for key, values := range c.Request.URL.Query() {
|
||||
if len(values) == 0 {
|
||||
continue
|
||||
}
|
||||
query[key] = values[0]
|
||||
}
|
||||
resp, err := o.Client.AccessURL(ctx, &third.AccessURLReq{Name: name, Query: query})
|
||||
if err != nil {
|
||||
if errs.ErrArgs.Is(err) {
|
||||
c.String(http.StatusBadRequest, err.Error())
|
||||
|
||||
@@ -196,6 +196,12 @@ func (u *UserApi) UnSubscriberStatus(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.SubscribeOrCancelUsersStatus, u.Client, c)
|
||||
}
|
||||
|
||||
// GetUserStatus Get the online status of the user.
|
||||
func (u *UserApi) GetUserStatus(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.GetUserStatus, u.Client, c)
|
||||
}
|
||||
|
||||
// GetSubscribeUsersStatus Get the online status of subscribers.
|
||||
func (u *UserApi) GetSubscribeUsersStatus(c *gin.Context) {
|
||||
a2r.Call(user.UserClient.GetSubscribeUsersStatus, u.Client, c)
|
||||
}
|
||||
|
||||
@@ -40,7 +40,13 @@ type Req struct {
|
||||
}
|
||||
|
||||
func (r *Req) String() string {
|
||||
return utils.StructToJsonString(r)
|
||||
var tReq Req
|
||||
tReq.ReqIdentifier = r.ReqIdentifier
|
||||
tReq.Token = r.Token
|
||||
tReq.SendID = r.SendID
|
||||
tReq.OperationID = r.OperationID
|
||||
tReq.MsgIncr = r.MsgIncr
|
||||
return utils.StructToJsonString(tReq)
|
||||
}
|
||||
|
||||
type Resp struct {
|
||||
@@ -53,7 +59,13 @@ type Resp struct {
|
||||
}
|
||||
|
||||
func (r *Resp) String() string {
|
||||
return utils.StructToJsonString(r)
|
||||
var tResp Resp
|
||||
tResp.ReqIdentifier = r.ReqIdentifier
|
||||
tResp.MsgIncr = r.MsgIncr
|
||||
tResp.OperationID = r.OperationID
|
||||
tResp.ErrCode = r.ErrCode
|
||||
tResp.ErrMsg = r.ErrMsg
|
||||
return utils.StructToJsonString(tResp)
|
||||
}
|
||||
|
||||
type MessageHandler interface {
|
||||
|
||||
@@ -145,7 +145,7 @@ func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) {
|
||||
len(modifyMsgList),
|
||||
)
|
||||
conversationIDMsg := msgprocessor.GetChatConversationIDByMsg(ctxMsgList[0].message)
|
||||
conversationIDNotification := msgprocessor.GetNotificationConversationID(ctxMsgList[0].message)
|
||||
conversationIDNotification := msgprocessor.GetNotificationConversationIDByMsg(ctxMsgList[0].message)
|
||||
och.handleMsg(ctx, msgChannelValue.uniqueKey, conversationIDMsg, storageMsgList, notStorageMsgList)
|
||||
och.handleNotification(
|
||||
ctx,
|
||||
|
||||
+74
-19
@@ -16,6 +16,9 @@ package group
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
@@ -73,20 +76,33 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
|
||||
userRpcClient := rpcclient.NewUserRpcClient(client)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client)
|
||||
conversationRpcClient := rpcclient.NewConversationRpcClient(client)
|
||||
database := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase())
|
||||
pbGroup.RegisterGroupServer(server, &groupServer{
|
||||
GroupDatabase: database,
|
||||
User: userRpcClient,
|
||||
Notification: notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) {
|
||||
users, err := userRpcClient.GetUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil
|
||||
}),
|
||||
conversationRpcClient: conversationRpcClient,
|
||||
msgRpcClient: msgRpcClient,
|
||||
var gs groupServer
|
||||
database := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase(), gs.groupMemberHashCode)
|
||||
gs.GroupDatabase = database
|
||||
gs.User = userRpcClient
|
||||
gs.Notification = notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) {
|
||||
users, err := userRpcClient.GetUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil
|
||||
})
|
||||
gs.conversationRpcClient = conversationRpcClient
|
||||
gs.msgRpcClient = msgRpcClient
|
||||
pbGroup.RegisterGroupServer(server, &gs)
|
||||
//pbGroup.RegisterGroupServer(server, &groupServer{
|
||||
// GroupDatabase: database,
|
||||
// User: userRpcClient,
|
||||
// Notification: notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) {
|
||||
// users, err := userRpcClient.GetUsersInfo(ctx, userIDs)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil
|
||||
// }),
|
||||
// conversationRpcClient: conversationRpcClient,
|
||||
// msgRpcClient: msgRpcClient,
|
||||
//})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -161,6 +177,9 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbGroup.CreateGroupR
|
||||
if req.OwnerUserID == "" {
|
||||
return nil, errs.ErrArgs.Wrap("no group owner")
|
||||
}
|
||||
if req.GroupInfo.GroupType != constant.WorkingGroup {
|
||||
return nil, errs.ErrArgs.Wrap(fmt.Sprintf("group type %d not support", req.GroupInfo.GroupType))
|
||||
}
|
||||
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -690,8 +709,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup
|
||||
return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed")
|
||||
}
|
||||
var inGroup bool
|
||||
_, err = s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.FromUserID)
|
||||
if err == nil {
|
||||
if _, err := s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil {
|
||||
inGroup = true // 已经在群里了
|
||||
} else if !s.IsNotFound(err) {
|
||||
return nil, err
|
||||
@@ -718,6 +736,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
log.ZDebug(ctx, "GroupApplicationResponse", "inGroup", inGroup, "HandleResult", req.HandleResult, "member", member)
|
||||
if err := s.GroupDatabase.HandlerGroupRequest(ctx, req.GroupID, req.FromUserID, req.HandledMsg, req.HandleResult, member); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -727,12 +746,14 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup
|
||||
return nil, err
|
||||
}
|
||||
s.Notification.GroupApplicationAcceptedNotification(ctx, req)
|
||||
if member == nil {
|
||||
log.ZDebug(ctx, "GroupApplicationResponse", "member is nil")
|
||||
} else {
|
||||
s.Notification.MemberEnterNotification(ctx, req.GroupID, req.FromUserID)
|
||||
}
|
||||
case constant.GroupResponseRefuse:
|
||||
s.Notification.GroupApplicationRejectedNotification(ctx, req)
|
||||
}
|
||||
if member != nil {
|
||||
s.Notification.MemberEnterNotification(ctx, req)
|
||||
}
|
||||
return &pbGroup.GroupApplicationResponseResp{}, nil
|
||||
}
|
||||
|
||||
@@ -778,7 +799,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbGroup.JoinGroupReq)
|
||||
if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.Notification.MemberEnterDirectlyNotification(ctx, req.GroupID, req.InviterUserID)
|
||||
s.Notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID)
|
||||
return resp, nil
|
||||
}
|
||||
groupRequest := relationTb.GroupRequestModel{
|
||||
@@ -1486,3 +1507,37 @@ func (s *groupServer) GetGroupUsersReqApplicationList(ctx context.Context, req *
|
||||
resp.Total = total
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (s *groupServer) groupMemberHashCode(ctx context.Context, groupID string) (uint64, error) {
|
||||
userIDs, err := s.GroupDatabase.FindGroupMemberUserID(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var members []*sdkws.GroupMemberFullInfo
|
||||
if len(userIDs) > 0 {
|
||||
resp, err := s.GetGroupMembersInfo(ctx, &pbGroup.GetGroupMembersInfoReq{GroupID: groupID, UserIDs: userIDs})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
members = resp.Members
|
||||
utils.Sort(userIDs, true)
|
||||
}
|
||||
memberMap := utils.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
|
||||
return e.UserID
|
||||
})
|
||||
res := make([]*sdkws.GroupMemberFullInfo, 0, len(members))
|
||||
for _, userID := range userIDs {
|
||||
member, ok := memberMap[userID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
member.AppMangerLevel = 0
|
||||
res = append(res, member)
|
||||
}
|
||||
data, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
sum := md5.Sum(data)
|
||||
return binary.BigEndian.Uint64(sum[:]), nil
|
||||
}
|
||||
|
||||
@@ -26,10 +26,15 @@ import (
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
)
|
||||
|
||||
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (*msg.GetConversationsHasReadAndMaxSeqResp, error) {
|
||||
conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) {
|
||||
var conversationIDs []string
|
||||
if len(req.ConversationIDs) == 0 {
|
||||
conversationIDs, err = m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
conversationIDs = req.ConversationIDs
|
||||
}
|
||||
hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs)
|
||||
if err != nil {
|
||||
@@ -49,7 +54,7 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp := &msg.GetConversationsHasReadAndMaxSeqResp{Seqs: make(map[string]*msg.Seqs)}
|
||||
resp = &msg.GetConversationsHasReadAndMaxSeqResp{Seqs: make(map[string]*msg.Seqs)}
|
||||
for conversarionID, maxSeq := range maxSeqs {
|
||||
resp.Seqs[conversarionID] = &msg.Seqs{
|
||||
HasReadSeq: hasReadSeqs[conversarionID],
|
||||
|
||||
@@ -116,41 +116,78 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
|
||||
if total, chatLogs, err = m.MsgDatabase.SearchMessage(ctx, req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
sendIDs []string
|
||||
recvIDs []string
|
||||
groupIDs []string
|
||||
sendMap = make(map[string]string)
|
||||
recvMap = make(map[string]string)
|
||||
groupMap = make(map[string]*sdkws.GroupInfo)
|
||||
)
|
||||
for _, chatLog := range chatLogs {
|
||||
if chatLog.SenderNickname == "" {
|
||||
sendIDs = append(sendIDs, chatLog.SendID)
|
||||
}
|
||||
switch chatLog.SessionType {
|
||||
case constant.SingleChatType:
|
||||
recvIDs = append(recvIDs, chatLog.RecvID)
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
groupIDs = append(groupIDs, chatLog.GroupID)
|
||||
}
|
||||
}
|
||||
if len(sendIDs) != 0 {
|
||||
sendInfos, err := m.User.GetUsersInfo(ctx, sendIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, sendInfo := range sendInfos {
|
||||
sendMap[sendInfo.UserID] = sendInfo.Nickname
|
||||
}
|
||||
}
|
||||
if len(recvIDs) != 0 {
|
||||
recvInfos, err := m.User.GetUsersInfo(ctx, recvIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, recvInfo := range recvInfos {
|
||||
recvMap[recvInfo.UserID] = recvInfo.Nickname
|
||||
}
|
||||
}
|
||||
if len(groupIDs) != 0 {
|
||||
groupInfos, err := m.Group.GetGroupInfos(ctx, groupIDs, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, groupInfo := range groupInfos {
|
||||
groupMap[groupInfo.GroupID] = groupInfo
|
||||
}
|
||||
}
|
||||
for _, chatLog := range chatLogs {
|
||||
pbChatLog := &msg.ChatLog{}
|
||||
utils.CopyStructFields(pbChatLog, chatLog)
|
||||
pbChatLog.SendTime = chatLog.SendTime
|
||||
pbChatLog.CreateTime = chatLog.CreateTime
|
||||
if chatLog.SenderNickname == "" {
|
||||
sendUser, err := m.User.GetUserInfo(ctx, chatLog.SendID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbChatLog.SenderNickname = sendUser.Nickname
|
||||
pbChatLog.SenderNickname = sendMap[chatLog.SendID]
|
||||
}
|
||||
switch chatLog.SessionType {
|
||||
case constant.SingleChatType:
|
||||
recvUser, err := m.User.GetUserInfo(ctx, chatLog.RecvID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbChatLog.RecvNickname = recvUser.Nickname
|
||||
pbChatLog.RecvNickname = recvMap[chatLog.RecvID]
|
||||
|
||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||
group, err := m.Group.GetGroupInfo(ctx, chatLog.GroupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pbChatLog.SenderFaceURL = group.FaceURL
|
||||
pbChatLog.GroupMemberCount = group.MemberCount
|
||||
pbChatLog.RecvID = group.GroupID
|
||||
pbChatLog.GroupName = group.GroupName
|
||||
pbChatLog.GroupOwner = group.OwnerUserID
|
||||
pbChatLog.GroupType = group.GroupType
|
||||
pbChatLog.SenderFaceURL = groupMap[chatLog.GroupID].FaceURL
|
||||
pbChatLog.GroupMemberCount = groupMap[chatLog.GroupID].MemberCount
|
||||
pbChatLog.RecvID = groupMap[chatLog.GroupID].GroupID
|
||||
pbChatLog.GroupName = groupMap[chatLog.GroupID].GroupName
|
||||
pbChatLog.GroupOwner = groupMap[chatLog.GroupID].OwnerUserID
|
||||
pbChatLog.GroupType = groupMap[chatLog.GroupID].GroupType
|
||||
}
|
||||
resp.ChatLogs = append(resp.ChatLogs, pbChatLog)
|
||||
}
|
||||
|
||||
resp.ChatLogsNum = total
|
||||
return resp, nil
|
||||
}
|
||||
func (m *msgServer) GetServerTime(ctx context.Context, _ *msg.GetServerTimeReq) (*msg.GetServerTimeResp, error) {
|
||||
return &msg.GetServerTimeResp{ServerTime: utils.GetCurrentTimestampByMill()}, nil
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
|
||||
if groupMemberInfo.RoleLevel == constant.GroupOwner {
|
||||
return nil
|
||||
} else {
|
||||
if groupMemberInfo.MuteEndTime >= time.Now().Unix() {
|
||||
if groupMemberInfo.MuteEndTime >= time.Now().UnixMilli() {
|
||||
return errs.ErrMutedInGroup.Wrap()
|
||||
}
|
||||
if groupInfo.Status == constant.GroupStatusMuted && groupMemberInfo.RoleLevel != constant.GroupAdmin {
|
||||
|
||||
@@ -16,8 +16,11 @@ package third
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/third"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
@@ -152,7 +155,21 @@ func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.Co
|
||||
}
|
||||
|
||||
func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*third.AccessURLResp, error) {
|
||||
expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire)
|
||||
opt := &s3.AccessURLOption{}
|
||||
if len(req.Query) > 0 {
|
||||
switch req.Query["type"] {
|
||||
case "":
|
||||
case "image":
|
||||
opt.Image = &s3.Image{}
|
||||
opt.Image.Format = req.Query["format"]
|
||||
opt.Image.Width, _ = strconv.Atoi(req.Query["width"])
|
||||
opt.Image.Height, _ = strconv.Atoi(req.Query["height"])
|
||||
log.ZDebug(ctx, "AccessURL image", "name", req.Name, "option", opt.Image)
|
||||
default:
|
||||
return nil, errs.ErrArgs.Wrap("invalid query type")
|
||||
}
|
||||
}
|
||||
expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire, opt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
|
||||
if apiURL[len(apiURL)-1] != '/' {
|
||||
apiURL += "/"
|
||||
}
|
||||
apiURL += "object/"
|
||||
rdb, err := cache.NewRedis()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
+48
-17
@@ -20,17 +20,16 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/authverify"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||
registry "github.com/OpenIMSDK/tools/discoveryregistry"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/tx"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert"
|
||||
@@ -41,16 +40,17 @@ import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
type userServer struct {
|
||||
controller.UserDatabase
|
||||
notificationSender *notification.FriendNotificationSender
|
||||
friendRpcClient *rpcclient.FriendRpcClient
|
||||
RegisterCenter registry.SvcDiscoveryRegistry
|
||||
friendNotificationSender *notification.FriendNotificationSender
|
||||
userNotificationSender *notification.UserNotificationSender
|
||||
friendRpcClient *rpcclient.FriendRpcClient
|
||||
RegisterCenter registry.SvcDiscoveryRegistry
|
||||
}
|
||||
|
||||
func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
@@ -83,10 +83,11 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
||||
friendRpcClient := rpcclient.NewFriendRpcClient(client)
|
||||
msgRpcClient := rpcclient.NewMessageRpcClient(client)
|
||||
u := &userServer{
|
||||
UserDatabase: database,
|
||||
RegisterCenter: client,
|
||||
friendRpcClient: &friendRpcClient,
|
||||
notificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)),
|
||||
UserDatabase: database,
|
||||
RegisterCenter: client,
|
||||
friendRpcClient: &friendRpcClient,
|
||||
friendNotificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)),
|
||||
userNotificationSender: notification.NewUserNotificationSender(&msgRpcClient, notification.WithUserFunc(database.FindWithError)),
|
||||
}
|
||||
pbuser.RegisterUserServer(server, u)
|
||||
return u.UserDatabase.InitOnce(context.Background(), users)
|
||||
@@ -119,13 +120,13 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_ = s.notificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
|
||||
_ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
|
||||
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, friendID := range friends {
|
||||
s.notificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
|
||||
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -140,7 +141,7 @@ func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Se
|
||||
if err := s.UpdateByMap(ctx, req.UserID, m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.notificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
|
||||
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -273,6 +274,7 @@ func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbus
|
||||
return &pbuser.SubscribeOrCancelUsersStatusResp{}, nil
|
||||
}
|
||||
|
||||
// GetUserStatus Get the online status of the user.
|
||||
func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatusReq) (resp *pbuser.GetUserStatusResp, err error) {
|
||||
onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
|
||||
if err != nil {
|
||||
@@ -281,10 +283,39 @@ func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatu
|
||||
return &pbuser.GetUserStatusResp{StatusList: onlineStatusList}, nil
|
||||
}
|
||||
|
||||
// SetUserStatus Synchronize user's online status.
|
||||
func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatusReq) (resp *pbuser.SetUserStatusResp, err error) {
|
||||
err = s.UserDatabase.SetUserStatus(ctx, req.StatusList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, value := range req.StatusList {
|
||||
list, err := s.UserDatabase.GetSubscribedList(ctx, value.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, userID := range list {
|
||||
tips := &sdkws.UserStatusChangeTips{
|
||||
FromUserID: value.UserID,
|
||||
ToUserID: userID,
|
||||
Status: value.Status,
|
||||
PlatformID: value.PlatformIDs[0],
|
||||
}
|
||||
s.userNotificationSender.UserStatusChangeNotification(ctx, tips)
|
||||
}
|
||||
}
|
||||
return &pbuser.SetUserStatusResp{}, nil
|
||||
}
|
||||
|
||||
// GetSubscribeUsersStatus Get the online status of subscribers.
|
||||
func (s *userServer) GetSubscribeUsersStatus(ctx context.Context, req *pbuser.GetSubscribeUsersStatusReq) (*pbuser.GetSubscribeUsersStatusResp, error) {
|
||||
userList, err := s.UserDatabase.GetAllSubscribeList(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, userList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pbuser.GetSubscribeUsersStatusResp{StatusList: onlineStatusList}, nil
|
||||
}
|
||||
|
||||
@@ -26,12 +26,13 @@ import (
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||
)
|
||||
|
||||
func StartCronTask() error {
|
||||
func StartTask() error {
|
||||
fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime)
|
||||
msgTool, err := InitMsgTool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
msgTool.ConvertTools()
|
||||
c := cron.New()
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
@@ -90,7 +90,7 @@ func InitMsgTool() (*MsgTool, error) {
|
||||
tx.NewGorm(db),
|
||||
userMongoDB,
|
||||
)
|
||||
groupDatabase := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase())
|
||||
groupDatabase := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase(), nil)
|
||||
conversationDatabase := controller.NewConversationDatabase(
|
||||
relation.NewConversationGorm(db),
|
||||
cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), relation.NewConversationGorm(db)),
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/OpenIMSDK/tools/mcontext"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/msgprocessor"
|
||||
)
|
||||
|
||||
func (c *MsgTool) ConvertTools() {
|
||||
ctx := mcontext.NewCtx("convert")
|
||||
conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "get all conversation ids failed", err)
|
||||
return
|
||||
}
|
||||
for _, conversationID := range conversationIDs {
|
||||
conversationIDs = append(conversationIDs, msgprocessor.GetNotificationConversationIDByConversationID(conversationID))
|
||||
}
|
||||
userIDs, err := c.userDatabase.GetAllUserID(ctx, 0, 0)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "get all user ids failed", err)
|
||||
return
|
||||
}
|
||||
log.ZDebug(ctx, "all userIDs", "len userIDs", len(userIDs))
|
||||
for _, userID := range userIDs {
|
||||
conversationIDs = append(conversationIDs, msgprocessor.GetConversationIDBySessionType(constant.SingleChatType, userID, userID))
|
||||
conversationIDs = append(conversationIDs, msgprocessor.GetNotificationConversationID(constant.SingleChatType, userID, userID))
|
||||
}
|
||||
log.ZDebug(ctx, "all conversationIDs", "len userIDs", len(conversationIDs))
|
||||
c.msgDatabase.ConvertMsgsDocLen(ctx, conversationIDs)
|
||||
}
|
||||
@@ -120,6 +120,7 @@ type configStruct struct {
|
||||
AccessKeyID string `yaml:"accessKeyID"`
|
||||
SecretAccessKey string `yaml:"secretAccessKey"`
|
||||
SessionToken string `yaml:"sessionToken"`
|
||||
SignEndpoint string `yaml:"signEndpoint"`
|
||||
} `yaml:"minio"`
|
||||
Cos struct {
|
||||
BucketURL string `yaml:"bucketURL"`
|
||||
@@ -283,7 +284,8 @@ type notification struct {
|
||||
GroupInfoSetAnnouncement NotificationConf `yaml:"groupInfoSetAnnouncement"`
|
||||
GroupInfoSetName NotificationConf `yaml:"groupInfoSetName"`
|
||||
////////////////////////user///////////////////////
|
||||
UserInfoUpdated NotificationConf `yaml:"userInfoUpdated"`
|
||||
UserInfoUpdated NotificationConf `yaml:"userInfoUpdated"`
|
||||
UserStatusChanged NotificationConf `yaml:"userStatusChanged"`
|
||||
//////////////////////friend///////////////////////
|
||||
FriendApplicationAdded NotificationConf `yaml:"friendApplicationAdded"`
|
||||
FriendApplicationApproved NotificationConf `yaml:"friendApplicationApproved"`
|
||||
|
||||
@@ -78,6 +78,7 @@ func MsgDB2Pb(msgModel *unrelation.MsgDataModel) *sdkws.MsgData {
|
||||
msg.SendTime = msgModel.SendTime
|
||||
msg.CreateTime = msgModel.CreateTime
|
||||
msg.Status = msgModel.Status
|
||||
msg.IsRead = msgModel.IsRead
|
||||
msg.Options = msgModel.Options
|
||||
if msgModel.OfflinePush != nil {
|
||||
msg.OfflinePushInfo = &sdkws.OfflinePushInfo{
|
||||
|
||||
Vendored
+64
-20
@@ -16,10 +16,10 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
|
||||
"github.com/dtm-labs/rockscache"
|
||||
"github.com/redis/go-redis/v9"
|
||||
|
||||
@@ -33,7 +33,7 @@ const (
|
||||
groupExpireTime = time.Second * 60 * 60 * 12
|
||||
groupInfoKey = "GROUP_INFO:"
|
||||
groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
||||
groupMembersHashKey = "GROUP_MEMBERS_HASH:"
|
||||
groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
||||
groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
||||
joinedSuperGroupsKey = "JOIN_SUPER_GROUPS:"
|
||||
SuperGroupMemberIDsKey = "SUPER_GROUP_MEMBER_IDS:"
|
||||
@@ -96,6 +96,7 @@ type GroupCacheRedis struct {
|
||||
mongoDB unrelationTb.SuperGroupModelInterface
|
||||
expireTime time.Duration
|
||||
rcClient *rockscache.Client
|
||||
hashCode func(ctx context.Context, groupID string) (uint64, error)
|
||||
}
|
||||
|
||||
func NewGroupCacheRedis(
|
||||
@@ -104,13 +105,16 @@ func NewGroupCacheRedis(
|
||||
groupMemberDB relationTb.GroupMemberModelInterface,
|
||||
groupRequestDB relationTb.GroupRequestModelInterface,
|
||||
mongoClient unrelationTb.SuperGroupModelInterface,
|
||||
hashCode func(ctx context.Context, groupID string) (uint64, error),
|
||||
opts rockscache.Options,
|
||||
) GroupCache {
|
||||
rcClient := rockscache.NewClient(rdb, opts)
|
||||
return &GroupCacheRedis{
|
||||
rcClient: rcClient, expireTime: groupExpireTime,
|
||||
groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB,
|
||||
mongoDB: mongoClient, metaCache: NewMetaCacheRedis(rcClient),
|
||||
mongoDB: mongoClient,
|
||||
hashCode: hashCode,
|
||||
metaCache: NewMetaCacheRedis(rcClient),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,22 +294,61 @@ func (g *GroupCacheRedis) DelSuperGroupMemberIDs(groupIDs ...string) GroupCache
|
||||
|
||||
// groupMembersHash.
|
||||
func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID string) (hashCode uint64, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
g.rcClient,
|
||||
g.getGroupMembersHashKey(groupID),
|
||||
g.expireTime,
|
||||
func(ctx context.Context) (uint64, error) {
|
||||
userIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
utils.Sort(userIDs, true)
|
||||
bi := big.NewInt(0)
|
||||
bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16)
|
||||
return bi.Uint64(), nil
|
||||
},
|
||||
)
|
||||
return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime, func(ctx context.Context) (uint64, error) {
|
||||
return g.hashCode(ctx, groupID)
|
||||
})
|
||||
|
||||
//return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime,
|
||||
// func(ctx context.Context) (uint64, error) {
|
||||
// userIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// log.ZInfo(ctx, "GetGroupMembersHash", "groupID", groupID, "userIDs", userIDs)
|
||||
// var members []*relationTb.GroupMemberModel
|
||||
// if len(userIDs) > 0 {
|
||||
// members, err = g.GetGroupMembersInfo(ctx, groupID, userIDs)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// utils.Sort(userIDs, true)
|
||||
// }
|
||||
// memberMap := make(map[string]*relationTb.GroupMemberModel)
|
||||
// for i, member := range members {
|
||||
// memberMap[member.UserID] = members[i]
|
||||
// }
|
||||
// data := make([]string, 0, len(members)*11)
|
||||
// for _, userID := range userIDs {
|
||||
// member, ok := memberMap[userID]
|
||||
// if !ok {
|
||||
// continue
|
||||
// }
|
||||
// data = append(data,
|
||||
// member.GroupID,
|
||||
// member.UserID,
|
||||
// member.Nickname,
|
||||
// member.FaceURL,
|
||||
// strconv.Itoa(int(member.RoleLevel)),
|
||||
// strconv.FormatInt(member.JoinTime.UnixMilli(), 10),
|
||||
// strconv.Itoa(int(member.JoinSource)),
|
||||
// member.InviterUserID,
|
||||
// member.OperatorUserID,
|
||||
// strconv.FormatInt(member.MuteEndTime.UnixMilli(), 10),
|
||||
// member.Ex,
|
||||
// )
|
||||
// }
|
||||
// log.ZInfo(ctx, "hash data info", "userIDs.len", len(userIDs), "hash.data.len", len(data))
|
||||
// log.ZInfo(ctx, "json hash data", "groupID", groupID, "data", data)
|
||||
// val, err := json.Marshal(data)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// sum := md5.Sum(val)
|
||||
// code := binary.BigEndian.Uint64(sum[:])
|
||||
// log.ZInfo(ctx, "GetGroupMembersHash", "groupID", groupID, "hashCode", code, "num", len(members))
|
||||
// return code, nil
|
||||
// },
|
||||
//)
|
||||
}
|
||||
|
||||
func (g *GroupCacheRedis) GetGroupMemberHashMap(
|
||||
@@ -318,6 +361,7 @@ func (g *GroupCacheRedis) GetGroupMemberHashMap(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.ZInfo(ctx, "GetGroupMemberHashMap", "groupID", groupID, "hash", hash)
|
||||
num, err := g.GetGroupMemberNum(ctx, groupID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
Vendored
+77
-8
@@ -21,6 +21,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/user"
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
|
||||
@@ -37,6 +39,7 @@ const (
|
||||
olineStatusKey = "ONLINE_STATUS:"
|
||||
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
|
||||
statusMod = 501
|
||||
platformID = "_PlatformIDSuffix"
|
||||
)
|
||||
|
||||
type UserCache interface {
|
||||
@@ -92,6 +95,10 @@ func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string {
|
||||
return userGlobalRecvMsgOptKey + userID
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) getUserStatusHashKey(userID string, Id int32) string {
|
||||
return userID + "_" + string(Id) + platformID
|
||||
}
|
||||
|
||||
func (u *UserCacheRedis) GetUserInfo(ctx context.Context, userID string) (userInfo *relationTb.UserModel, err error) {
|
||||
return getCache(
|
||||
ctx,
|
||||
@@ -177,9 +184,9 @@ func (u *UserCacheRedis) GetUserStatus(ctx context.Context, userIDs []string) ([
|
||||
if err == redis.Nil {
|
||||
// key or field does not exist
|
||||
res = append(res, &user.OnlineStatus{
|
||||
UserID: userID,
|
||||
Status: 0,
|
||||
PlatformID: -1,
|
||||
UserID: userID,
|
||||
Status: constant.Offline,
|
||||
PlatformIDs: nil,
|
||||
})
|
||||
continue
|
||||
} else {
|
||||
@@ -211,12 +218,74 @@ func (u *UserCacheRedis) SetUserStatus(ctx context.Context, list []*user.OnlineS
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
_, err = u.rdb.HSet(ctx, key, status.UserID, string(jsonData)).Result()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
if isNewKey > 0 {
|
||||
if isNewKey == 0 {
|
||||
_, err = u.rdb.HSet(ctx, key, status.UserID, string(jsonData)).Result()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
u.rdb.Expire(ctx, key, userOlineStatusExpireTime)
|
||||
} else {
|
||||
result, err := u.rdb.HGet(ctx, key, status.UserID).Result()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
var onlineStatus user.OnlineStatus
|
||||
err = json.Unmarshal([]byte(result), &onlineStatus)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
onlineStatus.UserID = status.UserID
|
||||
if status.Status == constant.Offline {
|
||||
var newPlatformIDs []int32
|
||||
for _, val := range onlineStatus.PlatformIDs {
|
||||
if val != status.PlatformIDs[0] {
|
||||
newPlatformIDs = append(newPlatformIDs, val)
|
||||
}
|
||||
}
|
||||
if newPlatformIDs == nil {
|
||||
onlineStatus.Status = constant.Offline
|
||||
onlineStatus.PlatformIDs = nil
|
||||
newjsonData, err := json.Marshal(&onlineStatus)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
_, err = u.rdb.HSet(ctx, key, status.UserID, string(newjsonData)).Result()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
} else {
|
||||
onlineStatus.PlatformIDs = newPlatformIDs
|
||||
newjsonData, err := json.Marshal(&onlineStatus)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
_, err = u.rdb.HSet(ctx, key, status.UserID, string(newjsonData)).Result()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onlineStatus.Status = constant.Online
|
||||
// Judging whether to be kicked out.
|
||||
flag := false
|
||||
for _, val := range onlineStatus.PlatformIDs {
|
||||
if val == status.PlatformIDs[0] {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
onlineStatus.PlatformIDs = append(onlineStatus.PlatformIDs, status.PlatformIDs[0])
|
||||
}
|
||||
newjsonData, err := json.Marshal(&onlineStatus)
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
_, err = u.rdb.HSet(ctx, key, status.UserID, string(newjsonData)).Result()
|
||||
if err != nil {
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -209,6 +209,7 @@ func (f *friendDatabase) RefuseFriendRequest(
|
||||
if fr.HandleResult != 0 {
|
||||
return errs.ErrArgs.Wrap("the friend request has been processed")
|
||||
}
|
||||
log.ZDebug(ctx, "refuse friend request", "friendRequest db", fr, "friendRequest arg", friendRequest)
|
||||
friendRequest.HandleResult = constant.FriendResponseRefuse
|
||||
friendRequest.HandleTime = time.Now()
|
||||
err = f.friendRequest.Update(ctx, friendRequest)
|
||||
|
||||
@@ -153,7 +153,7 @@ func NewGroupDatabase(
|
||||
return database
|
||||
}
|
||||
|
||||
func InitGroupDatabase(db *gorm.DB, rdb redis.UniversalClient, database *mongo.Database) GroupDatabase {
|
||||
func InitGroupDatabase(db *gorm.DB, rdb redis.UniversalClient, database *mongo.Database, hashCode func(ctx context.Context, groupID string) (uint64, error)) GroupDatabase {
|
||||
rcOptions := rockscache.NewDefaultOptions()
|
||||
rcOptions.StrongConsistency = true
|
||||
rcOptions.RandomExpireAdjustment = 0.2
|
||||
@@ -170,6 +170,7 @@ func InitGroupDatabase(db *gorm.DB, rdb redis.UniversalClient, database *mongo.D
|
||||
relation.NewGroupMemberDB(db),
|
||||
relation.NewGroupRequest(db),
|
||||
unrelation.NewSuperGroupMongoDriver(database),
|
||||
hashCode,
|
||||
rcOptions,
|
||||
),
|
||||
)
|
||||
@@ -315,7 +316,7 @@ func (g *groupDatabase) FindGroupMember(
|
||||
userIDs []string,
|
||||
roleLevels []int32,
|
||||
) (totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||
if roleLevels == nil {
|
||||
if len(roleLevels) == 0 {
|
||||
for _, groupID := range groupIDs {
|
||||
groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, userIDs)
|
||||
if err != nil {
|
||||
@@ -386,8 +387,24 @@ func (g *groupDatabase) HandlerGroupRequest(
|
||||
handleResult int32,
|
||||
member *relationTb.GroupMemberModel,
|
||||
) error {
|
||||
cache := g.cache.NewCache()
|
||||
if err := g.tx.Transaction(func(tx any) error {
|
||||
//cache := g.cache.NewCache()
|
||||
//if err := g.tx.Transaction(func(tx any) error {
|
||||
// if err := g.groupRequestDB.NewTx(tx).UpdateHandler(ctx, groupID, userID, handledMsg, handleResult); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if member != nil {
|
||||
// if err := g.groupMemberDB.NewTx(tx).Create(ctx, []*relationTb.GroupMemberModel{member}); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// cache = cache.DelGroupMembersHash(groupID).DelGroupMemberIDs(groupID).DelGroupsMemberNum(groupID).DelJoinedGroupID(member.UserID)
|
||||
// }
|
||||
// return nil
|
||||
//}); err != nil {
|
||||
// return err
|
||||
//}
|
||||
//return cache.ExecDel(ctx)
|
||||
|
||||
return g.tx.Transaction(func(tx any) error {
|
||||
if err := g.groupRequestDB.NewTx(tx).UpdateHandler(ctx, groupID, userID, handledMsg, handleResult); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -395,13 +412,12 @@ func (g *groupDatabase) HandlerGroupRequest(
|
||||
if err := g.groupMemberDB.NewTx(tx).Create(ctx, []*relationTb.GroupMemberModel{member}); err != nil {
|
||||
return err
|
||||
}
|
||||
cache = cache.DelGroupMembersHash(groupID).DelGroupMemberIDs(groupID).DelGroupsMemberNum(groupID).DelJoinedGroupID(member.UserID)
|
||||
if err := g.cache.NewCache().DelGroupMembersHash(groupID).DelGroupMembersInfo(groupID, member.UserID).DelGroupMemberIDs(groupID).DelGroupsMemberNum(groupID).DelJoinedGroupID(member.UserID).ExecDel(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.ExecDel(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (g *groupDatabase) DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error {
|
||||
|
||||
@@ -118,6 +118,7 @@ type CommonMsgDatabase interface {
|
||||
pageNumber int32,
|
||||
showNumber int32,
|
||||
) (msgCount int64, userCount int64, groups []*unRelationTb.GroupCount, dateCount map[string]int64, err error)
|
||||
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)
|
||||
}
|
||||
|
||||
func NewCommonMsgDatabase(msgDocModel unRelationTb.MsgDocModelInterface, cacheModel cache.MsgModel) CommonMsgDatabase {
|
||||
@@ -730,34 +731,21 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio
|
||||
delStruct.delDocIDs = append(delStruct.delDocIDs, msgDocModel.DocID)
|
||||
delStruct.minSeq = msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.Seq
|
||||
} else {
|
||||
var hasMarkDelFlag bool
|
||||
var delMsgIndexs []int
|
||||
for i, MsgInfoModel := range msgDocModel.Msg {
|
||||
if MsgInfoModel != nil && MsgInfoModel.Msg != nil {
|
||||
if utils.GetCurrentTimestampByMill() > MsgInfoModel.Msg.SendTime+(remainTime*1000) {
|
||||
delMsgIndexs = append(delMsgIndexs, i)
|
||||
hasMarkDelFlag = true
|
||||
} else {
|
||||
// 到本条消息不需要删除, minSeq置为这条消息的seq
|
||||
if len(delStruct.delDocIDs) > 0 {
|
||||
log.ZDebug(ctx, "delete docs", "delDocIDs", delStruct.delDocIDs)
|
||||
}
|
||||
if err := db.msgDocDatabase.DeleteDocs(ctx, delStruct.delDocIDs); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if hasMarkDelFlag {
|
||||
log.ZDebug(ctx, "delete msg by index", "delMsgIndexs", delMsgIndexs, "docID", msgDocModel.DocID)
|
||||
// mark del all delMsgIndexs
|
||||
if err := db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, msgDocModel.DocID, delMsgIndexs); err != nil {
|
||||
return delStruct.getSetMinSeq(), err
|
||||
}
|
||||
}
|
||||
return MsgInfoModel.Msg.Seq, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(delMsgIndexs) > 0 {
|
||||
if err := db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, msgDocModel.DocID, delMsgIndexs); err != nil {
|
||||
log.ZError(ctx, "deleteMsgRecursion DeleteMsgsInOneDocByIndex failed", err, "conversationID", conversationID, "index", index)
|
||||
}
|
||||
delStruct.minSeq = int64(msgDocModel.Msg[delMsgIndexs[len(delMsgIndexs)-1]].Msg.Seq)
|
||||
}
|
||||
}
|
||||
// 继续递归 index+1
|
||||
seq, err := db.deleteMsgRecursion(ctx, conversationID, index+1, delStruct, remainTime)
|
||||
return seq, err
|
||||
}
|
||||
@@ -961,7 +949,14 @@ func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbMsg.Searc
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
if msg.IsRead {
|
||||
msg.Msg.IsRead = true
|
||||
}
|
||||
totalMsgs = append(totalMsgs, convert.MsgDB2Pb(msg.Msg))
|
||||
}
|
||||
return total, totalMsgs, nil
|
||||
}
|
||||
|
||||
func (db *commonMsgDatabase) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) {
|
||||
db.msgDocDatabase.ConvertMsgsDocLen(ctx, conversationIDs)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type S3Database interface {
|
||||
AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error)
|
||||
InitiateMultipartUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*cont.InitiateUploadResult, error)
|
||||
CompleteMultipartUpload(ctx context.Context, uploadID string, parts []string) (*cont.UploadResult, error)
|
||||
AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error)
|
||||
AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error)
|
||||
SetObject(ctx context.Context, info *relation.ObjectModel) error
|
||||
}
|
||||
|
||||
@@ -70,14 +70,19 @@ func (s *s3Database) SetObject(ctx context.Context, info *relation.ObjectModel)
|
||||
return s.obj.SetObject(ctx, info)
|
||||
}
|
||||
|
||||
func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration) (time.Time, string, error) {
|
||||
func (s *s3Database) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (time.Time, string, error) {
|
||||
obj, err := s.obj.Take(ctx, name)
|
||||
if err != nil {
|
||||
return time.Time{}, "", err
|
||||
}
|
||||
opt := &s3.AccessURLOption{
|
||||
ContentType: obj.ContentType,
|
||||
Filename: filepath.Base(obj.Name),
|
||||
if opt == nil {
|
||||
opt = &s3.AccessURLOption{}
|
||||
}
|
||||
if opt.ContentType == "" {
|
||||
opt.ContentType = obj.ContentType
|
||||
}
|
||||
if opt.Filename == "" {
|
||||
opt.Filename = filepath.Base(obj.Name)
|
||||
}
|
||||
expireTime := time.Now().Add(expire)
|
||||
rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt)
|
||||
|
||||
@@ -16,7 +16,8 @@ package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
"time"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/user"
|
||||
|
||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
@@ -52,7 +53,7 @@ type UserDatabase interface {
|
||||
CountTotal(ctx context.Context, before *time.Time) (int64, error)
|
||||
// CountRangeEverydayTotal Get the user increment in the range
|
||||
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
|
||||
//SubscribeUsersStatus Subscribe a user's presence status
|
||||
// SubscribeUsersStatus Subscribe a user's presence status
|
||||
SubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error
|
||||
// UnsubscribeUsersStatus unsubscribe a user's presence status
|
||||
UnsubscribeUsersStatus(ctx context.Context, userID string, userIDs []string) error
|
||||
|
||||
@@ -71,10 +71,13 @@ func (f *FriendRequestGorm) UpdateByMap(
|
||||
|
||||
// 更新记录 (非零值).
|
||||
func (f *FriendRequestGorm) Update(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
|
||||
fr2 := *friendRequest
|
||||
fr2.FromUserID = ""
|
||||
fr2.ToUserID = ""
|
||||
return utils.Wrap(
|
||||
f.db(ctx).
|
||||
Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID).
|
||||
Updates(friendRequest).
|
||||
Updates(fr2).
|
||||
Error,
|
||||
"",
|
||||
)
|
||||
|
||||
@@ -33,5 +33,6 @@ func NewMetaDB(db *gorm.DB, table any) *MetaDB {
|
||||
}
|
||||
|
||||
func (g *MetaDB) db(ctx context.Context) *gorm.DB {
|
||||
return g.DB.WithContext(ctx).Model(g.table)
|
||||
db := g.DB.WithContext(ctx).Model(g.table)
|
||||
return db
|
||||
}
|
||||
|
||||
@@ -86,7 +86,11 @@ func (u *UserGorm) Page(
|
||||
|
||||
// 获取所有用户ID.
|
||||
func (u *UserGorm) GetAllUserID(ctx context.Context, pageNumber, showNumber int32) (userIDs []string, err error) {
|
||||
return userIDs, errs.Wrap(u.db(ctx).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Pluck("user_id", &userIDs).Error)
|
||||
if pageNumber == 0 || showNumber == 0 {
|
||||
return userIDs, errs.Wrap(u.db(ctx).Pluck("user_id", &userIDs).Error)
|
||||
} else {
|
||||
return userIDs, errs.Wrap(u.db(ctx).Limit(int(showNumber)).Offset(int((pageNumber-1)*showNumber)).Pluck("user_id", &userIDs).Error)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGorm) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) {
|
||||
|
||||
@@ -257,5 +257,9 @@ func (c *Controller) IsNotFound(err error) bool {
|
||||
}
|
||||
|
||||
func (c *Controller) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
if opt.Image != nil {
|
||||
opt.Filename = ""
|
||||
opt.ContentType = ""
|
||||
}
|
||||
return c.impl.AccessURL(ctx, name, expire, opt)
|
||||
}
|
||||
|
||||
@@ -36,6 +36,19 @@ const (
|
||||
maxNumSize = 1000
|
||||
)
|
||||
|
||||
const (
|
||||
imagePng = "png"
|
||||
imageJpg = "jpg"
|
||||
imageJpeg = "jpeg"
|
||||
imageGif = "gif"
|
||||
imageWebp = "webp"
|
||||
)
|
||||
|
||||
const (
|
||||
videoSnapshotImagePng = "png"
|
||||
videoSnapshotImageJpg = "jpg"
|
||||
)
|
||||
|
||||
func NewCos() (s3.Interface, error) {
|
||||
conf := config.Config.Object.Cos
|
||||
u, err := url.Parse(conf.BucketURL)
|
||||
@@ -248,19 +261,44 @@ func (c *Cos) ListUploadedParts(ctx context.Context, uploadID string, name strin
|
||||
}
|
||||
|
||||
func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
var option *cos.PresignedURLOptions
|
||||
var imageMogr string
|
||||
var option cos.PresignedURLOptions
|
||||
if opt != nil {
|
||||
query := make(url.Values)
|
||||
if opt.Image != nil {
|
||||
// https://cloud.tencent.com/document/product/436/44880
|
||||
style := make([]string, 0, 2)
|
||||
wh := make([]string, 2)
|
||||
if opt.Image.Width > 0 {
|
||||
wh[0] = strconv.Itoa(opt.Image.Width)
|
||||
}
|
||||
if opt.Image.Height > 0 {
|
||||
wh[1] = strconv.Itoa(opt.Image.Height)
|
||||
}
|
||||
if opt.Image.Width > 0 || opt.Image.Height > 0 {
|
||||
style = append(style, strings.Join(wh, "x"))
|
||||
}
|
||||
switch opt.Image.Format {
|
||||
case
|
||||
imagePng,
|
||||
imageJpg,
|
||||
imageJpeg,
|
||||
imageGif,
|
||||
imageWebp:
|
||||
style = append(style, "format/"+opt.Image.Format)
|
||||
}
|
||||
if len(style) > 0 {
|
||||
imageMogr = "&imageMogr2/thumbnail/" + strings.Join(style, "/") + "/ignore-error/1"
|
||||
}
|
||||
}
|
||||
if opt.ContentType != "" {
|
||||
query.Set("response-content-type", opt.ContentType)
|
||||
}
|
||||
if opt.Filename != "" {
|
||||
query.Set("response-content-disposition", `attachment; filename="`+opt.Filename+`"`)
|
||||
query.Set("response-content-disposition", `attachment; filename=`+strconv.Quote(opt.Filename))
|
||||
}
|
||||
if len(query) > 0 {
|
||||
option = &cos.PresignedURLOptions{
|
||||
Query: &query,
|
||||
}
|
||||
option.Query = &query
|
||||
}
|
||||
}
|
||||
if expire <= 0 {
|
||||
@@ -268,9 +306,13 @@ func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration,
|
||||
} else if expire < time.Second {
|
||||
expire = time.Second
|
||||
}
|
||||
rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, c.credential.SecretID, c.credential.SecretKey, expire, option)
|
||||
rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, c.credential.SecretID, c.credential.SecretKey, expire, &option)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rawURL.String(), nil
|
||||
urlStr := rawURL.String()
|
||||
if imageMogr != "" {
|
||||
urlStr += imageMogr
|
||||
}
|
||||
return urlStr, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package minio
|
||||
|
||||
import (
|
||||
"image"
|
||||
_ "image/gif"
|
||||
_ "image/jpeg"
|
||||
_ "image/png"
|
||||
"io"
|
||||
|
||||
_ "golang.org/x/image/bmp"
|
||||
_ "golang.org/x/image/tiff"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
const (
|
||||
formatPng = "png"
|
||||
formatJpeg = "jpeg"
|
||||
formatJpg = "jpg"
|
||||
formatGif = "gif"
|
||||
)
|
||||
|
||||
func ImageStat(reader io.Reader) (image.Image, string, error) {
|
||||
return image.Decode(reader)
|
||||
}
|
||||
|
||||
func ImageWidthHeight(img image.Image) (int, int) {
|
||||
bounds := img.Bounds().Max
|
||||
return bounds.X, bounds.Y
|
||||
}
|
||||
|
||||
func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image {
|
||||
bounds := img.Bounds()
|
||||
imgWidth := bounds.Max.X
|
||||
imgHeight := bounds.Max.Y
|
||||
|
||||
// 计算缩放比例
|
||||
scaleWidth := float64(maxWidth) / float64(imgWidth)
|
||||
scaleHeight := float64(maxHeight) / float64(imgHeight)
|
||||
|
||||
// 如果都为0,则不缩放,返回原始图片
|
||||
if maxWidth == 0 && maxHeight == 0 {
|
||||
return img
|
||||
}
|
||||
|
||||
// 如果宽度和高度都大于0,则选择较小的缩放比例,以保持宽高比
|
||||
if maxWidth > 0 && maxHeight > 0 {
|
||||
scale := scaleWidth
|
||||
if scaleHeight < scaleWidth {
|
||||
scale = scaleHeight
|
||||
}
|
||||
|
||||
// 计算缩略图尺寸
|
||||
thumbnailWidth := int(float64(imgWidth) * scale)
|
||||
thumbnailHeight := int(float64(imgHeight) * scale)
|
||||
|
||||
// 使用"image"库的Resample方法生成缩略图
|
||||
thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight))
|
||||
for y := 0; y < thumbnailHeight; y++ {
|
||||
for x := 0; x < thumbnailWidth; x++ {
|
||||
srcX := int(float64(x) / scale)
|
||||
srcY := int(float64(y) / scale)
|
||||
thumbnail.Set(x, y, img.At(srcX, srcY))
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
// 如果只指定了宽度或高度,则根据最大不超过的规则生成缩略图
|
||||
if maxWidth > 0 {
|
||||
thumbnailWidth := maxWidth
|
||||
thumbnailHeight := int(float64(imgHeight) * scaleWidth)
|
||||
|
||||
// 使用"image"库的Resample方法生成缩略图
|
||||
thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight))
|
||||
for y := 0; y < thumbnailHeight; y++ {
|
||||
for x := 0; x < thumbnailWidth; x++ {
|
||||
srcX := int(float64(x) / scaleWidth)
|
||||
srcY := int(float64(y) / scaleWidth)
|
||||
thumbnail.Set(x, y, img.At(srcX, srcY))
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
if maxHeight > 0 {
|
||||
thumbnailWidth := int(float64(imgWidth) * scaleHeight)
|
||||
thumbnailHeight := maxHeight
|
||||
|
||||
// 使用"image"库的Resample方法生成缩略图
|
||||
thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight))
|
||||
for y := 0; y < thumbnailHeight; y++ {
|
||||
for x := 0; x < thumbnailWidth; x++ {
|
||||
srcX := int(float64(x) / scaleHeight)
|
||||
srcY := int(float64(y) / scaleHeight)
|
||||
thumbnail.Set(x, y, img.At(srcX, srcY))
|
||||
}
|
||||
}
|
||||
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
// 默认情况下,返回原始图片
|
||||
return img
|
||||
}
|
||||
+196
-12
@@ -15,16 +15,28 @@
|
||||
package minio
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
"github.com/minio/minio-go/v7/pkg/signer"
|
||||
@@ -43,6 +55,13 @@ const (
|
||||
maxNumSize = 10000
|
||||
)
|
||||
|
||||
const (
|
||||
maxImageWidth = 1024
|
||||
maxImageHeight = 1024
|
||||
maxImageSize = 1024 * 1024 * 50
|
||||
pathInfo = "openim/thumbnail"
|
||||
)
|
||||
|
||||
func NewMinio() (s3.Interface, error) {
|
||||
conf := config.Config.Object.Minio
|
||||
u, err := url.Parse(conf.Endpoint)
|
||||
@@ -60,11 +79,27 @@ func NewMinio() (s3.Interface, error) {
|
||||
m := &Minio{
|
||||
bucket: conf.Bucket,
|
||||
bucketURL: conf.Endpoint + "/" + conf.Bucket + "/",
|
||||
opts: opts,
|
||||
core: &minio.Core{Client: client},
|
||||
lock: &sync.Mutex{},
|
||||
init: false,
|
||||
}
|
||||
if conf.SignEndpoint == "" || conf.SignEndpoint == conf.Endpoint {
|
||||
m.opts = opts
|
||||
m.sign = m.core.Client
|
||||
} else {
|
||||
su, err := url.Parse(conf.SignEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.opts = &minio.Options{
|
||||
Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, conf.SessionToken),
|
||||
Secure: su.Scheme == "https",
|
||||
}
|
||||
m.sign, err = minio.New(su.Host, m.opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
if err := m.initMinio(ctx); err != nil {
|
||||
@@ -76,8 +111,10 @@ func NewMinio() (s3.Interface, error) {
|
||||
type Minio struct {
|
||||
bucket string
|
||||
bucketURL string
|
||||
location string
|
||||
opts *minio.Options
|
||||
core *minio.Core
|
||||
sign *minio.Client
|
||||
lock sync.Locker
|
||||
init bool
|
||||
}
|
||||
@@ -91,15 +128,43 @@ func (m *Minio) initMinio(ctx context.Context) error {
|
||||
if m.init {
|
||||
return nil
|
||||
}
|
||||
exists, err := m.core.Client.BucketExists(ctx, config.Config.Object.Minio.Bucket)
|
||||
conf := config.Config.Object.Minio
|
||||
exists, err := m.core.Client.BucketExists(ctx, conf.Bucket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("check bucket exists error: %w", err)
|
||||
}
|
||||
if !exists {
|
||||
if err := m.core.Client.MakeBucket(ctx, config.Config.Object.Minio.Bucket, minio.MakeBucketOptions{}); err != nil {
|
||||
if err := m.core.Client.MakeBucket(ctx, conf.Bucket, minio.MakeBucketOptions{}); err != nil {
|
||||
return fmt.Errorf("make bucket error: %w", err)
|
||||
}
|
||||
}
|
||||
m.location, err = m.core.Client.GetBucketLocation(ctx, conf.Bucket)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func() {
|
||||
if conf.SignEndpoint == "" || conf.SignEndpoint == conf.Endpoint {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
m.sign = m.core.Client
|
||||
log.ZWarn(
|
||||
context.Background(),
|
||||
"set sign bucket location cache panic",
|
||||
errors.New("failed to get private field value"),
|
||||
"recover",
|
||||
fmt.Sprintf("%+v", r),
|
||||
"development version",
|
||||
"github.com/minio/minio-go/v7 v7.0.61",
|
||||
)
|
||||
}
|
||||
}()
|
||||
blc := reflect.ValueOf(m.sign).Elem().FieldByName("bucketLocCache")
|
||||
vblc := reflect.New(reflect.PtrTo(blc.Type()))
|
||||
*(*unsafe.Pointer)(vblc.UnsafePointer()) = unsafe.Pointer(blc.UnsafeAddr())
|
||||
vblc.Elem().Elem().Interface().(interface{ Set(string, string) }).Set(conf.Bucket, m.location)
|
||||
}()
|
||||
m.init = true
|
||||
return nil
|
||||
}
|
||||
@@ -191,7 +256,7 @@ func (m *Minio) AuthSign(ctx context.Context, uploadID string, name string, expi
|
||||
return nil, err
|
||||
}
|
||||
request.Header.Set("X-Amz-Content-Sha256", unsignedPayload)
|
||||
request = signer.SignV4Trailer(*request, creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken, "us-east-1", nil)
|
||||
request = signer.SignV4Trailer(*request, creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken, m.location, nil)
|
||||
result.Parts[i] = s3.SignPart{
|
||||
PartNumber: partNumber,
|
||||
URL: request.URL.String(),
|
||||
@@ -206,7 +271,7 @@ func (m *Minio) PresignedPutObject(ctx context.Context, name string, expire time
|
||||
if err := m.initMinio(ctx); err != nil {
|
||||
return "", err
|
||||
}
|
||||
rawURL, err := m.core.Client.PresignedPutObject(ctx, m.bucket, name, expire)
|
||||
rawURL, err := m.sign.PresignedPutObject(ctx, m.bucket, name, expire)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -303,6 +368,19 @@ func (m *Minio) ListUploadedParts(ctx context.Context, uploadID string, name str
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *Minio) presignedGetObject(ctx context.Context, name string, expire time.Duration, query url.Values) (string, error) {
|
||||
if expire <= 0 {
|
||||
expire = time.Hour * 24 * 365 * 99 // 99 years
|
||||
} else if expire < time.Second {
|
||||
expire = time.Second
|
||||
}
|
||||
rawURL, err := m.sign.PresignedGetObject(ctx, m.bucket, name, expire, query)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return rawURL.String(), nil
|
||||
}
|
||||
|
||||
func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
if err := m.initMinio(ctx); err != nil {
|
||||
return "", err
|
||||
@@ -313,17 +391,123 @@ func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration
|
||||
reqParams.Set("response-content-type", opt.ContentType)
|
||||
}
|
||||
if opt.Filename != "" {
|
||||
reqParams.Set("response-content-disposition", `attachment; filename="`+opt.Filename+`"`)
|
||||
reqParams.Set("response-content-disposition", `attachment; filename=`+strconv.Quote(opt.Filename))
|
||||
}
|
||||
}
|
||||
if expire <= 0 {
|
||||
expire = time.Hour * 24 * 365 * 99 // 99 years
|
||||
} else if expire < time.Second {
|
||||
expire = time.Second
|
||||
if opt.Image == nil || (opt.Image.Width < 0 && opt.Image.Height < 0 && opt.Image.Format == "") || (opt.Image.Width > maxImageWidth || opt.Image.Height > maxImageHeight) {
|
||||
return m.presignedGetObject(ctx, name, expire, reqParams)
|
||||
}
|
||||
u, err := m.core.Client.PresignedGetObject(ctx, m.bucket, name, expire, reqParams)
|
||||
fileInfo, err := m.StatObject(ctx, name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return u.String(), nil
|
||||
if fileInfo.Size > maxImageSize {
|
||||
return "", errors.New("file size too large")
|
||||
}
|
||||
objectInfoPath := path.Join(pathInfo, fileInfo.ETag, "image.json")
|
||||
var (
|
||||
img image.Image
|
||||
info minioImageInfo
|
||||
)
|
||||
data, err := m.getObjectData(ctx, objectInfoPath, 1024)
|
||||
if err == nil {
|
||||
if err := json.Unmarshal(data, &info); err != nil {
|
||||
return "", fmt.Errorf("unmarshal minio image info.json error: %w", err)
|
||||
}
|
||||
if info.NotImage {
|
||||
return "", errors.New("not image")
|
||||
}
|
||||
} else if m.IsNotFound(err) {
|
||||
reader, err := m.core.Client.GetObject(ctx, m.bucket, name, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer reader.Close()
|
||||
imageInfo, format, err := ImageStat(reader)
|
||||
if err == nil {
|
||||
info.NotImage = false
|
||||
info.Format = format
|
||||
info.Width, info.Height = ImageWidthHeight(imageInfo)
|
||||
img = imageInfo
|
||||
} else {
|
||||
info.NotImage = true
|
||||
}
|
||||
data, err := json.Marshal(&info)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := m.core.Client.PutObject(ctx, m.bucket, objectInfoPath, bytes.NewReader(data), int64(len(data)), minio.PutObjectOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
if opt.Image.Width > info.Width || opt.Image.Width <= 0 {
|
||||
opt.Image.Width = info.Width
|
||||
}
|
||||
if opt.Image.Height > info.Height || opt.Image.Height <= 0 {
|
||||
opt.Image.Height = info.Height
|
||||
}
|
||||
opt.Image.Format = strings.ToLower(opt.Image.Format)
|
||||
if opt.Image.Format == formatJpg {
|
||||
opt.Image.Format = formatJpeg
|
||||
}
|
||||
switch opt.Image.Format {
|
||||
case formatPng:
|
||||
case formatJpeg:
|
||||
case formatGif:
|
||||
default:
|
||||
if info.Format == formatGif {
|
||||
opt.Image.Format = formatGif
|
||||
} else {
|
||||
opt.Image.Format = formatJpeg
|
||||
}
|
||||
}
|
||||
reqParams.Set("response-content-type", "image/"+opt.Image.Format)
|
||||
if opt.Image.Width == info.Width && opt.Image.Height == info.Height && opt.Image.Format == info.Format {
|
||||
return m.presignedGetObject(ctx, name, expire, reqParams)
|
||||
}
|
||||
cacheKey := filepath.Join(pathInfo, fileInfo.ETag, fmt.Sprintf("image_w%d_h%d.%s", opt.Image.Width, opt.Image.Height, opt.Image.Format))
|
||||
if _, err := m.core.Client.StatObject(ctx, m.bucket, cacheKey, minio.StatObjectOptions{}); err == nil {
|
||||
return m.presignedGetObject(ctx, cacheKey, expire, reqParams)
|
||||
} else if !m.IsNotFound(err) {
|
||||
return "", err
|
||||
}
|
||||
if img == nil {
|
||||
reader, err := m.core.Client.GetObject(ctx, m.bucket, name, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer reader.Close()
|
||||
img, _, err = ImageStat(reader)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
thumbnail := resizeImage(img, opt.Image.Width, opt.Image.Height)
|
||||
buf := bytes.NewBuffer(nil)
|
||||
switch opt.Image.Format {
|
||||
case formatPng:
|
||||
err = png.Encode(buf, thumbnail)
|
||||
case formatJpeg:
|
||||
err = jpeg.Encode(buf, thumbnail, nil)
|
||||
case formatGif:
|
||||
err = gif.Encode(buf, thumbnail, nil)
|
||||
}
|
||||
if _, err := m.core.Client.PutObject(ctx, m.bucket, cacheKey, buf, int64(buf.Len()), minio.PutObjectOptions{}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return m.presignedGetObject(ctx, cacheKey, expire, reqParams)
|
||||
}
|
||||
|
||||
func (m *Minio) getObjectData(ctx context.Context, name string, limit int64) ([]byte, error) {
|
||||
object, err := m.core.Client.GetObject(ctx, m.bucket, name, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer object.Close()
|
||||
if limit < 0 {
|
||||
return io.ReadAll(object)
|
||||
}
|
||||
return io.ReadAll(io.LimitReader(object, 1024))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package minio
|
||||
|
||||
type minioImageInfo struct {
|
||||
NotImage bool `json:"notImage,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
}
|
||||
@@ -36,6 +36,19 @@ const (
|
||||
maxNumSize = 10000
|
||||
)
|
||||
|
||||
const (
|
||||
imagePng = "png"
|
||||
imageJpg = "jpg"
|
||||
imageJpeg = "jpeg"
|
||||
imageGif = "gif"
|
||||
imageWebp = "webp"
|
||||
)
|
||||
|
||||
const (
|
||||
videoSnapshotImagePng = "png"
|
||||
videoSnapshotImageJpg = "jpg"
|
||||
)
|
||||
|
||||
func NewOSS() (s3.Interface, error) {
|
||||
conf := config.Config.Object.Oss
|
||||
if conf.BucketURL == "" {
|
||||
@@ -139,7 +152,7 @@ func (o *OSS) AuthSign(ctx context.Context, uploadID string, name string, expire
|
||||
}
|
||||
for i, partNumber := range partNumbers {
|
||||
rawURL := fmt.Sprintf(`%s%s?partNumber=%d&uploadId=%s`, o.bucketURL, name, partNumber, uploadID)
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, nil)
|
||||
request, err := http.NewRequest(http.MethodPut, rawURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -150,12 +163,7 @@ func (o *OSS) AuthSign(ctx context.Context, uploadID string, name string, expire
|
||||
request.Header.Set(oss.HTTPHeaderHost, request.Host)
|
||||
request.Header.Set(oss.HTTPHeaderDate, now)
|
||||
request.Header.Set(oss.HttpHeaderOssDate, now)
|
||||
authorization := fmt.Sprintf(
|
||||
`OSS %s:%s`,
|
||||
o.credentials.GetAccessKeyID(),
|
||||
o.getSignedStr(request, fmt.Sprintf(`/%s/%s?partNumber=%d&uploadId=%s`, o.bucket.BucketName, name, partNumber, uploadID), o.credentials.GetAccessKeySecret()),
|
||||
)
|
||||
request.Header.Set(oss.HTTPHeaderAuthorization, authorization)
|
||||
ossSignHeader(o.bucket.Client.Conn, request, fmt.Sprintf(`/%s/%s?partNumber=%d&uploadId=%s`, o.bucket.BucketName, name, partNumber, uploadID))
|
||||
delete(request.Header, oss.HTTPHeaderDate)
|
||||
result.Parts[i] = s3.SignPart{
|
||||
PartNumber: partNumber,
|
||||
@@ -266,11 +274,36 @@ func (o *OSS) ListUploadedParts(ctx context.Context, uploadID string, name strin
|
||||
func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||
var opts []oss.Option
|
||||
if opt != nil {
|
||||
if opt.Image != nil {
|
||||
// 文档地址: https://help.aliyun.com/zh/oss/user-guide/resize-images-4?spm=a2c4g.11186623.0.0.4b3b1e4fWW6yji
|
||||
var format string
|
||||
switch opt.Image.Format {
|
||||
case
|
||||
imagePng,
|
||||
imageJpg,
|
||||
imageJpeg,
|
||||
imageGif,
|
||||
imageWebp:
|
||||
format = opt.Image.Format
|
||||
default:
|
||||
opt.Image.Format = imageJpg
|
||||
}
|
||||
// https://oss-console-img-demo-cn-hangzhou.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/resize,h_100,m_lfit
|
||||
process := "image/resize,m_lfit"
|
||||
if opt.Image.Width > 0 {
|
||||
process += ",w_" + strconv.Itoa(opt.Image.Width)
|
||||
}
|
||||
if opt.Image.Height > 0 {
|
||||
process += ",h_" + strconv.Itoa(opt.Image.Height)
|
||||
}
|
||||
process += ",format," + format
|
||||
opts = append(opts, oss.Process(process))
|
||||
}
|
||||
if opt.ContentType != "" {
|
||||
opts = append(opts, oss.ResponseContentType(opt.ContentType))
|
||||
}
|
||||
if opt.Filename != "" {
|
||||
opts = append(opts, oss.ResponseContentDisposition(`attachment; filename="`+opt.Filename+`"`))
|
||||
opts = append(opts, oss.ResponseContentDisposition(`attachment; filename=`+strconv.Quote(opt.Filename)))
|
||||
}
|
||||
}
|
||||
if expire <= 0 {
|
||||
|
||||
@@ -1,96 +1,11 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package oss
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"hash"
|
||||
"io"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
)
|
||||
|
||||
func (o *OSS) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) {
|
||||
var keysList []string
|
||||
keysMap := make(map[string]string)
|
||||
srcKeys := make(map[string]string)
|
||||
|
||||
for k := range req.Header {
|
||||
srcKeys[strings.ToLower(k)] = ""
|
||||
}
|
||||
|
||||
for _, v := range o.bucket.Client.Config.AdditionalHeaders {
|
||||
if _, ok := srcKeys[strings.ToLower(v)]; ok {
|
||||
keysMap[strings.ToLower(v)] = ""
|
||||
}
|
||||
}
|
||||
|
||||
for k := range keysMap {
|
||||
keysList = append(keysList, k)
|
||||
}
|
||||
sort.Strings(keysList)
|
||||
return keysList, keysMap
|
||||
}
|
||||
|
||||
func (o *OSS) getSignedStr(req *http.Request, canonicalizedResource string, keySecret string) string {
|
||||
// Find out the "x-oss-"'s address in header of the request
|
||||
ossHeadersMap := make(map[string]string)
|
||||
additionalList, additionalMap := o.getAdditionalHeaderKeys(req)
|
||||
for k, v := range req.Header {
|
||||
if strings.HasPrefix(strings.ToLower(k), "x-oss-") {
|
||||
ossHeadersMap[strings.ToLower(k)] = v[0]
|
||||
} else if o.bucket.Client.Config.AuthVersion == oss.AuthV2 {
|
||||
if _, ok := additionalMap[strings.ToLower(k)]; ok {
|
||||
ossHeadersMap[strings.ToLower(k)] = v[0]
|
||||
}
|
||||
}
|
||||
}
|
||||
hs := newHeaderSorter(ossHeadersMap)
|
||||
|
||||
// Sort the ossHeadersMap by the ascending order
|
||||
hs.Sort()
|
||||
|
||||
// Get the canonicalizedOSSHeaders
|
||||
canonicalizedOSSHeaders := ""
|
||||
for i := range hs.Keys {
|
||||
canonicalizedOSSHeaders += hs.Keys[i] + ":" + hs.Vals[i] + "\n"
|
||||
}
|
||||
|
||||
// Give other parameters values
|
||||
// when sign URL, date is expires
|
||||
date := req.Header.Get(oss.HTTPHeaderDate)
|
||||
contentType := req.Header.Get(oss.HTTPHeaderContentType)
|
||||
contentMd5 := req.Header.Get(oss.HTTPHeaderContentMD5)
|
||||
|
||||
// default is v1 signature
|
||||
signStr := req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + canonicalizedResource
|
||||
h := hmac.New(func() hash.Hash { return sha1.New() }, []byte(keySecret))
|
||||
|
||||
// v2 signature
|
||||
if o.bucket.Client.Config.AuthVersion == oss.AuthV2 {
|
||||
signStr = req.Method + "\n" + contentMd5 + "\n" + contentType + "\n" + date + "\n" + canonicalizedOSSHeaders + strings.Join(additionalList, ";") + "\n" + canonicalizedResource
|
||||
h = hmac.New(func() hash.Hash { return sha256.New() }, []byte(keySecret))
|
||||
}
|
||||
_, _ = io.WriteString(h, signStr)
|
||||
signedStr := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
|
||||
return signedStr
|
||||
}
|
||||
//go:linkname ossSignHeader github.com/aliyun/aliyun-oss-go-sdk/oss.(*Conn).signHeader
|
||||
func ossSignHeader(c *oss.Conn, req *http.Request, canonicalizedResource string)
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package oss
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// headerSorter defines the key-value structure for storing the sorted data in signHeader.
|
||||
type headerSorter struct {
|
||||
Keys []string
|
||||
Vals []string
|
||||
}
|
||||
|
||||
// newHeaderSorter is an additional function for function SignHeader.
|
||||
func newHeaderSorter(m map[string]string) *headerSorter {
|
||||
hs := &headerSorter{
|
||||
Keys: make([]string, 0, len(m)),
|
||||
Vals: make([]string, 0, len(m)),
|
||||
}
|
||||
|
||||
for k, v := range m {
|
||||
hs.Keys = append(hs.Keys, k)
|
||||
hs.Vals = append(hs.Vals, v)
|
||||
}
|
||||
return hs
|
||||
}
|
||||
|
||||
// Sort is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Sort() {
|
||||
sort.Sort(hs)
|
||||
}
|
||||
|
||||
// Len is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Len() int {
|
||||
return len(hs.Vals)
|
||||
}
|
||||
|
||||
// Less is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Less(i, j int) bool {
|
||||
return bytes.Compare([]byte(hs.Keys[i]), []byte(hs.Keys[j])) < 0
|
||||
}
|
||||
|
||||
// Swap is an additional function for function SignHeader.
|
||||
func (hs *headerSorter) Swap(i, j int) {
|
||||
hs.Vals[i], hs.Vals[j] = hs.Vals[j], hs.Vals[i]
|
||||
hs.Keys[i], hs.Keys[j] = hs.Keys[j], hs.Keys[i]
|
||||
}
|
||||
@@ -116,9 +116,16 @@ type ListUploadedPartsResult struct {
|
||||
UploadedParts []UploadedPart `xml:"Part"`
|
||||
}
|
||||
|
||||
type Image struct {
|
||||
Format string `json:"format"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
}
|
||||
|
||||
type AccessURLOption struct {
|
||||
ContentType string `json:"contentType"`
|
||||
Filename string `json:"filename"`
|
||||
Image *Image `json:"image"`
|
||||
}
|
||||
|
||||
type Interface interface {
|
||||
|
||||
@@ -27,10 +27,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
singleGocMsgNum = 5000
|
||||
Msg = "msg"
|
||||
OldestList = 0
|
||||
NewestList = -1
|
||||
singleGocMsgNum = 100
|
||||
singleGocMsgNum5000 = 5000
|
||||
Msg = "msg"
|
||||
OldestList = 0
|
||||
NewestList = -1
|
||||
)
|
||||
|
||||
type MsgDocModel struct {
|
||||
@@ -128,6 +129,7 @@ type MsgDocModelInterface interface {
|
||||
pageNumber int32,
|
||||
showNumber int32,
|
||||
) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error)
|
||||
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)
|
||||
}
|
||||
|
||||
func (MsgDocModel) TableName() string {
|
||||
@@ -138,6 +140,10 @@ func (MsgDocModel) GetSingleGocMsgNum() int64 {
|
||||
return singleGocMsgNum
|
||||
}
|
||||
|
||||
func (MsgDocModel) GetSingleGocMsgNum5000() int64 {
|
||||
return singleGocMsgNum5000
|
||||
}
|
||||
|
||||
func (m *MsgDocModel) IsFull() bool {
|
||||
return m.Msg[len(m.Msg)-1].Msg != nil
|
||||
}
|
||||
|
||||
@@ -1073,11 +1073,6 @@ func (m *MsgMongoDriver) SearchMessage(ctx context.Context, req *msg.SearchMessa
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
for _, msg1 := range msgs {
|
||||
if msg1.IsRead {
|
||||
msg1.Msg.IsRead = true
|
||||
}
|
||||
}
|
||||
return total, msgs, nil
|
||||
}
|
||||
|
||||
@@ -1151,13 +1146,22 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa
|
||||
{"doc_id", 1},
|
||||
}},
|
||||
},
|
||||
{
|
||||
{"$unwind", bson.M{"path": "$msgs"}},
|
||||
},
|
||||
{
|
||||
{"$sort", bson.M{"msgs.msg.send_time": -1}},
|
||||
},
|
||||
}
|
||||
cursor, err := m.MsgCollection.Aggregate(ctx, pipe)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
var msgsDocs []table.MsgDocModel
|
||||
type docModel struct {
|
||||
DocID string `bson:"doc_id"`
|
||||
Msg *table.MsgInfoModel `bson:"msgs"`
|
||||
}
|
||||
var msgsDocs []docModel
|
||||
err = cursor.All(ctx, &msgsDocs)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -1167,41 +1171,39 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa
|
||||
}
|
||||
msgs := make([]*table.MsgInfoModel, 0)
|
||||
for index := range msgsDocs {
|
||||
for i := range msgsDocs[index].Msg {
|
||||
msg := msgsDocs[index].Msg[i]
|
||||
if msg == nil || msg.Msg == nil {
|
||||
continue
|
||||
}
|
||||
if msg.Revoke != nil {
|
||||
revokeContent := sdkws.MessageRevokedContent{
|
||||
RevokerID: msg.Revoke.UserID,
|
||||
RevokerRole: msg.Revoke.Role,
|
||||
ClientMsgID: msg.Msg.ClientMsgID,
|
||||
RevokerNickname: msg.Revoke.Nickname,
|
||||
RevokeTime: msg.Revoke.Time,
|
||||
SourceMessageSendTime: msg.Msg.SendTime,
|
||||
SourceMessageSendID: msg.Msg.SendID,
|
||||
SourceMessageSenderNickname: msg.Msg.SenderNickname,
|
||||
SessionType: msg.Msg.SessionType,
|
||||
Seq: msg.Msg.Seq,
|
||||
Ex: msg.Msg.Ex,
|
||||
}
|
||||
data, err := json.Marshal(&revokeContent)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
elem := sdkws.NotificationElem{
|
||||
Detail: string(data),
|
||||
}
|
||||
content, err := json.Marshal(&elem)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
msg.Msg.ContentType = constant.MsgRevokeNotification
|
||||
msg.Msg.Content = string(content)
|
||||
}
|
||||
msgs = append(msgs, msg)
|
||||
msgInfo := msgsDocs[index].Msg
|
||||
if msgInfo == nil || msgInfo.Msg == nil {
|
||||
continue
|
||||
}
|
||||
if msgInfo.Revoke != nil {
|
||||
revokeContent := sdkws.MessageRevokedContent{
|
||||
RevokerID: msgInfo.Revoke.UserID,
|
||||
RevokerRole: msgInfo.Revoke.Role,
|
||||
ClientMsgID: msgInfo.Msg.ClientMsgID,
|
||||
RevokerNickname: msgInfo.Revoke.Nickname,
|
||||
RevokeTime: msgInfo.Revoke.Time,
|
||||
SourceMessageSendTime: msgInfo.Msg.SendTime,
|
||||
SourceMessageSendID: msgInfo.Msg.SendID,
|
||||
SourceMessageSenderNickname: msgInfo.Msg.SenderNickname,
|
||||
SessionType: msgInfo.Msg.SessionType,
|
||||
Seq: msgInfo.Msg.Seq,
|
||||
Ex: msgInfo.Msg.Ex,
|
||||
}
|
||||
data, err := json.Marshal(&revokeContent)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
elem := sdkws.NotificationElem{
|
||||
Detail: string(data),
|
||||
}
|
||||
content, err := json.Marshal(&elem)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
msgInfo.Msg.ContentType = constant.MsgRevokeNotification
|
||||
msgInfo.Msg.Content = string(content)
|
||||
}
|
||||
msgs = append(msgs, msgInfo)
|
||||
}
|
||||
start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
|
||||
n := int32(len(msgs))
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package unrelation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/OpenIMSDK/tools/log"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
table "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
)
|
||||
|
||||
func (m *MsgMongoDriver) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) {
|
||||
for _, conversationID := range conversationIDs {
|
||||
regex := primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}
|
||||
cursor, err := m.MsgCollection.Find(ctx, bson.M{"doc_id": regex})
|
||||
if err != nil {
|
||||
log.ZError(ctx, "convertAll find msg doc failed", err, "conversationID", conversationID)
|
||||
continue
|
||||
}
|
||||
var msgDocs []table.MsgDocModel
|
||||
err = cursor.All(ctx, &msgDocs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "convertAll cursor all failed", err, "conversationID", conversationID)
|
||||
continue
|
||||
}
|
||||
if len(msgDocs) < 1 {
|
||||
continue
|
||||
}
|
||||
log.ZInfo(ctx, "msg doc convert", "conversationID", conversationID, "len(msgDocs)", len(msgDocs))
|
||||
if len(msgDocs[0].Msg) == int(m.model.GetSingleGocMsgNum5000()) {
|
||||
if _, err := m.MsgCollection.DeleteMany(ctx, bson.M{"doc_id": regex}); err != nil {
|
||||
log.ZError(ctx, "convertAll delete many failed", err, "conversationID", conversationID)
|
||||
continue
|
||||
}
|
||||
var newMsgDocs []interface{}
|
||||
for _, msgDoc := range msgDocs {
|
||||
if int64(len(msgDoc.Msg)) == m.model.GetSingleGocMsgNum() {
|
||||
continue
|
||||
}
|
||||
var index int64
|
||||
for index < int64(len(msgDoc.Msg)) {
|
||||
msg := msgDoc.Msg[index]
|
||||
if msg != nil && msg.Msg != nil {
|
||||
msgDocModel := table.MsgDocModel{DocID: m.model.GetDocID(conversationID, msg.Msg.Seq)}
|
||||
end := index + m.model.GetSingleGocMsgNum()
|
||||
if int(end) >= len(msgDoc.Msg) {
|
||||
msgDocModel.Msg = msgDoc.Msg[index:]
|
||||
} else {
|
||||
msgDocModel.Msg = msgDoc.Msg[index:end]
|
||||
}
|
||||
newMsgDocs = append(newMsgDocs, msgDocModel)
|
||||
index = end
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
_, err = m.MsgCollection.InsertMany(ctx, newMsgDocs)
|
||||
if err != nil {
|
||||
log.ZError(ctx, "convertAll insert many failed", err, "conversationID", conversationID, "len(newMsgDocs)", len(newMsgDocs))
|
||||
} else {
|
||||
log.ZInfo(ctx, "msg doc convert", "conversationID", conversationID, "len(newMsgDocs)", len(newMsgDocs))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ package unrelation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
|
||||
"github.com/OpenIMSDK/tools/errs"
|
||||
"github.com/OpenIMSDK/tools/utils"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||
)
|
||||
|
||||
// prefixes and suffixes.
|
||||
@@ -153,7 +155,7 @@ func (u *UserMongoDriver) RemoveSubscribedListFromUser(ctx context.Context, user
|
||||
return errs.Wrap(err)
|
||||
}
|
||||
|
||||
// GetAllSubscribeList Get all users subscribed by this user
|
||||
// GetAllSubscribeList Get all users subscribed by this user.
|
||||
func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string) (userIDList []string, err error) {
|
||||
var user unrelation.UserModel
|
||||
cursor := u.userCollection.FindOne(
|
||||
@@ -166,7 +168,7 @@ func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string
|
||||
return user.UserIDList, nil
|
||||
}
|
||||
|
||||
// GetSubscribedList Get the user subscribed by those users
|
||||
// GetSubscribedList Get the user subscribed by those users.
|
||||
func (u *UserMongoDriver) GetSubscribedList(ctx context.Context, userID string) (userIDList []string, err error) {
|
||||
var user unrelation.UserModel
|
||||
cursor := u.userCollection.FindOne(
|
||||
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
func GetNotificationConversationID(msg *sdkws.MsgData) string {
|
||||
func GetNotificationConversationIDByMsg(msg *sdkws.MsgData) string {
|
||||
switch msg.SessionType {
|
||||
case constant.SingleChatType:
|
||||
l := []string{msg.SendID, msg.RecvID}
|
||||
@@ -114,6 +114,30 @@ func GetConversationIDBySessionType(sessionType int, ids ...string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func GetNotificationConversationIDByConversationID(conversationID string) string {
|
||||
l := strings.Split(conversationID, "_")
|
||||
if len(l) > 1 {
|
||||
l[0] = "n"
|
||||
return strings.Join(l, "_")
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func GetNotificationConversationID(sessionType int, ids ...string) string {
|
||||
sort.Strings(ids)
|
||||
if len(ids) > 2 || len(ids) < 1 {
|
||||
return ""
|
||||
}
|
||||
switch sessionType {
|
||||
case constant.SingleChatType:
|
||||
return "n_" + strings.Join(ids, "_") // single chat
|
||||
case constant.SuperGroupChatType:
|
||||
return "n_" + ids[0] // super group chat
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func IsNotification(conversationID string) bool {
|
||||
return strings.HasPrefix(conversationID, "n_")
|
||||
}
|
||||
|
||||
@@ -56,7 +56,8 @@ func newContentTypeConf() map[int32]config.NotificationConf {
|
||||
constant.GroupInfoSetAnnouncementNotification: config.Config.Notification.GroupInfoSetAnnouncement,
|
||||
constant.GroupInfoSetNameNotification: config.Config.Notification.GroupInfoSetName,
|
||||
// user
|
||||
constant.UserInfoUpdatedNotification: config.Config.Notification.UserInfoUpdated,
|
||||
constant.UserInfoUpdatedNotification: config.Config.Notification.UserInfoUpdated,
|
||||
constant.UserStatusChangeNotification: config.Config.Notification.UserStatusChanged,
|
||||
// friend
|
||||
constant.FriendApplicationNotification: config.Config.Notification.FriendApplicationAdded,
|
||||
constant.FriendApplicationApprovedNotification: config.Config.Notification.FriendApplicationApproved,
|
||||
@@ -102,7 +103,8 @@ func newSessionTypeConf() map[int32]int32 {
|
||||
constant.GroupInfoSetAnnouncementNotification: constant.SuperGroupChatType,
|
||||
constant.GroupInfoSetNameNotification: constant.SuperGroupChatType,
|
||||
// user
|
||||
constant.UserInfoUpdatedNotification: constant.SingleChatType,
|
||||
constant.UserInfoUpdatedNotification: constant.SingleChatType,
|
||||
constant.UserStatusChangeNotification: constant.SingleChatType,
|
||||
// friend
|
||||
constant.FriendApplicationNotification: constant.SingleChatType,
|
||||
constant.FriendApplicationApprovedNotification: constant.SingleChatType,
|
||||
|
||||
@@ -136,7 +136,7 @@ func (f *FriendNotificationSender) FriendApplicationAddNotification(
|
||||
return f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) FriendApplicationAgreedNotification(
|
||||
func (f *FriendNotificationSender) FriendApplicationAgreedNotification(
|
||||
ctx context.Context,
|
||||
req *pbFriend.RespondFriendApplyReq,
|
||||
) error {
|
||||
@@ -144,10 +144,10 @@ func (c *FriendNotificationSender) FriendApplicationAgreedNotification(
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
}, HandleMsg: req.HandleMsg}
|
||||
return c.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips)
|
||||
return f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) FriendApplicationRefusedNotification(
|
||||
func (f *FriendNotificationSender) FriendApplicationRefusedNotification(
|
||||
ctx context.Context,
|
||||
req *pbFriend.RespondFriendApplyReq,
|
||||
) error {
|
||||
@@ -155,15 +155,15 @@ func (c *FriendNotificationSender) FriendApplicationRefusedNotification(
|
||||
FromUserID: req.FromUserID,
|
||||
ToUserID: req.ToUserID,
|
||||
}, HandleMsg: req.HandleMsg}
|
||||
return c.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips)
|
||||
return f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) FriendAddedNotification(
|
||||
func (f *FriendNotificationSender) FriendAddedNotification(
|
||||
ctx context.Context,
|
||||
operationID, opUserID, fromUserID, toUserID string,
|
||||
) error {
|
||||
tips := sdkws.FriendAddedTips{Friend: &sdkws.FriendInfo{}, OpUser: &sdkws.PublicUserInfo{}}
|
||||
user, err := c.getUsersInfo(ctx, []string{opUserID})
|
||||
user, err := f.getUsersInfo(ctx, []string{opUserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -171,52 +171,52 @@ func (c *FriendNotificationSender) FriendAddedNotification(
|
||||
tips.OpUser.Ex = user[0].GetEx()
|
||||
tips.OpUser.Nickname = user[0].GetNickname()
|
||||
tips.OpUser.FaceURL = user[0].GetFaceURL()
|
||||
friends, err := c.db.FindFriendsWithError(ctx, fromUserID, []string{toUserID})
|
||||
friends, err := f.db.FindFriendsWithError(ctx, fromUserID, []string{toUserID})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tips.Friend, err = convert.FriendDB2Pb(ctx, friends[0], c.getUsersInfoMap)
|
||||
tips.Friend, err = convert.FriendDB2Pb(ctx, friends[0], f.getUsersInfoMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
|
||||
return f.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *pbFriend.DeleteFriendReq) error {
|
||||
func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *pbFriend.DeleteFriendReq) error {
|
||||
tips := sdkws.FriendDeletedTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.OwnerUserID,
|
||||
ToUserID: req.FriendUserID,
|
||||
}}
|
||||
return c.Notification(ctx, req.OwnerUserID, req.FriendUserID, constant.FriendDeletedNotification, &tips)
|
||||
return f.Notification(ctx, req.OwnerUserID, req.FriendUserID, constant.FriendDeletedNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Context, fromUserID, toUserID string) error {
|
||||
func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Context, fromUserID, toUserID string) error {
|
||||
tips := sdkws.FriendInfoChangedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||
tips.FromToUserID.FromUserID = fromUserID
|
||||
tips.FromToUserID.ToUserID = toUserID
|
||||
return c.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
|
||||
return f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbFriend.AddBlackReq) error {
|
||||
func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbFriend.AddBlackReq) error {
|
||||
tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||
tips.FromToUserID.FromUserID = req.OwnerUserID
|
||||
tips.FromToUserID.ToUserID = req.BlackUserID
|
||||
return c.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackAddedNotification, &tips)
|
||||
return f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackAddedNotification, &tips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) BlackDeletedNotification(ctx context.Context, req *pbFriend.RemoveBlackReq) {
|
||||
func (f *FriendNotificationSender) BlackDeletedNotification(ctx context.Context, req *pbFriend.RemoveBlackReq) {
|
||||
blackDeletedTips := sdkws.BlackDeletedTips{FromToUserID: &sdkws.FromToUserID{
|
||||
FromUserID: req.OwnerUserID,
|
||||
ToUserID: req.BlackUserID,
|
||||
}}
|
||||
c.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips)
|
||||
f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips)
|
||||
}
|
||||
|
||||
func (c *FriendNotificationSender) FriendInfoUpdatedNotification(
|
||||
func (f *FriendNotificationSender) FriendInfoUpdatedNotification(
|
||||
ctx context.Context,
|
||||
changedUserID string,
|
||||
needNotifiedUserID string,
|
||||
) {
|
||||
tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID}
|
||||
c.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, constant.FriendInfoUpdatedNotification, &tips)
|
||||
f.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, constant.FriendInfoUpdatedNotification, &tips)
|
||||
}
|
||||
|
||||
@@ -220,7 +220,13 @@ func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs [
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) error {
|
||||
func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if opUser == nil {
|
||||
return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is nil")
|
||||
}
|
||||
@@ -260,6 +266,12 @@ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -267,6 +279,12 @@ func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context,
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -274,6 +292,12 @@ func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context,
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -281,6 +305,12 @@ func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Conte
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -288,6 +318,12 @@ func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx conte
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbGroup.JoinGroupReq) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -355,6 +391,12 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -377,6 +419,12 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -394,6 +442,12 @@ func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -401,6 +455,12 @@ func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context,
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, groupID, reason string, invitedUserIDList []string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -419,12 +479,18 @@ func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context,
|
||||
return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) {
|
||||
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||
func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, groupID string, entrantUserID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := g.getGroupMember(ctx, req.GroupID, req.FromUserID)
|
||||
user, err := g.getGroupMember(ctx, groupID, entrantUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -433,6 +499,12 @@ func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, r
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -440,6 +512,12 @@ func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -459,6 +537,12 @@ func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Conte
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -475,6 +559,12 @@ func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -494,6 +584,12 @@ func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, gr
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Context, groupID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -513,6 +609,12 @@ func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Conte
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -529,6 +631,12 @@ func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Con
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -545,6 +653,12 @@ func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -560,25 +674,6 @@ func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx c
|
||||
return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) MemberEnterDirectlyNotification(ctx context.Context, groupID string, entrantUserID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
if err != nil {
|
||||
log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
|
||||
}
|
||||
}()
|
||||
group, err := g.getGroupInfo(ctx, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user, err := g.getGroupMember(ctx, groupID, entrantUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tips := &sdkws.MemberEnterTips{Group: group, EntrantUser: user}
|
||||
return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberEnterNotification, tips)
|
||||
}
|
||||
|
||||
func (g *GroupNotificationSender) SuperGroupNotification(ctx context.Context, sendID, recvID string) (err error) {
|
||||
defer log.ZDebug(ctx, "return")
|
||||
defer func() {
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// Copyright © 2023 OpenIM. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/OpenIMSDK/protocol/constant"
|
||||
"github.com/OpenIMSDK/protocol/sdkws"
|
||||
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/controller"
|
||||
relationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/relation"
|
||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient"
|
||||
)
|
||||
|
||||
type UserNotificationSender struct {
|
||||
*rpcclient.NotificationSender
|
||||
getUsersInfo func(ctx context.Context, userIDs []string) ([]CommonUser, error)
|
||||
// db controller
|
||||
db controller.UserDatabase
|
||||
}
|
||||
|
||||
type userNotificationSenderOptions func(*UserNotificationSender)
|
||||
|
||||
func WithUserDB(db controller.UserDatabase) userNotificationSenderOptions {
|
||||
return func(u *UserNotificationSender) {
|
||||
u.db = db
|
||||
}
|
||||
}
|
||||
|
||||
func WithUserFunc(
|
||||
fn func(ctx context.Context, userIDs []string) (users []*relationTb.UserModel, err error),
|
||||
) userNotificationSenderOptions {
|
||||
return func(u *UserNotificationSender) {
|
||||
f := func(ctx context.Context, userIDs []string) (result []CommonUser, err error) {
|
||||
users, err := fn(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, user := range users {
|
||||
result = append(result, user)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
u.getUsersInfo = f
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserNotificationSender(
|
||||
msgRpcClient *rpcclient.MessageRpcClient,
|
||||
opts ...userNotificationSenderOptions,
|
||||
) *UserNotificationSender {
|
||||
f := &UserNotificationSender{
|
||||
NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient)),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(f)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func (u *UserNotificationSender) getUsersInfoMap(
|
||||
ctx context.Context,
|
||||
userIDs []string,
|
||||
) (map[string]*sdkws.UserInfo, error) {
|
||||
users, err := u.getUsersInfo(ctx, userIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := make(map[string]*sdkws.UserInfo)
|
||||
for _, user := range users {
|
||||
result[user.GetUserID()] = user.(*sdkws.UserInfo)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (u *UserNotificationSender) getFromToUserNickname(
|
||||
ctx context.Context,
|
||||
fromUserID, toUserID string,
|
||||
) (string, string, error) {
|
||||
users, err := u.getUsersInfoMap(ctx, []string{fromUserID, toUserID})
|
||||
if err != nil {
|
||||
return "", "", nil
|
||||
}
|
||||
return users[fromUserID].Nickname, users[toUserID].Nickname, nil
|
||||
}
|
||||
|
||||
func (u *UserNotificationSender) UserStatusChangeNotification(
|
||||
ctx context.Context,
|
||||
tips *sdkws.UserStatusChangeTips,
|
||||
) error {
|
||||
return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserStatusChangeNotification, tips)
|
||||
}
|
||||
@@ -158,6 +158,6 @@ func (u *UserRpcClient) GetAllUserIDs(ctx context.Context, pageNumber, showNumbe
|
||||
}
|
||||
|
||||
func (u *UserRpcClient) SetUserStatus(ctx context.Context, userID string, status int32, platformID int) error {
|
||||
_, err := u.Client.SetUserStatus(ctx, &user.SetUserStatusReq{StatusList: []*user.OnlineStatus{{UserID: userID, Status: status, PlatformID: int32(platformID)}}})
|
||||
_, err := u.Client.SetUserStatus(ctx, &user.SetUserStatusReq{StatusList: []*user.OnlineStatus{{UserID: userID, Status: status, PlatformIDs: []int32{int32(platformID)}}}})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -53,7 +54,19 @@ fi
|
||||
cd $OPENIM_ROOT
|
||||
|
||||
# CPU core number
|
||||
cpu_count=$(lscpu | grep -e '^CPU(s):' | awk '{print $2}')
|
||||
# Check the system type
|
||||
system_type=$(uname)
|
||||
|
||||
if [[ "$system_type" == "Darwin" ]]; then
|
||||
# macOS (using sysctl)
|
||||
cpu_count=$(sysctl -n hw.ncpu)
|
||||
elif [[ "$system_type" == "Linux" ]]; then
|
||||
# Linux (using lscpu)
|
||||
cpu_count=$(lscpu --parse | grep -E '^([^#].*,){3}[^#]' | sort -u | wc -l)
|
||||
else
|
||||
echo "Unsupported operating system: $system_type"
|
||||
exit 1
|
||||
fi
|
||||
echo -e "${GREEN_PREFIX}======> cpu_count=$cpu_count${COLOR_SUFFIX}"
|
||||
|
||||
# Count the number of concurrent compilations (half the number of cpus)
|
||||
|
||||
@@ -58,10 +58,10 @@ done
|
||||
|
||||
#Check launched service process
|
||||
check=$(ps aux | grep -w ./${openim_msgtransfer} | grep -v grep | wc -l)
|
||||
if [ $check -eq ${msg_transfer_service_num} ]; then
|
||||
if [ $check -ge ${msg_transfer_service_num} ]; then
|
||||
echo -e ${GREEN_PREFIX}"none port has been listening,belongs service is openImMsgTransfer"${COLOR_SUFFIX}
|
||||
else
|
||||
echo $check ${msg_transfer_service_num}
|
||||
echo $check ${msg_transfer_service_num}
|
||||
echo -e ${RED_PREFIX}"openImMsgTransfer service does not start normally, num err"${COLOR_SUFFIX}
|
||||
echo -e ${RED_PREFIX}"please check $OPENIM_ROOT/logs/openIM.log "${COLOR_SUFFIX}
|
||||
exit -1
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -18,7 +19,7 @@
|
||||
|
||||
# Common utilities, variables and checks for all build scripts.
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set +o nounset
|
||||
set -o pipefail
|
||||
|
||||
# Unset CDPATH, having it set messes up with script import paths
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -29,6 +30,15 @@ need_to_start_server_shell=(
|
||||
${SCRIPTS_ROOT}/start_cron.sh
|
||||
)
|
||||
|
||||
component_check=start_component_check.sh
|
||||
chmod +x $SCRIPTS_ROOT/$component_check
|
||||
$SCRIPTS_ROOT/$component_check
|
||||
if [ $? -ne 0 ]; then
|
||||
# Print error message and exit
|
||||
echo "${BOLD_PREFIX}${RED_PREFIX}Error executing ${component_check}. Exiting...${COLOR_SUFFIX}"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
#fixme The 10 second delay to start the project is for the docker-compose one-click to start openIM when the infrastructure dependencies are not started
|
||||
|
||||
sleep 10
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# OPENIM 项目源码根目录
|
||||
IAM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||
|
||||
# 生成文件存放目录
|
||||
LOCAL_OUTPUT_ROOT="${IAM_ROOT}/${OUT_DIR:-_output}"
|
||||
|
||||
# 设置统一的密码,方便记忆
|
||||
readonly PASSWORD=${PASSWORD:-'iam59!z$'}
|
||||
|
||||
# Linux系统 going 用户
|
||||
readonly LINUX_USERNAME=${LINUX_USERNAME:-going}
|
||||
# Linux root & going 用户密码
|
||||
readonly LINUX_PASSWORD=${LINUX_PASSWORD:-${PASSWORD}}
|
||||
|
||||
# 设置安装目录
|
||||
readonly INSTALL_DIR=${INSTALL_DIR:-/tmp/installation}
|
||||
mkdir -p ${INSTALL_DIR}
|
||||
readonly ENV_FILE=${IAM_ROOT}/scripts/install/environment.sh
|
||||
|
||||
# MariaDB 配置信息
|
||||
readonly MARIADB_ADMIN_USERNAME=${MARIADB_ADMIN_USERNAME:-root} # MariaDB root 用户
|
||||
readonly MARIADB_ADMIN_PASSWORD=${MARIADB_ADMIN_PASSWORD:-${PASSWORD}} # MariaDB root 用户密码
|
||||
readonly MARIADB_HOST=${MARIADB_HOST:-127.0.0.1:3306} # MariaDB 主机地址
|
||||
readonly MARIADB_DATABASE=${MARIADB_DATABASE:-openim} # MariaDB openim 应用使用的数据库名
|
||||
readonly MARIADB_USERNAME=${MARIADB_USERNAME:-openim} # openim 数据库用户名
|
||||
readonly MARIADB_PASSWORD=${MARIADB_PASSWORD:-${PASSWORD}} # openim 数据库密码
|
||||
|
||||
# Redis 配置信息
|
||||
readonly REDIS_HOST=${REDIS_HOST:-127.0.0.1} # Redis 主机地址
|
||||
readonly REDIS_PORT=${REDIS_PORT:-6379} # Redis 监听端口
|
||||
readonly REDIS_USERNAME=${REDIS_USERNAME:-''} # Redis 用户名
|
||||
readonly REDIS_PASSWORD=${REDIS_PASSWORD:-${PASSWORD}} # Redis 密码
|
||||
|
||||
# MongoDB 配置
|
||||
readonly MONGO_ADMIN_USERNAME=${MONGO_ADMIN_USERNAME:-root} # MongoDB root 用户
|
||||
readonly MONGO_ADMIN_PASSWORD=${MONGO_ADMIN_PASSWORD:-${PASSWORD}} # MongoDB root 用户密码
|
||||
readonly MONGO_HOST=${MONGO_HOST:-127.0.0.1} # MongoDB 地址
|
||||
readonly MONGO_PORT=${MONGO_PORT:-27017} # MongoDB 端口
|
||||
readonly MONGO_USERNAME=${MONGO_USERNAME:-openim} # MongoDB 用户名
|
||||
readonly MONGO_PASSWORD=${MONGO_PASSWORD:-${PASSWORD}} # MongoDB 密码
|
||||
|
||||
# openim 配置
|
||||
readonly IAM_DATA_DIR=${IAM_DATA_DIR:-/data/openim} # openim 各组件数据目录
|
||||
readonly IAM_INSTALL_DIR=${IAM_INSTALL_DIR:-/opt/openim} # openim 安装文件存放目录
|
||||
readonly IAM_CONFIG_DIR=${IAM_CONFIG_DIR:-/etc/openim} # openim 配置文件存放目录
|
||||
readonly IAM_LOG_DIR=${IAM_LOG_DIR:-/var/log/openim} # openim 日志文件存放目录
|
||||
readonly CA_FILE=${CA_FILE:-${IAM_CONFIG_DIR}/cert/ca.pem} # CA
|
||||
|
||||
# openim-apiserver 配置
|
||||
readonly IAM_APISERVER_HOST=${IAM_APISERVER_HOST:-127.0.0.1} # openim-apiserver 部署机器 IP 地址
|
||||
readonly IAM_APISERVER_GRPC_BIND_ADDRESS=${IAM_APISERVER_GRPC_BIND_ADDRESS:-0.0.0.0}
|
||||
readonly IAM_APISERVER_GRPC_BIND_PORT=${IAM_APISERVER_GRPC_BIND_PORT:-8081}
|
||||
readonly IAM_APISERVER_INSECURE_BIND_ADDRESS=${IAM_APISERVER_INSECURE_BIND_ADDRESS:-127.0.0.1}
|
||||
readonly IAM_APISERVER_INSECURE_BIND_PORT=${IAM_APISERVER_INSECURE_BIND_PORT:-8080}
|
||||
readonly IAM_APISERVER_SECURE_BIND_ADDRESS=${IAM_APISERVER_SECURE_BIND_ADDRESS:-0.0.0.0}
|
||||
readonly IAM_APISERVER_SECURE_BIND_PORT=${IAM_APISERVER_SECURE_BIND_PORT:-8443}
|
||||
readonly IAM_APISERVER_SECURE_TLS_CERT_KEY_CERT_FILE=${IAM_APISERVER_SECURE_TLS_CERT_KEY_CERT_FILE:-${IAM_CONFIG_DIR}/cert/openim-apiserver.pem}
|
||||
readonly IAM_APISERVER_SECURE_TLS_CERT_KEY_PRIVATE_KEY_FILE=${IAM_APISERVER_SECURE_TLS_CERT_KEY_PRIVATE_KEY_FILE:-${IAM_CONFIG_DIR}/cert/openim-apiserver-key.pem}
|
||||
|
||||
# openim-authz-server 配置
|
||||
readonly IAM_AUTHZ_SERVER_HOST=${IAM_AUTHZ_SERVER_HOST:-127.0.0.1} # openim-authz-server 部署机器 IP 地址
|
||||
readonly IAM_AUTHZ_SERVER_INSECURE_BIND_ADDRESS=${IAM_AUTHZ_SERVER_INSECURE_BIND_ADDRESS:-127.0.0.1}
|
||||
readonly IAM_AUTHZ_SERVER_INSECURE_BIND_PORT=${IAM_AUTHZ_SERVER_INSECURE_BIND_PORT:-9090}
|
||||
readonly IAM_AUTHZ_SERVER_SECURE_BIND_ADDRESS=${IAM_AUTHZ_SERVER_SECURE_BIND_ADDRESS:-0.0.0.0}
|
||||
readonly IAM_AUTHZ_SERVER_SECURE_BIND_PORT=${IAM_AUTHZ_SERVER_SECURE_BIND_PORT:-9443}
|
||||
readonly IAM_AUTHZ_SERVER_SECURE_TLS_CERT_KEY_CERT_FILE=${IAM_AUTHZ_SERVER_SECURE_TLS_CERT_KEY_CERT_FILE:-${IAM_CONFIG_DIR}/cert/openim-authz-server.pem}
|
||||
readonly IAM_AUTHZ_SERVER_SECURE_TLS_CERT_KEY_PRIVATE_KEY_FILE=${IAM_AUTHZ_SERVER_SECURE_TLS_CERT_KEY_PRIVATE_KEY_FILE:-${IAM_CONFIG_DIR}/cert/openim-authz-server-key.pem}
|
||||
readonly IAM_AUTHZ_SERVER_CLIENT_CA_FILE=${IAM_AUTHZ_SERVER_CLIENT_CA_FILE:-${CA_FILE}}
|
||||
readonly IAM_AUTHZ_SERVER_RPCSERVER=${IAM_AUTHZ_SERVER_RPCSERVER:-${IAM_APISERVER_HOST}:${IAM_APISERVER_GRPC_BIND_PORT}}
|
||||
|
||||
# openim-pump 配置
|
||||
readonly IAM_PUMP_HOST=${IAM_PUMP_HOST:-127.0.0.1} # openim-pump 部署机器 IP 地址
|
||||
readonly IAM_PUMP_COLLECTION_NAME=${IAM_PUMP_COLLECTION_NAME:-iam_analytics}
|
||||
readonly IAM_PUMP_MONGO_URL=${IAM_PUMP_MONGO_URL:-mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOST}:${MONGO_PORT}/${IAM_PUMP_COLLECTION_NAME}?authSource=${IAM_PUMP_COLLECTION_NAME}}
|
||||
|
||||
# openim-watcher配置
|
||||
readonly IAM_WATCHER_HOST=${IAM_WATCHER_HOST:-127.0.0.1} # openim-watcher 部署机器 IP 地址
|
||||
|
||||
# iamctl 配置
|
||||
readonly CONFIG_USER_USERNAME=${CONFIG_USER_USERNAME:-admin}
|
||||
readonly CONFIG_USER_PASSWORD=${CONFIG_USER_PASSWORD:-Admin@2021}
|
||||
readonly CONFIG_USER_CLIENT_CERTIFICATE=${CONFIG_USER_CLIENT_CERTIFICATE:-${HOME}/.openim/cert/admin.pem}
|
||||
readonly CONFIG_USER_CLIENT_KEY=${CONFIG_USER_CLIENT_KEY:-${HOME}/.openim/cert/admin-key.pem}
|
||||
readonly CONFIG_SERVER_ADDRESS=${CONFIG_SERVER_ADDRESS:-${IAM_APISERVER_HOST}:${IAM_APISERVER_SECURE_BIND_PORT}}
|
||||
readonly CONFIG_SERVER_CERTIFICATE_AUTHORITY=${CONFIG_SERVER_CERTIFICATE_AUTHORITY:-${CA_FILE}}
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2020 Lingfei Kong <colin404@foxmail.com>. All rights reserved.
|
||||
# Use of this source code is governed by a MIT style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# 本脚本功能:根据 scripts/environment.sh 配置,生成 OPENIM 组件 YAML 配置文件。
|
||||
# 示例:genconfig.sh scripts/environment.sh configs/openim-apiserver.yaml
|
||||
|
||||
env_file="$1"
|
||||
template_file="$2"
|
||||
|
||||
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||
|
||||
source "${OPENIM_ROOT}/scripts/lib/init.sh"
|
||||
|
||||
if [ $# -ne 2 ];then
|
||||
openim::log::error "Usage: genconfig.sh scripts/environment.sh configs/openim-apiserver.yaml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source "${env_file}"
|
||||
|
||||
declare -A envs
|
||||
|
||||
set +u
|
||||
for env in $(sed -n 's/^[^#].*${\(.*\)}.*/\1/p' ${template_file})
|
||||
do
|
||||
if [ -z "$(eval echo \$${env})" ];then
|
||||
openim::log::error "environment variable '${env}' not set"
|
||||
missing=true
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${missing}" ];then
|
||||
openim::log::error 'You may run `source scripts/environment.sh` to set these environment'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval "cat << EOF
|
||||
$(cat ${template_file})
|
||||
EOF"
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright © 2023 OpenIM. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -31,6 +32,44 @@ COLOR_BGREEN='\033[42m';COLOR_BYELLOW='\033[43m';
|
||||
COLOR_BBLUE='\033[44m';COLOR_BMAGENTA='\033[45m';
|
||||
COLOR_BCYAN='\033[46m';COLOR_BWHITE='\033[47m';
|
||||
|
||||
# Color definitions
|
||||
COLOR_SUFFIX="\033[0m" # End all colors and special effects
|
||||
BLACK_PREFIX="\033[30m" # Black prefix
|
||||
RED_PREFIX="\033[31m" # Red prefix
|
||||
GREEN_PREFIX="\033[32m" # Green prefix
|
||||
YELLOW_PREFIX="\033[33m" # Yellow prefix
|
||||
BLUE_PREFIX="\033[34m" # Blue prefix
|
||||
SKY_BLUE_PREFIX="\033[36m" # Sky blue prefix
|
||||
WHITE_PREFIX="\033[37m" # White prefix
|
||||
BOLD_PREFIX="\033[1m" # Bold prefix
|
||||
UNDERLINE_PREFIX="\033[4m" # Underline prefix
|
||||
ITALIC_PREFIX="\033[3m" # Italic prefix
|
||||
BRIGHT_GREEN_PREFIX='\033[1;32m' # Bright green prefix
|
||||
CYAN_PREFIX="\033[0;36m" # Cyan prefix
|
||||
|
||||
# --- helper functions for logs ---
|
||||
info()
|
||||
{
|
||||
echo -e "[${GREEN_PREFIX}INFO${COLOR_SUFFIX}] " "$@"
|
||||
}
|
||||
warn()
|
||||
{
|
||||
echo -e "[${YELLOW_PREFIX}WARN${COLOR_SUFFIX}] " "$@" >&2
|
||||
}
|
||||
fatal()
|
||||
{
|
||||
echo -e "[${RED_PREFIX}ERROR${COLOR_SUFFIX}] " "$@" >&2
|
||||
exit 1
|
||||
}
|
||||
debug()
|
||||
{
|
||||
echo -e "[${BLUE_PREFIX}DEBUG${COLOR_SUFFIX}]===> " "$@"
|
||||
}
|
||||
success()
|
||||
{
|
||||
echo -e "${BRIGHT_GREEN_PREFIX}===> [SUCCESS] <===${COLOR_SUFFIX}\n=> " "$@"
|
||||
}
|
||||
|
||||
# Print colors you can use
|
||||
openim::color::print_color()
|
||||
{
|
||||
|
||||
@@ -709,3 +709,23 @@ if [[ -z "${color_start-}" ]]; then
|
||||
fi
|
||||
|
||||
# ex: ts=2 sw=2 et filetype=sh
|
||||
|
||||
|
||||
# input: [10023, 2323, 3434]
|
||||
# output: 10023 2323 3434
|
||||
|
||||
# Function function: Converts a list to a string, removing Spaces and parentheses
|
||||
function list_to_string() {
|
||||
ports_list=$* # 获取传入的参数列表
|
||||
sub_s1=$(echo $ports_list | sed 's/ //g') # 去除空格
|
||||
sub_s2=${sub_s1//,/ } # 将逗号替换为空格
|
||||
sub_s3=${sub_s2#*[} # 去除左括号及其之前的内容
|
||||
sub_s4=${sub_s3%]*} # 去除右括号及其之后的内容
|
||||
ports_array=$sub_s4 # 将处理后的字符串赋值给变量 ports_array
|
||||
}
|
||||
|
||||
# Function Function: Remove Spaces in the string
|
||||
function remove_space() {
|
||||
value=$* # 获取传入的参数
|
||||
result=$(echo $value | sed 's/ //g') # 去除空格
|
||||
}
|
||||
|
||||
@@ -46,6 +46,12 @@ BIN_DIR := $(OUTPUT_DIR)/bin
|
||||
$(shell mkdir -p $(BIN_DIR))
|
||||
endif
|
||||
|
||||
# BIN_TOOLS_DIR: Directory where executable files are stored.
|
||||
ifeq ($(origin BIN_TOOLS_DIR),undefined)
|
||||
BIN_TOOLS_DIR := $(OUTPUT_DIR)/bin-tools
|
||||
$(shell mkdir -p $(BIN_TOOLS_DIR))
|
||||
endif
|
||||
|
||||
# TOOLS_DIR: The directory where tools are stored for build and testing.
|
||||
ifeq ($(origin TOOLS_DIR),undefined)
|
||||
TOOLS_DIR := $(OUTPUT_DIR)/tools
|
||||
|
||||
@@ -44,8 +44,8 @@ ifeq ($(origin GOBIN), undefined)
|
||||
GOBIN := $(GOPATH)/bin
|
||||
endif
|
||||
|
||||
# COMMANDS is Specify all files under ${ROOT_DIR}/cmd/ except those ending in.md
|
||||
COMMANDS ?= $(filter-out %.md, $(wildcard ${ROOT_DIR}/cmd/*))
|
||||
# COMMANDS is Specify all files under ${ROOT_DIR}/cmd/ and ${ROOT_DIR}/tools/ except those ending in.md
|
||||
COMMANDS ?= $(filter-out %.md, $(wildcard ${ROOT_DIR}/cmd/* ${ROOT_DIR}/tools/*))
|
||||
ifeq (${COMMANDS},)
|
||||
$(error Could not determine COMMANDS, set ROOT_DIR or run in source dir)
|
||||
endif
|
||||
@@ -127,8 +127,13 @@ go.build.%:
|
||||
$(BIN_DIR)/platforms/$(OS)/$(ARCH)/$$(basename $${d})$(GO_OUT_EXT) $${d}/main.go; \
|
||||
done; \
|
||||
else \
|
||||
CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \
|
||||
$(BIN_DIR)/platforms/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/cmd/$(COMMAND)/main.go; \
|
||||
if [ -f $(ROOT_DIR)/cmd/$(COMMAND)/main.go ]; then \
|
||||
CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \
|
||||
$(BIN_DIR)/platforms/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/cmd/$(COMMAND)/main.go; \
|
||||
elif [ -f $(ROOT_DIR)/tools/$(COMMAND)/main.go ]; then \
|
||||
CGO_ENABLED=0 GOOS=$(OS) GOARCH=$(ARCH) $(GO) build $(GO_BUILD_FLAGS) -o \
|
||||
$(BIN_TOOLS_DIR)/platforms/$(OS)/$(ARCH)/$(COMMAND)$(GO_OUT_EXT) $(ROOT_DIR)/tools/$(COMMAND)/main.go; \
|
||||
fi \
|
||||
fi
|
||||
|
||||
## go.install: Install deployment openim
|
||||
@@ -178,7 +183,7 @@ go.test.cover: go.test.junit-report
|
||||
## go.format: Run unit test and format codes
|
||||
.PHONY: go.format
|
||||
go.format: tools.verify.golines tools.verify.goimports
|
||||
@echo "===========> Formating codes"
|
||||
@echo "===========> Formatting codes"
|
||||
@$(FIND) -type f -name '*.go' -not -name '*pb*' | $(XARGS) gofmt -s -w
|
||||
@$(FIND) -type f -name '*.go' -not -name '*pb*' | $(XARGS) $(TOOLS_DIR)/goimports -w -local $(ROOT_PACKAGE)
|
||||
@$(FIND) -type f -name '*.go' -not -name '*pb*' | $(XARGS) $(TOOLS_DIR)/golines -w --max-len=200 --reformat-tags --shorten-comments --ignore-generated .
|
||||
@@ -197,8 +202,8 @@ go.updates: tools.verify.go-mod-outdated
|
||||
## go.clean: Clean all builds directories and files
|
||||
.PHONY: go.clean
|
||||
go.clean:
|
||||
@echo "===========> Cleaning all builds TMP_DIR($(TMP_DIR)) AND BIN_DIR($(BIN_DIR))"
|
||||
@-rm -vrf $(TMP_DIR) $(BIN_DIR)
|
||||
@echo "===========> Cleaning all builds TMP_DIR($(TMP_DIR)) AND BIN_DIR($(BIN_DIR)) AND BIN_TOOLS_DIR($(BIN_TOOLS_DIR))"
|
||||
@-rm -vrf $(TMP_DIR) $(BIN_DIR) $(BIN_TOOLS_DIR)
|
||||
@echo "===========> End clean..."
|
||||
|
||||
## copyright.help: Show copyright help
|
||||
|
||||
@@ -30,7 +30,6 @@ echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}"
|
||||
|
||||
bin_dir="$BIN_DIR"
|
||||
logs_dir="$OPENIM_ROOT/logs"
|
||||
sdk_db_dir="$OPENIM_ROOT/sdk/db/"
|
||||
|
||||
ulimit -n 200000
|
||||
|
||||
@@ -51,11 +50,11 @@ if [ ${#rpc_ports[@]} -ne ${#ws_ports[@]} ]; then
|
||||
fi
|
||||
#Check if the service exists
|
||||
#If it is exists,kill this process
|
||||
check=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | wc -l)
|
||||
if [ $check -ge 1 ]; then
|
||||
oldPid=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | awk '{print $2}')
|
||||
kill -9 ${oldPid}
|
||||
fi
|
||||
#check=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | wc -l)
|
||||
#if [ $check -ge 1 ]; then
|
||||
# oldPid=$(ps aux | grep -w ./${openim_msggateway} | grep -v grep | awk '{print $2}')
|
||||
# kill -9 ${oldPid}
|
||||
#fi
|
||||
#Waiting port recycling
|
||||
sleep 1
|
||||
cd ${msg_gateway_binary_root}
|
||||
@@ -79,5 +78,5 @@ if [ $check -ge 1 ]; then
|
||||
echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allNewPid}${COLOR_SUFFIX}
|
||||
echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${BACKGROUND_GREEN}${openim_msggateway}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX}
|
||||
exit -1
|
||||
fi
|
||||
|
||||
@@ -28,7 +28,6 @@ echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}"
|
||||
|
||||
bin_dir="$BIN_DIR"
|
||||
logs_dir="$OPENIM_ROOT/logs"
|
||||
sdk_db_dir="$OPENIM_ROOT/sdk/db/"
|
||||
|
||||
cd $OPENIM_ROOT
|
||||
|
||||
@@ -38,12 +37,12 @@ prome_ports=($ports_array)
|
||||
|
||||
#Check if the service exists
|
||||
#If it is exists,kill this process
|
||||
check=`ps | grep -w ./${openim_msgtransfer} | grep -v grep| wc -l`
|
||||
if [ $check -ge 1 ]
|
||||
then
|
||||
oldPid=`ps | grep -w ./${openim_msgtransfer} | grep -v grep|awk '{print $2}'`
|
||||
kill -9 $oldPid
|
||||
fi
|
||||
#check=`ps | grep -w ./${openim_msgtransfer} | grep -v grep| wc -l`
|
||||
#if [ $check -ge 1 ]
|
||||
#then
|
||||
#oldPid=`ps | grep -w ./${openim_msgtransfer} | grep -v grep|awk '{print $2}'`
|
||||
# kill -9 $oldPid
|
||||
#fi
|
||||
#Waiting port recycling
|
||||
sleep 1
|
||||
|
||||
@@ -59,15 +58,15 @@ for ((i = 0; i < ${msg_transfer_service_num}; i++)); do
|
||||
done
|
||||
|
||||
#Check launched service process
|
||||
check=`ps | grep -w ./${openim_msgtransfer} | grep -v grep| wc -l`
|
||||
check=`ps -axu| grep -w ./${openim_msgtransfer} | grep -v grep| wc -l`
|
||||
if [ $check -ge 1 ]
|
||||
then
|
||||
newPid=`ps | grep -w ./${openim_msgtransfer} | grep -v grep|awk '{print $2}'`
|
||||
newPid=`ps -axu| grep -w ./${openim_msgtransfer} | grep -v grep|awk '{print $2}'`
|
||||
allPorts=""
|
||||
echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX}
|
||||
echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${openim_msgtransfer}${COLOR_SUFFIX}
|
||||
echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX}
|
||||
echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${BACKGROUND_GREEN}${openim_msgtransfer}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX}
|
||||
exit -1
|
||||
fi
|
||||
|
||||
+19
-1
@@ -40,6 +40,19 @@ declare -A supported_architectures=(
|
||||
["darwin-x86_64"]="_output/bin/platforms/darwin/amd64" # Alias for darwin-amd64
|
||||
)
|
||||
|
||||
declare -A supported_architectures_tools=(
|
||||
["linux-amd64"]="_output/bin-tools/platforms/linux/amd64"
|
||||
["linux-arm64"]="_output/bin-tools/platforms/linux/arm64"
|
||||
["linux-mips64"]="_output/bin-tools/platforms/linux/mips64"
|
||||
["linux-mips64le"]="_output/bin-tools/platforms/linux/mips64le"
|
||||
["linux-ppc64le"]="_output/bin-tools/platforms/linux/ppc64le"
|
||||
["linux-s390x"]="_output/bin-tools/platforms/linux/s390x"
|
||||
["darwin-amd64"]="_output/bin-tools/platforms/darwin/amd64"
|
||||
["windows-amd64"]="_output/bin-tools/platforms/windows/amd64"
|
||||
["linux-x86_64"]="_output/bin-tools/platforms/linux/amd64" # Alias for linux-amd64
|
||||
["darwin-x86_64"]="_output/bin-tools/platforms/darwin/amd64" # Alias for darwin-amd64
|
||||
)
|
||||
|
||||
# Check if the architecture and version are supported
|
||||
if [[ -z ${supported_architectures["$version-$architecture"]} ]]; then
|
||||
echo -e "${BLUE_PREFIX}================> Unsupported architecture: $architecture or version: $version${COLOR_SUFFIX}"
|
||||
@@ -50,6 +63,7 @@ echo -e "${BLUE_PREFIX}================> Architecture: $architecture${COLOR_SUFF
|
||||
|
||||
# Set the BIN_DIR based on the architecture and version
|
||||
BIN_DIR=${supported_architectures["$version-$architecture"]}
|
||||
BIN_DIR_TOOLS=${supported_architectures_tools["$version-$architecture"]}
|
||||
|
||||
echo -e "${BLUE_PREFIX}================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR${COLOR_SUFFIX}"
|
||||
|
||||
@@ -84,6 +98,10 @@ config_path="$OPENIM_ROOT/config/config.yaml"
|
||||
configfile_path="$OPENIM_ROOT/config"
|
||||
log_path="$OPENIM_ROOT/log"
|
||||
|
||||
|
||||
component_check="component"
|
||||
component_check_binary_root="$OPENIM_ROOT/$BIN_DIR_TOOLS"
|
||||
|
||||
# servicefile dir path
|
||||
service_source_root=(
|
||||
# api service file
|
||||
@@ -120,4 +138,4 @@ service_names=(
|
||||
"${msg_name}"
|
||||
"${push_name}"
|
||||
# "${sdk_server_name}"
|
||||
)
|
||||
)
|
||||
@@ -32,7 +32,6 @@ echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}"
|
||||
|
||||
bin_dir="$BIN_DIR"
|
||||
logs_dir="$OPENIM_ROOT/logs"
|
||||
sdk_db_dir="$OPENIM_ROOT/sdk/db/"
|
||||
|
||||
cd "$OPENIM_ROOT/scripts/"
|
||||
|
||||
@@ -45,11 +44,11 @@ prome_ports=($ports_array)
|
||||
|
||||
#Check if the service exists
|
||||
#If it is exists,kill this process
|
||||
check=$(ps | grep -w ./${push_name} | grep -v grep | wc -l)
|
||||
if [ $check -ge 1 ]; then
|
||||
oldPid=$(ps | grep -w ./${push_name} | grep -v grep | awk '{print $2}')
|
||||
kill -9 $oldPid
|
||||
fi
|
||||
#check=$(ps | grep -w ./${push_name} | grep -v grep | wc -l)
|
||||
#if [ $check -ge 1 ]; then
|
||||
# oldPid=$(ps | grep -w ./${push_name} | grep -v grep | awk '{print $2}')
|
||||
# kill -9 $oldPid
|
||||
#fi
|
||||
#Waiting port recycling
|
||||
sleep 1
|
||||
cd ${push_binary_root}
|
||||
@@ -61,9 +60,9 @@ done
|
||||
|
||||
sleep 3
|
||||
#Check launched service process
|
||||
check=$(ps | grep -w ./${push_name} | grep -v grep | wc -l)
|
||||
check=$(ps -axu| grep -w ./${push_name} | grep -v grep | wc -l)
|
||||
if [ $check -ge 1 ]; then
|
||||
newPid=$(ps | grep -w ./${push_name} | grep -v grep | awk '{print $2}')
|
||||
newPid=$(ps -axu| grep -w ./${push_name} | grep -v grep | awk '{print $2}')
|
||||
ports=$(netstat -netulp | grep -w ${newPid} | awk '{print $4}' | awk -F '[:]' '{print $NF}')
|
||||
allPorts=""
|
||||
|
||||
@@ -75,5 +74,5 @@ if [ $check -ge 1 ]; then
|
||||
echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${newPid}${COLOR_SUFFIX}
|
||||
echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${BACKGROUND_GREEN}${allPorts}${COLOR_SUFFIX}
|
||||
else
|
||||
echo -e ${BACKGROUND_GREEN}${push_name}${COLOR_SUFFIX}${RED_PREFIX}"\n SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX}
|
||||
exit -1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
cd %~p0../_output/bin/platforms/windows
|
||||
start api.exe -p 10002
|
||||
start auth.exe -p 10060
|
||||
start conversation.exe -p 10080
|
||||
start friend.exe -p 10020
|
||||
start group.exe -p 10050
|
||||
start msg.exe -p 10030
|
||||
start msggateway.exe -p 10040 -w 10001
|
||||
start msgtransfer.exe
|
||||
start third.exe -p 10090
|
||||
start push.exe -p 10070
|
||||
start user.exe -p 10010
|
||||
+14
-2
@@ -38,7 +38,6 @@ fi
|
||||
|
||||
bin_dir="$OPENIM_ROOT/_output/bin"
|
||||
logs_dir="$OPENIM_ROOT/logs"
|
||||
sdk_db_dir="$OPENIM_ROOT/sdk/db/"
|
||||
|
||||
if [ ! -d "$bin_dir" ]; then
|
||||
mkdir -p "$bin_dir"
|
||||
@@ -78,6 +77,19 @@ need_to_start_server_shell=(
|
||||
start_cron.sh
|
||||
)
|
||||
|
||||
component_check=start_component_check.sh
|
||||
echo -e ""
|
||||
chmod +x $component_check
|
||||
echo -e "=========> ${BACKGROUND_GREEN}Executing ${component_check}...${COLOR_SUFFIX}"
|
||||
echo -e ""
|
||||
./$component_check
|
||||
if [ $? -ne 0 ]; then
|
||||
# Print error message and exit
|
||||
echo -e "${RED_PREFIX}${BOLD_PREFIX}Error executing ${component_check}. Exiting...${COLOR_SUFFIX}"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
|
||||
# Loop through the script names and execute them
|
||||
for i in ${need_to_start_server_shell[*]}; do
|
||||
chmod +x $i
|
||||
@@ -92,7 +104,7 @@ for i in ${need_to_start_server_shell[*]}; do
|
||||
# Check if the script executed successfully
|
||||
if [ $? -ne 0 ]; then
|
||||
# Print error message and exit
|
||||
echo "${BOLD_PREFIX}${RED_PREFIX}Error executing ${i}. Exiting...${COLOR_SUFFIX}"
|
||||
echo -e "${RED_PREFIX}${BOLD_PREFIX}Error executing ${i}. Exiting(please check open-im-server/logs/openIM.log)... ${COLOR_SUFFIX}"
|
||||
exit -1
|
||||
fi
|
||||
done
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user