Compare commits
54 Commits
v3.1.1-beta.3
...
v3.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
| ef399caa4d | |||
| 7994b377f7 | |||
| 45a0266b20 | |||
| 8b365ae49f | |||
| 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 |
@@ -75,6 +75,17 @@ OpenIMSDK/OpenKF:
|
|||||||
dest: .github/.codecov.yml
|
dest: .github/.codecov.yml
|
||||||
replace: false
|
replace: false
|
||||||
|
|
||||||
|
openim-sigs/openim-docker:
|
||||||
|
- source: ./config
|
||||||
|
dest: ./openim-server/config
|
||||||
|
replace: true
|
||||||
|
- source: ./docs
|
||||||
|
dest: ./openim-server/docs
|
||||||
|
replace: true
|
||||||
|
- source: ./scripts
|
||||||
|
dest: ./openim-server/scripts
|
||||||
|
replace: true
|
||||||
|
|
||||||
group:
|
group:
|
||||||
# first group:common to all warehouses
|
# first group:common to all warehouses
|
||||||
# TODO: add the required warehouse here
|
# TODO: add the required warehouse here
|
||||||
|
|||||||
@@ -40,10 +40,13 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
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
|
- name: Start Docker Compose
|
||||||
run: |
|
run: |
|
||||||
sudo docker compose stop
|
|
||||||
sudo sleep 30
|
|
||||||
sudo docker compose up -d
|
sudo docker compose up -d
|
||||||
sudo sleep 60
|
sudo sleep 60
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
|
|||||||
+1
-1
@@ -156,7 +156,7 @@ flycheck_*.el
|
|||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
# Go workspace file
|
# Go workspace file
|
||||||
go.work
|
# go.work
|
||||||
|
|
||||||
### JetBrains ###
|
### JetBrains ###
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
|||||||
+5
-6
@@ -10,14 +10,14 @@ ENV GOPROXY=$GOPROXY
|
|||||||
# Set up the working directory
|
# Set up the working directory
|
||||||
WORKDIR /openim/openim-server
|
WORKDIR /openim/openim-server
|
||||||
|
|
||||||
COPY go.mod go.sum ./
|
COPY go.mod go.sum go.work go.work.sum ./
|
||||||
RUN go mod download
|
#RUN go mod download
|
||||||
|
|
||||||
# Copy all files to the container
|
# Copy all files to the container
|
||||||
ADD . .
|
ADD . .
|
||||||
|
|
||||||
RUN /bin/sh -c "make clean"
|
RUN make clean
|
||||||
RUN /bin/sh -c "make build"
|
RUN make build
|
||||||
|
|
||||||
FROM ghcr.io/openim-sigs/openim-bash-image:latest
|
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 ${OPENIM_SERVER_CMDDIR} /openim/openim-server/scripts
|
||||||
COPY --from=builder ${SERVER_WORKDIR}/config /openim/openim-server/config
|
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/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">
|
<p align="center">
|
||||||
<a href="./README.md"><b> English </b></a> •
|
<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>
|
||||||
|
|
||||||
|
|
||||||
</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
|
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. ✨
|
||||||
- Easy to integrate
|
|
||||||
- Excellent scalability
|
|
||||||
- High performance
|
|
||||||
- Lightweight
|
|
||||||
- Supports multiple protocols
|
|
||||||
|
|
||||||
## Community
|
## :star2: Why OpenIM
|
||||||
- Visit the official website: [OpenIM Developer Documentation](https://www.openim.online/)
|
|
||||||
|
|
||||||
## 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
|
1. Clone the project
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# choose what you need
|
# 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
|
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
|
```bash
|
||||||
USER=root #no need to modify
|
USER=root #no need to modify
|
||||||
@@ -70,9 +137,9 @@ DATA_DIR=./ #designate large disk directory
|
|||||||
3. Deploy and start
|
3. Deploy and start
|
||||||
|
|
||||||
> **Note**
|
> **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
|
make install
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -84,9 +151,9 @@ make check
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details> <summary>Compile from Source</summary>
|
||||||
### Compile from source
|
|
||||||
|
|
||||||
Ur need `Go 1.18` or higher version, and `make`.
|
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
|
BRANCH=release-v3.1
|
||||||
git clone -b $BRANCH https://github.com/OpenIMSDK/Open-IM-Server openim && export openim=$(pwd)/openim && cd $openim && make build
|
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)
|
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.
|
`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.
|
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.
|
- Used for RPC service discovery and registration, cluster support.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
zookeeper:
|
zookeeper:
|
||||||
schema: openim #Not recommended to modify
|
schema: openim #Not recommended to modify
|
||||||
address: [ 127.0.0.1:2181 ] #address
|
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.
|
- Used for storing users, relationships, and groups, supports master-slave database.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
mysql:
|
mysql:
|
||||||
address: [ 127.0.0.1:13306 ] #address
|
address: [ 127.0.0.1:13306 ] #address
|
||||||
username: root #username
|
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.
|
- Used for storing offline messages, supports mongo sharded clusters.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
mongo:
|
mongo:
|
||||||
uri: #Use this value directly if not empty
|
uri: #Use this value directly if not empty
|
||||||
address: [ 127.0.0.1:37017 ] #address
|
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.
|
- Used for storing message sequence numbers, latest messages, user tokens, and mysql cache, supports cluster deployment.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
redis:
|
redis:
|
||||||
address: [ 127.0.0.1:16379 ] #address
|
address: [ 127.0.0.1:16379 ] #address
|
||||||
username: #username
|
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.
|
- Used for message queues, for message decoupling, supports cluster deployment.
|
||||||
|
|
||||||
```
|
```bash
|
||||||
kafka:
|
kafka:
|
||||||
username: #username
|
username: #username
|
||||||
password: #password
|
password: #password
|
||||||
@@ -188,27 +258,31 @@ The config/config.yaml file has detailed configuration instructions for the stor
|
|||||||
msgToModify: modify
|
msgToModify: modify
|
||||||
```
|
```
|
||||||
|
|
||||||
### Start and Stop Services
|
</details>
|
||||||
|
|
||||||
|
<details> <summary>Start and Stop Services</summary>
|
||||||
|
|
||||||
Start services
|
Start services
|
||||||
|
|
||||||
```
|
```
|
||||||
./start_all.sh;
|
./scripts/start_all.sh;
|
||||||
```
|
```
|
||||||
|
|
||||||
Check services
|
Check services
|
||||||
|
|
||||||
```
|
```
|
||||||
./check_all.sh
|
./scripts/check_all.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Stop services
|
Stop services
|
||||||
|
|
||||||
```
|
```
|
||||||
./stop_all.sh
|
./scripts/stop_all.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
### Open IM Ports
|
</details>
|
||||||
|
|
||||||
|
<details> <summary>Open IM Ports</summary>
|
||||||
|
|
||||||
| TCP Port | Description | Operation |
|
| 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: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 |
|
| 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 Port | Description | Operation |
|
||||||
| --------- | --------------------------------------------------- | ----------------------------------------------------- |
|
| --------- | --------------------------------------------------- | ----------------------------------------------------- |
|
||||||
| TCP:10008 | Business system, such as registration, login etc | Port release or nginx reverse proxy, and firewall off |
|
| 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 |
|
| 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
|
Delve into the heart of Open-IM-Server's functionality with our architecture diagram.
|
||||||
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.
|
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 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
|
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 want anyone to get involved in our community, we offer gifts and rewards, and we welcome you to join us every Thursday night.
|
|
||||||
|
|
||||||
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
|
## :busts_in_silhouette: Community
|
||||||
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.
|
|
||||||
|
|
||||||

|
+ 📚 [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}
|
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}/_output/bin/platforms /openim/openim-server/_output/bin/platforms
|
||||||
COPY --from=builder ${SERVER_WORKDIR}/config /openim/openim-server/config
|
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/_output/bin/platforms /openim/openim-server/_output/bin/platforms
|
||||||
COPY --from=builder /openim/openim-server/config /openim/openim-server/config
|
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
|
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 10230
|
||||||
EXPOSE 20230
|
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 10120
|
||||||
EXPOSE 20120
|
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 10150
|
||||||
EXPOSE 20150
|
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 10130
|
||||||
EXPOSE 20130
|
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
|
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
|
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() {
|
func main() {
|
||||||
cronTaskCmd := cmd.NewCronTaskCmd()
|
cronTaskCmd := cmd.NewCronTaskCmd()
|
||||||
if err := cronTaskCmd.Exec(tools.StartCronTask); err != nil {
|
if err := cronTaskCmd.Exec(tools.StartTask); err != nil {
|
||||||
panic(err.Error())
|
panic(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -124,15 +124,19 @@ api:
|
|||||||
# Session token
|
# Session token
|
||||||
# Configuration for Tencent COS
|
# Configuration for Tencent COS
|
||||||
# Configuration for Aliyun OSS
|
# 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:
|
object:
|
||||||
enable: "minio"
|
enable: "minio"
|
||||||
apiURL: http://127.0.0.1:10002/object/
|
apiURL: "http://127.0.0.1:10002"
|
||||||
minio:
|
minio:
|
||||||
bucket: "openim"
|
bucket: "openim"
|
||||||
endpoint: http://127.0.0.1:10005
|
endpoint: "http://127.0.0.1:10005"
|
||||||
accessKeyID: root
|
accessKeyID: "root"
|
||||||
secretAccessKey: openIM123
|
secretAccessKey: "openIM123"
|
||||||
sessionToken: ""
|
sessionToken: ""
|
||||||
|
signEndpoint: "http://127.0.0.1:10005"
|
||||||
cos:
|
cos:
|
||||||
bucketURL: "https://temp-1252357374.cos.ap-chengdu.myqcloud.com"
|
bucketURL: "https://temp-1252357374.cos.ap-chengdu.myqcloud.com"
|
||||||
secretID: ""
|
secretID: ""
|
||||||
@@ -142,7 +146,7 @@ object:
|
|||||||
endpoint: "https://oss-cn-chengdu.aliyuncs.com"
|
endpoint: "https://oss-cn-chengdu.aliyuncs.com"
|
||||||
bucket: "demo-9999999"
|
bucket: "demo-9999999"
|
||||||
bucketURL: "https://demo-9999999.oss-cn-chengdu.aliyuncs.com"
|
bucketURL: "https://demo-9999999.oss-cn-chengdu.aliyuncs.com"
|
||||||
accessKeyID: root
|
accessKeyID: ""
|
||||||
accessKeySecret: ""
|
accessKeySecret: ""
|
||||||
sessionToken: ""
|
sessionToken: ""
|
||||||
|
|
||||||
|
|||||||
@@ -304,6 +304,16 @@ userInfoUpdated:
|
|||||||
desc: "Remove a blocked user"
|
desc: "Remove a blocked user"
|
||||||
ext: "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#########################
|
#####################conversation#########################
|
||||||
conversationChanged:
|
conversationChanged:
|
||||||
isSendMsg: false
|
isSendMsg: false
|
||||||
|
|||||||
@@ -2,6 +2,17 @@
|
|||||||
|
|
||||||
OpenIM 支持很多种集群化部署方式,包括但不限于 helm, sealos, kubeam, kubesphere, kubeflow, kuboard, kubespray, k3s, k3d, k3c, k3sup, k3v, k3x
|
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
|
```bash
|
||||||
|
|||||||
+12
-12
@@ -52,7 +52,6 @@ services:
|
|||||||
net.core.somaxconn: 1024
|
net.core.somaxconn: 1024
|
||||||
command: redis-server --requirepass ${PASSWORD} --appendonly yes
|
command: redis-server --requirepass ${PASSWORD} --appendonly yes
|
||||||
|
|
||||||
|
|
||||||
zookeeper:
|
zookeeper:
|
||||||
image: wurstmeister/zookeeper
|
image: wurstmeister/zookeeper
|
||||||
ports:
|
ports:
|
||||||
@@ -63,7 +62,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
restart: always
|
restart: always
|
||||||
|
network_mode: "host"
|
||||||
|
|
||||||
kafka:
|
kafka:
|
||||||
image: wurstmeister/kafka
|
image: wurstmeister/kafka
|
||||||
@@ -74,7 +73,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
TZ: Asia/Shanghai
|
TZ: Asia/Shanghai
|
||||||
KAFKA_BROKER_ID: 0
|
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_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_ADVERTISED_LISTENERS: INSIDE://127.0.0.1:9092,OUTSIDE://103.116.45.174:9092
|
||||||
KAFKA_LISTENERS: INSIDE://:9092,OUTSIDE://:9093
|
KAFKA_LISTENERS: INSIDE://:9092,OUTSIDE://:9093
|
||||||
@@ -82,6 +81,7 @@ services:
|
|||||||
KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
|
KAFKA_INTER_BROKER_LISTENER_NAME: INSIDE
|
||||||
depends_on:
|
depends_on:
|
||||||
- zookeeper
|
- zookeeper
|
||||||
|
network_mode: "host"
|
||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
@@ -98,9 +98,9 @@ services:
|
|||||||
restart: always
|
restart: always
|
||||||
command: minio server /data --console-address ':9090'
|
command: minio server /data --console-address ':9090'
|
||||||
|
|
||||||
|
openim-server:
|
||||||
openim_server:
|
image: ghcr.io/openimsdk/openim-server:latest
|
||||||
image: ghcr.io/openim-sigs/openim-server:v1.0.0-debug.11 #ghcr.io/openimsdk/openim-server:main
|
# build: .
|
||||||
container_name: openim-server
|
container_name: openim-server
|
||||||
volumes:
|
volumes:
|
||||||
- ./logs:/openim/openim-server/logs
|
- ./logs:/openim/openim-server/logs
|
||||||
@@ -122,19 +122,19 @@ services:
|
|||||||
max-file: "2"
|
max-file: "2"
|
||||||
|
|
||||||
openim-chat:
|
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
|
container_name: openim-chat
|
||||||
volumes:
|
volumes:
|
||||||
- ./_output/openim/openim-chat/logs:/openim/openim-chat/logs
|
- ./openim-chat/logs:/openim/openim-chat/logs
|
||||||
- ./_output/openim/openim-chat/config:/openim/openim-chat/config
|
- ./openim-chat/config:/openim/openim-chat/config
|
||||||
- ./_output/openim/openim-chat/scripts:/openim/openim-chat/scripts
|
- ./openim-chat/scripts:/openim/openim-chat/scripts
|
||||||
restart: always
|
restart: always
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
- mongodb
|
- mongodb
|
||||||
- redis
|
- redis
|
||||||
- minio
|
- minio
|
||||||
- openim_server
|
- openim-server
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
logging:
|
logging:
|
||||||
driver: json-file
|
driver: json-file
|
||||||
@@ -150,7 +150,7 @@ services:
|
|||||||
# ports:
|
# ports:
|
||||||
# - 9091:9091
|
# - 9091:9091
|
||||||
depends_on:
|
depends_on:
|
||||||
- openim_server
|
- openim-server
|
||||||
command: --web.listen-address=:9091 --config.file="/etc/prometheus/prometheus.yml"
|
command: --web.listen-address=:9091 --config.file="/etc/prometheus/prometheus.yml"
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
docs/.generated_docs
|
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.md
|
||||||
docs/guide/en-US/cmd/openim/openim_color.md
|
docs/guide/en-US/cmd/openim/openim_color.md
|
||||||
docs/guide/en-US/cmd/openim/openim_completion.md
|
docs/guide/en-US/cmd/openim/openim_completion.md
|
||||||
|
|||||||
+201
-209
@@ -1,23 +1,19 @@
|
|||||||
## Go 代码开发规范
|
## OpenIM development specification
|
||||||
在Go 项目开发中,一个好的编码规范可以极大的提高代码质量。为了帮你节省时间和精力,这里我整理了一份清晰、可直接套用的 Go 编码规范,供你参考。
|
We have very high standards for code style and specification, and we want our products to be polished and perfect
|
||||||
|
|
||||||
这份规范,是我参考了 Go 官方提供的编码规范,以及 Go 社区沉淀的一些比较合理的规范之后,加入自己的理解总结出的,它比很多公司内部的规范更全面,你掌握了,以后在面试大厂的时候,或者在大厂里写代码的时候,都会让人高看你一眼,觉得你code很专业。
|
## 1. Code style
|
||||||
|
|
||||||
这份编码规范中包含代码风格、命名规范、注释规范、类型、控制结构、函数、GOPATH 设置规范、依赖管理和最佳实践九类规范。如果你觉得这些规范内容太多了,看完一遍也记不住,这完全没关系。你可以多看几遍,也可以在用到时把它翻出来,在实际应用中掌握。这篇特别放送的内容,更多是作为写代码时候的一个参考手册。
|
### 1.1 Code format
|
||||||
|
|
||||||
## 1. 代码风格
|
- Code must be formatted with `gofmt`.
|
||||||
|
- Leave spaces between operators and operands.
|
||||||
### 1.1 代码格式
|
- It is recommended that a line of code does not exceed 120 characters. If the part exceeds, please use an appropriate line break method. But there are also some exception scenarios, such as import lines, code automatically generated by tools, and struct fields with tags.
|
||||||
|
- The file length cannot exceed 800 lines.
|
||||||
- 代码都必须用 `gofmt` 进行格式化。
|
- Function length cannot exceed 80 lines.
|
||||||
- 运算符和操作数之间要留空格。
|
- import specification
|
||||||
- 建议一行代码不超过120个字符,超过部分,请采用合适的换行方式换行。但也有些例外场景,例如import行、工具自动生成的代码、带tag的struct字段。
|
- All code must be formatted with `goimports` (it is recommended to set the code Go code editor to: run `goimports` on save).
|
||||||
- 文件长度不能超过800行。
|
- Do not use relative paths to import packages, such as `import ../util/net`.
|
||||||
- 函数长度不能超过80行。
|
- Import aliases must be used when the package name does not match the last directory name of the import path, or when multiple identical package names conflict.
|
||||||
- import规范
|
|
||||||
- 代码都必须用`goimports`进行格式化(建议将代码Go代码编辑器设置为:保存时运行 `goimports`)。
|
|
||||||
- 不要使用相对路径引入包,例如 `import ../util/net` 。
|
|
||||||
- 包名称与导入路径的最后一个目录名不匹配时,或者多个相同包名冲突时,则必须使用导入别名。
|
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -26,32 +22,32 @@
|
|||||||
//good
|
//good
|
||||||
jwt "github.com/dgrijalva/jwt-go/v4"
|
jwt "github.com/dgrijalva/jwt-go/v4"
|
||||||
```
|
```
|
||||||
- 导入的包建议进行分组,匿名包的引用使用一个新的分组,并对匿名包引用进行说明。
|
- Imported packages are suggested to be grouped, and anonymous package references use a new group, and anonymous package references are explained.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
// go 标准包
|
// go standard package
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
// 第三方包
|
// third party package
|
||||||
"github.com/jinzhu/gorm"
|
"github.com/jinzhu/gorm"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
// 匿名包单独分组,并对匿名包引用进行说明
|
// Anonymous packages are grouped separately, and anonymous package references are explained
|
||||||
// import mysql driver
|
// import mysql driver
|
||||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||||
|
|
||||||
// 内部包
|
// inner package
|
||||||
v1 "github.com/marmotedu/api/apiserver/v1"
|
v1 "github.com/marmotedu/api/apiserver/v1"
|
||||||
metav1 "github.com/marmotedu/apimachinery/pkg/meta/v1"
|
metav1 "github.com/marmotedu/apimachinery/pkg/meta/v1"
|
||||||
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.2 声明、初始化和定义
|
### 1.2 Declaration, initialization and definition
|
||||||
|
|
||||||
当函数中需要使用到多个变量时,可以在函数开始处使用`var`声明。在函数外部声明必须使用 `var` ,不要采用 `:=` ,容易踩到变量的作用域的问题。
|
When multiple variables need to be used in a function, the `var` declaration can be used at the beginning of the function. Declaration outside the function must use `var`, do not use `:=`, it is easy to step on the scope of the variable.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var (
|
var (
|
||||||
@@ -60,7 +56,7 @@ var (
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
- 在初始化结构引用时,请使用`&T{}`代替`new(T)`,以使其与结构体初始化一致。
|
- When initializing a structure reference, please use `&T{}` instead of `new(T)` to make it consistent with structure initialization.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -71,7 +67,7 @@ sptr.Name = "bar"
|
|||||||
sptr := &T{Name: "bar"}
|
sptr := &T{Name: "bar"}
|
||||||
```
|
```
|
||||||
|
|
||||||
- struct 声明和初始化格式采用多行,定义如下。
|
- The struct declaration and initialization format takes multiple lines and is defined as follows.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type User struct{
|
type User struct{
|
||||||
@@ -85,7 +81,7 @@ user := User{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 相似的声明放在一组,同样适用于常量、变量和类型声明。
|
- Similar declarations are grouped together, and the same applies to constant, variable, and type declarations.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -99,14 +95,14 @@ import (
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
- 尽可能指定容器容量,以便为容器预先分配内存,例如:
|
- Specify container capacity where possible to pre-allocate memory for the container, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
v := make(map[int]string, 4)
|
v := make(map[int]string, 4)
|
||||||
v := make([]string, 0, 4)
|
v := make([]string, 0, 4)
|
||||||
```
|
```
|
||||||
|
|
||||||
- 在顶层,使用标准var关键字。请勿指定类型,除非它与表达式的类型不同。
|
- At the top level, use the standard var keyword. Do not specify a type unless it is different from the type of the expression.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -116,13 +112,13 @@ func F() string { return "A" }
|
|||||||
|
|
||||||
// good
|
// good
|
||||||
var_s = F()
|
var_s = F()
|
||||||
// 由于 F 已经明确了返回一个字符串类型,因此我们没有必要显式指定_s 的类型
|
// Since F already explicitly returns a string type, we don't need to explicitly specify the type of _s
|
||||||
// 还是那种类型
|
// still of that type
|
||||||
|
|
||||||
func F() string { return "A" }
|
func F() string { return "A" }
|
||||||
```
|
```
|
||||||
|
|
||||||
- 对于未导出的顶层常量和变量,使用`_`作为前缀。
|
- Use `_` as a prefix for unexported top-level constants and variables.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -138,7 +134,7 @@ const (
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
- 嵌入式类型(例如 mutex)应位于结构体内的字段列表的顶部,并且必须有一个空行将嵌入式字段与常规字段分隔开。
|
- Embedded types (such as mutexes) should be at the top of the field list within the struct, and there must be a blank line separating embedded fields from regular fields.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -155,9 +151,9 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 1.3 错误处理
|
### 1.3 Error Handling
|
||||||
|
|
||||||
- `error`作为函数的值返回,必须对`error`进行处理,或将返回值赋值给明确忽略。对于`defer xx.Close()`可以不用显式处理。
|
- `error` is returned as the value of the function, `error` must be handled, or the return value assigned to explicitly ignore. For `defer xx.Close()`, there is no need to explicitly handle it.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func load() error {
|
func load() error {
|
||||||
@@ -171,7 +167,7 @@ load()
|
|||||||
_ = load()
|
_ = load()
|
||||||
```
|
```
|
||||||
|
|
||||||
- `error`作为函数的值返回且有多个返回值的时候,`error`必须是最后一个参数。
|
- When `error` is returned as the value of a function and there are multiple return values, `error` must be the last parameter.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -185,7 +181,7 @@ func load() (int, error) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 尽早进行错误处理,并尽早返回,减少嵌套。
|
- Perform error handling as early as possible and return as early as possible to reduce nesting.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -203,7 +199,7 @@ if err != nil {
|
|||||||
// normal code
|
// normal code
|
||||||
```
|
```
|
||||||
|
|
||||||
- 如果需要在 if 之外使用函数调用的结果,则应采用下面的方式。
|
- If you need to use the result of the function call outside if, you should use the following method.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -218,7 +214,7 @@ if err != nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 错误要单独判断,不与其他逻辑组合判断。
|
- Errors should be judged independently, not combined with other logic.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -241,7 +237,7 @@ if v == nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 如果返回值需要初始化,则采用下面的方式。
|
- If the return value needs to be initialized, use the following method.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
v, err := f()
|
v, err := f()
|
||||||
@@ -251,8 +247,8 @@ if err != nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 错误描述建议
|
- Bug description suggestions
|
||||||
- 错误描述用小写字母开头,结尾不要加标点符号,例如:
|
- Error descriptions start with a lowercase letter and do not end with punctuation, for example:
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
errors.New("Redis connection failed")
|
errors.New("Redis connection failed")
|
||||||
@@ -261,34 +257,34 @@ if err != nil {
|
|||||||
// good
|
// good
|
||||||
errors.New("redis connection failed")
|
errors.New("redis connection failed")
|
||||||
```
|
```
|
||||||
- 告诉用户他们可以做什么,而不是告诉他们不能做什么。
|
- Tell users what they can do, not what they can't.
|
||||||
- 当声明一个需求时,用must 而不是should。例如,`must be greater than 0、must match regex '[a-z]+'`。
|
- When declaring a requirement, use must instead of should. For example, `must be greater than 0, must match regex '[a-z]+'`.
|
||||||
- 当声明一个格式不对时,用must not。例如,`must not contain`。
|
- When declaring that a format is incorrect, use must not. For example, `must not contain`.
|
||||||
- 当声明一个动作时用may not。例如,`may not be specified when otherField is empty、only name may be specified`。
|
- Use may not when declaring an action. For example, `may not be specified when otherField is empty, only name may be specified`.
|
||||||
- 引用文字字符串值时,请在单引号中指示文字。例如,`ust not contain '..'`。
|
- When quoting a literal string value, indicate the literal in single quotes. For example, `ust not contain '..'`.
|
||||||
- 当引用另一个字段名称时,请在反引号中指定该名称。例如,must be greater than `request`。
|
- When referencing another field name, specify that name in backticks. For example, must be greater than `request`.
|
||||||
- 指定不等时,请使用单词而不是符号。例如,`must be less than 256、must be greater than or equal to 0 (不要用 larger than、bigger than、more than、higher than)`。
|
- When specifying unequal, use words instead of symbols. For example, `must be less than 256, must be greater than or equal to 0 (do not use larger than, bigger than, more than, higher than)`.
|
||||||
- 指定数字范围时,请尽可能使用包含范围。
|
- When specifying ranges of numbers, use inclusive ranges whenever possible.
|
||||||
- 建议 Go 1.13 以上,error 生成方式为 `fmt.Errorf("module xxx: %w", err)`。
|
- Go 1.13 or above is recommended, and the error generation method is `fmt.Errorf("module xxx: %w", err)`.
|
||||||
|
|
||||||
### 1.4 panic处理
|
### 1.4 panic processing
|
||||||
|
|
||||||
- 在业务逻辑处理中禁止使用panic。
|
- Panic is prohibited in business logic processing.
|
||||||
- 在main包中,只有当程序完全不可运行时使用panic,例如无法打开文件、无法连接数据库导致程序无法正常运行。
|
- In the main package, panic is only used when the program is completely inoperable, for example, the file cannot be opened, the database cannot be connected, and the program cannot run normally.
|
||||||
- 在main包中,使用 `log.Fatal` 来记录错误,这样就可以由log来结束程序,或者将panic抛出的异常记录到日志文件中,方便排查问题。
|
- In the main package, use `log.Fatal` to record errors, so that the program can be terminated by the log, or the exception thrown by the panic can be recorded in the log file, which is convenient for troubleshooting.
|
||||||
- 可导出的接口一定不能有panic。
|
- An exportable interface must not panic.
|
||||||
- 包内建议采用error而不是panic来传递错误。
|
- It is recommended to use error instead of panic to convey errors in the package.
|
||||||
|
|
||||||
### 1.5 单元测试
|
### 1.5 Unit Tests
|
||||||
|
|
||||||
- 单元测试文件名命名规范为 `example_test.go`。
|
- The unit test filename naming convention is `example_test.go`.
|
||||||
- 每个重要的可导出函数都要编写测试用例。
|
- Write a test case for every important exportable function.
|
||||||
- 因为单元测试文件内的函数都是不对外的,所以可导出的结构体、函数等可以不带注释。
|
- Because the functions in the unit test file are not external, the exportable structures, functions, etc. can be uncommented.
|
||||||
- 如果存在 `func (b *Bar) Foo` ,单测函数可以为 `func TestBar_Foo`。
|
- If `func (b *Bar) Foo` exists, the single test function can be `func TestBar_Foo`.
|
||||||
|
|
||||||
### 1.6 类型断言失败处理
|
### 1.6 Type assertion failure handling
|
||||||
|
|
||||||
- type assertion 的单个返回值针对不正确的类型将产生 panic。请始终使用 “comma ok”的惯用法。
|
- A single return value from a type assertion will panic for an incorrect type. Always use the "comma ok" idiom.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -301,85 +297,85 @@ if !ok {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 2. 命名规范
|
## 2. Naming convention
|
||||||
|
|
||||||
命名规范是代码规范中非常重要的一部分,一个统一的、短小的、精确的命名规范可以大大提高代码的可读性,也可以借此规避一些不必要的Bug。
|
The naming convention is a very important part of the code specification. A uniform, short, and precise naming convention can greatly improve the readability of the code and avoid unnecessary bugs.
|
||||||
|
|
||||||
### 2.1 包命名
|
### 2.1 Package Naming
|
||||||
|
|
||||||
- 包名必须和目录名一致,尽量采取有意义、简短的包名,不要和标准库冲突。
|
- The package name must be consistent with the directory name, try to use a meaningful and short package name, and do not conflict with the standard library.
|
||||||
- 包名全部小写,没有大写或下划线,使用多级目录来划分层级。
|
- Package names are all lowercase, without uppercase or underscores, and use multi-level directories to divide the hierarchy.
|
||||||
- 项目名可以通过中划线来连接多个单词。
|
- Item names can connect multiple words with dashes.
|
||||||
- 包名以及包所在的目录名,不要使用复数,例如,是`net/url`,而不是`net/urls`。
|
- Do not use plurals for the package name and the directory name where the package is located, for example, `net/url` instead of `net/urls`.
|
||||||
- 不要用 common、util、shared 或者 lib 这类宽泛的、无意义的包名。
|
- Don't use broad, meaningless package names like common, util, shared or lib.
|
||||||
- 包名要简单明了,例如 net、time、log。
|
- The package name should be simple and clear, such as net, time, log.
|
||||||
|
|
||||||
### 2.2 函数命名
|
### 2.2 Function Naming
|
||||||
|
|
||||||
- 函数名采用驼峰式,首字母根据访问控制决定使用大写或小写,例如:`MixedCaps`或者`mixedCaps`。
|
- The function name is in camel case, and the first letter is uppercase or lowercase according to the access control decision,For example: `MixedCaps` or `mixedCaps`.
|
||||||
- 代码生成工具自动生成的代码(如`xxxx.pb.go`)和为了对相关测试用例进行分组,而采用的下划线(如`TestMyFunction_WhatIsBeingTested`)排除此规则。
|
- Code automatically generated by code generation tools (such as `xxxx.pb.go`) and underscores used to group related test cases (such as `TestMyFunction_WhatIsBeingTested`) exclude this rule.
|
||||||
|
|
||||||
### 2.3 文件命名
|
### 2.3 File Naming
|
||||||
|
|
||||||
- 文件名要简短有意义。
|
- Keep the filename short and meaningful.
|
||||||
- 文件名应小写,并使用下划线分割单词。
|
- Filenames should be lowercase and use underscores to separate words.
|
||||||
|
|
||||||
### 2.4 结构体命名
|
### 2.4 Structure Naming
|
||||||
|
|
||||||
- 采用驼峰命名方式,首字母根据访问控制决定使用大写或小写,例如`MixedCaps`或者`mixedCaps`。
|
- The camel case is adopted, and the first letter is uppercase or lowercase according to the access control, such as `MixedCaps` or `mixedCaps`.
|
||||||
- 结构体名不应该是动词,应该是名词,比如 `Node`、`NodeSpec`。
|
- Struct names should not be verbs, but should be nouns, such as `Node`, `NodeSpec`.
|
||||||
- 避免使用Data、Info这类无意义的结构体名。
|
- Avoid using meaningless structure names such as Data and Info.
|
||||||
- 结构体的声明和初始化应采用多行,例如:
|
- The declaration and initialization of the structure should take multiple lines, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// User 多行声明
|
// User multi-line declaration
|
||||||
type User struct {
|
type User struct {
|
||||||
Name string
|
name string
|
||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 多行初始化
|
// multi-line initialization
|
||||||
u := User{
|
u := User{
|
||||||
UserName: "belm",
|
UserName: "belm",
|
||||||
Email: "nosbelm@qq.com",
|
Email: "nosbelm@qq.com",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.5 接口命名
|
### 2.5 Interface Naming
|
||||||
|
|
||||||
- 接口命名的规则,基本和结构体命名规则保持一致:
|
- The interface naming rules are basically consistent with the structure naming rules:
|
||||||
- 单个函数的接口名以 “er"”作为后缀(例如Reader,Writer),有时候可能导致蹩脚的英文,但是没关系。
|
- Interface names of individual functions suffixed with "er"" (e.g. Reader, Writer) can sometimes lead to broken English, but that's okay.
|
||||||
- 两个函数的接口名以两个函数名命名,例如ReadWriter。
|
- The interface name of the two functions is named after the two function names, eg ReadWriter.
|
||||||
- 三个以上函数的接口名,类似于结构体名。
|
- An interface name for more than three functions, similar to a structure name.
|
||||||
|
|
||||||
例如:
|
For example:
|
||||||
|
|
||||||
```
|
```
|
||||||
// Seeking to an offset before the start of the file is an error.
|
// Seeking to an offset before the start of the file is an error.
|
||||||
// Seeking to any positive offset is legal, but the behavior of subsequent
|
// Seeking to any positive offset is legal, but the behavior of subsequent
|
||||||
// I/O operations on the underlying object is implementation-dependent.
|
// I/O operations on the underlying object are implementation-dependent.
|
||||||
type Seeker interface {
|
type Seeker interface {
|
||||||
Seek(offset int64, whence int) (int64, error)
|
Seek(offset int64, whence int) (int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadWriter is the interface that groups the basic Read and Write methods.
|
// ReadWriter is the interface that groups the basic Read and Write methods.
|
||||||
type ReadWriter interface {
|
type ReadWriter interface {
|
||||||
Reader
|
reader
|
||||||
Writer
|
Writer
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.6 变量命名
|
### 2.6 Variable Naming
|
||||||
|
|
||||||
- 变量名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。
|
- Variable names must follow camel case, and the initial letter is uppercase or lowercase according to the access control decision.
|
||||||
- 在相对简单(对象数量少、针对性强)的环境中,可以将一些名称由完整单词简写为单个字母,例如:
|
- In relatively simple (few objects, highly targeted) environments, some names can be abbreviated from full words to single letters, for example:
|
||||||
- user 可以简写为 u;
|
- user can be abbreviated as u;
|
||||||
- userID 可以简写 uid。
|
- userID can be abbreviated as uid.
|
||||||
- 特有名词时,需要遵循以下规则:
|
- When using proper nouns, the following rules need to be followed:
|
||||||
- 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient。
|
- If the variable is private and the proper noun is the first word, use lowercase, such as apiClient.
|
||||||
- 其他情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID。
|
- In other cases, the original wording of the noun should be used, such as APIClient, repoID, UserID.
|
||||||
|
|
||||||
下面列举了一些常见的特有名词。
|
Some common nouns are listed below.
|
||||||
|
|
||||||
```
|
```
|
||||||
// A GonicMapper that contains a list of common initialisms taken from golang/lint
|
// A GonicMapper that contains a list of common initialisms taken from golang/lint
|
||||||
@@ -420,7 +416,7 @@ var LintGonicMapper = GonicMapper{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 若变量类型为bool类型,则名称应以Has,Is,Can或Allow开头,例如:
|
- If the variable type is bool, the name should start with Has, Is, Can or Allow, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var has Conflict bool
|
var has Conflict bool
|
||||||
@@ -429,13 +425,13 @@ var canManage bool
|
|||||||
var allowGitHook bool
|
var allowGitHook bool
|
||||||
```
|
```
|
||||||
|
|
||||||
- 局部变量应当尽可能短小,比如使用buf指代buffer,使用i指代index。
|
- Local variables should be as short as possible, for example, use buf to refer to buffer, and use i to refer to index.
|
||||||
- 代码生成工具自动生成的代码可排除此规则(如`xxx.pb.go`里面的Id)
|
- The code automatically generated by the code generation tool can exclude this rule (such as the Id in `xxx.pb.go`)
|
||||||
|
|
||||||
### 2.7 常量命名
|
### 2.7 Constant naming
|
||||||
|
|
||||||
- 常量名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。
|
- The constant name must follow the camel case, and the initial letter is uppercase or lowercase according to the access control decision.
|
||||||
- 如果是枚举类型的常量,需要先创建相应类型:
|
- If it is a constant of enumeration type, you need to create the corresponding type first:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Code defines an error code type.
|
// Code defines an error code type.
|
||||||
@@ -450,9 +446,9 @@ const (
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.8 Error的命名
|
### 2.8 Error naming
|
||||||
|
|
||||||
- Error类型应该写成FooError的形式。
|
- The Error type should be written in the form of FooError.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type ExitError struct {
|
type ExitError struct {
|
||||||
@@ -460,18 +456,18 @@ type ExitError struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- Error变量写成ErrFoo的形式。
|
- The Error variable is written in the form of ErrFoo.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var ErrFormat = errors. New("unknown format")
|
var ErrFormat = errors. New("unknown format")
|
||||||
```
|
```
|
||||||
|
|
||||||
## 3. 注释规范
|
## 3. Comment specification
|
||||||
|
|
||||||
- 每个可导出的名字都要有注释,该注释对导出的变量、函数、结构体、接口等进行简要介绍。
|
- Each exportable name must have a comment, which briefly introduces the exported variables, functions, structures, interfaces, etc.
|
||||||
- 全部使用单行注释,禁止使用多行注释。
|
- All single-line comments are used, and multi-line comments are prohibited.
|
||||||
- 和代码的规范一样,单行注释不要过长,禁止超过 120 字符,超过的请使用换行展示,尽量保持格式优雅。
|
- Same as the code specification, single-line comments should not be too long, and no more than 120 characters are allowed. If it exceeds, please use a new line to display, and try to keep the format elegant.
|
||||||
- 注释必须是完整的句子,以需要注释的内容作为开头,句点作为结尾,`格式为 // 名称 描述.`。例如:
|
- A comment must be a complete sentence, starting with the content to be commented and ending with a period, `the format is // name description.`. For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -487,9 +483,9 @@ func PrintFlags(flags *pflag.FlagSet) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 所有注释掉的代码在提交code review前都应该被删除,否则应该说明为什么不删除,并给出后续处理建议。
|
- All commented out code should be deleted before submitting code review, otherwise, it should explain why it is not deleted, and give follow-up processing suggestions.
|
||||||
|
|
||||||
- 在多段注释之间可以使用空行分隔加以区分,如下所示:
|
- Multiple comments can be separated by blank lines, as follows:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Package superman implements methods for saving the world.
|
// Package superman implements methods for saving the world.
|
||||||
@@ -499,10 +495,10 @@ func PrintFlags(flags *pflag.FlagSet) {
|
|||||||
package superman
|
package superman
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.1 包注释
|
### 3.1 Package Notes
|
||||||
|
|
||||||
- 每个包都有且仅有一个包级别的注释。
|
- Each package has one and only one package-level annotation.
|
||||||
- 包注释统一用 // 进行注释,格式为 `// Package 包名 包描述`,例如:
|
- Package comments are uniformly commented with // in the format of `// Package package name package description`, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce
|
// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce
|
||||||
@@ -510,15 +506,15 @@ package superman
|
|||||||
package genericclioptions
|
package genericclioptions
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.2 变量/常量注释
|
### 3.2 Variable/Constant Comments
|
||||||
|
|
||||||
- 每个可导出的变量/常量都必须有注释说明,`格式为// 变量名 变量描述`,例如:
|
- Each variable/constant that can be exported must have a comment description, `the format is // variable name variable description`, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// ErrSigningMethod defines invalid signing method error.
|
// ErrSigningMethod defines invalid signing method error.
|
||||||
var ErrSigningMethod = errors. New("Invalid signing method")
|
var ErrSigningMethod = errors. New("Invalid signing method")
|
||||||
```
|
```
|
||||||
- 出现大块常量或变量定义时,可在前面注释一个总的说明,然后在每一行常量的前一行或末尾详细注释该常量的定义,例如:
|
- When there is a large block of constant or variable definition, you can comment a general description in front, and then comment the definition of the constant in detail before or at the end of each line of constant, for example:
|
||||||
```go
|
```go
|
||||||
// Code must start with 1xxxxx.
|
// Code must start with 1xxxxx.
|
||||||
const (
|
const (
|
||||||
@@ -535,10 +531,10 @@ const (
|
|||||||
ErrValidation
|
ErrValidation
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
### 3.3 结构体注释
|
### 3.3 Structure Annotation
|
||||||
|
|
||||||
- 每个需要导出的结构体或者接口都必须有注释说明,格式为 `// 结构体名 结构体描述.`。
|
- Each structure or interface that needs to be exported must have a comment description, the format is `// structure name structure description.`.
|
||||||
- 结构体内的可导出成员变量名,如果意义不明确,必须要给出注释,放在成员变量的前一行或同一行的末尾。例如:
|
- The name of the exportable member variable in the structure, if the meaning is not clear, a comment must be given and placed before the member variable or at the end of the same line. For example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// User represents a user restful resource. It is also used as gorm model.
|
// User represents a user restful resource. It is also used as gorm model.
|
||||||
@@ -554,9 +550,9 @@ type User struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.4 方法注释
|
### 3.4 Method Notes
|
||||||
|
|
||||||
每个需要导出的函数或者方法都必须有注释,格式为// 函数名 函数描述.,例如:
|
Each function or method that needs to be exported must have a comment, the format is // function name function description., for examplelike:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// BeforeUpdate run before update database record.
|
// BeforeUpdate run before update database record.
|
||||||
@@ -566,20 +562,20 @@ func (p *Policy) BeforeUpdate() (err error) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 3.5 类型注释
|
### 3.5 Type annotations
|
||||||
|
|
||||||
- 每个需要导出的类型定义和类型别名都必须有注释说明,格式为 `// 类型名 类型描述.`,例如:
|
- Each type definition and type alias that needs to be exported must have a comment description, the format is `// type name type description.`, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Code defines an error code type.
|
// Code defines an error code type.
|
||||||
type Code int
|
type Code int
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4. 类型
|
## 4. Type
|
||||||
|
|
||||||
### 4.1 字符串
|
### 4.1 Strings
|
||||||
|
|
||||||
- 空字符串判断。
|
- Empty string judgment.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -593,7 +589,7 @@ if len(s) == 0 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `[]byte`/`string`相等比较。
|
- `[]byte`/`string` equality comparison.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -611,7 +607,7 @@ bytes.Compare(s1, s2) == 0
|
|||||||
bytes. Compare(s1, s2) != 0
|
bytes. Compare(s1, s2) != 0
|
||||||
```
|
```
|
||||||
|
|
||||||
- 复杂字符串使用raw字符串避免字符转义。
|
- Complex strings use raw strings to avoid character escaping.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -621,9 +617,9 @@ regexp.MustCompile("\\.")
|
|||||||
regexp.MustCompile(`\.`)
|
regexp.MustCompile(`\.`)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.2 切片
|
### 4.2 Slicing
|
||||||
|
|
||||||
- 空slice判断。
|
- Empty slice judgment.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -637,9 +633,9 @@ if slice != nil && len(slice) == 0 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
上面判断同样适用于map、channel。
|
The above judgment also applies to map and channel.
|
||||||
|
|
||||||
- 声明slice。
|
- Declare a slice.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -650,7 +646,7 @@ s := make([]string, 0)
|
|||||||
var s[]string
|
var s[]string
|
||||||
```
|
```
|
||||||
|
|
||||||
- slice复制。
|
- slice copy.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -666,7 +662,7 @@ for i := range b1 {
|
|||||||
copy(b2, b1)
|
copy(b2, b1)
|
||||||
```
|
```
|
||||||
|
|
||||||
- slice新增。
|
- slice added.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -680,16 +676,16 @@ var a, b []int
|
|||||||
b = append(b, a...)
|
b = append(b, a...)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.3 结构体
|
### 4.3 Structure
|
||||||
|
|
||||||
- struct初始化。
|
- struct initialization.
|
||||||
|
|
||||||
struct以多行格式初始化。
|
The struct is initialized in multi-line format.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type user struct {
|
type user struct {
|
||||||
Id int64
|
Id int64
|
||||||
Name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
u1 := user{100, "Colin"}
|
u1 := user{100, "Colin"}
|
||||||
@@ -700,11 +696,11 @@ u2 := user{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5. 控制结构
|
## 5. Control Structure
|
||||||
|
|
||||||
### 5.1 if
|
### 5.1 if
|
||||||
|
|
||||||
- if 接受初始化语句,约定如下方式建立局部变量。
|
- if accepts the initialization statement, the convention is to create local variables in the following way.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if err := loadConfig(); err != nil {
|
if err := loadConfig(); err != nil {
|
||||||
@@ -713,7 +709,7 @@ if err := loadConfig(); err != nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- if 对于bool类型的变量,应直接进行真假判断。
|
- if For variables of bool type, true and false judgments should be made directly.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
var isAllow bool
|
var isAllow bool
|
||||||
@@ -724,7 +720,7 @@ if isAllow {
|
|||||||
|
|
||||||
### 5.2 for
|
### 5.2 for
|
||||||
|
|
||||||
- 采用短声明建立局部变量。
|
- Create local variables using short declarations.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
sum := 0
|
sum := 0
|
||||||
@@ -733,7 +729,7 @@ for i := 0; i < 10; i++ {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 不要在 for 循环里面使用 defer,defer只有在函数退出时才会执行。
|
- Don't use defer in for loop, defer will only be executed when the function exits.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// bad
|
// bad
|
||||||
@@ -761,7 +757,7 @@ for file := range files {
|
|||||||
|
|
||||||
### 5.3 range
|
### 5.3 range
|
||||||
|
|
||||||
- 如果只需要第一项(key),就丢弃第二个。
|
- If only the first item (key) is needed, discard the second.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
for key := range keys {
|
for key := range keys {
|
||||||
@@ -769,7 +765,7 @@ for key := range keys {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- 如果只需要第二项,则把第一项置为下划线。
|
- If only the second item is required, underline the first item.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
sum := 0
|
sum := 0
|
||||||
@@ -780,7 +776,7 @@ for _, value := range array {
|
|||||||
|
|
||||||
### 5.4 switch
|
### 5.4 switch
|
||||||
|
|
||||||
- 必须要有default。
|
- must have default.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
switch os := runtime.GOOS; os {
|
switch os := runtime.GOOS; os {
|
||||||
@@ -794,37 +790,37 @@ switch os := runtime.GOOS; os {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 5.5 goto
|
### 5.5 goto
|
||||||
- 业务代码禁止使用 goto 。
|
- Business code prohibits the use of goto.
|
||||||
- 框架或其他底层源码尽量不用。
|
- Try not to use frameworks or other low-level source code.
|
||||||
|
|
||||||
## 6. 函数
|
## 6. Functions
|
||||||
|
|
||||||
- 传入变量和返回变量以小写字母开头。
|
- Incoming variables and return variables start with a lowercase letter.
|
||||||
- 函数参数个数不能超过5个。
|
- The number of function parameters cannot exceed 5.
|
||||||
- 函数分组与顺序
|
- Function grouping and ordering
|
||||||
- 函数应按粗略的调用顺序排序。
|
- Functions should be sorted in rough calling order.
|
||||||
- 同一文件中的函数应按接收者分组。
|
- Functions in the same file should be grouped by receiver.
|
||||||
- 尽量采用值传递,而非指针传递。
|
- Try to use value transfer instead of pointer transfer.
|
||||||
- 传入参数是 map、slice、chan、interface ,不要传递指针。
|
- The incoming parameters are map, slice, chan, interface, do not pass pointers.
|
||||||
|
|
||||||
### 6.1 函数参数
|
### 6.1 Function parameters
|
||||||
|
|
||||||
- 如果函数返回相同类型的两个或三个参数,或者如果从上下文中不清楚结果的含义,使用命名返回,其他情况不建议使用命名返回,例如:
|
- If the function returns two or three arguments of the same type, or if the meaning of the result is not clear from the context, use named returns, otherwise it is not recommended to use named returns, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func coordinate() (x, y float64, err error) {
|
func coordinate() (x, y float64, err error) {
|
||||||
// normal code
|
// normal code
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- 传入变量和返回变量都以小写字母开头。
|
- Both incoming and returned variables start with a lowercase letter.
|
||||||
- 尽量用值传递,非指针传递。
|
- Try to pass by value instead of pointer.
|
||||||
- 参数数量均不能超过5个。
|
- The number of parameters cannot exceed 5.
|
||||||
- 多返回值最多返回三个,超过三个请使用 struct。
|
- Multiple return values can return up to three, and if there are more than three, please use struct.
|
||||||
|
|
||||||
### 6.2 defer
|
### 6.2 defer
|
||||||
|
|
||||||
- 当存在资源创建时,应紧跟defer释放资源(可以大胆使用defer,defer在Go1.14版本中,性能大幅提升,defer的性能损耗即使在性能敏感型的业务中,也可以忽略)。
|
- When resources are created, resources should be released immediately after defer (defer can be used boldly, the performance of defer is greatly improved in Go1.14 version, and the performance loss of defer can be ignored even in performance-sensitive businesses).
|
||||||
- 先判断是否错误,再defer释放资源,例如:
|
- First judge whether there is an error, and then defer to release resources, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
rep, err := http. Get(url)
|
rep, err := http. Get(url)
|
||||||
@@ -835,46 +831,46 @@ if err != nil {
|
|||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
```
|
```
|
||||||
|
|
||||||
### 6.3 方法的接收器
|
### 6.3 Method Receiver
|
||||||
|
|
||||||
- 推荐以类名第一个英文首字母的小写作为接收器的命名。
|
- It is recommended to use the lowercase of the first English letter of the class name as the name of the receiver.
|
||||||
- 接收器的命名在函数超过20行的时候不要用单字符。
|
- Don't use a single character in the name of the receiver when the function exceeds 20 lines.
|
||||||
- 接收器的命名不能采用me、this、self这类易混淆名称。
|
- The name of the receiver cannot use confusing names such as me, this, and self.
|
||||||
|
|
||||||
### 6.4 嵌套
|
### 6.4 Nesting
|
||||||
- 嵌套深度不能超过4层。
|
- The nesting depth cannot exceed 4 levels.
|
||||||
|
|
||||||
### 6.5 变量命名
|
### 6.5 Variable Naming
|
||||||
- 变量声明尽量放在变量第一次使用的前面,遵循就近原则。
|
- The variable declaration should be placed before the first use of the variable as far as possible, following the principle of proximity.
|
||||||
- 如果魔法数字出现超过两次,则禁止使用,改用一个常量代替,例如:
|
- If the magic number appears more than twice, it is forbidden to use it and use a constant instead, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// PI...
|
// PI...
|
||||||
const Prise = 3.14
|
const Price = 3.14
|
||||||
|
|
||||||
func getAppleCost(n float64) float64 {
|
func getAppleCost(n float64) float64 {
|
||||||
return Prise * n
|
return Price * n
|
||||||
}
|
}
|
||||||
|
|
||||||
func getOrangeCost(n float64) float64 {
|
func getOrangeCost(n float64) float64 {
|
||||||
return Prise * n
|
return Price * n
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 7. GOPATH 设置规范
|
## 7. GOPATH setting specification
|
||||||
- Go 1.11 之后,弱化了 GOPATH 规则,已有代码(很多库肯定是在1.11之前建立的)肯定符合这个规则,建议保留 GOPATH 规则,便于维护代码。
|
- After Go 1.11, the GOPATH rule has been weakened. Existing code (many libraries must have been created before 1.11) must conform to this rule. It is recommended to keep the GOPATH rule to facilitate code maintenance.
|
||||||
- 建议只使用一个 GOPATH,不建议使用多个 GOPATH。如果使用多个GOPATH,编译生效的 bin 目录是在第一个 GOPATH 下。
|
- Only one GOPATH is recommended, multiple GOPATHs are not recommended. If multiple GOPATHs are used, the bin directory where compilation takes effect is under the first GOPATH.
|
||||||
|
|
||||||
## 8. 依赖管理
|
## 8. Dependency Management
|
||||||
|
|
||||||
- Go 1.11 以上必须使用 Go Modules。
|
- Go 1.11 and above must use Go Modules.
|
||||||
- 使用Go Modules作为依赖管理的项目时,不建议提交vendor目录。
|
- When using Go Modules as a dependency management project, it is not recommended to submit the vendor directory.
|
||||||
- 使用Go Modules作为依赖管理的项目时,必须提交go.sum文件。
|
- When using Go Modules as a dependency management project, the go.sum file must be submitted.
|
||||||
|
|
||||||
### 9. 最佳实践
|
### 9. Best Practices
|
||||||
|
|
||||||
- 尽量少用全局变量,而是通过参数传递,使每个函数都是“无状态”的。这样可以减少耦合,也方便分工和单元测试。
|
- Minimize the use of global variables, but pass parameters, so that each function is "stateless". This reduces coupling and facilitates division of labor and unit testing.
|
||||||
- 在编译时验证接口的符合性,例如:
|
- Verify interface compliance at compile time, for example:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type LogHandler struct {
|
type LogHandler struct {
|
||||||
@@ -883,19 +879,15 @@ type LogHandler struct {
|
|||||||
}
|
}
|
||||||
var_http.Handler = LogHandler{}
|
var_http.Handler = LogHandler{}
|
||||||
```
|
```
|
||||||
- 服务器处理请求时,应该创建一个context,保存该请求的相关信息(如requestID),并在函数调用链中传递。
|
- When the server processes a request, it should create a context, save the relevant information of the request (such as requestID), and pass it in the function call chain.
|
||||||
|
|
||||||
### 9.1 性能
|
### 9.1 Performance
|
||||||
- string 表示的是不可变的字符串变量,对 string 的修改是比较重的操作,基本上都需要重新申请内存。所以,如果没有特殊需要,需要修改时多使用 []byte。
|
- string represents an immutable string variable, modifying string is a relatively heavy operation, and basically needs to re-apply for memory. Therefore, if there is no special need, use []byte more when you need to modify.
|
||||||
- 优先使用 strconv 而不是 fmt。
|
- Prefer strconv over fmt.
|
||||||
|
|
||||||
### 9.2 注意事项
|
### 9.2 Precautions
|
||||||
|
|
||||||
- append 要小心自动分配内存,append 返回的可能是新分配的地址。
|
- append Be careful about automatically allocating memory, append may return a newly allocated address.
|
||||||
- 如果要直接修改 map 的 value 值,则 value 只能是指针,否则要覆盖原来的值。
|
- If you want to directly modify the value of the map, the value can only be a pointer, otherwise the original value must be overwritten.
|
||||||
- map 在并发中需要加锁。
|
- map needs to be locked during concurrency.
|
||||||
- 编译过程无法检查 interface{} 的转换,只能在运行时检查,小心引起 panic。
|
- The conversion of interface{} cannot be checked during compilation, it can only be checked at runtime, be careful to cause panic.
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
这里向你介绍了九类常用的编码规范。但今天的最后,我要在这里提醒你一句:规范是人定的,你也可以根据需要,制定符合你项目的规范,但同时我也建议你采纳这些业界沉淀下来的规范,并通过工具来确保规范的执行。
|
|
||||||
|
|||||||
+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.
|
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`.
|
||||||
- `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.
|
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.
|
- `ga`: General Availability, the first stable release.
|
||||||
- `r/release/or nothing`: The final release version, intended for general users.
|
- `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,
|
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`.
|
||||||
- `MINOR` version changes when features are added in a backward-compatible manner, and
|
|
||||||
- `PATCH` version changes when backward-compatible bugs are fixed.
|
|
||||||
|
|
||||||
## Milestones and Branching
|
## Milestones, Branching, and Addressing Major Bugs
|
||||||
|
|
||||||
|
**About:**
|
||||||
|
|
||||||
+ [OpenIM Milestones](https://github.com/OpenIMSDK/Open-IM-Server/milestones)
|
+ [OpenIM Milestones](https://github.com/OpenIMSDK/Open-IM-Server/milestones)
|
||||||
+ [OpenIM Tags](https://github.com/OpenIMSDK/Open-IM-Server/tags)
|
+ [OpenIM Tags](https://github.com/OpenIMSDK/Open-IM-Server/tags)
|
||||||
+ [OpenIM Branches](https://github.com/OpenIMSDK/Open-IM-Server/branches)
|
+ [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
|
```bash
|
||||||
|
bashCopy codebashCopy code# Checkout the branch for the version that needs the bug fix
|
||||||
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
|
|
||||||
git checkout release-v3.1
|
git checkout release-v3.1
|
||||||
|
|
||||||
# Create a new branch for the bug fix
|
# Create a new branch for the bug fix
|
||||||
@@ -92,10 +104,21 @@ git merge release-v3.1
|
|||||||
# Push the updates to the main branch
|
# Push the updates to the main branch
|
||||||
git push origin main
|
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/bwmarrin/snowflake v0.3.0 // indirect
|
||||||
github.com/dtm-labs/rockscache v0.1.1
|
github.com/dtm-labs/rockscache v0.1.1
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
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/gogo/protobuf v1.3.2
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/golang/protobuf v1.5.3
|
github.com/golang/protobuf v1.5.3
|
||||||
@@ -25,19 +25,19 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
go.mongodb.org/mongo-driver v1.12.1
|
go.mongodb.org/mongo-driver v1.12.1
|
||||||
golang.org/x/image v0.9.0 // indirect
|
golang.org/x/image v0.11.0
|
||||||
google.golang.org/api v0.134.0
|
google.golang.org/api v0.136.0
|
||||||
google.golang.org/grpc v1.57.0
|
google.golang.org/grpc v1.57.0
|
||||||
google.golang.org/protobuf v1.31.0
|
google.golang.org/protobuf v1.31.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gorm.io/driver/mysql v1.5.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/google/uuid v1.3.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/OpenIMSDK/protocol v0.0.4
|
github.com/OpenIMSDK/protocol v0.0.12
|
||||||
github.com/OpenIMSDK/tools v0.0.13
|
github.com/OpenIMSDK/tools v0.0.13
|
||||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible
|
||||||
github.com/go-redis/redis v6.15.9+incompatible
|
github.com/go-redis/redis v6.15.9+incompatible
|
||||||
@@ -47,11 +47,11 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.110.4 // indirect
|
cloud.google.com/go v0.110.6 // indirect
|
||||||
cloud.google.com/go/compute v1.20.1 // indirect
|
cloud.google.com/go/compute v1.23.0 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||||
cloud.google.com/go/firestore v1.11.0 // 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/longrunning v0.5.1 // indirect
|
||||||
cloud.google.com/go/storage v1.30.1 // indirect
|
cloud.google.com/go/storage v1.30.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.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/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/arch v0.3.0 // indirect
|
golang.org/x/arch v0.3.0 // indirect
|
||||||
golang.org/x/net v0.12.0 // indirect
|
golang.org/x/net v0.14.0 // indirect
|
||||||
golang.org/x/oauth2 v0.10.0 // indirect
|
golang.org/x/oauth2 v0.11.0 // indirect
|
||||||
golang.org/x/sync v0.3.0 // indirect
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
golang.org/x/sys v0.10.0 // indirect
|
golang.org/x/sys v0.11.0 // indirect
|
||||||
golang.org/x/text v0.11.0 // indirect
|
golang.org/x/text v0.12.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
google.golang.org/appengine v1.6.7 // 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 v0.0.0-20230803162519-f966b187b2e5 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771 // 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 (
|
require (
|
||||||
@@ -137,7 +138,6 @@ require (
|
|||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
go.uber.org/zap v1.24.0 // indirect
|
go.uber.org/zap v1.24.0 // indirect
|
||||||
golang.org/x/crypto v0.11.0 // indirect
|
golang.org/x/crypto v0.12.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.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.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.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk=
|
cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q=
|
||||||
cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI=
|
||||||
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
|
cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY=
|
||||||
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
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 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
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 h1:PPgtwcYUOXV2jFe1bV3nda3RCrOa8cvBjTOn2MQVfW8=
|
||||||
cloud.google.com/go/firestore v1.11.0/go.mod h1:b38dKhgzlmNNGTNZZwe7ZRFEuRab1Hay3/DBsIGKKy4=
|
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.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y=
|
||||||
cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk=
|
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 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI=
|
||||||
cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc=
|
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=
|
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 h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
|
||||||
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
|
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/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.12 h1:79rBgpKBF4TMy5tkLH3DQNiZFVmxKMP7VyX+kmJh4d8=
|
||||||
github.com/OpenIMSDK/protocol v0.0.4/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
|
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 h1:rcw4HS8S2DPZR9UOBxD8/ol9UBMzXBypzOVEytDRIMo=
|
||||||
github.com/OpenIMSDK/tools v0.0.13/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI=
|
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=
|
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/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 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
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.15.0 h1:nDU5XeOKtB3GEa+uB7GNYwhVKsgjAR7VgKoNB6ryXfw=
|
||||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
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 h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
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=
|
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-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-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.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
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/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.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
|
||||||
golang.org/x/image v0.9.0/go.mod h1:jtrku+n79PfroUbvDdeUWMAI+heR786BofxrbiSF+J0=
|
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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
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-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.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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
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-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.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
|
||||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
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-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-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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.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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.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.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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-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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
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 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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=
|
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-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 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
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.136.0 h1:e/6enzUE1s4tGPa6Q3ZYShKTtvRc+1Jq0rrafhppmOs=
|
||||||
google.golang.org/api v0.134.0/go.mod h1:sjRL3UnjTx5UqNQS9EWr9N8p7xbHpy1k0XGRLCf3Spk=
|
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.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.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
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-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-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-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-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g=
|
||||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130/go.mod h1:O9kGHb51iE/nOGvQaDUuadVYqovW56s5emA88lQnj6Y=
|
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-20230706204954-ccb25ca9f130 h1:XVeBY8d/FaK4848myy41HBqnDwvxeV3zMZhwN1TvAMU=
|
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-20230706204954-ccb25ca9f130/go.mod h1:mPBs5jNgx2GuQGvFwUvVKqtn6HsUw9nP64BedgvqEsQ=
|
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-20230720185612-659f7aaaa771 h1:Z8qdAF9GFsmcUuWQ5KVYIpP3PCKydn/YKORnghIalu4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230720185612-659f7aaaa771/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
|
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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
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 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
|
||||||
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
|
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.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
||||||
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
|
gorm.io/gorm v1.25.3 h1:zi4rHZj1anhZS2EuEODMhDisGy+Daq9jtPrNGgbQYD8=
|
||||||
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
|
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-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/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=
|
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
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Copyright © 2023 OpenIM. All rights reserved.
|
# Copyright © 2023 OpenIM. All rights reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# 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) {
|
func (m *MessageApi) SearchMsg(c *gin.Context) {
|
||||||
a2r.Call(msg.MsgClient.SearchMessage, m.Client, c)
|
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", ParseToken, u.GetUsers)
|
||||||
userRouterGroup.POST("/get_users_online_status", ParseToken, u.GetUsersOnlineStatus)
|
userRouterGroup.POST("/get_users_online_status", ParseToken, u.GetUsersOnlineStatus)
|
||||||
userRouterGroup.POST("/get_users_online_token_detail", ParseToken, u.GetUsersOnlineTokenDetail)
|
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("/unsubscribe_users_status", ParseToken, u.UnSubscriberStatus)
|
||||||
userRouterGroup.POST("/get_users_status", ParseToken, u.GetUserStatus)
|
userRouterGroup.POST("/get_users_status", ParseToken, u.GetUserStatus)
|
||||||
|
userRouterGroup.POST("/get_subscribe_users_status", ParseToken, u.GetSubscribeUsersStatus)
|
||||||
}
|
}
|
||||||
// friend routing group
|
// friend routing group
|
||||||
friendRouterGroup := r.Group("/friend", ParseToken)
|
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("/batch_send_msg", m.BatchSendMsg)
|
||||||
msgGroup.POST("/check_msg_is_send_success", m.CheckMsgIsSendSuccess)
|
msgGroup.POST("/check_msg_is_send_success", m.CheckMsgIsSendSuccess)
|
||||||
|
msgGroup.POST("/get_server_time", m.GetServerTime)
|
||||||
}
|
}
|
||||||
// Conversation
|
// Conversation
|
||||||
conversationGroup := r.Group("/conversation", ParseToken)
|
conversationGroup := r.Group("/conversation", ParseToken)
|
||||||
|
|||||||
@@ -83,7 +83,14 @@ func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
|
|||||||
operationID = strconv.Itoa(rand.Int())
|
operationID = strconv.Itoa(rand.Int())
|
||||||
}
|
}
|
||||||
ctx := mcontext.SetOperationID(c, operationID)
|
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 err != nil {
|
||||||
if errs.ErrArgs.Is(err) {
|
if errs.ErrArgs.Is(err) {
|
||||||
c.String(http.StatusBadRequest, err.Error())
|
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)
|
a2r.Call(user.UserClient.SubscribeOrCancelUsersStatus, u.Client, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserStatus Get the online status of the user.
|
||||||
func (u *UserApi) GetUserStatus(c *gin.Context) {
|
func (u *UserApi) GetUserStatus(c *gin.Context) {
|
||||||
a2r.Call(user.UserClient.GetUserStatus, u.Client, c)
|
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 {
|
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 {
|
type Resp struct {
|
||||||
@@ -53,7 +59,13 @@ type Resp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Resp) String() string {
|
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 {
|
type MessageHandler interface {
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) {
|
|||||||
len(modifyMsgList),
|
len(modifyMsgList),
|
||||||
)
|
)
|
||||||
conversationIDMsg := msgprocessor.GetChatConversationIDByMsg(ctxMsgList[0].message)
|
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.handleMsg(ctx, msgChannelValue.uniqueKey, conversationIDMsg, storageMsgList, notStorageMsgList)
|
||||||
och.handleNotification(
|
och.handleNotification(
|
||||||
ctx,
|
ctx,
|
||||||
|
|||||||
+69
-14
@@ -16,6 +16,9 @@ package group
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
@@ -73,20 +76,33 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
|
|||||||
userRpcClient := rpcclient.NewUserRpcClient(client)
|
userRpcClient := rpcclient.NewUserRpcClient(client)
|
||||||
msgRpcClient := rpcclient.NewMessageRpcClient(client)
|
msgRpcClient := rpcclient.NewMessageRpcClient(client)
|
||||||
conversationRpcClient := rpcclient.NewConversationRpcClient(client)
|
conversationRpcClient := rpcclient.NewConversationRpcClient(client)
|
||||||
database := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase())
|
var gs groupServer
|
||||||
pbGroup.RegisterGroupServer(server, &groupServer{
|
database := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase(), gs.groupMemberHashCode)
|
||||||
GroupDatabase: database,
|
gs.GroupDatabase = database
|
||||||
User: userRpcClient,
|
gs.User = userRpcClient
|
||||||
Notification: notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) {
|
gs.Notification = notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) {
|
||||||
users, err := userRpcClient.GetUsersInfo(ctx, userIDs)
|
users, err := userRpcClient.GetUsersInfo(ctx, userIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil
|
return utils.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil
|
||||||
}),
|
|
||||||
conversationRpcClient: conversationRpcClient,
|
|
||||||
msgRpcClient: msgRpcClient,
|
|
||||||
})
|
})
|
||||||
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +177,9 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbGroup.CreateGroupR
|
|||||||
if req.OwnerUserID == "" {
|
if req.OwnerUserID == "" {
|
||||||
return nil, errs.ErrArgs.Wrap("no group owner")
|
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 {
|
if err := authverify.CheckAccessV3(ctx, req.OwnerUserID); err != nil {
|
||||||
return nil, err
|
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")
|
return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed")
|
||||||
}
|
}
|
||||||
var inGroup bool
|
var inGroup bool
|
||||||
_, err = s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.FromUserID)
|
if _, err := s.GroupDatabase.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil {
|
||||||
if err == nil {
|
|
||||||
inGroup = true // 已经在群里了
|
inGroup = true // 已经在群里了
|
||||||
} else if !s.IsNotFound(err) {
|
} else if !s.IsNotFound(err) {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -718,6 +736,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup
|
|||||||
return nil, err
|
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 {
|
if err := s.GroupDatabase.HandlerGroupRequest(ctx, req.GroupID, req.FromUserID, req.HandledMsg, req.HandleResult, member); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -727,12 +746,14 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbGroup
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.Notification.GroupApplicationAcceptedNotification(ctx, req)
|
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:
|
case constant.GroupResponseRefuse:
|
||||||
s.Notification.GroupApplicationRejectedNotification(ctx, req)
|
s.Notification.GroupApplicationRejectedNotification(ctx, req)
|
||||||
}
|
}
|
||||||
if member != nil {
|
|
||||||
s.Notification.MemberEnterNotification(ctx, req)
|
|
||||||
}
|
|
||||||
return &pbGroup.GroupApplicationResponseResp{}, nil
|
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 {
|
if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.Notification.MemberEnterDirectlyNotification(ctx, req.GroupID, req.InviterUserID)
|
s.Notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
groupRequest := relationTb.GroupRequestModel{
|
groupRequest := relationTb.GroupRequestModel{
|
||||||
@@ -1486,3 +1507,37 @@ func (s *groupServer) GetGroupUsersReqApplicationList(ctx context.Context, req *
|
|||||||
resp.Total = total
|
resp.Total = total
|
||||||
return resp, nil
|
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,11 +26,16 @@ import (
|
|||||||
"github.com/OpenIMSDK/tools/log"
|
"github.com/OpenIMSDK/tools/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (*msg.GetConversationsHasReadAndMaxSeqResp, error) {
|
func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) {
|
||||||
conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
var conversationIDs []string
|
||||||
|
if len(req.ConversationIDs) == 0 {
|
||||||
|
conversationIDs, err = m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
conversationIDs = req.ConversationIDs
|
||||||
|
}
|
||||||
hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs)
|
hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -49,7 +54,7 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
for conversarionID, maxSeq := range maxSeqs {
|
||||||
resp.Seqs[conversarionID] = &msg.Seqs{
|
resp.Seqs[conversarionID] = &msg.Seqs{
|
||||||
HasReadSeq: hasReadSeqs[conversarionID],
|
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 {
|
if total, chatLogs, err = m.MsgDatabase.SearchMessage(ctx, req); err != nil {
|
||||||
return nil, err
|
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 {
|
for _, chatLog := range chatLogs {
|
||||||
pbChatLog := &msg.ChatLog{}
|
pbChatLog := &msg.ChatLog{}
|
||||||
utils.CopyStructFields(pbChatLog, chatLog)
|
utils.CopyStructFields(pbChatLog, chatLog)
|
||||||
pbChatLog.SendTime = chatLog.SendTime
|
pbChatLog.SendTime = chatLog.SendTime
|
||||||
pbChatLog.CreateTime = chatLog.CreateTime
|
pbChatLog.CreateTime = chatLog.CreateTime
|
||||||
if chatLog.SenderNickname == "" {
|
if chatLog.SenderNickname == "" {
|
||||||
sendUser, err := m.User.GetUserInfo(ctx, chatLog.SendID)
|
pbChatLog.SenderNickname = sendMap[chatLog.SendID]
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pbChatLog.SenderNickname = sendUser.Nickname
|
|
||||||
}
|
}
|
||||||
switch chatLog.SessionType {
|
switch chatLog.SessionType {
|
||||||
case constant.SingleChatType:
|
case constant.SingleChatType:
|
||||||
recvUser, err := m.User.GetUserInfo(ctx, chatLog.RecvID)
|
pbChatLog.RecvNickname = recvMap[chatLog.RecvID]
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
pbChatLog.RecvNickname = recvUser.Nickname
|
|
||||||
|
|
||||||
case constant.GroupChatType, constant.SuperGroupChatType:
|
case constant.GroupChatType, constant.SuperGroupChatType:
|
||||||
group, err := m.Group.GetGroupInfo(ctx, chatLog.GroupID)
|
pbChatLog.SenderFaceURL = groupMap[chatLog.GroupID].FaceURL
|
||||||
if err != nil {
|
pbChatLog.GroupMemberCount = groupMap[chatLog.GroupID].MemberCount
|
||||||
return nil, err
|
pbChatLog.RecvID = groupMap[chatLog.GroupID].GroupID
|
||||||
}
|
pbChatLog.GroupName = groupMap[chatLog.GroupID].GroupName
|
||||||
pbChatLog.SenderFaceURL = group.FaceURL
|
pbChatLog.GroupOwner = groupMap[chatLog.GroupID].OwnerUserID
|
||||||
pbChatLog.GroupMemberCount = group.MemberCount
|
pbChatLog.GroupType = groupMap[chatLog.GroupID].GroupType
|
||||||
pbChatLog.RecvID = group.GroupID
|
|
||||||
pbChatLog.GroupName = group.GroupName
|
|
||||||
pbChatLog.GroupOwner = group.OwnerUserID
|
|
||||||
pbChatLog.GroupType = group.GroupType
|
|
||||||
}
|
}
|
||||||
resp.ChatLogs = append(resp.ChatLogs, pbChatLog)
|
resp.ChatLogs = append(resp.ChatLogs, pbChatLog)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.ChatLogsNum = total
|
resp.ChatLogsNum = total
|
||||||
return resp, nil
|
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 {
|
if groupMemberInfo.RoleLevel == constant.GroupOwner {
|
||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
if groupMemberInfo.MuteEndTime >= time.Now().Unix() {
|
if groupMemberInfo.MuteEndTime >= time.Now().UnixMilli() {
|
||||||
return errs.ErrMutedInGroup.Wrap()
|
return errs.ErrMutedInGroup.Wrap()
|
||||||
}
|
}
|
||||||
if groupInfo.Status == constant.GroupStatusMuted && groupMemberInfo.RoleLevel != constant.GroupAdmin {
|
if groupInfo.Status == constant.GroupStatusMuted && groupMemberInfo.RoleLevel != constant.GroupAdmin {
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ package third
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/s3"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/third"
|
"github.com/OpenIMSDK/protocol/third"
|
||||||
"github.com/OpenIMSDK/tools/errs"
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
"github.com/OpenIMSDK/tools/log"
|
"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) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e
|
|||||||
if apiURL[len(apiURL)-1] != '/' {
|
if apiURL[len(apiURL)-1] != '/' {
|
||||||
apiURL += "/"
|
apiURL += "/"
|
||||||
}
|
}
|
||||||
|
apiURL += "object/"
|
||||||
rdb, err := cache.NewRedis()
|
rdb, err := cache.NewRedis()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
+43
-12
@@ -20,17 +20,16 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/log"
|
||||||
|
"github.com/OpenIMSDK/tools/tx"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/authverify"
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/authverify"
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/unrelation"
|
"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"
|
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/config"
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert"
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/convert"
|
||||||
@@ -41,14 +40,15 @@ import (
|
|||||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient"
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient"
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification"
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/rpcclient/notification"
|
||||||
|
|
||||||
"google.golang.org/grpc"
|
pbuser "github.com/OpenIMSDK/protocol/user"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/tools/utils"
|
"github.com/OpenIMSDK/tools/utils"
|
||||||
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type userServer struct {
|
type userServer struct {
|
||||||
controller.UserDatabase
|
controller.UserDatabase
|
||||||
notificationSender *notification.FriendNotificationSender
|
friendNotificationSender *notification.FriendNotificationSender
|
||||||
|
userNotificationSender *notification.UserNotificationSender
|
||||||
friendRpcClient *rpcclient.FriendRpcClient
|
friendRpcClient *rpcclient.FriendRpcClient
|
||||||
RegisterCenter registry.SvcDiscoveryRegistry
|
RegisterCenter registry.SvcDiscoveryRegistry
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,8 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
|
|||||||
UserDatabase: database,
|
UserDatabase: database,
|
||||||
RegisterCenter: client,
|
RegisterCenter: client,
|
||||||
friendRpcClient: &friendRpcClient,
|
friendRpcClient: &friendRpcClient,
|
||||||
notificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)),
|
friendNotificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)),
|
||||||
|
userNotificationSender: notification.NewUserNotificationSender(&msgRpcClient, notification.WithUserFunc(database.FindWithError)),
|
||||||
}
|
}
|
||||||
pbuser.RegisterUserServer(server, u)
|
pbuser.RegisterUserServer(server, u)
|
||||||
return u.UserDatabase.InitOnce(context.Background(), users)
|
return u.UserDatabase.InitOnce(context.Background(), users)
|
||||||
@@ -119,13 +120,13 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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)
|
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, friendID := range friends {
|
for _, friendID := range friends {
|
||||||
s.notificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
|
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
|
||||||
}
|
}
|
||||||
return resp, nil
|
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 {
|
if err := s.UpdateByMap(ctx, req.UserID, m); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
s.notificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
|
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
|
||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +274,7 @@ func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbus
|
|||||||
return &pbuser.SubscribeOrCancelUsersStatusResp{}, nil
|
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) {
|
func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatusReq) (resp *pbuser.GetUserStatusResp, err error) {
|
||||||
onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
|
onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -281,10 +283,39 @@ func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatu
|
|||||||
return &pbuser.GetUserStatusResp{StatusList: onlineStatusList}, nil
|
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) {
|
func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatusReq) (resp *pbuser.SetUserStatusResp, err error) {
|
||||||
err = s.UserDatabase.SetUserStatus(ctx, req.StatusList)
|
err = s.UserDatabase.SetUserStatus(ctx, req.StatusList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
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"
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StartCronTask() error {
|
func StartTask() error {
|
||||||
fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime)
|
fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime)
|
||||||
msgTool, err := InitMsgTool()
|
msgTool, err := InitMsgTool()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
msgTool.ConvertTools()
|
||||||
c := cron.New()
|
c := cron.New()
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func InitMsgTool() (*MsgTool, error) {
|
|||||||
tx.NewGorm(db),
|
tx.NewGorm(db),
|
||||||
userMongoDB,
|
userMongoDB,
|
||||||
)
|
)
|
||||||
groupDatabase := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase())
|
groupDatabase := controller.InitGroupDatabase(db, rdb, mongo.GetDatabase(), nil)
|
||||||
conversationDatabase := controller.NewConversationDatabase(
|
conversationDatabase := controller.NewConversationDatabase(
|
||||||
relation.NewConversationGorm(db),
|
relation.NewConversationGorm(db),
|
||||||
cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), 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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
# 需要先启动OpenIMServer
|
||||||
|
zookeeper:
|
||||||
|
schema: openim
|
||||||
|
zkAddr:
|
||||||
|
- 127.0.0.1:2181
|
||||||
|
username: ""
|
||||||
|
password: ""
|
||||||
|
|
||||||
|
chatApi:
|
||||||
|
openImChatApiPort: [ 10008 ]
|
||||||
|
listenIP:
|
||||||
|
|
||||||
|
adminApi:
|
||||||
|
openImAdminApiPort: [ 10009 ]
|
||||||
|
listenIP:
|
||||||
|
|
||||||
|
rpc:
|
||||||
|
registerIP: #作为rpc启动时,注册到zookeeper的IP,api/gateway能访问到此ip和对应的rpcPort中的端口
|
||||||
|
listenIP: #默认为0.0.0.0
|
||||||
|
|
||||||
|
rpcPort:
|
||||||
|
openImAdminPort: [ 30200 ]
|
||||||
|
openImChatPort: [ 30300 ]
|
||||||
|
rpcRegisterName:
|
||||||
|
openImAdminName: admin
|
||||||
|
openImChatName: chat
|
||||||
|
|
||||||
|
# 没有配置表示和OpenIM一致
|
||||||
|
mysql:
|
||||||
|
# address: [ 127.0.0.1:13306 ] #目前仅支持单机
|
||||||
|
# username: root #用户名
|
||||||
|
# password: openIM123 #密码
|
||||||
|
# database: openIM_v2 #不建议修改
|
||||||
|
# maxOpenConn: 1000 #最大连接数
|
||||||
|
# maxIdleConn: 100 #最大空闲连接数
|
||||||
|
# maxLifeTime: 60 #连接可以重复使用的最长时间(秒)
|
||||||
|
# logLevel: 4 #日志级别 1=slient 2=error 3=warn 4=info
|
||||||
|
# slowThreshold: 500 #慢语句阈值 (毫秒)
|
||||||
|
database: openim_enterprise
|
||||||
|
|
||||||
|
# 没有配置表示和OpenIM一致
|
||||||
|
log:
|
||||||
|
storageLocation: ../logs/ #存放目录
|
||||||
|
# rotationTime: 24 #日志旋转时间
|
||||||
|
# remainRotationCount: 2 #日志数量
|
||||||
|
# remainLogLevel: 6 #日志级别 6表示全都打印,
|
||||||
|
# isStdout: false
|
||||||
|
# isJson: false
|
||||||
|
# withStack: false
|
||||||
|
|
||||||
|
# secret: openIM123
|
||||||
|
#tokenPolicy:
|
||||||
|
# expire: 86400
|
||||||
|
|
||||||
|
verifyCode:
|
||||||
|
validTime: 300 # 验证码有效时间
|
||||||
|
validCount: 5 # 验证码有效次数
|
||||||
|
uintTime: 86400 # 单位时间间隔
|
||||||
|
maxCount: 10 # 单位时间内最大获取次数
|
||||||
|
superCode: "666666" # 超级验证码(只有use为空时使用)
|
||||||
|
len: 6 # 验证码长度
|
||||||
|
use: "" # 使用的验证码服务(use: "ali")
|
||||||
|
ali:
|
||||||
|
endpoint: "dysmsapi.aliyuncs.com"
|
||||||
|
accessKeyId: ""
|
||||||
|
accessKeySecret: ""
|
||||||
|
signName: ""
|
||||||
|
verificationCodeTemplateCode: ""
|
||||||
|
|
||||||
|
# 获取ip的header,没有配置直接获取远程地址
|
||||||
|
#proxyHeader: "X-Forwarded-For"
|
||||||
|
|
||||||
|
adminList:
|
||||||
|
- adminID: admin1
|
||||||
|
nickname: chat1
|
||||||
|
imAdmin: openIM123456
|
||||||
|
- adminID: admin2
|
||||||
|
nickname: chat2
|
||||||
|
imAdmin: openIM654321
|
||||||
|
- adminID: admin3
|
||||||
|
nickname: chat3
|
||||||
|
imAdmin: openIMAdmin
|
||||||
|
|
||||||
|
|
||||||
|
openIMUrl: "http://127.0.0.1:10002"
|
||||||
|
|
||||||
|
redis:
|
||||||
|
address: [ 127.0.0.1:16379 ]
|
||||||
|
username:
|
||||||
|
password: openIM123
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
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.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
Copyright © {{.Year}} {{.Holder}} 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.
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/..
|
||||||
|
|
||||||
|
source $OPENIM_ROOT/scripts/style_info.sh
|
||||||
|
source $OPENIM_ROOT/scripts/path_info.sh
|
||||||
|
source $OPENIM_ROOT/scripts/function.sh
|
||||||
|
|
||||||
|
list1=$(cat $config_path | grep openImPushPort | awk -F '[:]' '{print $NF}')
|
||||||
|
list2=$(cat $config_path | grep pushPrometheusPort | awk -F '[:]' '{print $NF}')
|
||||||
|
list_to_string $list1
|
||||||
|
rpc_ports=($ports_array)
|
||||||
|
list_to_string $list2
|
||||||
|
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
|
||||||
|
#Waiting port recycling
|
||||||
|
sleep 1
|
||||||
|
cd ${push_binary_root}
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#rpc_ports[@]}; i++)); do
|
||||||
|
nohup ./${push_name} -port ${rpc_ports[$i]} -prometheus_port ${prome_ports[$i]} >>../logs/openIM.log 2>&1 &
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep 3
|
||||||
|
#Check launched service process
|
||||||
|
check=$(ps | 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}')
|
||||||
|
ports=$(netstat -netulp | grep -w ${newPid} | awk '{print $4}' | awk -F '[:]' '{print $NF}')
|
||||||
|
allPorts=""
|
||||||
|
|
||||||
|
for i in $ports; do
|
||||||
|
allPorts=${allPorts}"$i "
|
||||||
|
done
|
||||||
|
echo -e ${SKY_BLUE_PREFIX}"SERVICE START SUCCESS "${COLOR_SUFFIX}
|
||||||
|
echo -e ${SKY_BLUE_PREFIX}"SERVICE_NAME: "${COLOR_SUFFIX}${YELLOW_PREFIX}${push_name}${COLOR_SUFFIX}
|
||||||
|
echo -e ${SKY_BLUE_PREFIX}"PID: "${COLOR_SUFFIX}${YELLOW_PREFIX}${newPid}${COLOR_SUFFIX}
|
||||||
|
echo -e ${SKY_BLUE_PREFIX}"LISTENING_PORT: "${COLOR_SUFFIX}${YELLOW_PREFIX}${allPorts}${COLOR_SUFFIX}
|
||||||
|
else
|
||||||
|
echo -e ${YELLOW_PREFIX}${push_name}${COLOR_SUFFIX}${RED_PREFIX}"SERVICE START ERROR, PLEASE CHECK openIM.log"${COLOR_SUFFIX}
|
||||||
|
fi
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
set "api_apps=admin-api chat-api"
|
||||||
|
set "rpc_apps=admin-rpc chat-rpc"
|
||||||
|
|
||||||
|
for %%a in (%api_apps%) do (
|
||||||
|
go build -o %%a.exe ../cmd/api/%%a/main.go
|
||||||
|
)
|
||||||
|
|
||||||
|
for %%a in (%rpc_apps%) do (
|
||||||
|
go build -o %%a.exe ../cmd/rpc/%%a/main.go
|
||||||
|
)
|
||||||
|
|
||||||
|
move *exe ../cmd
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
source $SCRIPTS_ROOT/style_info.sh
|
||||||
|
source $SCRIPTS_ROOT/path_info.sh
|
||||||
|
source $SCRIPTS_ROOT/function.sh
|
||||||
|
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}"
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}"
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
echo -e ""
|
||||||
|
|
||||||
|
echo -e "${BACKGROUND_BLUE}===============> Building all using make build binary files ${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
echo -e ""
|
||||||
|
echo -e "${BOLD_PREFIX}____________________________________________________________ ${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
|
||||||
|
bin_dir="$BIN_DIR"
|
||||||
|
logs_dir="$OPENIM_ROOT/logs"
|
||||||
|
|
||||||
|
echo "==> bin_dir=$bin_dir"
|
||||||
|
echo "==> logs_dir=$logs_dir"
|
||||||
|
|
||||||
|
|
||||||
|
cd $SCRIPTS_ROOT/..
|
||||||
|
|
||||||
|
# CPU core number
|
||||||
|
cpu_count=$(lscpu | grep -e '^CPU(s):' | awk '{print $2}')
|
||||||
|
echo -e "${GREEN_PREFIX}======> cpu_count=$cpu_count${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
# Count the number of concurrent compilations (half the number of cpus)
|
||||||
|
compile_count=$((cpu_count / 2))
|
||||||
|
|
||||||
|
# Execute 'make build' run the make command for concurrent compilation
|
||||||
|
make -j$compile_count build
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "make build Error, script exits"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the current operating system and architecture
|
||||||
|
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
|
||||||
|
# Select the repository home directory based on the operating system and architecture
|
||||||
|
if [[ "$OS" == "darwin" ]]; then
|
||||||
|
if [[ "$ARCH" == "x86_64" ]]; then
|
||||||
|
REPO_DIR="darwin/amd64"
|
||||||
|
else
|
||||||
|
REPO_DIR="darwin/386"
|
||||||
|
fi
|
||||||
|
elif [[ "$OS" == "linux" ]]; then
|
||||||
|
if [[ "$ARCH" == "x86_64" ]]; then
|
||||||
|
REPO_DIR="linux/amd64"
|
||||||
|
elif [[ "$ARCH" == "arm64" ]]; then
|
||||||
|
REPO_DIR="linux/arm64"
|
||||||
|
elif [[ "$ARCH" == "mips64" ]]; then
|
||||||
|
REPO_DIR="linux/mips64"
|
||||||
|
elif [[ "$ARCH" == "mips64le" ]]; then
|
||||||
|
REPO_DIR="linux/mips64le"
|
||||||
|
elif [[ "$ARCH" == "ppc64le" ]]; then
|
||||||
|
REPO_DIR="linux/ppc64le"
|
||||||
|
elif [[ "$ARCH" == "s390x" ]]; then
|
||||||
|
REPO_DIR="linux/s390x"
|
||||||
|
else
|
||||||
|
REPO_DIR="linux/386"
|
||||||
|
fi
|
||||||
|
elif [[ "$OS" == "windows" ]]; then
|
||||||
|
if [[ "$ARCH" == "x86_64" ]]; then
|
||||||
|
REPO_DIR="windows/amd64"
|
||||||
|
else
|
||||||
|
REPO_DIR="windows/386"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo -e "${RED_PREFIX}Unsupported OS: $OS${COLOR_SUFFIX}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Determine if all scripts were successfully built
|
||||||
|
BUILD_SUCCESS=true
|
||||||
|
FAILED_SCRIPTS=()
|
||||||
|
|
||||||
|
for binary in $(find _output/bin/platforms/$REPO_DIR -type f); do
|
||||||
|
if [[ ! -x $binary ]]; then
|
||||||
|
FAILED_SCRIPTS+=("$binary")
|
||||||
|
BUILD_SUCCESS=false
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo -e " "
|
||||||
|
|
||||||
|
echo -e "${BOLD_PREFIX}=====================> Build Results <=====================${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
echo -e " "
|
||||||
|
|
||||||
|
if [[ "$BUILD_SUCCESS" == true ]]; then
|
||||||
|
echo -e "${GREEN_PREFIX}All binaries built successfully.${COLOR_SUFFIX}"
|
||||||
|
else
|
||||||
|
echo -e "${RED_PREFIX}Some binary builds failed. Please check the following binary files:${COLOR_SUFFIX}"
|
||||||
|
for script in "${FAILED_SCRIPTS[@]}"; do
|
||||||
|
echo -e "${RED_PREFIX}$script${COLOR_SUFFIX}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e " "
|
||||||
|
|
||||||
|
echo -e "${BOLD_PREFIX}============================================================${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
echo -e " "
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/..
|
||||||
|
|
||||||
|
|
||||||
|
IMAGE_VERSION=v1.1.0
|
||||||
|
image=openim/openim_chat:$IMAGE_VERSION
|
||||||
|
|
||||||
|
|
||||||
|
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||||
|
chmod +x $OPENIM_ROOT/scripts/*.sh
|
||||||
|
|
||||||
|
$OPENIM_ROOT/scripts/build_all_service.sh
|
||||||
|
|
||||||
|
docker build -t $image . -f $OPENIM_ROOT/deploy.Dockerfile
|
||||||
|
|
||||||
|
docker push $image
|
||||||
|
|
||||||
|
echo -e ${YELLOW_PREFIX}"docker build success"${COLOR_SUFFIX}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/..
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
source $SCRIPTS_ROOT/style_info.sh
|
||||||
|
source $SCRIPTS_ROOT/path_info.sh
|
||||||
|
source $SCRIPTS_ROOT/function.sh
|
||||||
|
|
||||||
|
service_port_name=(
|
||||||
|
openImChatApiPort
|
||||||
|
openImAdminApiPort
|
||||||
|
#api port name
|
||||||
|
openImAdminPort
|
||||||
|
openImChatPort
|
||||||
|
)
|
||||||
|
|
||||||
|
switch=$(cat $config_path | grep demoswitch |awk -F '[:]' '{print $NF}')
|
||||||
|
for i in ${service_port_name[*]}; do
|
||||||
|
list=$(cat $config_path | grep -w ${i} | awk -F '[:]' '{print $NF}')
|
||||||
|
list_to_string $list
|
||||||
|
for j in ${ports_array}; do
|
||||||
|
port=$(ss -tunlp| grep -E 'api|rpc|open_im' | awk '{print $5}' | grep -w ${j} | awk -F '[:]' '{print $NF}')
|
||||||
|
if [[ ${port} -ne ${j} ]]; then
|
||||||
|
echo -e ${YELLOW_PREFIX}${i}${COLOR_SUFFIX}${RED_PREFIX}" service does not start normally,not initiated port is "${COLOR_SUFFIX}${YELLOW_PREFIX}${j}${COLOR_SUFFIX}
|
||||||
|
echo -e ${RED_PREFIX}"please check ../logs/openIM.log "${COLOR_SUFFIX}
|
||||||
|
exit -1
|
||||||
|
else
|
||||||
|
echo -e ${j}${GREEN_PREFIX}" port has been listening,belongs service is "${i}${COLOR_SUFFIX}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||||
|
|
||||||
|
source "$SCRIPTS_ROOT/style_info.sh"
|
||||||
|
source "$SCRIPTS_ROOT/path_info.sh"
|
||||||
|
source "$SCRIPTS_ROOT/function.sh"
|
||||||
|
|
||||||
|
printf "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=%s${COLOR_SUFFIX}\n" "$SCRIPTS_ROOT"
|
||||||
|
printf "${YELLOW_PREFIX}=======>OPENIM_ROOT=%s${COLOR_SUFFIX}\n" "$OPENIM_ROOT"
|
||||||
|
printf "${YELLOW_PREFIX}=======>pwd=%s${COLOR_SUFFIX}\n" "$PWD"
|
||||||
|
|
||||||
|
bin_dir="$BIN_DIR"
|
||||||
|
logs_dir="$SCRIPTS_ROOT/../logs"
|
||||||
|
|
||||||
|
printf "${YELLOW_PREFIX}=======>bin_dir=%s${COLOR_SUFFIX}\n" "$bin_dir"
|
||||||
|
printf "${YELLOW_PREFIX}=======>logs_dir=%s${COLOR_SUFFIX}\n" "$logs_dir"
|
||||||
|
printf "${YELLOW_PREFIX}=======>sdk_db_dir=%s${COLOR_SUFFIX}\n" "$sdk_db_dir"
|
||||||
|
|
||||||
|
# Service filenames
|
||||||
|
service_filenames=(
|
||||||
|
chat-api
|
||||||
|
admin-api
|
||||||
|
#rpc
|
||||||
|
admin-rpc
|
||||||
|
chat-rpc
|
||||||
|
)
|
||||||
|
|
||||||
|
# Service config port names
|
||||||
|
service_port_names=(
|
||||||
|
openImChatApiPort
|
||||||
|
openImAdminApiPort
|
||||||
|
#api port name
|
||||||
|
openImAdminPort
|
||||||
|
openImChatPort
|
||||||
|
)
|
||||||
|
|
||||||
|
service_prometheus_port_names=()
|
||||||
|
|
||||||
|
cd "$SCRIPTS_ROOT"
|
||||||
|
|
||||||
|
# Function to kill a service
|
||||||
|
kill_service() {
|
||||||
|
local service_name=$1
|
||||||
|
local pid=$(pgrep -f "$service_name")
|
||||||
|
if [ -n "$pid" ]; then
|
||||||
|
echo "$service_name service has been started, pid: $pid"
|
||||||
|
echo "Killing the service $service_name, pid: $pid"
|
||||||
|
killall "$service_name"
|
||||||
|
sleep 0.5
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#service_filenames[*]}; i++)); do
|
||||||
|
service_name="${service_filenames[$i]}"
|
||||||
|
kill_service "$service_name"
|
||||||
|
cd "$SCRIPTS_ROOT"
|
||||||
|
|
||||||
|
# Get the rpc ports from the configuration file
|
||||||
|
readarray -t portList < "$config_path"
|
||||||
|
service_ports=()
|
||||||
|
for line in "${portList[@]}"; do
|
||||||
|
if [[ $line == *"${service_port_names[$i]}"* ]]; then
|
||||||
|
port=$(echo "$line" | awk -F ':' '{print $NF}')
|
||||||
|
service_ports+=("$port")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Start related rpc services based on the number of ports
|
||||||
|
for port in "${service_ports[@]}"; do
|
||||||
|
# Start the service in the background
|
||||||
|
cmd="$bin_dir/$service_name -port $port --config_folder_path $config_path"
|
||||||
|
if [[ $i -eq 0 || $i -eq 1 ]]; then
|
||||||
|
cmd="$bin_dir/$service_name -port $port --config_folder_path $config_path"
|
||||||
|
fi
|
||||||
|
echo "$cmd"
|
||||||
|
nohup "$cmd" >> "${logs_dir}/openIM.log" 2>&1 &
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep infinity
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
#input:[10023,2323,3434]
|
||||||
|
#output:10023 2323 3434
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
remove_space(){
|
||||||
|
value=$*
|
||||||
|
result=`echo $value | sed 's/ //g'`
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIMSDK.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
# Store this file as .git/hooks/commit-msg in your repository in order to
|
||||||
|
# enforce checking for proper commit message format before actual commits.
|
||||||
|
# You may need to make the scripts executable by 'chmod +x .git/hooks/commit-msg'.
|
||||||
|
|
||||||
|
# commit-msg use go-gitlint tool, install go-gitlint via `go get github.com/llorllale/go-gitlint/cmd/go-gitlint`
|
||||||
|
# go-gitlint --msg-file="$1"
|
||||||
|
|
||||||
|
# An example hook scripts to check the commit log message.
|
||||||
|
# Called by "git commit" with one argument, the name of the file
|
||||||
|
# that has the commit message. The hook should exit with non-zero
|
||||||
|
# status after issuing an appropriate message if it wants to stop the
|
||||||
|
# commit. The hook is allowed to edit the commit message file.
|
||||||
|
|
||||||
|
YELLOW="\e[93m"
|
||||||
|
GREEN="\e[32m"
|
||||||
|
RED="\e[31m"
|
||||||
|
ENDCOLOR="\e[0m"
|
||||||
|
|
||||||
|
printMessage() {
|
||||||
|
printf "${YELLOW}OpenIM : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printSuccess() {
|
||||||
|
printf "${GREEN}OpenIM : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printError() {
|
||||||
|
printf "${RED}OpenIM : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printMessage "Running the OpenIM commit-msg hook."
|
||||||
|
|
||||||
|
# This example catches duplicate Signed-off-by lines.
|
||||||
|
|
||||||
|
test "" = "$(grep '^Signed-off-by: ' "$1" |
|
||||||
|
sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
|
||||||
|
echo >&2 Duplicate Signed-off-by lines.
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: go-gitlint dir set
|
||||||
|
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../..
|
||||||
|
GITLINT_DIR="$OPENIM_ROOT/_output/tools/go-gitlint"
|
||||||
|
|
||||||
|
$GITLINT_DIR \
|
||||||
|
--msg-file=$1 \
|
||||||
|
--subject-regex="^(build|chore|ci|docs|feat|feature|fix|perf|refactor|revert|style|bot|test)(.*)?:\s?.*" \
|
||||||
|
--subject-maxlen=150 \
|
||||||
|
--subject-minlen=10 \
|
||||||
|
--body-regex=".*" \
|
||||||
|
--max-parents=1
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
if ! command -v $GITLINT_DIR &>/dev/null; then
|
||||||
|
printError "$GITLINT_DIR not found. Please run 'make tools' OR 'make tools.verify.go-gitlint' make verto install it."
|
||||||
|
fi
|
||||||
|
printError "Please fix your commit message to match kubecub coding standards"
|
||||||
|
printError "https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
### Add Sign-off-by line to the end of the commit message
|
||||||
|
# Get local git config
|
||||||
|
NAME=$(git config user.name)
|
||||||
|
EMAIL=$(git config user.email)
|
||||||
|
|
||||||
|
# Check if the commit message contains a sign-off line
|
||||||
|
grep -qs "^Signed-off-by: " "$1"
|
||||||
|
SIGNED_OFF_BY_EXISTS=$?
|
||||||
|
|
||||||
|
# Add "Signed-off-by" line if it doesn't exist
|
||||||
|
if [ $SIGNED_OFF_BY_EXISTS -ne 0 ]; then
|
||||||
|
echo -e "\nSigned-off-by: $NAME <$EMAIL>" >> "$1"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIMSDK.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
# This is a pre-commit hook that ensures attempts to commit files that are
|
||||||
|
# are larger than $limit to your _local_ repo fail, with a helpful error message.
|
||||||
|
|
||||||
|
# You can override the default limit of 2MB by supplying the environment variable:
|
||||||
|
# GIT_FILE_SIZE_LIMIT=50000000 git commit -m "test: this commit is allowed file sizes up to 50MB"
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
|
||||||
|
LC_ALL=C
|
||||||
|
|
||||||
|
local_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
valid_branch_regex="^(main|master|develop|release(-[a-zA-Z0-9._-]+)?)$|(feature|feat|openim|hotfix|test|bug|bot|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$"
|
||||||
|
|
||||||
|
YELLOW="\e[93m"
|
||||||
|
GREEN="\e[32m"
|
||||||
|
RED="\e[31m"
|
||||||
|
ENDCOLOR="\e[0m"
|
||||||
|
|
||||||
|
printMessage() {
|
||||||
|
printf "${YELLOW}openim : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printSuccess() {
|
||||||
|
printf "${GREEN}openim : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printError() {
|
||||||
|
printf "${RED}openim : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printMessage "Running local openim pre-commit hook."
|
||||||
|
|
||||||
|
# flutter format .
|
||||||
|
# https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694#file-githook-md
|
||||||
|
# TODO! GIT_FILE_SIZE_LIMIT=50000000 git commit -m "test: this commit is allowed file sizes up to 50MB"
|
||||||
|
# Maximum file size limit in bytes
|
||||||
|
limit=${GIT_FILE_SIZE_LIMIT:-2000000} # Default 2MB
|
||||||
|
limitInMB=$(( $limit / 1000000 ))
|
||||||
|
|
||||||
|
function file_too_large(){
|
||||||
|
filename=$0
|
||||||
|
filesize=$(( $1 / 2**20 ))
|
||||||
|
|
||||||
|
cat <<HEREDOC
|
||||||
|
|
||||||
|
File $filename is $filesize MB, which is larger than github's maximum
|
||||||
|
file size (2 MB). We will not be able to push this file to GitHub.
|
||||||
|
Commit aborted
|
||||||
|
|
||||||
|
HEREDOC
|
||||||
|
git status
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move to the repo root so git files paths make sense
|
||||||
|
repo_root=$( git rev-parse --show-toplevel )
|
||||||
|
cd $repo_root
|
||||||
|
|
||||||
|
empty_tree=$( git hash-object -t tree /dev/null )
|
||||||
|
|
||||||
|
if git rev-parse --verify HEAD > /dev/null 2>&1
|
||||||
|
then
|
||||||
|
against=HEAD
|
||||||
|
else
|
||||||
|
against="$empty_tree"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set split so that for loop below can handle spaces in file names by splitting on line breaks
|
||||||
|
IFS='
|
||||||
|
'
|
||||||
|
|
||||||
|
shouldFail=false
|
||||||
|
for file in $( git diff-index --cached --name-only $against ); do
|
||||||
|
file_size=$(([ ! -f $file ] && echo 0) || (ls -la $file | awk '{ print $5 }'))
|
||||||
|
if [ "$file_size" -gt "$limit" ]; then
|
||||||
|
printError "File $file is $(( $file_size / 10**6 )) MB, which is larger than our configured limit of $limitInMB MB"
|
||||||
|
shouldFail=true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if $shouldFail
|
||||||
|
then
|
||||||
|
printMessage "If you really need to commit this file, you can override the size limit by setting the GIT_FILE_SIZE_LIMIT environment variable, e.g. GIT_FILE_SIZE_LIMIT=42000000 for 42MB. Or, commit with the --no-verify switch to skip the check entirely."
|
||||||
|
printError "Commit aborted"
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! $local_branch =~ $valid_branch_regex ]]
|
||||||
|
then
|
||||||
|
printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex.
|
||||||
|
Your commit will be rejected. You should rename your branch to a valid name(feat/name OR bug/name) and try again."
|
||||||
|
printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIMSDK.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# ==============================================================================
|
||||||
|
#
|
||||||
|
|
||||||
|
YELLOW="\e[93m"
|
||||||
|
GREEN="\e[32m"
|
||||||
|
RED="\e[31m"
|
||||||
|
ENDCOLOR="\e[0m"
|
||||||
|
|
||||||
|
local_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
valid_branch_regex="^(main|master|develop|release(-[a-zA-Z0-9._-]+)?)$|(feature|feat|openim|hotfix|test|bug|ci|cicd|style|)\/[a-z0-9._-]+$|^HEAD$"
|
||||||
|
|
||||||
|
printMessage() {
|
||||||
|
printf "${YELLOW}OpenIM : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printSuccess() {
|
||||||
|
printf "${GREEN}OpenIM : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printError() {
|
||||||
|
printf "${RED}OpenIM : $1${ENDCOLOR}\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
printMessage "Running local OpenIM pre-push hook."
|
||||||
|
|
||||||
|
if [[ `git status --porcelain` ]]; then
|
||||||
|
printError "This scripts needs to run against committed code only. Please commit or stash you changes."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
COLOR_SUFFIX="\033[0m"
|
||||||
|
|
||||||
|
BLACK_PREFIX="\033[30m"
|
||||||
|
RED_PREFIX="\033[31m"
|
||||||
|
GREEN_PREFIX="\033[32m"
|
||||||
|
BACKGROUND_GREEN="\033[33m"
|
||||||
|
BLUE_PREFIX="\033[34m"
|
||||||
|
PURPLE_PREFIX="\033[35m"
|
||||||
|
SKY_BLUE_PREFIX="\033[36m"
|
||||||
|
WHITE_PREFIX="\033[37m"
|
||||||
|
BOLD_PREFIX="\033[1m"
|
||||||
|
UNDERLINE_PREFIX="\033[4m"
|
||||||
|
ITALIC_PREFIX="\033[3m"
|
||||||
|
|
||||||
|
# Function to print colored text
|
||||||
|
print_color() {
|
||||||
|
local text=$1
|
||||||
|
local color=$2
|
||||||
|
echo -e "${color}${text}${COLOR_SUFFIX}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to print section separator
|
||||||
|
print_separator() {
|
||||||
|
print_color "==========================================================" ${PURPLE_PREFIX}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get current time
|
||||||
|
time=$(date +"%Y-%m-%d %H:%M:%S")
|
||||||
|
|
||||||
|
# Print section separator
|
||||||
|
print_separator
|
||||||
|
|
||||||
|
# Print time of submission
|
||||||
|
print_color "PTIME: ${time}" "${BOLD_PREFIX}${CYAN_PREFIX}"
|
||||||
|
echo ""
|
||||||
|
author=$(git config user.name)
|
||||||
|
repository=$(basename -s .git $(git config --get remote.origin.url))
|
||||||
|
|
||||||
|
# Print additional information if needed
|
||||||
|
print_color "Repository: ${repository}" "${BLUE_PREFIX}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
print_color "Author: ${author}" "${PURPLE_PREFIX}"
|
||||||
|
|
||||||
|
# Print section separator
|
||||||
|
print_separator
|
||||||
|
|
||||||
|
file_list=$(git diff --name-status HEAD @{u})
|
||||||
|
added_files=$(grep -c '^A' <<< "$file_list")
|
||||||
|
modified_files=$(grep -c '^M' <<< "$file_list")
|
||||||
|
deleted_files=$(grep -c '^D' <<< "$file_list")
|
||||||
|
|
||||||
|
print_color "Added Files: ${added_files}" "${BACKGROUND_GREEN}"
|
||||||
|
print_color "Modified Files: ${modified_files}" "${BACKGROUND_GREEN}"
|
||||||
|
print_color "Deleted Files: ${deleted_files}" "${BACKGROUND_GREEN}"
|
||||||
|
|
||||||
|
if [[ ! $local_branch =~ $valid_branch_regex ]]
|
||||||
|
then
|
||||||
|
printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex.
|
||||||
|
Your commit will be rejected. You should rename your branch to a valid name(feat/name OR bug/name) and try again."
|
||||||
|
printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
#
|
||||||
|
#printMessage "Running the Flutter analyzer"
|
||||||
|
#flutter analyze
|
||||||
|
#
|
||||||
|
#if [ $? -ne 0 ]; then
|
||||||
|
# printError "Flutter analyzer error"
|
||||||
|
# exit 1
|
||||||
|
#fi
|
||||||
|
#
|
||||||
|
#printMessage "Finished running the Flutter analyzer"
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#Don't put the space between "="
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
|
||||||
|
|
||||||
|
# Determine the architecture and version
|
||||||
|
architecture=$(uname -m)
|
||||||
|
version=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
source $SCRIPTS_ROOT/style_info.sh
|
||||||
|
|
||||||
|
cd $SCRIPTS_ROOT
|
||||||
|
|
||||||
|
# Define the supported architectures and corresponding bin directories
|
||||||
|
declare -A supported_architectures=(
|
||||||
|
["linux-amd64"]="_output/bin/platforms/linux/amd64"
|
||||||
|
["linux-arm64"]="_output/bin/platforms/linux/arm64"
|
||||||
|
["linux-mips64"]="_output/bin/platforms/linux/mips64"
|
||||||
|
["linux-mips64le"]="_output/bin/platforms/linux/mips64le"
|
||||||
|
["linux-ppc64le"]="_output/bin/platforms/linux/ppc64le"
|
||||||
|
["linux-s390x"]="_output/bin/platforms/linux/s390x"
|
||||||
|
["darwin-amd64"]="_output/bin/platforms/darwin/amd64"
|
||||||
|
["windows-amd64"]="_output/bin/platforms/windows/amd64"
|
||||||
|
["linux-x86_64"]="_output/bin/platforms/linux/amd64" # Alias for linux-amd64
|
||||||
|
["darwin-x86_64"]="_output/bin/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}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${BLUE_PREFIX}================> Architecture: $architecture${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
# Set the BIN_DIR based on the architecture and version
|
||||||
|
BIN_DIR=${SCRIPTS_ROOT}/../${supported_architectures["$version-$architecture"]}
|
||||||
|
|
||||||
|
echo -e "${BLUE_PREFIX}================> BIN_DIR: $OPENIM_ROOT/$BIN_DIR${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
#Global configuration file default dir
|
||||||
|
config_path="$OPENIM_ROOT/config/config.yaml"
|
||||||
|
configfile_path="$OPENIM_ROOT/config"
|
||||||
|
log_path="$OPENIM_ROOT/log"
|
||||||
|
|
||||||
|
#servicefile dir path
|
||||||
|
service_source_root=(
|
||||||
|
#api service file
|
||||||
|
$OPENIM_ROOT/cmd/api/chat-api/
|
||||||
|
$OPENIM_ROOT/cmd/api/admin-api/
|
||||||
|
#rpc service file
|
||||||
|
$OPENIM_ROOT/cmd/rpc/admin-rpc/
|
||||||
|
$OPENIM_ROOT/cmd/rpc/chat-rpc/
|
||||||
|
)
|
||||||
|
#service filename
|
||||||
|
service_names=(
|
||||||
|
chat-api
|
||||||
|
admin-api
|
||||||
|
chat-rpc
|
||||||
|
admin-rpc
|
||||||
|
)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
cd /d %~dp0../cmd
|
||||||
|
start admin-rpc.exe
|
||||||
|
start chat-rpc.exe
|
||||||
|
start chat-api.exe
|
||||||
|
start admin-api.exe
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
#Include shell font styles and some basic information
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(dirname "${SCRIPTS_ROOT}")/..
|
||||||
|
|
||||||
|
source $SCRIPTS_ROOT/style_info.sh
|
||||||
|
source $SCRIPTS_ROOT/path_info.sh
|
||||||
|
source $SCRIPTS_ROOT/function.sh
|
||||||
|
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>SCRIPTS_ROOT=$SCRIPTS_ROOT${COLOR_SUFFIX}"
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>OPENIM_ROOT=$OPENIM_ROOT${COLOR_SUFFIX}"
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>pwd=$PWD${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
# if [ ! -d "${OPENIM_ROOT}/_output/bin/platforms" ]; then
|
||||||
|
# cd $OPENIM_ROOT
|
||||||
|
# # exec build_all_service.sh
|
||||||
|
# "${SCRIPTS_ROOT}/build_all_service.sh"
|
||||||
|
# fi
|
||||||
|
|
||||||
|
bin_dir="$BIN_DIR"
|
||||||
|
logs_dir="$SCRIPTS_ROOT/../logs"
|
||||||
|
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>bin_dir=$bin_dir${COLOR_SUFFIX}"
|
||||||
|
echo -e "${YELLOW_PREFIX}=======>logs_dir=$logs_dir${COLOR_SUFFIX}"
|
||||||
|
|
||||||
|
#service filename
|
||||||
|
service_filename=(
|
||||||
|
chat-api
|
||||||
|
admin-api
|
||||||
|
#rpc
|
||||||
|
admin-rpc
|
||||||
|
chat-rpc
|
||||||
|
)
|
||||||
|
|
||||||
|
#service config port name
|
||||||
|
service_port_name=(
|
||||||
|
openImChatApiPort
|
||||||
|
openImAdminApiPort
|
||||||
|
#api port name
|
||||||
|
openImAdminPort
|
||||||
|
openImChatPort
|
||||||
|
)
|
||||||
|
|
||||||
|
service_prometheus_port_name=(
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# Automatically created when there is no bin, logs folder
|
||||||
|
if [ ! -d $logs_dir ]; then
|
||||||
|
mkdir -p $logs_dir
|
||||||
|
fi
|
||||||
|
cd $SCRIPTS_ROOT
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#service_filename[*]}; i++)); do
|
||||||
|
#Check whether the service exists
|
||||||
|
# service_name="ps |grep -w ${service_filename[$i]} |grep -v grep"
|
||||||
|
# count="${service_name}| wc -l"
|
||||||
|
#
|
||||||
|
# if [ $(eval ${count}) -gt 0 ]; then
|
||||||
|
# pid="${service_name}| awk '{print \$2}'"
|
||||||
|
# echo "${service_filename[$i]} service has been started,pid:$(eval $pid)"
|
||||||
|
# echo "killing the service ${service_filename[$i]} pid:$(eval $pid)"
|
||||||
|
# #kill the service that existed
|
||||||
|
# kill -9 $(eval $pid)
|
||||||
|
# sleep 0.5
|
||||||
|
# fi
|
||||||
|
cd $SCRIPTS_ROOT
|
||||||
|
|
||||||
|
#Get the rpc port in the configuration file
|
||||||
|
portList=$(cat $config_path | grep ${service_port_name[$i]} | awk -F '[:]' '{print $NF}')
|
||||||
|
list_to_string ${portList}
|
||||||
|
service_ports=($ports_array)
|
||||||
|
|
||||||
|
#Start related rpc services based on the number of ports
|
||||||
|
for ((j = 0; j < ${#service_ports[*]}; j++)); do
|
||||||
|
#Start the service in the background
|
||||||
|
cmd="$bin_dir/${service_filename[$i]} -port ${service_ports[$j]} --config_folder_path ${config_path}"
|
||||||
|
if [ $i -eq 0 -o $i -eq 1 ]; then
|
||||||
|
cmd="$bin_dir/${service_filename[$i]} -port ${service_ports[$j]} --config_folder_path ${config_path}"
|
||||||
|
fi
|
||||||
|
echo $cmd
|
||||||
|
nohup $cmd >>${logs_dir}/openIM.log 2>&1 &
|
||||||
|
sleep 1
|
||||||
|
# pid="netstat -ntlp|grep $j |awk '{printf \$7}'|cut -d/ -f1"
|
||||||
|
# echo -e "${GREEN_PREFIX}${service_filename[$i]} start success,port number:${service_ports[$j]} pid:$(eval $pid)$COLOR_SUFFIX"
|
||||||
|
done
|
||||||
|
done
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copyright © 2023 OpenIM open source community. 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.
|
||||||
|
|
||||||
|
#fixme This scripts is to stop the service
|
||||||
|
SCRIPTS_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||||
|
OPENIM_ROOT=$(cd $(dirname "${BASH_SOURCE[0]}")/.. &&pwd)
|
||||||
|
|
||||||
|
source $OPENIM_ROOT/scripts/style_info.sh
|
||||||
|
source $OPENIM_ROOT/scripts/path_info.sh
|
||||||
|
source $SCRIPTS_ROOT/function.sh
|
||||||
|
|
||||||
|
service_port_name=(
|
||||||
|
openImChatApiPort
|
||||||
|
openImAdminApiPort
|
||||||
|
#api port name
|
||||||
|
openImAdminPort
|
||||||
|
openImChatPort
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in ${service_port_name[*]}; do
|
||||||
|
list=$(cat $OPENIM_ROOT/config/config.yaml | grep -w ${i} | awk -F '[:]' '{print $NF}')
|
||||||
|
list_to_string $list
|
||||||
|
for j in ${ports_array}; do
|
||||||
|
name="ps -aux |grep -w $j |grep -v grep"
|
||||||
|
count="${name}| wc -l"
|
||||||
|
if [ $(eval ${count}) -gt 0 ]; then
|
||||||
|
pid="${name}| awk '{print \$2}'"
|
||||||
|
echo -e "${SKY_BLUE_PREFIX}Killing service:$i pid:$(eval $pid)${COLOR_SUFFIX}"
|
||||||
|
#kill the service that existed
|
||||||
|
kill -9 $(eval $pid)
|
||||||
|
echo -e "${SKY_BLUE_PREFIX}service:$i was killed ${COLOR_SUFFIX}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
function style-info() {
|
||||||
|
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
|
||||||
|
PURPLE_PREFIX="\033[35m" # Purple 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
|
||||||
|
|
||||||
|
CYAN_PREFIX="033[0;36m" # Cyan prefix
|
||||||
|
|
||||||
|
BACKGROUND_BLACK="\033[40m" # Black background
|
||||||
|
BACKGROUND_RED="\033[41m" # Red background
|
||||||
|
BACKGROUND_GREEN="\033[42m" # Green background
|
||||||
|
BACKGROUND_YELLOW="\033[43m" # Yellow background
|
||||||
|
BACKGROUND_BLUE="\033[44m" # Blue background
|
||||||
|
BACKGROUND_PURPLE="\033[45m" # Purple background
|
||||||
|
BACKGROUND_SKY_BLUE="\033[46m" # Sky blue background
|
||||||
|
BACKGROUND_WHITE="\033[47m" # White background
|
||||||
|
|
||||||
|
BLINK="\033[5m" # Blinking effect
|
||||||
|
INVERT="\033[7m" # Invert color
|
||||||
|
HIDE="\033[8m" # Hide text
|
||||||
|
|
||||||
|
GRAY_PREFIX="\033[90m" # Gray prefix
|
||||||
|
LIGHT_RED_PREFIX="\033[91m" # Light red prefix
|
||||||
|
LIGHT_GREEN_PREFIX="\033[92m" # Light green prefix
|
||||||
|
LIGHT_YELLOW_PREFIX="\033[93m" # Light yellow prefix
|
||||||
|
LIGHT_BLUE_PREFIX="\033[94m" # Light blue prefix
|
||||||
|
LIGHT_PURPLE_PREFIX="\033[95m" # Light purple prefix
|
||||||
|
LIGHT_SKY_BLUE_PREFIX="\033[96m" # Light sky blue prefix
|
||||||
|
LIGHT_WHITE_PREFIX="\033[97m" # Light white prefix
|
||||||
|
|
||||||
|
BACKGROUND_GRAY="\033[100m" # Gray background
|
||||||
|
BACKGROUND_LIGHT_RED="\033[101m" # Light red background
|
||||||
|
BACKGROUND_LIGHT_GREEN="\033[102m" # Light green background
|
||||||
|
BACKGROUND_LIGHT_YELLOW="\033[103m" # Light yellow background
|
||||||
|
BACKGROUND_LIGHT_BLUE="\033[104m" # Light blue background
|
||||||
|
BACKGROUND_LIGHT_PURPLE="\033[105m" # Light purple background
|
||||||
|
BACKGROUND_LIGHT_SKY_BLUE="\033[106m" # Light sky blue background
|
||||||
|
BACKGROUND_LIGHT_WHITE="\033[107m" # Light white background
|
||||||
|
}
|
||||||
|
|
||||||
|
style-info
|
||||||
@@ -120,6 +120,7 @@ type configStruct struct {
|
|||||||
AccessKeyID string `yaml:"accessKeyID"`
|
AccessKeyID string `yaml:"accessKeyID"`
|
||||||
SecretAccessKey string `yaml:"secretAccessKey"`
|
SecretAccessKey string `yaml:"secretAccessKey"`
|
||||||
SessionToken string `yaml:"sessionToken"`
|
SessionToken string `yaml:"sessionToken"`
|
||||||
|
SignEndpoint string `yaml:"signEndpoint"`
|
||||||
} `yaml:"minio"`
|
} `yaml:"minio"`
|
||||||
Cos struct {
|
Cos struct {
|
||||||
BucketURL string `yaml:"bucketURL"`
|
BucketURL string `yaml:"bucketURL"`
|
||||||
@@ -284,6 +285,7 @@ type notification struct {
|
|||||||
GroupInfoSetName NotificationConf `yaml:"groupInfoSetName"`
|
GroupInfoSetName NotificationConf `yaml:"groupInfoSetName"`
|
||||||
////////////////////////user///////////////////////
|
////////////////////////user///////////////////////
|
||||||
UserInfoUpdated NotificationConf `yaml:"userInfoUpdated"`
|
UserInfoUpdated NotificationConf `yaml:"userInfoUpdated"`
|
||||||
|
UserStatusChanged NotificationConf `yaml:"userStatusChanged"`
|
||||||
//////////////////////friend///////////////////////
|
//////////////////////friend///////////////////////
|
||||||
FriendApplicationAdded NotificationConf `yaml:"friendApplicationAdded"`
|
FriendApplicationAdded NotificationConf `yaml:"friendApplicationAdded"`
|
||||||
FriendApplicationApproved NotificationConf `yaml:"friendApplicationApproved"`
|
FriendApplicationApproved NotificationConf `yaml:"friendApplicationApproved"`
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ func MsgDB2Pb(msgModel *unrelation.MsgDataModel) *sdkws.MsgData {
|
|||||||
msg.SendTime = msgModel.SendTime
|
msg.SendTime = msgModel.SendTime
|
||||||
msg.CreateTime = msgModel.CreateTime
|
msg.CreateTime = msgModel.CreateTime
|
||||||
msg.Status = msgModel.Status
|
msg.Status = msgModel.Status
|
||||||
|
msg.IsRead = msgModel.IsRead
|
||||||
msg.Options = msgModel.Options
|
msg.Options = msgModel.Options
|
||||||
if msgModel.OfflinePush != nil {
|
if msgModel.OfflinePush != nil {
|
||||||
msg.OfflinePushInfo = &sdkws.OfflinePushInfo{
|
msg.OfflinePushInfo = &sdkws.OfflinePushInfo{
|
||||||
|
|||||||
Vendored
+64
-20
@@ -16,10 +16,10 @@ package cache
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/big"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/tools/log"
|
||||||
|
|
||||||
"github.com/dtm-labs/rockscache"
|
"github.com/dtm-labs/rockscache"
|
||||||
"github.com/redis/go-redis/v9"
|
"github.com/redis/go-redis/v9"
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ const (
|
|||||||
groupExpireTime = time.Second * 60 * 60 * 12
|
groupExpireTime = time.Second * 60 * 60 * 12
|
||||||
groupInfoKey = "GROUP_INFO:"
|
groupInfoKey = "GROUP_INFO:"
|
||||||
groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
groupMemberIDsKey = "GROUP_MEMBER_IDS:"
|
||||||
groupMembersHashKey = "GROUP_MEMBERS_HASH:"
|
groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
|
||||||
groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
groupMemberInfoKey = "GROUP_MEMBER_INFO:"
|
||||||
joinedSuperGroupsKey = "JOIN_SUPER_GROUPS:"
|
joinedSuperGroupsKey = "JOIN_SUPER_GROUPS:"
|
||||||
SuperGroupMemberIDsKey = "SUPER_GROUP_MEMBER_IDS:"
|
SuperGroupMemberIDsKey = "SUPER_GROUP_MEMBER_IDS:"
|
||||||
@@ -96,6 +96,7 @@ type GroupCacheRedis struct {
|
|||||||
mongoDB unrelationTb.SuperGroupModelInterface
|
mongoDB unrelationTb.SuperGroupModelInterface
|
||||||
expireTime time.Duration
|
expireTime time.Duration
|
||||||
rcClient *rockscache.Client
|
rcClient *rockscache.Client
|
||||||
|
hashCode func(ctx context.Context, groupID string) (uint64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGroupCacheRedis(
|
func NewGroupCacheRedis(
|
||||||
@@ -104,13 +105,16 @@ func NewGroupCacheRedis(
|
|||||||
groupMemberDB relationTb.GroupMemberModelInterface,
|
groupMemberDB relationTb.GroupMemberModelInterface,
|
||||||
groupRequestDB relationTb.GroupRequestModelInterface,
|
groupRequestDB relationTb.GroupRequestModelInterface,
|
||||||
mongoClient unrelationTb.SuperGroupModelInterface,
|
mongoClient unrelationTb.SuperGroupModelInterface,
|
||||||
|
hashCode func(ctx context.Context, groupID string) (uint64, error),
|
||||||
opts rockscache.Options,
|
opts rockscache.Options,
|
||||||
) GroupCache {
|
) GroupCache {
|
||||||
rcClient := rockscache.NewClient(rdb, opts)
|
rcClient := rockscache.NewClient(rdb, opts)
|
||||||
return &GroupCacheRedis{
|
return &GroupCacheRedis{
|
||||||
rcClient: rcClient, expireTime: groupExpireTime,
|
rcClient: rcClient, expireTime: groupExpireTime,
|
||||||
groupDB: groupDB, groupMemberDB: groupMemberDB, groupRequestDB: groupRequestDB,
|
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.
|
// groupMembersHash.
|
||||||
func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID string) (hashCode uint64, err error) {
|
func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID string) (hashCode uint64, err error) {
|
||||||
return getCache(
|
return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime, func(ctx context.Context) (uint64, error) {
|
||||||
ctx,
|
return g.hashCode(ctx, groupID)
|
||||||
g.rcClient,
|
})
|
||||||
g.getGroupMembersHashKey(groupID),
|
|
||||||
g.expireTime,
|
//return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime,
|
||||||
func(ctx context.Context) (uint64, error) {
|
// func(ctx context.Context) (uint64, error) {
|
||||||
userIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
// userIDs, err := g.GetGroupMemberIDs(ctx, groupID)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return 0, err
|
// return 0, err
|
||||||
}
|
// }
|
||||||
utils.Sort(userIDs, true)
|
// log.ZInfo(ctx, "GetGroupMembersHash", "groupID", groupID, "userIDs", userIDs)
|
||||||
bi := big.NewInt(0)
|
// var members []*relationTb.GroupMemberModel
|
||||||
bi.SetString(utils.Md5(strings.Join(userIDs, ";"))[0:8], 16)
|
// if len(userIDs) > 0 {
|
||||||
return bi.Uint64(), nil
|
// 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(
|
func (g *GroupCacheRedis) GetGroupMemberHashMap(
|
||||||
@@ -318,6 +361,7 @@ func (g *GroupCacheRedis) GetGroupMemberHashMap(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
log.ZInfo(ctx, "GetGroupMemberHashMap", "groupID", groupID, "hash", hash)
|
||||||
num, err := g.GetGroupMemberNum(ctx, groupID)
|
num, err := g.GetGroupMemberNum(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
Vendored
+72
-3
@@ -21,6 +21,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/protocol/constant"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/user"
|
"github.com/OpenIMSDK/protocol/user"
|
||||||
"github.com/OpenIMSDK/tools/errs"
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
|
|
||||||
@@ -37,6 +39,7 @@ const (
|
|||||||
olineStatusKey = "ONLINE_STATUS:"
|
olineStatusKey = "ONLINE_STATUS:"
|
||||||
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
|
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
|
||||||
statusMod = 501
|
statusMod = 501
|
||||||
|
platformID = "_PlatformIDSuffix"
|
||||||
)
|
)
|
||||||
|
|
||||||
type UserCache interface {
|
type UserCache interface {
|
||||||
@@ -92,6 +95,10 @@ func (u *UserCacheRedis) getUserGlobalRecvMsgOptKey(userID string) string {
|
|||||||
return userGlobalRecvMsgOptKey + userID
|
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) {
|
func (u *UserCacheRedis) GetUserInfo(ctx context.Context, userID string) (userInfo *relationTb.UserModel, err error) {
|
||||||
return getCache(
|
return getCache(
|
||||||
ctx,
|
ctx,
|
||||||
@@ -178,8 +185,8 @@ func (u *UserCacheRedis) GetUserStatus(ctx context.Context, userIDs []string) ([
|
|||||||
// key or field does not exist
|
// key or field does not exist
|
||||||
res = append(res, &user.OnlineStatus{
|
res = append(res, &user.OnlineStatus{
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
Status: 0,
|
Status: constant.Offline,
|
||||||
PlatformID: -1,
|
PlatformIDs: nil,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
@@ -211,12 +218,74 @@ func (u *UserCacheRedis) SetUserStatus(ctx context.Context, list []*user.OnlineS
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrap(err)
|
return errs.Wrap(err)
|
||||||
}
|
}
|
||||||
|
if isNewKey == 0 {
|
||||||
_, err = u.rdb.HSet(ctx, key, status.UserID, string(jsonData)).Result()
|
_, err = u.rdb.HSet(ctx, key, status.UserID, string(jsonData)).Result()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errs.Wrap(err)
|
return errs.Wrap(err)
|
||||||
}
|
}
|
||||||
if isNewKey > 0 {
|
|
||||||
u.rdb.Expire(ctx, key, userOlineStatusExpireTime)
|
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
|
return nil
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ func (f *friendDatabase) RefuseFriendRequest(
|
|||||||
if fr.HandleResult != 0 {
|
if fr.HandleResult != 0 {
|
||||||
return errs.ErrArgs.Wrap("the friend request has been processed")
|
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.HandleResult = constant.FriendResponseRefuse
|
||||||
friendRequest.HandleTime = time.Now()
|
friendRequest.HandleTime = time.Now()
|
||||||
err = f.friendRequest.Update(ctx, friendRequest)
|
err = f.friendRequest.Update(ctx, friendRequest)
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ func NewGroupDatabase(
|
|||||||
return database
|
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 := rockscache.NewDefaultOptions()
|
||||||
rcOptions.StrongConsistency = true
|
rcOptions.StrongConsistency = true
|
||||||
rcOptions.RandomExpireAdjustment = 0.2
|
rcOptions.RandomExpireAdjustment = 0.2
|
||||||
@@ -170,6 +170,7 @@ func InitGroupDatabase(db *gorm.DB, rdb redis.UniversalClient, database *mongo.D
|
|||||||
relation.NewGroupMemberDB(db),
|
relation.NewGroupMemberDB(db),
|
||||||
relation.NewGroupRequest(db),
|
relation.NewGroupRequest(db),
|
||||||
unrelation.NewSuperGroupMongoDriver(database),
|
unrelation.NewSuperGroupMongoDriver(database),
|
||||||
|
hashCode,
|
||||||
rcOptions,
|
rcOptions,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@@ -315,7 +316,7 @@ func (g *groupDatabase) FindGroupMember(
|
|||||||
userIDs []string,
|
userIDs []string,
|
||||||
roleLevels []int32,
|
roleLevels []int32,
|
||||||
) (totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
) (totalGroupMembers []*relationTb.GroupMemberModel, err error) {
|
||||||
if roleLevels == nil {
|
if len(roleLevels) == 0 {
|
||||||
for _, groupID := range groupIDs {
|
for _, groupID := range groupIDs {
|
||||||
groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, userIDs)
|
groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, userIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -386,8 +387,24 @@ func (g *groupDatabase) HandlerGroupRequest(
|
|||||||
handleResult int32,
|
handleResult int32,
|
||||||
member *relationTb.GroupMemberModel,
|
member *relationTb.GroupMemberModel,
|
||||||
) error {
|
) error {
|
||||||
cache := g.cache.NewCache()
|
//cache := g.cache.NewCache()
|
||||||
if err := g.tx.Transaction(func(tx any) error {
|
//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 {
|
if err := g.groupRequestDB.NewTx(tx).UpdateHandler(ctx, groupID, userID, handledMsg, handleResult); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -395,13 +412,12 @@ func (g *groupDatabase) HandlerGroupRequest(
|
|||||||
if err := g.groupMemberDB.NewTx(tx).Create(ctx, []*relationTb.GroupMemberModel{member}); err != nil {
|
if err := g.groupMemberDB.NewTx(tx).Create(ctx, []*relationTb.GroupMemberModel{member}); err != nil {
|
||||||
return err
|
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 nil
|
|
||||||
}); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return cache.ExecDel(ctx)
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *groupDatabase) DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error {
|
func (g *groupDatabase) DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error {
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ type CommonMsgDatabase interface {
|
|||||||
pageNumber int32,
|
pageNumber int32,
|
||||||
showNumber int32,
|
showNumber int32,
|
||||||
) (msgCount int64, userCount int64, groups []*unRelationTb.GroupCount, dateCount map[string]int64, err error)
|
) (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 {
|
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.delDocIDs = append(delStruct.delDocIDs, msgDocModel.DocID)
|
||||||
delStruct.minSeq = msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.Seq
|
delStruct.minSeq = msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.Seq
|
||||||
} else {
|
} else {
|
||||||
var hasMarkDelFlag bool
|
|
||||||
var delMsgIndexs []int
|
var delMsgIndexs []int
|
||||||
for i, MsgInfoModel := range msgDocModel.Msg {
|
for i, MsgInfoModel := range msgDocModel.Msg {
|
||||||
if MsgInfoModel != nil && MsgInfoModel.Msg != nil {
|
if MsgInfoModel != nil && MsgInfoModel.Msg != nil {
|
||||||
if utils.GetCurrentTimestampByMill() > MsgInfoModel.Msg.SendTime+(remainTime*1000) {
|
if utils.GetCurrentTimestampByMill() > MsgInfoModel.Msg.SendTime+(remainTime*1000) {
|
||||||
delMsgIndexs = append(delMsgIndexs, i)
|
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)
|
if len(delMsgIndexs) > 0 {
|
||||||
// mark del all delMsgIndexs
|
|
||||||
if err := db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, msgDocModel.DocID, delMsgIndexs); err != nil {
|
if err := db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, msgDocModel.DocID, delMsgIndexs); err != nil {
|
||||||
return delStruct.getSetMinSeq(), err
|
log.ZError(ctx, "deleteMsgRecursion DeleteMsgsInOneDocByIndex failed", err, "conversationID", conversationID, "index", index)
|
||||||
|
}
|
||||||
|
delStruct.minSeq = int64(msgDocModel.Msg[delMsgIndexs[len(delMsgIndexs)-1]].Msg.Seq)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MsgInfoModel.Msg.Seq, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 继续递归 index+1
|
|
||||||
seq, err := db.deleteMsgRecursion(ctx, conversationID, index+1, delStruct, remainTime)
|
seq, err := db.deleteMsgRecursion(ctx, conversationID, index+1, delStruct, remainTime)
|
||||||
return seq, err
|
return seq, err
|
||||||
}
|
}
|
||||||
@@ -961,7 +949,14 @@ func (db *commonMsgDatabase) SearchMessage(ctx context.Context, req *pbMsg.Searc
|
|||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
for _, msg := range msgs {
|
for _, msg := range msgs {
|
||||||
|
if msg.IsRead {
|
||||||
|
msg.Msg.IsRead = true
|
||||||
|
}
|
||||||
totalMsgs = append(totalMsgs, convert.MsgDB2Pb(msg.Msg))
|
totalMsgs = append(totalMsgs, convert.MsgDB2Pb(msg.Msg))
|
||||||
}
|
}
|
||||||
return total, totalMsgs, nil
|
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)
|
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)
|
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)
|
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
|
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)
|
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)
|
obj, err := s.obj.Take(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, "", err
|
return time.Time{}, "", err
|
||||||
}
|
}
|
||||||
opt := &s3.AccessURLOption{
|
if opt == nil {
|
||||||
ContentType: obj.ContentType,
|
opt = &s3.AccessURLOption{}
|
||||||
Filename: filepath.Base(obj.Name),
|
}
|
||||||
|
if opt.ContentType == "" {
|
||||||
|
opt.ContentType = obj.ContentType
|
||||||
|
}
|
||||||
|
if opt.Filename == "" {
|
||||||
|
opt.Filename = filepath.Base(obj.Name)
|
||||||
}
|
}
|
||||||
expireTime := time.Now().Add(expire)
|
expireTime := time.Now().Add(expire)
|
||||||
rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt)
|
rawURL, err := s.s3.AccessURL(ctx, obj.Key, expire, opt)
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
"time"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/user"
|
"github.com/OpenIMSDK/protocol/user"
|
||||||
|
|
||||||
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
unRelationTb "github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||||
|
|||||||
@@ -71,10 +71,13 @@ func (f *FriendRequestGorm) UpdateByMap(
|
|||||||
|
|
||||||
// 更新记录 (非零值).
|
// 更新记录 (非零值).
|
||||||
func (f *FriendRequestGorm) Update(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
|
func (f *FriendRequestGorm) Update(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
|
||||||
|
fr2 := *friendRequest
|
||||||
|
fr2.FromUserID = ""
|
||||||
|
fr2.ToUserID = ""
|
||||||
return utils.Wrap(
|
return utils.Wrap(
|
||||||
f.db(ctx).
|
f.db(ctx).
|
||||||
Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID).
|
Where("from_user_id = ? AND to_user_id =?", friendRequest.FromUserID, friendRequest.ToUserID).
|
||||||
Updates(friendRequest).
|
Updates(fr2).
|
||||||
Error,
|
Error,
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -33,5 +33,6 @@ func NewMetaDB(db *gorm.DB, table any) *MetaDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *MetaDB) db(ctx context.Context) *gorm.DB {
|
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,8 +86,12 @@ func (u *UserGorm) Page(
|
|||||||
|
|
||||||
// 获取所有用户ID.
|
// 获取所有用户ID.
|
||||||
func (u *UserGorm) GetAllUserID(ctx context.Context, pageNumber, showNumber int32) (userIDs []string, err error) {
|
func (u *UserGorm) GetAllUserID(ctx context.Context, pageNumber, showNumber int32) (userIDs []string, err 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)
|
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) {
|
func (u *UserGorm) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) {
|
||||||
err = u.db(ctx).Model(&relation.UserModel{}).Where("user_id = ?", userID).Pluck("global_recv_msg_opt", &opt).Error
|
err = u.db(ctx).Model(&relation.UserModel{}).Where("user_id = ?", userID).Pluck("global_recv_msg_opt", &opt).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) {
|
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)
|
return c.impl.AccessURL(ctx, name, expire, opt)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,19 @@ const (
|
|||||||
maxNumSize = 1000
|
maxNumSize = 1000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
imagePng = "png"
|
||||||
|
imageJpg = "jpg"
|
||||||
|
imageJpeg = "jpeg"
|
||||||
|
imageGif = "gif"
|
||||||
|
imageWebp = "webp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
videoSnapshotImagePng = "png"
|
||||||
|
videoSnapshotImageJpg = "jpg"
|
||||||
|
)
|
||||||
|
|
||||||
func NewCos() (s3.Interface, error) {
|
func NewCos() (s3.Interface, error) {
|
||||||
conf := config.Config.Object.Cos
|
conf := config.Config.Object.Cos
|
||||||
u, err := url.Parse(conf.BucketURL)
|
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) {
|
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 {
|
if opt != nil {
|
||||||
query := make(url.Values)
|
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 != "" {
|
if opt.ContentType != "" {
|
||||||
query.Set("response-content-type", opt.ContentType)
|
query.Set("response-content-type", opt.ContentType)
|
||||||
}
|
}
|
||||||
if opt.Filename != "" {
|
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 {
|
if len(query) > 0 {
|
||||||
option = &cos.PresignedURLOptions{
|
option.Query = &query
|
||||||
Query: &query,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if expire <= 0 {
|
if expire <= 0 {
|
||||||
@@ -268,9 +306,13 @@ func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration,
|
|||||||
} else if expire < time.Second {
|
} else if expire < time.Second {
|
||||||
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 {
|
if err != nil {
|
||||||
return "", err
|
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
|
package minio
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/tools/log"
|
||||||
"github.com/minio/minio-go/v7"
|
"github.com/minio/minio-go/v7"
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
"github.com/minio/minio-go/v7/pkg/signer"
|
"github.com/minio/minio-go/v7/pkg/signer"
|
||||||
@@ -43,6 +55,13 @@ const (
|
|||||||
maxNumSize = 10000
|
maxNumSize = 10000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxImageWidth = 1024
|
||||||
|
maxImageHeight = 1024
|
||||||
|
maxImageSize = 1024 * 1024 * 50
|
||||||
|
pathInfo = "openim/thumbnail"
|
||||||
|
)
|
||||||
|
|
||||||
func NewMinio() (s3.Interface, error) {
|
func NewMinio() (s3.Interface, error) {
|
||||||
conf := config.Config.Object.Minio
|
conf := config.Config.Object.Minio
|
||||||
u, err := url.Parse(conf.Endpoint)
|
u, err := url.Parse(conf.Endpoint)
|
||||||
@@ -60,11 +79,27 @@ func NewMinio() (s3.Interface, error) {
|
|||||||
m := &Minio{
|
m := &Minio{
|
||||||
bucket: conf.Bucket,
|
bucket: conf.Bucket,
|
||||||
bucketURL: conf.Endpoint + "/" + conf.Bucket + "/",
|
bucketURL: conf.Endpoint + "/" + conf.Bucket + "/",
|
||||||
opts: opts,
|
|
||||||
core: &minio.Core{Client: client},
|
core: &minio.Core{Client: client},
|
||||||
lock: &sync.Mutex{},
|
lock: &sync.Mutex{},
|
||||||
init: false,
|
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)
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
if err := m.initMinio(ctx); err != nil {
|
if err := m.initMinio(ctx); err != nil {
|
||||||
@@ -76,8 +111,10 @@ func NewMinio() (s3.Interface, error) {
|
|||||||
type Minio struct {
|
type Minio struct {
|
||||||
bucket string
|
bucket string
|
||||||
bucketURL string
|
bucketURL string
|
||||||
|
location string
|
||||||
opts *minio.Options
|
opts *minio.Options
|
||||||
core *minio.Core
|
core *minio.Core
|
||||||
|
sign *minio.Client
|
||||||
lock sync.Locker
|
lock sync.Locker
|
||||||
init bool
|
init bool
|
||||||
}
|
}
|
||||||
@@ -91,15 +128,43 @@ func (m *Minio) initMinio(ctx context.Context) error {
|
|||||||
if m.init {
|
if m.init {
|
||||||
return nil
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("check bucket exists error: %w", err)
|
return fmt.Errorf("check bucket exists error: %w", err)
|
||||||
}
|
}
|
||||||
if !exists {
|
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)
|
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
|
m.init = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -191,7 +256,7 @@ func (m *Minio) AuthSign(ctx context.Context, uploadID string, name string, expi
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
request.Header.Set("X-Amz-Content-Sha256", unsignedPayload)
|
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{
|
result.Parts[i] = s3.SignPart{
|
||||||
PartNumber: partNumber,
|
PartNumber: partNumber,
|
||||||
URL: request.URL.String(),
|
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 {
|
if err := m.initMinio(ctx); err != nil {
|
||||||
return "", err
|
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 {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -303,6 +368,19 @@ func (m *Minio) ListUploadedParts(ctx context.Context, uploadID string, name str
|
|||||||
return res, nil
|
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) {
|
func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||||
if err := m.initMinio(ctx); err != nil {
|
if err := m.initMinio(ctx); err != nil {
|
||||||
return "", err
|
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)
|
reqParams.Set("response-content-type", opt.ContentType)
|
||||||
}
|
}
|
||||||
if opt.Filename != "" {
|
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 {
|
if opt.Image == nil || (opt.Image.Width < 0 && opt.Image.Height < 0 && opt.Image.Format == "") || (opt.Image.Width > maxImageWidth || opt.Image.Height > maxImageHeight) {
|
||||||
expire = time.Hour * 24 * 365 * 99 // 99 years
|
return m.presignedGetObject(ctx, name, expire, reqParams)
|
||||||
} else if expire < time.Second {
|
|
||||||
expire = time.Second
|
|
||||||
}
|
}
|
||||||
u, err := m.core.Client.PresignedGetObject(ctx, m.bucket, name, expire, reqParams)
|
fileInfo, err := m.StatObject(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
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
|
maxNumSize = 10000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
imagePng = "png"
|
||||||
|
imageJpg = "jpg"
|
||||||
|
imageJpeg = "jpeg"
|
||||||
|
imageGif = "gif"
|
||||||
|
imageWebp = "webp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
videoSnapshotImagePng = "png"
|
||||||
|
videoSnapshotImageJpg = "jpg"
|
||||||
|
)
|
||||||
|
|
||||||
func NewOSS() (s3.Interface, error) {
|
func NewOSS() (s3.Interface, error) {
|
||||||
conf := config.Config.Object.Oss
|
conf := config.Config.Object.Oss
|
||||||
if conf.BucketURL == "" {
|
if conf.BucketURL == "" {
|
||||||
@@ -139,7 +152,7 @@ func (o *OSS) AuthSign(ctx context.Context, uploadID string, name string, expire
|
|||||||
}
|
}
|
||||||
for i, partNumber := range partNumbers {
|
for i, partNumber := range partNumbers {
|
||||||
rawURL := fmt.Sprintf(`%s%s?partNumber=%d&uploadId=%s`, o.bucketURL, name, partNumber, uploadID)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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.HTTPHeaderHost, request.Host)
|
||||||
request.Header.Set(oss.HTTPHeaderDate, now)
|
request.Header.Set(oss.HTTPHeaderDate, now)
|
||||||
request.Header.Set(oss.HttpHeaderOssDate, now)
|
request.Header.Set(oss.HttpHeaderOssDate, now)
|
||||||
authorization := fmt.Sprintf(
|
ossSignHeader(o.bucket.Client.Conn, request, fmt.Sprintf(`/%s/%s?partNumber=%d&uploadId=%s`, o.bucket.BucketName, name, partNumber, uploadID))
|
||||||
`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)
|
|
||||||
delete(request.Header, oss.HTTPHeaderDate)
|
delete(request.Header, oss.HTTPHeaderDate)
|
||||||
result.Parts[i] = s3.SignPart{
|
result.Parts[i] = s3.SignPart{
|
||||||
PartNumber: partNumber,
|
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) {
|
func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
|
||||||
var opts []oss.Option
|
var opts []oss.Option
|
||||||
if opt != nil {
|
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 != "" {
|
if opt.ContentType != "" {
|
||||||
opts = append(opts, oss.ResponseContentType(opt.ContentType))
|
opts = append(opts, oss.ResponseContentType(opt.ContentType))
|
||||||
}
|
}
|
||||||
if opt.Filename != "" {
|
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 {
|
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
|
package oss
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"hash"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"sort"
|
_ "unsafe"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (o *OSS) getAdditionalHeaderKeys(req *http.Request) ([]string, map[string]string) {
|
//go:linkname ossSignHeader github.com/aliyun/aliyun-oss-go-sdk/oss.(*Conn).signHeader
|
||||||
var keysList []string
|
func ossSignHeader(c *oss.Conn, req *http.Request, canonicalizedResource 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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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"`
|
UploadedParts []UploadedPart `xml:"Part"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Image struct {
|
||||||
|
Format string `json:"format"`
|
||||||
|
Width int `json:"width"`
|
||||||
|
Height int `json:"height"`
|
||||||
|
}
|
||||||
|
|
||||||
type AccessURLOption struct {
|
type AccessURLOption struct {
|
||||||
ContentType string `json:"contentType"`
|
ContentType string `json:"contentType"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
|
Image *Image `json:"image"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Interface interface {
|
type Interface interface {
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
singleGocMsgNum = 5000
|
singleGocMsgNum = 100
|
||||||
|
singleGocMsgNum5000 = 5000
|
||||||
Msg = "msg"
|
Msg = "msg"
|
||||||
OldestList = 0
|
OldestList = 0
|
||||||
NewestList = -1
|
NewestList = -1
|
||||||
@@ -128,6 +129,7 @@ type MsgDocModelInterface interface {
|
|||||||
pageNumber int32,
|
pageNumber int32,
|
||||||
showNumber int32,
|
showNumber int32,
|
||||||
) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error)
|
) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error)
|
||||||
|
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (MsgDocModel) TableName() string {
|
func (MsgDocModel) TableName() string {
|
||||||
@@ -138,6 +140,10 @@ func (MsgDocModel) GetSingleGocMsgNum() int64 {
|
|||||||
return singleGocMsgNum
|
return singleGocMsgNum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (MsgDocModel) GetSingleGocMsgNum5000() int64 {
|
||||||
|
return singleGocMsgNum5000
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MsgDocModel) IsFull() bool {
|
func (m *MsgDocModel) IsFull() bool {
|
||||||
return m.Msg[len(m.Msg)-1].Msg != nil
|
return m.Msg[len(m.Msg)-1].Msg != nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/tools/log"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/msg"
|
"github.com/OpenIMSDK/protocol/msg"
|
||||||
|
|
||||||
"github.com/OpenIMSDK/protocol/constant"
|
"github.com/OpenIMSDK/protocol/constant"
|
||||||
@@ -1073,11 +1075,6 @@ func (m *MsgMongoDriver) SearchMessage(ctx context.Context, req *msg.SearchMessa
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
for _, msg1 := range msgs {
|
|
||||||
if msg1.IsRead {
|
|
||||||
msg1.Msg.IsRead = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return total, msgs, nil
|
return total, msgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1151,40 +1148,46 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa
|
|||||||
{"doc_id", 1},
|
{"doc_id", 1},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
{"$unwind", bson.M{"path": "$msgs"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{"$sort", bson.M{"msgs.msg.send_time": -1}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
cursor, err := m.MsgCollection.Aggregate(ctx, pipe)
|
cursor, err := m.MsgCollection.Aggregate(ctx, pipe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
type docModel struct {
|
||||||
var msgsDocs []table.MsgDocModel
|
DocID string `bson:"doc_id"`
|
||||||
|
Msg *table.MsgInfoModel `bson:"msgs"`
|
||||||
|
}
|
||||||
|
var msgsDocs []docModel
|
||||||
err = cursor.All(ctx, &msgsDocs)
|
err = cursor.All(ctx, &msgsDocs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
if len(msgsDocs) == 0 {
|
log.ZDebug(ctx, "query mongoDB", "result", msgsDocs)
|
||||||
return 0, nil, errs.Wrap(mongo.ErrNoDocuments)
|
|
||||||
}
|
|
||||||
msgs := make([]*table.MsgInfoModel, 0)
|
msgs := make([]*table.MsgInfoModel, 0)
|
||||||
for index := range msgsDocs {
|
for index := range msgsDocs {
|
||||||
for i := range msgsDocs[index].Msg {
|
msgInfo := msgsDocs[index].Msg
|
||||||
msg := msgsDocs[index].Msg[i]
|
if msgInfo == nil || msgInfo.Msg == nil {
|
||||||
if msg == nil || msg.Msg == nil {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if msg.Revoke != nil {
|
if msgInfo.Revoke != nil {
|
||||||
revokeContent := sdkws.MessageRevokedContent{
|
revokeContent := sdkws.MessageRevokedContent{
|
||||||
RevokerID: msg.Revoke.UserID,
|
RevokerID: msgInfo.Revoke.UserID,
|
||||||
RevokerRole: msg.Revoke.Role,
|
RevokerRole: msgInfo.Revoke.Role,
|
||||||
ClientMsgID: msg.Msg.ClientMsgID,
|
ClientMsgID: msgInfo.Msg.ClientMsgID,
|
||||||
RevokerNickname: msg.Revoke.Nickname,
|
RevokerNickname: msgInfo.Revoke.Nickname,
|
||||||
RevokeTime: msg.Revoke.Time,
|
RevokeTime: msgInfo.Revoke.Time,
|
||||||
SourceMessageSendTime: msg.Msg.SendTime,
|
SourceMessageSendTime: msgInfo.Msg.SendTime,
|
||||||
SourceMessageSendID: msg.Msg.SendID,
|
SourceMessageSendID: msgInfo.Msg.SendID,
|
||||||
SourceMessageSenderNickname: msg.Msg.SenderNickname,
|
SourceMessageSenderNickname: msgInfo.Msg.SenderNickname,
|
||||||
SessionType: msg.Msg.SessionType,
|
SessionType: msgInfo.Msg.SessionType,
|
||||||
Seq: msg.Msg.Seq,
|
Seq: msgInfo.Msg.Seq,
|
||||||
Ex: msg.Msg.Ex,
|
Ex: msgInfo.Msg.Ex,
|
||||||
}
|
}
|
||||||
data, err := json.Marshal(&revokeContent)
|
data, err := json.Marshal(&revokeContent)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -1197,15 +1200,16 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
msg.Msg.ContentType = constant.MsgRevokeNotification
|
msgInfo.Msg.ContentType = constant.MsgRevokeNotification
|
||||||
msg.Msg.Content = string(content)
|
msgInfo.Msg.Content = string(content)
|
||||||
}
|
|
||||||
msgs = append(msgs, msg)
|
|
||||||
}
|
}
|
||||||
|
msgs = append(msgs, msgInfo)
|
||||||
}
|
}
|
||||||
start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
|
start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
|
||||||
n := int32(len(msgs))
|
n := int32(len(msgs))
|
||||||
if start+req.Pagination.ShowNumber < n {
|
if start >= n {
|
||||||
|
return n, []*table.MsgInfoModel{}, nil
|
||||||
|
} else if start+req.Pagination.ShowNumber < n {
|
||||||
msgs = msgs[start : start+req.Pagination.ShowNumber]
|
msgs = msgs[start : start+req.Pagination.ShowNumber]
|
||||||
} else {
|
} else {
|
||||||
msgs = msgs[start:]
|
msgs = msgs[start:]
|
||||||
|
|||||||
@@ -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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
|
||||||
"github.com/OpenIMSDK/tools/errs"
|
"github.com/OpenIMSDK/tools/errs"
|
||||||
"github.com/OpenIMSDK/tools/utils"
|
"github.com/OpenIMSDK/tools/utils"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
"go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
|
||||||
|
"github.com/OpenIMSDK/Open-IM-Server/pkg/common/db/table/unrelation"
|
||||||
)
|
)
|
||||||
|
|
||||||
// prefixes and suffixes.
|
// prefixes and suffixes.
|
||||||
@@ -153,7 +155,7 @@ func (u *UserMongoDriver) RemoveSubscribedListFromUser(ctx context.Context, user
|
|||||||
return errs.Wrap(err)
|
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) {
|
func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string) (userIDList []string, err error) {
|
||||||
var user unrelation.UserModel
|
var user unrelation.UserModel
|
||||||
cursor := u.userCollection.FindOne(
|
cursor := u.userCollection.FindOne(
|
||||||
@@ -166,7 +168,7 @@ func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string
|
|||||||
return user.UserIDList, nil
|
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) {
|
func (u *UserMongoDriver) GetSubscribedList(ctx context.Context, userID string) (userIDList []string, err error) {
|
||||||
var user unrelation.UserModel
|
var user unrelation.UserModel
|
||||||
cursor := u.userCollection.FindOne(
|
cursor := u.userCollection.FindOne(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetNotificationConversationID(msg *sdkws.MsgData) string {
|
func GetNotificationConversationIDByMsg(msg *sdkws.MsgData) string {
|
||||||
switch msg.SessionType {
|
switch msg.SessionType {
|
||||||
case constant.SingleChatType:
|
case constant.SingleChatType:
|
||||||
l := []string{msg.SendID, msg.RecvID}
|
l := []string{msg.SendID, msg.RecvID}
|
||||||
@@ -114,6 +114,30 @@ func GetConversationIDBySessionType(sessionType int, ids ...string) string {
|
|||||||
return ""
|
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 {
|
func IsNotification(conversationID string) bool {
|
||||||
return strings.HasPrefix(conversationID, "n_")
|
return strings.HasPrefix(conversationID, "n_")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ func newContentTypeConf() map[int32]config.NotificationConf {
|
|||||||
constant.GroupInfoSetNameNotification: config.Config.Notification.GroupInfoSetName,
|
constant.GroupInfoSetNameNotification: config.Config.Notification.GroupInfoSetName,
|
||||||
// user
|
// user
|
||||||
constant.UserInfoUpdatedNotification: config.Config.Notification.UserInfoUpdated,
|
constant.UserInfoUpdatedNotification: config.Config.Notification.UserInfoUpdated,
|
||||||
|
constant.UserStatusChangeNotification: config.Config.Notification.UserStatusChanged,
|
||||||
// friend
|
// friend
|
||||||
constant.FriendApplicationNotification: config.Config.Notification.FriendApplicationAdded,
|
constant.FriendApplicationNotification: config.Config.Notification.FriendApplicationAdded,
|
||||||
constant.FriendApplicationApprovedNotification: config.Config.Notification.FriendApplicationApproved,
|
constant.FriendApplicationApprovedNotification: config.Config.Notification.FriendApplicationApproved,
|
||||||
@@ -103,6 +104,7 @@ func newSessionTypeConf() map[int32]int32 {
|
|||||||
constant.GroupInfoSetNameNotification: constant.SuperGroupChatType,
|
constant.GroupInfoSetNameNotification: constant.SuperGroupChatType,
|
||||||
// user
|
// user
|
||||||
constant.UserInfoUpdatedNotification: constant.SingleChatType,
|
constant.UserInfoUpdatedNotification: constant.SingleChatType,
|
||||||
|
constant.UserStatusChangeNotification: constant.SingleChatType,
|
||||||
// friend
|
// friend
|
||||||
constant.FriendApplicationNotification: constant.SingleChatType,
|
constant.FriendApplicationNotification: constant.SingleChatType,
|
||||||
constant.FriendApplicationApprovedNotification: 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)
|
return f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FriendNotificationSender) FriendApplicationAgreedNotification(
|
func (f *FriendNotificationSender) FriendApplicationAgreedNotification(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
req *pbFriend.RespondFriendApplyReq,
|
req *pbFriend.RespondFriendApplyReq,
|
||||||
) error {
|
) error {
|
||||||
@@ -144,10 +144,10 @@ func (c *FriendNotificationSender) FriendApplicationAgreedNotification(
|
|||||||
FromUserID: req.FromUserID,
|
FromUserID: req.FromUserID,
|
||||||
ToUserID: req.ToUserID,
|
ToUserID: req.ToUserID,
|
||||||
}, HandleMsg: req.HandleMsg}
|
}, 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,
|
ctx context.Context,
|
||||||
req *pbFriend.RespondFriendApplyReq,
|
req *pbFriend.RespondFriendApplyReq,
|
||||||
) error {
|
) error {
|
||||||
@@ -155,15 +155,15 @@ func (c *FriendNotificationSender) FriendApplicationRefusedNotification(
|
|||||||
FromUserID: req.FromUserID,
|
FromUserID: req.FromUserID,
|
||||||
ToUserID: req.ToUserID,
|
ToUserID: req.ToUserID,
|
||||||
}, HandleMsg: req.HandleMsg}
|
}, 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,
|
ctx context.Context,
|
||||||
operationID, opUserID, fromUserID, toUserID string,
|
operationID, opUserID, fromUserID, toUserID string,
|
||||||
) error {
|
) error {
|
||||||
tips := sdkws.FriendAddedTips{Friend: &sdkws.FriendInfo{}, OpUser: &sdkws.PublicUserInfo{}}
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -171,52 +171,52 @@ func (c *FriendNotificationSender) FriendAddedNotification(
|
|||||||
tips.OpUser.Ex = user[0].GetEx()
|
tips.OpUser.Ex = user[0].GetEx()
|
||||||
tips.OpUser.Nickname = user[0].GetNickname()
|
tips.OpUser.Nickname = user[0].GetNickname()
|
||||||
tips.OpUser.FaceURL = user[0].GetFaceURL()
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
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{
|
tips := sdkws.FriendDeletedTips{FromToUserID: &sdkws.FromToUserID{
|
||||||
FromUserID: req.OwnerUserID,
|
FromUserID: req.OwnerUserID,
|
||||||
ToUserID: req.FriendUserID,
|
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 := sdkws.FriendInfoChangedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||||
tips.FromToUserID.FromUserID = fromUserID
|
tips.FromToUserID.FromUserID = fromUserID
|
||||||
tips.FromToUserID.ToUserID = toUserID
|
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 := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}}
|
||||||
tips.FromToUserID.FromUserID = req.OwnerUserID
|
tips.FromToUserID.FromUserID = req.OwnerUserID
|
||||||
tips.FromToUserID.ToUserID = req.BlackUserID
|
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{
|
blackDeletedTips := sdkws.BlackDeletedTips{FromToUserID: &sdkws.FromToUserID{
|
||||||
FromUserID: req.OwnerUserID,
|
FromUserID: req.OwnerUserID,
|
||||||
ToUserID: req.BlackUserID,
|
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,
|
ctx context.Context,
|
||||||
changedUserID string,
|
changedUserID string,
|
||||||
needNotifiedUserID string,
|
needNotifiedUserID string,
|
||||||
) {
|
) {
|
||||||
tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID}
|
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
|
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 {
|
if opUser == nil {
|
||||||
return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is 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) {
|
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 {
|
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||||
return err
|
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) {
|
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 {
|
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||||
return err
|
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) {
|
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 {
|
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||||
return err
|
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) {
|
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 {
|
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -288,6 +318,12 @@ func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbGroup.JoinGroupReq) (err error) {
|
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)
|
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -355,6 +391,12 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) {
|
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)
|
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -377,6 +419,12 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbGroup.TransferGroupOwnerReq) (err error) {
|
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)
|
group, err := g.getGroupInfo(ctx, req.GroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -394,6 +442,12 @@ func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) (err error) {
|
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 {
|
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||||
return err
|
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) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, req *pbGroup.GroupApplicationResponseReq) (err error) {
|
func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, groupID string, entrantUserID string) (err error) {
|
||||||
group, err := g.getGroupInfo(ctx, req.GroupID)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user, err := g.getGroupMember(ctx, req.GroupID, req.FromUserID)
|
user, err := g.getGroupMember(ctx, groupID, entrantUserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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) {
|
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 {
|
if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
|
||||||
return err
|
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) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -475,6 +559,12 @@ func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) (err error) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -545,6 +653,12 @@ func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
|
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)
|
group, err := g.getGroupInfo(ctx, groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -560,25 +674,6 @@ func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx c
|
|||||||
return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips)
|
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) {
|
func (g *GroupNotificationSender) SuperGroupNotification(ctx context.Context, sendID, recvID string) (err error) {
|
||||||
defer log.ZDebug(ctx, "return")
|
defer log.ZDebug(ctx, "return")
|
||||||
defer func() {
|
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 {
|
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
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Copyright © 2023 OpenIM. All rights reserved.
|
# Copyright © 2023 OpenIM. All rights reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@@ -53,7 +54,19 @@ fi
|
|||||||
cd $OPENIM_ROOT
|
cd $OPENIM_ROOT
|
||||||
|
|
||||||
# CPU core number
|
# 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}"
|
echo -e "${GREEN_PREFIX}======> cpu_count=$cpu_count${COLOR_SUFFIX}"
|
||||||
|
|
||||||
# Count the number of concurrent compilations (half the number of cpus)
|
# Count the number of concurrent compilations (half the number of cpus)
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user