diff --git a/.dockerignore b/.dockerignore
index 263798e07e..cf6c3cd596 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -5,9 +5,8 @@
.git
# Ignore build artifacts
-_output/
logs/
-
+_output/
# Ignore non-essential documentation
README.md
README-zh_CN.md
@@ -18,8 +17,6 @@ CHANGELOG/
# Ignore testing and linting configuration
.golangci.yml
-# Ignore deployment-related files
-docker-compose.yaml
# Ignore assets
assets/
diff --git a/.env b/.env
new file mode 100644
index 0000000000..6c2baa41d8
--- /dev/null
+++ b/.env
@@ -0,0 +1,13 @@
+
+MONGO_IMAGE=mongo:6.0.2
+REDIS_IMAGE=redis:7.0.0
+ZOOKEEPER_IMAGE=bitnami/zookeeper:3.8
+KAFKA_IMAGE=bitnami/kafka:3.5.1
+MINIO_IMAGE=minio/minio:RELEASE.2024-01-11T07-46-16Z
+
+
+OPENIM_WEB_FRONT_IMAGE=openim/openim-web-front:release-v3.5.1
+OPENIM_ADMIN_FRONT_IMAGE=openim/openim-admin-front:release-v1.7
+
+DATA_DIR=./
+
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..dfdb8b771c
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.sh text eol=lf
diff --git a/.github/workflows/create-branch-on-tag.yml b/.github/workflows/create-branch-on-tag.bak
similarity index 100%
rename from .github/workflows/create-branch-on-tag.yml
rename to .github/workflows/create-branch-on-tag.bak
diff --git a/.github/workflows/docker-buildx.yml b/.github/workflows/docker-buildx.bak
similarity index 100%
rename from .github/workflows/docker-buildx.yml
rename to .github/workflows/docker-buildx.bak
diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.bak
similarity index 72%
rename from .github/workflows/e2e-test.yml
rename to .github/workflows/e2e-test.bak
index f98221e41b..6231697c25 100644
--- a/.github/workflows/e2e-test.yml
+++ b/.github/workflows/e2e-test.bak
@@ -73,14 +73,20 @@ jobs:
- name: Docker Operations
run: |
- sudo make init
sudo docker compose up -d
+ sudo bash bootstrap.sh
+ sudo mage
sudo sleep 20
- name: Module Operations
run: |
- sudo make tidy
- sudo make tools.verify.go-gitlint
+ echo "===========> Verifying go-gitlint is installed"
+ if [ ! -f ./_output/tools/go-gitlint ]; then
+ export GOBIN=$(pwd)/_output/tools
+ echo "===========> Installing The default installation path is /home/ubuntu/DF/open-im-server/_output/tools/go-gitlint"
+ sudo go install github.com/marmotedu/go-gitlint/cmd/go-gitlint@latest
+ echo "===========> go-gitlint is installed in /home/ubuntu/DF/open-im-server/_output/tools/go-gitlint"
+ fi
- name: Build, Start(make build && make start)
run: |
@@ -90,7 +96,8 @@ jobs:
run: |
sudo ./scripts/install/install.sh -s
- - name: Exec OpenIM API test (make test-api)
+# - name: Exec OpenIM API test (make test-api)
+ - name: Exec OpenIM test (make test)
run: |
mkdir -p ./tmp
touch ./tmp/test.md
@@ -98,23 +105,27 @@ jobs:
echo "## OpenIM API Test" >> ./tmp/test.md
echo "Command Output for OpenIM API Test
" >> ./tmp/test.md
echo "" >> ./tmp/test.md
- sudo make test-api | tee -a ./tmp/test.md
+ echo "===========> Run api test"
+ ./scripts/install/test.sh
+ echo "===========> Run api test" >> ./tmp/test.md
+ ./scripts/install/test.sh >> ./tmp/test.md
echo "
" >> ./tmp/test.md
echo " " >> ./tmp/test.md
- sudo make test-api
+ echo "===========> Run api test"
+ ./scripts/install/test.sh
- - name: Exec OpenIM E2E Test (make test-e2e)
- run: |
- echo "" >> ./tmp/test.md
- echo "## OpenIM E2E Test" >> ./tmp/test.md
- echo "Command Output for OpenIM E2E Test
" >> ./tmp/test.md
- echo "" >> ./tmp/test.md
- sudo make test-e2e | tee -a ./tmp/test.md
- echo "
" >> ./tmp/test.md
- echo " " >> ./tmp/test.md
+ # - name: Exec OpenIM E2E Test (make test-e2e)
+ # run: |
+ # echo "" >> ./tmp/test.md
+ # echo "## OpenIM E2E Test" >> ./tmp/test.md
+ # echo "Command Output for OpenIM E2E Test
" >> ./tmp/test.md
+ # echo "" >> ./tmp/test.md
+ # sudo make test-e2e | tee -a ./tmp/test.md
+ # echo "
" >> ./tmp/test.md
+ # echo " " >> ./tmp/test.md
- sudo make test-e2e
+ # sudo make test-e2e
- name: Comment PR with file
uses: thollander/actions-comment-pull-request@v2
diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.bak
similarity index 91%
rename from .github/workflows/golangci-lint.yml
rename to .github/workflows/golangci-lint.bak
index 31e491d6b6..64bd498c54 100644
--- a/.github/workflows/golangci-lint.yml
+++ b/.github/workflows/golangci-lint.bak
@@ -29,7 +29,13 @@ jobs:
go-version: '1.21'
cache: false
- name: OpenIM Scripts Verification(make verify)
- run: sudo make verify
+ run: |
+ cd scripts
+ for script in verify-*; do
+ if [ -x "$script" ]; then
+ ./"$script"
+ fi
+ done
- name: golangci-lint
uses: golangci/golangci-lint-action@v4.0.0
with:
diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml
index f47283997d..83d495a0e4 100644
--- a/.github/workflows/openimci.yml
+++ b/.github/workflows/openimci.yml
@@ -1,3 +1,4 @@
+
# Copyright © 2023 OpenIM open source community. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,8 +12,7 @@
# 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.
-
-name: OpenIM CI Aotu Build and Install
+name: OpenIM CI Auto Build
on:
push:
@@ -36,270 +36,156 @@ on:
- "CONTRIBUTING/**"
- "**.md"
- "docs/**"
-
-env:
- GO_VERSION: "1.19"
- GOLANGCI_VERSION: "v1.50.1"
+ workflow_dispatch:
jobs:
- openim:
- name: Test with go ${{ matrix.go_version }} on ${{ matrix.os }}
- runs-on: ${{ matrix.os }}
- permissions:
- contents: write
- pull-requests: write
- environment:
- name: openim
- strategy:
- matrix:
- go_version: ["1.19","1.20","1.21"]
- os: [ubuntu-latest]
- steps:
- - name: Setup
- uses: actions/checkout@v4
-
- - name: Set up Go ${{ matrix.go_version }}
- uses: actions/setup-go@v5
- with:
- go-version: ${{ matrix.go_version }}
- id: go
-
- - name: Install Task
- uses: arduino/setup-task@v1
- with:
- version: '3.x' # If available, use the latest major version that's compatible
- repo-token: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Code Typecheck Detector
- uses: kubecub/typecheck@main
-
- - name: Conformity Checker for Project
- uses: kubecub/standardizer@main
-
- - name: Module Operations
- run: |
- sudo make tidy
- sudo make tools.verify.go-gitlint
-
- - name: Format Code
- run: sudo make format
- continue-on-error: true
-
- - name: Generate Files
- run: make gen
- continue-on-error: true
-
- - name: Build Source
- run: sudo make build
-
- - name: Build multiarch PLATFORMS
- if: startsWith(github.ref, 'refs/heads/release-')
- run: |
- sudo make multiarch
-
- - name: Cleanup Build
- run: sudo make clean
-
- - name: Set Current Directory
- id: set_directory
- run: echo "::set-output name=directory::$(pwd)"
- continue-on-error: true
-
- - name: Collect and Display Test Coverage
- id: collect_coverage
- run: |
- cd ${{ steps.set_directory.outputs.directory }}
- make cover
- echo "::set-output name=coverage_file::./_output/tmp/coverage.out"
- echo "Test Coverage:"
- cat ${{ steps.collect_coverage.outputs.coverage_file }}
- continue-on-error: true
-
- openim-start:
- name: Test OpenIM install/start on ${{ matrix.os }}-${{ matrix.arch }}
- runs-on: ${{ matrix.os }}
- permissions:
- contents: write
- pull-requests: write
- environment:
- name: openim
- strategy:
- matrix:
- go_version: ["1.21"]
- os: ["ubuntu-latest"]
- steps:
- - name: Checkout and Install OpenIM
- uses: actions/checkout@v4
- - name: Install Task
- uses: arduino/setup-task@v1
- with:
- version: '3.x' # If available, use the latest major version that's compatible
- repo-token: ${{ secrets.GITHUB_TOKEN }}
- - name: Run OpenIM make install start
- run: |
- sudo make install
-
- # - name: Check the OpenIM environment and status
- # run: |
- # sudo docker images
- # sudo docker ps
- - name: Check the OpenIM environment and status
- if: runner.os == 'Linux' && matrix.arch == 'amd64'
- id: docker_info
- run: |
- sleep 30
- echo "images<> $GITHUB_ENV
- sudo docker images >> $GITHUB_ENV
- echo "EOF" >> $GITHUB_ENV
- echo "containers<> $GITHUB_ENV
- sudo docker ps >> $GITHUB_ENV
- echo "EOF" >> $GITHUB_ENV
-
- - name: Comment PR
- uses: thollander/actions-comment-pull-request@v2
- if: runner.os == 'Linux' && matrix.arch == 'amd64'
- with:
- message: |
- > [!TIP]
- > Run make install to check the status
-
- ### Docker Images:
- Click to expand docker images
- ```bash
- ${{ env.images }}
- ```
-
-
- ### Docker Processes:
- Click to expand docker ps
- ```bash
- ${{ env.containers }}
- ```
-
- GITHUB_TOKEN: ${{ secrets.BOT_GITHUB_TOKEN }}
-
- execute-scripts:
- name: Execute OpenIM Script On ${{ matrix.os }}-${{ matrix.arch }}
- runs-on: ${{ matrix.os }}
+ build-linux:
+ name: Execute OpenIM Script On Linux
+ runs-on: ubuntu-latest
permissions:
- contents: write
- pull-requests: write
+ contents: write
+ pull-requests: write
environment:
name: openim
strategy:
matrix:
- go_version: ["1.21"]
- os: ["ubuntu-latest", "macos-latest"]
arch: [arm64, armv7, amd64]
+
steps:
- - name: Checkout code
- uses: actions/checkout@v4
-
- - name: Set up Go ${{ matrix.go_version }}
- uses: actions/setup-go@v5
- with:
- go-version: ${{ matrix.go_version }}
- id: go
+ - uses: actions/checkout@v3
- - name: Install Task
- uses: arduino/setup-task@v1
+ - name: Set up Go
+ uses: actions/setup-go@v4
with:
- version: '3.x' # If available, use the latest major version that's compatible
- repo-token: ${{ secrets.GITHUB_TOKEN }}
-
- # - name: Install latest Bash (macOS only)
- # if: runner.os == 'macOS' && matrix.arch == 'arm64'
- # run: |
- # /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- # brew update
-
- # brew install bash
- # brew install gnu-sed
-
- # echo "/usr/local/bin" >> $GITHUB_PATH
- # echo "$(brew --prefix)/opt/gnu-sed/libexec/gnubin" >> $GITHUB_PATH
- # continue-on-error: true
+ go-version: '1.21'
- - name: Set up Docker for Ubuntu
- if: runner.os == 'Linux'
+ - name: Set up Docker for Linux
run: |
- sudo make init
sudo docker compose up -d
- sudo sleep 20
-
- # - name: Set up Docker for macOS
- # if: runner.os == 'macOS' && matrix.arch == 'arm64'
- # run: |
- # brew install --cask docker
- # open /Applications/Docker.app
-
- # sleep 10
- # docker-compose --version || brew install docker-compose
+ sudo sleep 30 # Increased sleep time for better stability
+ timeout-minutes: 20 # Increased timeout for Docker setup
- # docker-compose up -d
- # sleep 20
- - name: Module Operations for Ubuntu
- if: runner.os == 'Linux'
- run: |
- sudo make tidy
- sudo make tools.verify.go-gitlint
-
- # - name: Module Operations for macOS
- # if: runner.os == 'macOS'
- # run: |
- # make tidy
- # make tools.verify.go-gitlint
-
- - name: Build, Start, Check Services and Print Logs for Ubuntu
- if: runner.os == 'Linux'
- run: |
- sudo make build
- sudo make start
- sudo make check
-
- - name: Restart Services and Print Logs for Ubuntu
- if: runner.os == 'Linux' && matrix.arch == 'amd64'
- run: |
- sudo make restart
- sudo make check
-
- - name: Build, Start, Check Services and Print Logs for macOS
- if: runner.os == 'macOS' && matrix.arch == 'arm64'
- run: |
- make build
-
- openim-test-build-image:
- name: Build OpenIM Docker Image
- runs-on: ubuntu-latest
- permissions:
- contents: write
- pull-requests: write
- environment:
- name: openim
- steps:
- - name: Checkout code
- uses: actions/checkout@v4
- - name: Set up Go ${{ matrix.go_version }}
- uses: actions/setup-go@v5
- with:
- go-version: ${{ matrix.go_version }}
- id: go
-
- - name: Install Task
- uses: arduino/setup-task@v1
- with:
- version: '3.x' # If available, use the latest major version that's compatible
- repo-token: ${{ secrets.GITHUB_TOKEN }}
+ - name: init
+ run: sudo bash bootstrap.sh
+ timeout-minutes: 20
- - name: Test Docker Build
+ - name: Build, Start, Check Services and Print Logs for Linux
run: |
- sudo make init
- sudo make image
+ sudo mage
+ sudo mage start
+ sudo mage check
+
- - name: Get OpenIM Docker Images Status
- id: docker_processes
+ - name: Restart Services and Print Logs
run: |
- sudo docker images
- sudo docker ps
+ sudo mage stop
+ sudo mage start
+ sudo mage check
+
+
+# build-mac:
+# name: Execute OpenIM Script On macOS
+# runs-on: macos-latest
+# permissions:
+# contents: write
+# pull-requests: write
+# environment:
+# name: openim
+# strategy:
+# matrix:
+# arch: [arm64, armv7, amd64]
+#
+# steps:
+# - uses: actions/checkout@v3
+
+# - name: Set up Go
+# uses: actions/setup-go@v4
+# with:
+# go-version: '1.21'
+
+
+# while ! docker system info > /dev/null 2>&1; do
+# echo "Waiting for Docker to start..."
+# sleep 10 # Increased delay to ensure Docker starts properly
+# done
+
+# - name: Install Docker
+# run: |
+# brew install docker
+# brew install docker-compose
+# sleep 10
+# docker-compose up -d
+# sleep 30
+# timeout-minutes: 20
+#
+
+# - name: init
+# run: sudo bash bootstrap.sh
+# timeout-minutes: 20
+
+# - name: Build, Start, Check Services and Print Logs for Linux
+# run: |
+# sudo mage
+# sudo mage start
+# sudo mage check
+
+# - name: Restart Services and Print Logs
+# run: |
+# sudo mage stop
+# sudo mage start
+# sudo mage check
+
+# build-windows:
+# name: Execute OpenIM Script On Windows
+# runs-on: windows-latest
+# permissions:
+# contents: write
+# pull-requests: write
+# environment:
+# name: openim
+# strategy:
+# matrix:
+# arch: [arm64, armv7, amd64]
+#
+# steps:
+# - uses: actions/checkout@v3
+
+# - name: Set up Go
+# uses: actions/setup-go@v4
+# with:
+# go-version: '1.21'
+
+# - name: Set up Docker for Windows
+# run: |
+# $images = @("zookeeper", "redis", "kafka")
+# foreach ($image in $images) {
+# $tag = "$image:latest"
+# docker pull $tag | Out-Null
+# if ($LASTEXITCODE -ne 0) {
+# Write-Host "Skipping $image as it is not available for Windows"
+# } else {
+# Write-Host "Successfully pulled $image"
+# }
+# }
+# docker compose up -d
+# Start-Sleep -Seconds 30
+# timeout-minutes: 20
+# shell: pwsh
+
+# - name: init
+# run: bootstrap.bat
+# timeout-minutes: 20
+
+# - name: Build, Start, Check Services and Print Logs for Linux
+# run: |
+# mage
+# mage start
+# mage check
+
+# - name: Restart Services and Print Logs
+# run: |
+# mage stop
+# mage start
+# mage check
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.bak
similarity index 96%
rename from .github/workflows/pull-request.yml
rename to .github/workflows/pull-request.bak
index c123566f1c..f7c5900ce7 100644
--- a/.github/workflows/pull-request.yml
+++ b/.github/workflows/pull-request.bak
@@ -56,7 +56,8 @@ jobs:
- name: Generate all necessary files, such as error code files
run: |
- make generate
+ make gen.docgo.doc
+ make gen
echo "Generate all necessary files successfully"
continue-on-error: true
@@ -68,9 +69,9 @@ jobs:
echo "Generate all necessary files successfully"
continue-on-error: true
- - name: Generate Vertions
+ - name: Generate Versions Including Pre-release Identifiers
run: |
- latest_tag=$(git describe --tags `git rev-list --tags --max-count=1` | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
+ latest_tag=$(git describe --tags `git rev-list --tags --max-count=1`)
echo $latest_tag > pkg/common/config/version
continue-on-error: true
diff --git a/.gitignore b/.gitignore
index 5142fe5513..fb8d428d24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,7 +34,6 @@ deployments/charts/generated-configs/
### OpenIM Config ###
.env
config/config.yaml
-config/openim.yaml
config/alertmanager.yml
config/prometheus.yml
config/email.tmpl
diff --git a/.golangci.yml b/.golangci.yml
index c262cfa2f0..ae8cea6732 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -39,19 +39,19 @@ run:
# from this option's value (see skip-dirs-use-default).
# "/" will be replaced by current OS file path separator to properly work
# on Windows.
- skip-dirs:
- - components
- - docs
- - util
- - .*~
- - api/swagger/docs
- - server/docs
- - components/mnt/config/certs
- - logs
+ # skip-dirs:
+ # - components
+ # - docs
+ # - util
+ # - .*~
+ # - api/swagger/docs
+ # - server/docs
+ # - components/mnt/config/certs
+ # - logs
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
- skip-dirs-use-default: true
+ # skip-dirs-use-default: true
# which files to skip: they will be analyzed, but issues from them
# won't be reported. Default value is empty list, but there is
@@ -59,15 +59,15 @@ run:
# autogenerated files. If it's not please let us know.
# "/" will be replaced by current OS file path separator to properly work
# on Windows.
- skip-files:
- - ".*\\.my\\.go$"
- - _test.go
- - ".*_test.go"
- - "mocks/"
- - ".github/"
- - "logs/"
- - "_output/"
- - "components/"
+ # skip-files:
+ # - ".*\\.my\\.go$"
+ # - _test.go
+ # - ".*_test.go"
+ # - "mocks/"
+ # - ".github/"
+ # - "logs/"
+ # - "_output/"
+ # - "components/"
# by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules":
# If invoked with -mod=readonly, the go command is disallowed from the implicit
@@ -87,7 +87,7 @@ run:
# output configuration options
output:
# colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number"
- format: colored-line-number
+ # format: colored-line-number
# print lines of code with issue, default is true
print-issued-lines: true
@@ -118,8 +118,7 @@ linters-settings:
right-to-left-isolate: true
first-strong-isolate: true
pop-directional-isolate: true
- # checks assignments with too many blank identifiers; default is 2
- max-blank-identifiers: 2
+
dupl:
# tokens count to trigger issue, 150 by default
threshold: 200
@@ -151,6 +150,11 @@ linters-settings:
comparison: true
exhaustive:
+ # Program elements to check for exhaustiveness.
+ # Default: [ switch ]
+ check:
+ - switch
+ - map
# check switch statements in generated files also
check-generated: false
# indicates that switch statements are to be considered exhaustive if a
@@ -162,33 +166,34 @@ linters-settings:
ignore-enum-members: ""
# consider enums only in package scopes, not in inner scopes
package-scope-only: false
- exhaustivestruct:
- struct-patterns:
- - '*.Test'
- - '*.Test2'
- - '*.Embedded'
- - '*.External'
-
- # forbidigo:
+
+
+ forbidigo:
# # Forbid the following identifiers (identifiers are written using regexp):
- # forbid:
- # - ^print.*$
- # - 'fmt\.Print.*'
- # - fmt.Println.* # too much log noise
+ forbid:
+ # - ^print.*$
+ - 'fmt\.Print.*'
+ - fmt.Println.* # too much log noise
+ - ^unsafe\..*$
+ - ^init$
+ - ^os.Exit$
+ - ^fmt.Print.*$
+ - errors.New.*$
+ - ^fmt.Println.*$
+ - ^panic$
+ - painc
# - ginkgo\\.F.* # these are used just for local development
# # Exclude godoc examples from forbidigo checks. Default is true.
# exclude_godoc_examples: false
+
funlen:
- lines: 150
+ lines: 220
statements: 80
- gci:
- # put imports beginning with prefix after 3rd-party packages;
- # only support one prefix
- # if not set, use goimports.local-prefixes
- prefix: github.com/openimsdk/open-im-server
+
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 30
+
goconst:
# minimal length of string constant, 3 by default
min-len: 3
@@ -214,9 +219,6 @@ linters-settings:
# By default list of stable checks is used.
enabled-checks:
#- rangeValCopy
- - nestingreduce
- - truncatecmp
- - unnamedresult
- ruleguard
# Which checks should be disabled; can't be combined with 'enabled-checks'; default is empty
@@ -245,9 +247,6 @@ linters-settings:
hugeParam:
# size in bytes that makes the warning trigger (default 80)
sizeThreshold: 80
- nestingReduce:
- # min number of statements inside a branch to trigger a warning (default 5)
- bodyWidth: 5
rangeExprCopy:
# size in bytes that makes the warning trigger (default 512)
sizeThreshold: 512
@@ -261,15 +260,10 @@ linters-settings:
ruleguard:
# path to a gorules file for the ruleguard checker
rules: ''
- truncateCmp:
- # whether to skip int/uint/uintptr types (default true)
- skipArchDependent: true
underef:
# whether to skip (*x).method() calls where x is a pointer receiver (default true)
skipRecvDeref: true
- unnamedResult:
- # whether to check exported functions
- checkExported: true
+
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 30
@@ -286,9 +280,12 @@ linters-settings:
# list of regexps for excluding particular comment lines from check
exclude:
# example: exclude comments which contain numbers
- # - '[0-9]+'
+ - '[0-9]+'
+ - 'func\s+\w+'
+ - 'FIXME:'
+ - '.*func.*'
# check that each sentence starts with a capital letter
- capital: false
+ capital: true
godox:
# report any comments starting with keywords, this is useful for TODO or FIXME comments that
# might be left in the code accidentally and should be resolved before merging
@@ -311,15 +308,15 @@ linters-settings:
# by default
extra-rules: false
- goheader:
- values:
- const:
+ # goheader:
+ # values:
+ # const:
# define here const type values in format k:v, for example:
# COMPANY: MY COMPANY
- regexp:
+ # regexp:
# define here regexp type values, for example
# AUTHOR: .*@mycompany\.com
- template: # |-
+ # template: # |-
# put here copyright header template for source code files, for example:
# Note: {{ YEAR }} is a builtin value that returns the year relative to the current machine time.
#
@@ -337,21 +334,45 @@ linters-settings:
# 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.
- template-path:
+ # template-path:
# also as alternative of directive 'template' you may put the path to file with the template source
+
goimports:
# put imports beginning with prefix after 3rd-party packages;
# it's a comma-separated list of prefixes
local-prefixes: github.com/openimsdk/open-im-server
gomnd:
- settings:
- mnd:
- # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
- checks: argument,case,condition,operation,return,assign
- # ignored-numbers: 1000
- # ignored-files: magic_.*.go
- # ignored-functions: math.*
+ # List of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description.
+ # Default: ["argument", "case", "condition", "operation", "return", "assign"]
+ checks:
+ - argument
+ - case
+ - condition
+ - operation
+ - return
+ - assign
+ # List of numbers to exclude from analysis.
+ # The numbers should be written as string.
+ # Values always ignored: "1", "1.0", "0" and "0.0"
+ # Default: []
+ ignored-numbers:
+ - '0666'
+ - '0755'
+ - '42'
+ # List of file patterns to exclude from analysis.
+ # Values always ignored: `.+_test.go`
+ # Default: []
+ ignored-files:
+ - 'magic1_.+\.go$'
+ # List of function patterns to exclude from analysis.
+ # Following functions are always ignored: `time.Date`,
+ # `strconv.FormatInt`, `strconv.FormatUint`, `strconv.FormatFloat`,
+ # `strconv.ParseInt`, `strconv.ParseUint`, `strconv.ParseFloat`.
+ # Default: []
+ ignored-functions:
+ - '^math\.'
+ - '^webhook\.StatusText$'
gomoddirectives:
# Allow local `replace` directives. Default is false.
replace-local: true
@@ -363,6 +384,7 @@ linters-settings:
retract-allow-no-explanation: false
# Forbid the use of the `exclude` directives. Default is false.
exclude-forbidden: false
+
gomodguard:
allowed:
modules:
@@ -426,9 +448,6 @@ linters-settings:
checks: [ "all" ]
govet:
- # report about shadowed variables
- check-shadowing: false
-
# settings per analyzer
settings:
printf: # analyzer name, run `go tool vet help` to see all analyzers
@@ -445,15 +464,25 @@ linters-settings:
disable:
- shadow
disable-all: false
- # depguard:
- # list-type: blacklist
- # include-go-root: false
- # packages:
- # - github.com/Sirupsen/logrus
- # packages-with-error-message:
- # # specify an error message to output when a blacklisted package is used
- # - github.com/Sirupsen/logrus: "logging is allowed only by logutils.Log"
+ depguard:
+ rules:
+ prevent_unmaintained_packages:
+ list-mode: lax # allow unless explicitely denied
+ files:
+ - $all
+ - "!$test"
+ allow:
+ - $gostd
+ deny:
+ - pkg: io/ioutil
+ desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil"
+ - pkg: github.com/OpenIMSDK
+ desc: "The OpenIM organization has been replaced with lowercase, please do not use uppercase organization name, you will use openimsdk"
+ - pkg: log
+ desc: "We have a wrapped log package at openim, we recommend you to use our wrapped log package, https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md"
+ - pkg: errors
+ desc: "We have a wrapped errors package at openim, we recommend you to use our wrapped errors package, https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md"
importas:
# if set to `true`, force to use alias.
@@ -463,6 +492,8 @@ linters-settings:
# using `servingv1` alias for `knative.dev/serving/pkg/apis/serving/v1` package
- pkg: knative.dev/serving/pkg/apis/serving/v1
alias: servingv1
+ - pkg: gopkg.in/yaml.v2
+ alias: yaml
# using `autoscalingv1alpha1` alias for `knative.dev/serving/pkg/apis/autoscaling/v1alpha1` package
- pkg: knative.dev/serving/pkg/apis/autoscaling/v1alpha1
alias: autoscalingv1alpha1
@@ -471,8 +502,6 @@ linters-settings:
# see https://github.com/julz/importas#use-regular-expression for details
- pkg: knative.dev/serving/pkg/apis/(\w+)/(v[\w\d]+)
alias: $1$2
- # using `jwt` alias for `github.com/appleboy/gin-jwt/v2` package
- jwt: github.com/appleboy/gin-jwt/v2
ireturn:
# ireturn allows using `allow` and `reject` settings at the same time.
@@ -503,9 +532,6 @@ linters-settings:
line-length: 250
# tab width in spaces. Default to 1.
tab-width: 4
- maligned:
- # print struct with more effective memory layout or not, false by default
- suggest-new: true
misspell:
# Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English.
@@ -538,8 +564,6 @@ linters-settings:
nolintlint:
# Disable to ensure that all nolint directives actually have an effect. Default is true.
allow-unused: false
- # Disable to ensure that nolint directives don't have a leading space. Default is true.
- allow-leading-space: true
# Exclude following linters from requiring an explanation. Default is [].
allow-no-explanation: [ ]
# Enable to require an explanation of nonzero length after each nolint directive. Default is false.
@@ -563,14 +587,13 @@ linters-settings:
strict: false
# Please refer to https://github.com/yeya24/promlinter#usage for detailed usage.
disabled-linters:
- # - "Help"
- # - "MetricUnits"
- # - "Counter"
- # - "HistogramSummaryReserved"
- # - "MetricTypeInName"
- # - "ReservedChars"
- # - "CamelCase"
- # - "lintUnitAbbreviations"
+ - "Help"
+ - "MetricUnits"
+ - "Counter"
+ - "HistogramSummaryReserved"
+ - "MetricTypeInName"
+ - "ReservedChars"
+ - "CamelCase"
predeclared:
# comma-separated list of predeclared identifiers to not report on
@@ -580,6 +603,7 @@ linters-settings:
rowserrcheck:
packages:
- github.com/jmoiron/sqlx
+
revive:
# see https://github.com/mgechev/revive#available-rules for details.
ignore-generated-header: true
@@ -587,15 +611,27 @@ linters-settings:
rules:
- name: indent-error-flow
severity: warning
+ - name: exported
+ severity: warning
+ - name: var-naming
+ arguments: [ [ "OpenIM"] ]
+ # arguments: [ ["ID", "HTTP", "URL", "URI", "API", "APIKey", "Token", "TokenID", "TokenSecret", "TokenKey", "TokenSecret", "JWT", "JWTToken", "JWTTokenID", "JWTTokenSecret", "JWTTokenKey", "JWTTokenSecret", "OAuth", "OAuthToken", "RPC" ] ]
+ - name: atomic
+ - name: line-length-limit
+ severity: error
+ arguments: [200]
+ - name: unhandled-error
+ arguments : ["fmt.Printf", "myFunction"]
+
staticcheck:
# Select the Go version to target. The default is '1.13'.
- go: "1.16"
+ go: "1.20"
# https://staticcheck.io/docs/options#checks
checks: [ "all" ]
stylecheck:
# Select the Go version to target. The default is '1.13'.
- go: "1.16"
+ go: "1.20"
# https://staticcheck.io/docs/options#checks
checks: [ "all", "-ST1000", "-ST1003", "-ST1016", "-ST1020", "-ST1021", "-ST1022" ]
@@ -607,7 +643,6 @@ linters-settings:
# https://staticcheck.io/docs/options#http_status_code_whitelist
http-status-code-whitelist: [ "200", "400", "404", "500" ]
-
tagliatelle:
# check the struck tag name case
case:
@@ -653,12 +688,11 @@ linters-settings:
# if it's called for subdir of a project it can't find external interfaces. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
check-exported: false
- unused:
+ # unused:
# treat code as a program (not a library) and report unused exported identifiers; default is false.
# XXX: if you enable this setting, unused will report a lot of false-positives in text editors:
# if it's called for subdir of a project it can't find funcs usages. All text editor integrations
# with golangci-lint call it on a directory with the changed file.
- check-exported: false
whitespace:
multi-if: false # Enforces newlines (or comments) after every multi-line if statement
multi-func: false # Enforces newlines (or comments) after every multi-line function signature
@@ -672,6 +706,7 @@ linters-settings:
- errors.New(
- errors.Unwrap(
- .Wrap(
+ - .WrapMsg(
- .Wrapf(
- .WithMessage(
- .WithMessagef(
@@ -679,6 +714,8 @@ linters-settings:
ignorePackageGlobs:
- encoding/*
- github.com/pkg/*
+ - github.com/openimsdk/*
+ - github.com/OpenIMSDK/*
wsl:
# If true append is only allowed to be cuddled if appending value is
@@ -705,7 +742,6 @@ linters-settings:
# Allow only slices initialized with a length of zero. Default is false.
always: false
-
# The custom section can be used to define linter plugins to be loaded at runtime. See README doc
# for more info.
#custom:
@@ -731,16 +767,21 @@ linters:
- errcheck
- decorder
- ineffassign
+ - forbidigo
- revive
- reassign
- tparallel
- unconvert
+ - fieldalignment
- dupl
- dupword
- errname
- gci
- - goheader
+ - exhaustive
+ - gocritic
- goprintffuncname
+ - gomnd
+ - goconst
- gosec
- misspell # Spelling mistakes
- staticcheck # Static analysis
@@ -761,6 +802,7 @@ issues:
exclude:
- tools/.*
- test/.*
+ - components/*
- third_party/.*
# Excluding configuration per-path, per-linter, per-text and per-source
diff --git a/CONTRIBUTING-zh_CN.md b/CONTRIBUTING-zh_CN.md
index ee3c0b8f85..c10d7337f8 100644
--- a/CONTRIBUTING-zh_CN.md
+++ b/CONTRIBUTING-zh_CN.md
@@ -1,4 +1,4 @@
-# How do I contribute code to OpenIM
+# 如何给OpenIM贡献代码(提交pull request)
Englist ·
@@ -28,6 +28,55 @@
Türkçe
-
-
+本指南将以 [openimsdk/open-im-server](https://github.com/openimsdk/open-im-server)为例详细说明如何为 OpenIM 项目贡献代码。我们采用“一问题一分支”的策略,确保每个 Issue 都对应一个专门的分支,以便有效管理代码变更。
+
+## 1. Fork 仓库
+前往 [openimsdk/open-im-server](https://github.com/openimsdk/open-im-server) GitHub 页面,点击右上角的 "Fork" 按钮,将仓库 Fork 到你的 GitHub 账户下。
+
+## 2. 克隆仓库
+将你 Fork 的仓库克隆到本地:
+```bash
+git clone https://github.com/your-username/open-im-server.git
+```
+
+## 3. 设置远程上游
+添加原始仓库为远程上游以便跟踪其更新:
+```bash
+git remote add upstream https://github.com/openimsdk/open-im-server.git
+```
+
+## 4. 创建 Issue
+在原始仓库中创建一个新的 Issue,详细描述你遇到的问题或希望添加的新功能。
+
+## 5. 创建新分支
+基于主分支创建一个新分支,并使用描述性的名称与 Issue ID,例如:
+```bash
+git checkout -b fix-bug-123
+```
+
+## 6. 提交更改
+在你的本地分支上进行更改后,提交这些更改:
+```bash
+git add .
+git commit -m "Describe your changes in detail"
+```
+
+## 7. 推送分支
+将你的分支推送回你的 GitHub Fork:
+```bash
+git push origin fix-bug-123
+```
+
+## 8. 创建 Pull Request
+在 GitHub 上转到你的 Fork 仓库,点击 "Pull Request" 按钮。确保 PR 描述清楚,并链接到相关的 Issue。
+
+## 9. 签署 CLA
+如果这是你第一次提交 PR,你需要在 PR 的评论中回复:
+```
+I have read the CLA Document and I hereby sign the CLA
+```
+## 其他说明
+
+如果需要将同一修改提交到两个不同的分支(例如 `main` 和 `release-v3.7`),应从对应的远程分支分别创建两个新分支。首先在一个分支上完成修改,然后使用 `cherry-pick` 命令将这些更改应用到另一个分支。之后,为每个分支独立提交 Pull Request。
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f7d2c07492..a85ba891fc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -27,475 +27,55 @@
Ελληνικά ·
Türkçe
+This guide will explain in detail how to contribute code to the OpenIM project, using `openimsdk/open-im-server` as an example. We adopt a "one issue, one branch" strategy to ensure each issue corresponds to a dedicated branch, allowing for effective management of code changes.
-
-
-
-
-So, you want to hack on open-im-server? Yay!
-
-First of all, thank you for considering contributing to our project! We appreciate your time and effort, and we value any contribution, whether it's reporting a bug, suggesting a new feature, or submitting a pull request.
-
-![Hello OpenIM Image](assets/demo/hello-openim.png)
-
-> Use `make demo` start contributing fast.
-
-This document provides guidelines and best practices to help you contribute effectively.
-
-## 📇Topics
-
-- [How do I contribute code to OpenIM](#how-do-i-contribute-code-to-openim)
- - [📇Topics](#topics)
- - [What we expect of you](#what-we-expect-of-you)
- - [Code of ConductCode of Conduct](#code-of-conductcode-of-conduct)
- - [Code and doc contribution](#code-and-doc-contribution)
- - [Where should I start?](#where-should-i-start)
- - [Design documents](#design-documents)
- - [Getting Started](#getting-started)
- - [Style and Specification](#style-and-specification)
- - [Reporting security issues](#reporting-security-issues)
- - [Reporting general issues](#reporting-general-issues)
- - [Commit Rules](#commit-rules)
- - [PR Description](#pr-description)
- - [Docs Contribution](#docs-contribution)
- - [Engage to help anything](#engage-to-help-anything)
- - [Release version](#release-version)
- - [Contact Us](#contact-us)
-
-## What we expect of you
-
-We hope that anyone can join open-im-server , even if you are a student, writer, translator
-
-Please meet the minimum version of the Go language published in [go.mod](./go.mod). If you want to manage the Go language version, we provide tools tHow do I contribute code to OpenIMo install [gvm](https://github.com/moovweb/gvm) in our [Makefile](./Makefile)
-
-You'd better use Linux OR WSL as the development environment, Linux with [Makefile](./Makefile) can help you quickly build and test open-im-server project.
-
-If you are familiar with [Makefile](./Makefile) , you can easily see the clever design of the open-im-server Makefile. Storing the necessary tools such as golangci in the `/tools` directory can avoid some tool version issues.
-
-The [Makefile](./Makefile) is for every developer, even if you don't know how to use the Makefile tool, don't worry, we provide two great commands to get you up to speed with the Makefile architecture, `make help` and `make help-all`, it can reduce problems of the developing environment.
-
-In accordance with the naming conventions adopted by OpenIM and drawing reference from the Google Naming Conventions as per the guidelines available at https://google.github.io/styleguide/go/, the following expectations for naming practices within the project are set forth:
-
-+ https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md
-+ https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md
-
-
-## Code of ConductCode of Conduct
-
-#### Code and doc contribution
-
-Every action to make project open-im-server better is encouraged. On GitHub, every improvement for open-im-server could be via a [PR](https://github.com/openimsdk/open-im-server/pulls) (short for pull request).
-
-+ If you find a typo, try to fix it!
-+ If you find a bug, try to fix it!
-+ If you find some redundant codes, try to remove them!
-+ If you find some test cases missing, try to add them!
-+ If you could enhance a feature, please **DO NOT** hesitate!
-+ If you find code implicit, try to add comments to make it clear!
-+ If you find code ugly, try to refactor that!
-+ If you can help to improve documents, it could not be better!
-+ If you find document incorrect, just do it and fix that!
-+ ...
-
-#### Where should I start?
-
-+ If you are new to the project, don't know how to contribute open-im-server, please check out the [good first issue](https://github.com/openimsdk/open-im-server/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label.
-+ You should be good at filtering the open-im-server issue tags and finding the ones you like, such as [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Abug+) fixes.
-+ If you are looking for something to work on, check out our [open issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc).
-+ If you have an idea for a new feature, please [open an issue](https://github.com/openimsdk/open-im-server/issues/new/choose), and we can discuss it.
-
-#### Design documents
-
-For any substantial design, there should be a well-crafted design document. This document is not just a simple record, but also a detailed description and manifestation, which can help team members better understand the design thinking and grasp the design direction. In the process of writing the design document, we can choose to use tools such as `Google Docs` or `Notion`, and even mark RFC in [issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) or [discussions](https://github.com/openimsdk/open-im-server/discussions) for better collaboration. Of course, after completing the design document, we should also add it to our [Shared Drive](https://drive.google.com/drive/) and notify the appropriate working group to let everyone know of its existence. Only by doing so can we maximize the effectiveness of the design document and provide strong support for the smooth progress of the project.
-
-Anybody can access the shared Drive for reading. To get access to comment. Once you've done that, head to the [shared Drive](https://drive.google.com/) and behold all the docs.
-
-In addition to that, we'd love to invite you to [join our Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) where you can play with your imagination, tell us what you're working on, and get a quick response.
-
-When documenting a new design, we recommend a 2-step approach:
-
-1. Use the short-form RFC template to outline your ideas and get early feedback.
-2. Once you have received sufficient feedback and consensus, you may use the longer-form design doc template to specify and discuss your design in more details.
-
-In order to contribute a feature to open-im-server you'll need to go through the following steps:
-
-+ Discuss your idea with the appropriate [working groups](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) on the working group's Slack channel.
-+ Once there is general agreement that the feature is useful, create a GitHub issue to track the discussion. The issue should include information about the requirements and use cases that it is trying to address.
-+ Include a discussion of the proposed design and technical details of the implementation in the issue.
-
-But keep in mind that there is no guarantee of it being accepted and so it is usually best to get agreement on the idea/design before time is spent coding it. However, sometimes seeing the exact code change can help focus discussions, so the choice is up to you.
-
-## Getting Started
-
-To propose PR for the open-im-server item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps:
-
-1. Fork the repository(open-im-server)
-
-2. **CLONE** your own repository to main locally. Use `git clone https://github.com//open-im-server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make.
-
-3. **Initialize Git Hooks with `make init-githooks`**
-
- After cloning the repository, it's recommended to set up Git hooks to streamline your workflow and ensure your contributions adhere to OpenIM's community standards. Git hooks are scripts that run automatically every time a particular event occurs in a Git repository, such as before a commit or push. To initialize Git hooks for the OpenIM server repository, use the `make init-githooks` command.
-
- - **Enabling Git Hooks Mode**: By running `make init-githooks` and entering `1` when prompted, you enable Git hooks mode. This action will generate a series of hooks within the `.git/hooks/` directory. These hooks impose certain checks on your commits and pushes, ensuring they meet the quality and standards expected by the OpenIM community. For instance, commit hooks might enforce a specific commit message format, while push hooks could check for code style or linting issues.
-
- - **Benefits for First-Time Contributors**: This setup is especially beneficial for new contributors. It guides you to make professional, community-standard-compliant Pull Requests (PRs) and PR descriptions right from your first contribution. By automating checks and balances, it reduces the chances of common mistakes and speeds up the review process.
-
- - **Disabling Git Hooks**: If for any reason you wish to remove the Git hooks, simply run `make init-githooks` again and enter `2` when prompted. This will delete the existing Git hooks, removing the automatic checks and constraints from your Git operations. However, keep in mind that manually ensuring your contributions adhere to community standards without the aid of Git hooks requires diligence.
-
- > [!NOTE] Utilizing Git hooks through the `make init-githooks` command is a straightforward yet powerful way to ensure your contributions are consistent and high-quality. It's a step towards fostering a professional and efficient development environment in the OpenIM project.
-
-
-
-4. **Set Remote** upstream to be `https://github.com/openimsdk/open-im-server.git` using the following two commands:
-
- ```bash
- ❯ git remote add upstream https://github.com/openimsdk/open-im-server.git
- ❯ git remote set-url --push upstream no-pushing
- ```
-
- With this remote setting, you can check your git remote configuration like this:
-
- ```bash
- ❯ git remote -v
- origin https://github.com//open-im-server.git (fetch)
- origin https://github.com//open-im-server.git (push)
- upstream https://github.com/openimsdk/open-im-server.git (fetch)
- upstream no-pushing (push)
- ```
-
- Adding this, we can easily synchronize local branches with upstream branches.
-
-5. Create a new branch for your changes (use a descriptive name, such as `fix-bug-123` or `add-new-feature`).
-
- ```bash
- ❯ cd open-im-server
- ❯ git fetch upstream
- ❯ git checkout upstream/main
- ```
-
- Create a new branch:
-
- ```bash
- ❯ git checkout -b
- ```
-
- Make any change on the `new-branch` then use [Makefile](./Makefile) build and test your codes.
-
-
-6. **Commit your changes** to your local branch, lint before committing and commit with sign-off
-
- ```bash
- ❯ git rebase upstream/main
- ❯ make lint # golangci-lint run -c .golangci.yml
- ❯ git add -A # add changes to staging
- ❯ git commit -a -s -m "message for your changes" # -s adds a Signed-off-by trailer
- ```
-
-7. **Push your branch** to your forked repository, it is recommended to have only one commit for a PR.
-
- ```bash
- # sync up with upstream
- ❯ git fetch upstream main
- ❯ git rebase upstream/main
- ❯
- ❯ git rebase -i # rebase with interactive mode to squash your commits into a single one
- ❯ git push # push to the remote repository, if it's a first time push, run git push --set-upstream origin # sync up with upstream
- ❯ git fetch upstream main
- git rebase upstream/main
-
- ❯ git rebase -i # rebase with interactive mode to squash your commits into a single one
- ❯ git push # push to the remote repository, if it's a first time push, run git push --set-upstream origin
- ```
-
- You can also use `git commit -s --amend && git push -f` to update modifications on the previous commit.
-
- If you have developed multiple features in the same branch, you should create PR separately by rebasing to the main branch between each push:
-
- ```bash
- # create new branch, for example git checkout -b feature/infra
- ❯ git checkout -b
- # update some code, feature1
- ❯ git add -A
- ❯ git commit -m -s "feat: feature one"
- ❯ git push # if it's first time push, run git push --set-upstream origin
- # then create pull request, and merge
- # update some new feature, feature2, rebase main branch first.
- ❯ git rebase upstream/main # rebase the current branch to upstream/main branch
- ❯ git add -A
- ❯ git commit -m -s "feat: feature two"
- ```
-
- **Verifying Your Pull Request with `make all` Command**
-
- Before verifying, you may need to complete the basic deployment of OpenIM to get familiar with the deployment status of OpenIM. Please read [this deployment document](https://docs.openim.io/zh-Hans/guides/gettingStarted/imSourceCodeDeployment), which will tell you how to deploy OpenIM middleware and OpenIM services in detail
-
- Before submitting your Pull Request (PR), it's crucial to ensure that it passes all the necessary checks and verifications to maintain the quality and integrity of the OpenIM server project. To facilitate this process, we have encapsulated a series of validation steps into the `make all` command.
-
- - **Purpose of `make all` Command**: The `make all` command serves as a comprehensive pre-PR verification tool. It sequentially executes a variety of tasks designed to scrutinize your changes from multiple angles, ensuring they are ready for submission.
-
- - **Included Commands**:
- - `tidy`: Cleans up the module by removing unused dependencies.
- - `gen`: Generates necessary files from templates or specifications, ensuring that your codebase is up-to-date with its dependencies.
- - `add-copyright`: Checks for and adds copyright notices to files, ensuring compliance with legal requirements.
- - `verify`: Verifies the integrity and consistency of the code, dependencies, and various checks.
- - `test-api`: Runs API tests to ensure that your changes do not break any existing functionality and adhere to the expected behaviors.
- - `lint`: Analyzes the code for potential stylistic or programming errors, enforcing the project's coding standards.
- - `cover`: Measures the code coverage of tests, helping you understand how much of the code is being tested.
- - `restart`: (Optionally) restarts services or applications to ensure that changes are correctly applied and functioning in a live environment.
-
- - **Executing the Command**: To run the `make all` command, simply navigate to the root directory of your cloned repository in your terminal and execute:
- ```bash
- make all
- ```
- This command will sequentially perform all the listed actions, outputting any warnings or errors encountered during the process. It's a vital step to catch any issues early and ensure your contribution meets the quality standards set by the OpenIM community.
-
- - **Benefits**: By using `make all` for pre-PR verification, you significantly increase the likelihood of your PR being accepted on the first review. It not only demonstrates your commitment to quality but also streamlines the review process by minimizing back-and-forth due to common issues that can be caught automatically.
-
-
- **Troubleshooting Git Push Failures**
-
- When working with Git, encountering errors during push operations is not uncommon. Two primary reasons you might face push failures are due to firewall restrictions or authentication issues. Here’s how you can troubleshoot and resolve these problems.
-
- **Firewall Errors**
-
- If you're behind a corporate firewall or your network restricts certain types of traffic, you might encounter issues when trying to push your changes via HTTPS. This is because firewalls can block the ports used by the HTTPS protocol. To resolve this issue, you can configure Git to use a proxy.
-
- If you have a local proxy server set up, you can direct Git to use it by setting the `https_proxy` and `http_proxy` environment variables. Open your terminal or command prompt and run the following commands:
-
- ```bash
- export https_proxy="http://127.0.0.1:7890"
- export http_proxy="http://127.0.0.1:7890"
- ```
-
- Replace `127.0.0.1:7890` with the address and port of your proxy server. These commands set the proxy for the current session. If you want to make these changes permanent, add them to your `.bashrc`, `.bash_profile`, or equivalent shell configuration file.
-
- **Using SSH Instead of HTTPS**
-
- An alternative to using HTTPS is to set up an SSH connection for Git operations. SSH connections are often not blocked by firewalls and do not require proxy settings. Additionally, SSH provides a secure channel and can simplify the authentication process since it relies on SSH keys rather than username and password credentials.
-
- To use SSH with Git, you first need to generate an SSH key pair and add the public key to your GitHub account (or another Git hosting service).
-
- 1. **Generate SSH Key Pair**: Open your terminal and run `ssh-keygen -t rsa -b 4096 -C "your_email@example.com"`, replacing `your_email@example.com` with your email. Press enter to accept the default file location and passphrase prompts.
-
- 2. **Add SSH Key to SSH-Agent**: Ensure the ssh-agent is running with `eval "$(ssh-agent -s)"` and then add your SSH private key to the ssh-agent using `ssh-add ~/.ssh/id_rsa`.
-
- 3. **Add SSH Key to GitHub**: Copy your SSH public key to your clipboard with `cat ~/.ssh/id_rsa.pub | clip` (Windows) or `pbcopy < ~/.ssh/id_rsa.pub` (Mac). Go to GitHub, navigate to Settings > SSH and GPG keys, and add a new SSH key, pasting your key into the field provided.
-
- 4. **Switch to SSH in Your Repository**: Change your repository's remote URL from HTTPS to SSH. You can find the SSH URL in your repository settings on GitHub and use `git remote set-url origin git@github.com:username/repository.git` to switch.
-
- **Authentication Errors**
-
- If you're experiencing authentication errors, it might be due to missing or incorrect credentials. Ensure you have added your SSH key to your Git hosting service. You can test your SSH connection with `ssh -T git@github.com` (replace `github.com` with your Git hosting service's domain). If successful, you'll receive a welcome message.
-
- For HTTPS users, check that your username and password (or personal access token for services like GitHub that no longer accept password authentication for Git operations) are correct.
-
-8. **Open a pull request** to `openimsdk/open-im-server:main`
-
- It is recommended to review your changes before filing a pull request. Check if your code doesn't conflict with the main branch and no redundant code is included.
-
- > [!TIP] There is a [good blog post documenting](https://nsddd.top/posts/participating-in-this-project/) the entire push contribution process.
-
-
-## Style and Specification
-
-We divide the problem into security and general problems:
-
-#### Reporting security issues
-
-Security issues are always treated seriously. As our usual principle, we discourage anyone to spread security issues. If you find a security issue of open-im-server, please do not discuss it in public and even do not open a public issue.
-
-Instead we encourage you to send us a private email to info@openim.io to report this.
-
-#### Reporting general issues
-
-To be honest, we regard every user of open-im-serveras a very kind contributor. After experiencing open-im-server, you may have some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/openimsdk/open-im-server/issues/new/choose).
-
-Since we collaborate project open-im-server in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one.
-
-To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](https://github.com/OpenIMSDK/.github/tree/main/.github/ISSUE_TEMPLATE) for issue reporters. You can find three kinds of issue templates there: question, bug report and feature request. Please **BE SURE** to follow the instructions to fill fields in template.
-
-**There are a lot of cases when you could open an issue:**
-
-+ bug report
-+ feature request
-+ open-im-server performance issues
-+ feature proposal
-+ feature design
-+ help wanted
-+ doc incomplete
-+ test improvement
-+ any questions on open-im-server project
-+ and so on
-
-Also, we must be reminded when submitting a new question about open-im-server, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on.
-
-#### Commit Rules
-
-Actually in open-im-server, we take two rules serious when committing:
-
-**🥇 Commit Message:**
-
-Commit message could help reviewers better understand what the purpose of submitted PR is. It could help accelerate the code review procedure as well. We encourage contributors to use **EXPLICIT** commit message rather than ambiguous message. In general, we advocate the following commit message type:
-
-We use [Semantic Commits](https://www.conventionalcommits.org/en/v1.0.0/) to make it easier to understand what a commit does and to build pretty changelogs. Please use the following prefixes for your commits:
-
-+ `docs: xxxx`. For example, "docs: add docs about storage installation".
-+ `feature: xxxx`.For example, "feature: make result show in sorted order".
-+ `bugfix: xxxx`. For example, "bugfix: fix panic when input nil parameter".
-+ `style: xxxx`. For example, "style: format the code style of Constants.java".
-+ `refactor: xxxx.` For example, "refactor: simplify to make codes more readable".
-+ `test: xxx`. For example, "test: add unit test case for func InsertIntoArray".
-+ `chore: xxx.` For example, "chore: integrate travis-ci". It's the type of mantainance change.
-+ other readable and explicit expression ways.
-
-On the other side, we discourage contributors from committing message like the following ways:
-
-+ ~~fix bug~~
-+ ~~update~~
-+ ~~add doc~~
-
-**🥈 Commit Content:**
-
-Commit content represents all content changes included in one commit. We had better include things in one single commit which could support reviewer's complete review without any other commits' help.
-
-In another word, contents in one single commit can pass the CI to avoid code mess. In brief, there are two minor rules for us to keep in mind:
-
-1. avoid very large change in a commit.
-2. complete and reviewable for each commit.
-3. words are written in lowercase English, not uppercase English or other languages such as Chinese.
-
-No matter what the commit message, or commit content is, we do take more emphasis on code review.
-
-An example for this could be:
+### 1. Fork the Repository
+Go to the `openimsdk/open-im-server` GitHub page, click the "Fork" button in the upper right corner to fork the repository to your GitHub account.
+### 2. Clone the Repository
+Clone the forked repository to your local machine:
```bash
-❯ git commit -a -s -m "docs: add a new section to the README"
+git clone https://github.com/your-username/open-im-server.git
```
-#### PR Description
-
-PR is the only way to make change to open-im-server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/.github/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request.
-
-You can find some very formal PR in [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) issues and learn about them.
-
-**📖 Opening PRs:**
-
-+ As long as you are working on your PR, please mark it as a draft
-+ Please make sure that your PR is up-to-date with the latest changes in `main`
-+ Mention the issue that your PR is addressing (Fix: #{ID_1}, #{ID_2})
-+ Make sure that your PR passes all checks
-
-**🈴 Reviewing PRs:**
-
-+ Be respectful and constructive
-+ Assign yourself to the PR (comment `/assign`)
-+ Check if all checks are passing
-+ Suggest changes instead of simply commenting on found issues
-+ If you are unsure about something, ask the author
-+ If you are not sure if the changes work, try them out
-+ Reach out to other reviewers if you are unsure about something
-+ If you are happy with the changes, approve the PR
-+ Merge the PR once it has all approvals and the checks are passing
-
-**⚠️ DCO check:**
-
-We have a DCO check that runs on every pull request to ensure code quality and maintainability. This check verifies that the commit has been signed off, indicating that you have read and agreed to the provisions of the Developer Certificate of Origin. If you have not yet signed off on the commit, you can use the following command to sign off on the last commit you made:
-
+### 3. Set Upstream Remote
+Add the original repository as a remote upstream to track updates:
```bash
-❯ git commit --amend --signoff
+git remote add upstream https://github.com/openimsdk/open-im-server.git
```
-Please note that signing off on a commit is a commitment that you have read and agreed to the provisions of the Developer Certificate of Origin. If you have not yet read this document, we strongly recommend that you take some time to read it carefully. If you have any questions about the content of this document, or if you need further assistance, please contact an administrator or relevant personnel.
-
-You can also automate signing off your commits by adding the following to your `.zshrc` or `.bashrc`:
+### 4. Create an Issue
+Create a new issue in the original repository describing the problem you are facing or the new feature you want to add. For significant feature adjustments, propose an RFC issue to facilitate broad discussion and participation from community members.
-```go
-git() {
- if [ $# -gt 0 ] && [[ "$1" == "commit" ]] ; then
- shift
- command git commit --signoff "$@"
- else
- command git "$@"
- fi
-}
+### 5. Create a New Branch
+Create a new branch based on the main branch and name it descriptively, including the Issue ID, for example:
+```bash
+git checkout -b fix-bug-123
```
+### 6. Commit Changes
+After making changes on your local branch, commit them:
+```bash
+git add .
+git commit -m "Describe your changes in detail"
+```
-#### Docs Contribution
-
-The documentation for open-im-server includes:
-
-+ [README.md](https://github.com/openimsdk/open-im-server/blob/main/README.md): This file includes the basic information and instructions for getting started with open-im-server.
-+ [CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to open-im-server's codebase, such as how to submit issues, pull requests, and code reviews.
-+ [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for open-im-server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips.
-
-Please obey the following rules to better format the docs, which would greatly improve the reading experience.
-
-1. Please do not use Chinese punctuations in English docs, and vice versa.
-2. Please use upper case letters where applicable, like the first letter of sentences / headings, etc.
-3. Please specify a language for each Markdown code blocks, unless there's no associated languages.
-4. Please insert a whitespace between Chinese and English words.
-5. Please use the correct case for technical terms, such as using `HTTP` instead of http, `MySQL` rather than mysql, `Kubernetes` instead of kubernetes, etc.
-6. Please check if there's any typos in the docs before submitting PRs.
-
-## Engage to help anything
-
-We choose GitHub as the primary place for open-im-server to collaborate. So the latest updates of open-im-server are always here. Although contributions via PR is an explicit way to help, we still call for any other ways.
-
-+ reply to other's [issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) if you could;
-+ help solve other user's problems;
-+ help review other's [PR](https://github.com/openimsdk/open-im-server/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc) design;
-+ discuss about open-im-server to make things clearer;
-+ advocate [open-im-server](https://google.com/search?q=open-im-server) technology beyond GitHub;
-+ write blogs on open-im-server and so on.
-
-In a word, **ANY HELP IS CONTRIBUTION.**
-
-## Release version
-
-Releases of open-im-server are done using [Release Please](https://github.com/googleapis/release-please) and [GoReleaser](https://goreleaser.com/). The workflow looks like this:
-
-🎯 A PR is merged to the `main` branch:
-
-+ Release please is triggered, creates or updates a new release PR
-+ This is done with every merge to main, the current release PR is updated every time
-
-🎯 Merging the 'release please' PR to `main`:
-
-+ Release please is triggered, creates a new release and updates the changelog based on the commit messages
-+ GoReleaser is triggered, builds the binaries and attaches them to the release
-+ Containers are created and pushed to the container registry
-
-With the next relevant merge, a new release PR will be created and the process starts again
-
-**👀 Manually setting the version:**
-
-If you want to manually set the version, you can create a PR with an empty commit message that contains the version number in the commit message. For example:
-
-Such a commit can get produced as follows:
-
-````bash
-❯ git commit --allow-empty -m "chore: release 0.0.3" -m "Release-As: 0.0.3
-````
-
-For the complex release process, in fact, and encapsulation as CICD, you only need to tag locally, and then publish the tag to OpenIM github to complete the entire OpenIM release process.
-
-In addition to CICD, we also do a complex release command locally, which can help you complete the full platform compilation, testing, and release to Minio, just by using the `make release` command.
-Please [read the detailed documents](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/release.md)
-
-
-## Contact Us
-
-We value close connections with our users, developers, and contributors here at open-im-server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
+### 7. Push the Branch
+Push your branch back to your GitHub fork:
+```bash
+git push origin fix-bug-123
+```
-Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of open-im-server. You can ask technical questions, seek help, or share your experiences with other users of open-im-server.
+### 8. Create a Pull Request
+Go to your fork on GitHub, click the "Pull Request" button. Make sure the PR description is clear and links to the related Issue.
+#### 🅰 Fixed issue #issueID
-In addition to Slack, we also offer the following ways to get in touch:
+### 9. Sign the CLA
+If this is your first time submitting a PR, you need to reply in the PR comments:
+```
+I have read the CLA Document and I hereby sign the CLA
+```
-+ : We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 open-im-server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) team channel.
-+ : Get in touch with us on [Gmail](info@openim.io). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email.
-+ : Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with open-im-server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information.
-+ : Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of open-im-server. We will process your request as soon as possible.
+### Additional Notes
+If the same modification needs to be submitted to two different branches (e.g., main and release-v3.7), create two new branches from the corresponding remote branches. First complete the modification in one branch, then use the `cherry-pick` command to apply these changes to the other branch. After that, submit a separate Pull Request for each branch.
-Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us.
diff --git a/Dockerfile b/Dockerfile
index d4c2e26024..746dddf65a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,31 +1,49 @@
-# Build Stage
-FROM golang:1.20 AS builder
+# Use Go 1.21 Alpine as the base image for building the application
+FROM golang:1.21-alpine as builder
-# Set go mod installation source and proxy
-ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
+# Define the base directory for the application as an environment variable
+ENV SERVER_DIR=/openim-server
-ENV GO111MODULE=$GO111MODULE
-ENV GOPROXY=$GOPROXY
+# Set the working directory inside the container based on the environment variable
+WORKDIR $SERVER_DIR
-# Set up the working directory
-WORKDIR /openim/openim-server
+# Set the Go proxy to improve dependency resolution speed
+ENV GOPROXY=https://goproxy.io,direct
+# Copy all files from the current directory into the container
+COPY . .
-# Copy all files to the container
-ADD . .
+RUN go mod download
-RUN make clean
-RUN make build
+# Install Mage to use for building the application
+RUN go install github.com/magefile/mage@v1.15.0
-FROM ghcr.io/openim-sigs/openim-ubuntu-image:latest
+# Optionally build your application if needed
+RUN mage build
-WORKDIR ${SERVER_WORKDIR}
+# Using Alpine Linux with Go environment for the final image
+FROM golang:1.21-alpine
-# Copy scripts and binary files to the production image
-COPY --from=builder ${OPENIM_SERVER_BINDIR} /openim/openim-server/_output/bin
-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}/deployments /openim/openim-server/deployments
+# Install necessary packages, such as bash
+RUN apk add --no-cache bash
-CMD ["/openim/openim-server/scripts/docker-start-all.sh"]
+# Set the environment and work directory
+ENV SERVER_DIR=/openim-server
+WORKDIR $SERVER_DIR
+
+
+# Copy the compiled binaries and mage from the builder image to the final image
+COPY --from=builder $SERVER_DIR/_output $SERVER_DIR/_output
+COPY --from=builder $SERVER_DIR/config $SERVER_DIR/config
+COPY --from=builder /go/bin/mage /usr/local/bin/mage
+COPY --from=builder $SERVER_DIR/magefile_windows.go $SERVER_DIR/
+COPY --from=builder $SERVER_DIR/magefile_unix.go $SERVER_DIR/
+COPY --from=builder $SERVER_DIR/magefile.go $SERVER_DIR/
+COPY --from=builder $SERVER_DIR/start-config.yml $SERVER_DIR/
+COPY --from=builder $SERVER_DIR/go.mod $SERVER_DIR/
+COPY --from=builder $SERVER_DIR/go.sum $SERVER_DIR/
+
+RUN go get github.com/openimsdk/gomake@v0.0.9
+
+# Set the command to run when the container starts
+ENTRYPOINT ["sh", "-c", "mage start && tail -f /dev/null"]
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 818372fb8f..0000000000
--- a/Makefile
+++ /dev/null
@@ -1,256 +0,0 @@
-# ==============================================================================
-# define the default goal
-#
-
-.DEFAULT_GOAL := help
-
-## all: Run tidy, gen, add-copyright, format, lint, cover, build ✨
-.PHONY: all
-all: tidy gen add-copyright verify test-api lint cover restart
-
-# ==============================================================================
-# Build set
-
-ROOT_PACKAGE=github.com/openimsdk/open-im-server
-# TODO: This is version control for the future https://github.com/openimsdk/open-im-server/issues/574
-VERSION_PACKAGE=github.com/openimsdk/open-im-server/v3/pkg/version
-
-# ==============================================================================
-# Includes
-
-include scripts/make-rules/common.mk # make sure include common.mk at the first include line
-include scripts/make-rules/common-versions.mk
-include scripts/make-rules/golang.mk
-include scripts/make-rules/image.mk
-include scripts/make-rules/copyright.mk
-include scripts/make-rules/gen.mk
-include scripts/make-rules/dependencies.mk
-include scripts/make-rules/tools.mk
-include scripts/make-rules/release.mk
-include scripts/make-rules/swagger.mk
-
-# ==============================================================================
-# Usage
-
-define USAGE_OPTIONS
-
-Options:
-
- DEBUG Whether or not to generate debug symbols. Default is 0.
-
- BINS Binaries to build. Default is all binaries under cmd.
- This option is available when using: make {build}(.multiarch)
- Example: make build BINS="openim-api openim-cmdutils".
-
- PLATFORMS Platform to build for. Default is linux_arm64 and linux_amd64.
- This option is available when using: make {build}.multiarch
- Example: make multiarch PLATFORMS="linux_s390x linux_mips64
- linux_mips64le darwin_amd64 windows_amd64 linux_amd64 linux_arm64".
-
- V Set to 1 enable verbose build. Default is 0.
-endef
-export USAGE_OPTIONS
-
-# ==============================================================================
-# Targets
-
-## init: Initialize openim server project ✨
-.PHONY: init
-init:
- @$(MAKE) gen.init
-
-## init-githooks: Initialize git hooks ✨
-.PHONY: init-githooks
-init-githooks:
- @$(MAKE) gen.init-githooks
-
-## gen: Generate all necessary files. ✨
-.PHONY: gen
-gen:
- @$(MAKE) gen.run
-
-## demo: Run demo get started with Makefiles quickly ✨
-.PHONY: demo
-demo:
- @$(MAKE) go.demo
-
-## version: Check version of openim. ✨
-.PHONY: version
-version:
- @$(MAKE) go.versionchecker
-
-## build: Build binaries by default ✨
-.PHONY: build
-build:
- @$(MAKE) go.build
-
-## start: Start openim ✨
-.PHONY: start
-start:
- @$(MAKE) go.start
-
-## stop: Stop openim ✨
-.PHONY: stop
-stop:
- @$(MAKE) go.stop
-
-## restart: Restart openim (make init configuration file is initialized) ✨
-.PHONY: restart
-restart: clean stop build start check
-
-## multiarch: Build binaries for multiple platforms. See option PLATFORMS. ✨
-.PHONY: multiarch
-multiarch:
- @$(MAKE) go.build.multiarch
-
-## verify: execute all verity scripts. ✨
-.PHONY: verify
-verify:
- @$(MAKE) go.verify
-
-## install: Install deployment openim ✨
-.PHONY: install
-install:
- @$(MAKE) go.install
-
-## check: Check OpenIM deployment ✨
-.PHONY: check
-check:
- @$(MAKE) go.check
-
-## check-component: Check OpenIM component deployment ✨
-.PHONY: check-component
-check-component:
- @$(MAKE) go.check-component
-
-## tidy: tidy go.mod ✨
-.PHONY: tidy
-tidy:
- @$(GO) mod tidy
-
-## vendor: vendor go.mod ✨
-.PHONY: vendor
-vendor:
- @$(GO) mod vendor
-
-## style: code style -> fmt,vet,lint ✨
-.PHONY: style
-style: fmt vet lint
-
-## fmt: Run go fmt against code. ✨
-.PHONY: fmt
-fmt:
- @$(GO) fmt ./...
-
-## vet: Run go vet against code. ✨
-.PHONY: vet
-vet:
- @$(GO) vet ./...
-
-## lint: Check syntax and styling of go sources. ✨
-.PHONY: lint
-lint:
- @$(MAKE) go.lint
-
-## format: Gofmt (reformat) package sources (exclude vendor dir if existed). ✨
-.PHONY: format
-format:
- @$(MAKE) go.format
-
-## test: Run unit test. ✨
-.PHONY: test
-test:
- @$(MAKE) go.test
-
-## cover: Run unit test and get test coverage. ✨
-.PHONY: cover
-cover:
- @$(MAKE) go.test.cover
-
-## test-api: Run api test. ✨
-.PHONY: test-api
-test-api:
- @$(MAKE) go.test.api
-
-## test-e2e: Run e2e test
-test-e2e:
- @$(MAKE) go.test.e2e
-
-## updates: Check for updates to go.mod dependencies. ✨
-.PHONY: updates
- @$(MAKE) go.updates
-
-## imports: task to automatically handle import packages in Go files using goimports tool. ✨
-.PHONY: imports
-imports:
- @$(MAKE) go.imports
-
-## clean: Delete all files created by the build, as well as all log files. ✨
-.PHONY: clean
-clean:
- @$(MAKE) go.clean
-
-## image: Build docker images for host arch. ✨
-.PHONY: image
-image:
- @$(MAKE) image.build
-
-## image.multiarch: Build docker images for multiple platforms. See option PLATFORMS. ✨
-.PHONY: image.multiarch
-image.multiarch:
- @$(MAKE) image.build.multiarch
-
-## push: Build docker images for host arch and push images to registry. ✨
-.PHONY: push
-push:
- @$(MAKE) image.push
-
-## push.multiarch: Build docker images for multiple platforms and push images to registry. ✨
-.PHONY: push.multiarch
-push.multiarch:
- @$(MAKE) image.push.multiarch
-
-## tools: Install dependent tools. ✨
-.PHONY: tools
-tools:
- @$(MAKE) tools.install
-
-## swagger: Generate swagger document. ✨
-.PHONY: swagger
-swagger:
- @$(MAKE) swagger.run
-
-## serve-swagger: Serve swagger spec and docs. ✨
-.PHONY: swagger.serve
-serve-swagger:
- @$(MAKE) swagger.serve
-
-## verify-copyright: Verify the license headers for all files. ✨
-.PHONY: verify-copyright
-verify-copyright:
- @$(MAKE) copyright.verify
-
-## add-copyright: Add copyright ensure source code files have license headers. ✨
-.PHONY: add-copyright
-add-copyright:
- @$(MAKE) copyright.add
-
-## advertise: Project introduction, become a contributor ✨
-.PHONY: advertise
-advertise:
- @$(MAKE) copyright.advertise
-
-## release: release the project ✨
-.PHONY: release
-release: release.verify release.ensure-tag
- @scripts/release.sh
-
-## help: Show this help info. ✨
-.PHONY: help
-help: Makefile
- $(call makehelp)
-
-## help-all: Show all help details info. ✨
-.PHONY: help-all
-help-all: go.help copyright.help tools.help image.help dependencies.help gen.help release.help swagger.help help
- $(call makeallhelp)
diff --git a/README.md b/README.md
index 175db3326b..045d28f470 100644
--- a/README.md
+++ b/README.md
@@ -51,137 +51,89 @@
-## :busts_in_silhouette: Community
+## :busts_in_silhouette: Join Our Community
-+ 💬 [Follow our Twitter account](https://twitter.com/founder_im63606)
-+ 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging)
-+ 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)
-+ :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg)
-+ 📚 [OpenIM Community](https://github.com/OpenIMSDK/community)
-+ 💕 [OpenIM Interest Group](https://github.com/Openim-sigs)
++ 💬 [Follow us on Twitter](https://twitter.com/founder_im63606)
++ 🚀 [Join our Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2hljfom5u-9ZuzP3NfEKW~BJKbpLm0Hw)
++ :eyes: [Join our WeChat Group](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg)
## Ⓜ️ About OpenIM
-OpenIM is a service platform specifically designed for integrating chat, audio-video calls, notifications, and AI chatbots into applications. It provides a range of powerful APIs and Webhooks, enabling developers to easily incorporate these interactive features into their applications. OpenIM is not a standalone chat application, but rather serves as a platform to support other applications in achieving rich communication functionalities. The following diagram illustrates the interaction between AppServer, AppClient, OpenIMServer, and OpenIMSDK to explain in detail.
+Unlike standalone chat applications such as Telegram, Signal, and Rocket.Chat, OpenIM offers an open-source instant messaging solution designed specifically for developers rather than as a directly installable standalone chat app. Comprising OpenIM SDK and OpenIM Server, it provides developers with a complete set of tools and services to integrate instant messaging functions into their applications, including message sending and receiving, user management, and group management. Overall, OpenIM aims to provide developers with the necessary tools and framework to implement efficient instant messaging solutions in their applications.
![App-OpenIM Relationship](./docs/images/oepnim-design.png)
-## 🚀 About OpenIMSDK
+## 🚀 Introduction to OpenIMSDK
-**OpenIMSDK** is an IM SDK designed for **OpenIMServer**, created specifically for embedding in client applications. Its main features and modules are as follows:
+**OpenIMSDK**, designed for **OpenIMServer**, is an IM SDK created specifically for integration into client applications. It supports various functionalities and modules:
+ 🌟 Main Features:
-
- - 📦 Local storage
- - 🔔 Listener callbacks
- - 🛡️ API wrapping
- - 🌐 Connection management
+ - 📦 Local Storage
+ - 🔔 Listener Callbacks
+ - 🛡️ API Wrapping
+ - 🌐 Connection Management
+ 📚 Main Modules:
-
1. 🚀 Initialization and Login
2. 👤 User Management
- 3. 👫 Friend Management
+ 3. 👫 Friends Management
4. 🤖 Group Functions
- 5. 💬 Conversation Handling
-
-It is built using Golang and supports cross-platform deployment, ensuring a consistent access experience across all platforms.
-
-👉 **[Explore GO SDK](https://github.com/openimsdk/openim-sdk-core)**
-
-## 🌐 About OpenIMServer
+ 5. 💬 Session Handling
-+ **OpenIMServer** has the following characteristics:
- - 🌐 Microservice architecture: Supports cluster mode, including a gateway and multiple rpc services.
- - 🚀 Diverse deployment methods: Supports deployment via source code, Kubernetes, or Docker.
- - Support for massive user base: Super large groups with hundreds of thousands of users, tens of millions of users, and billions of messages.
+Built with Golang and supports cross-platform deployment to ensure a consistent integration experience across all platforms.
-### Enhanced Business Functionality:
+👉 **[Explore the GO SDK](https://github.com/openimsdk/openim-sdk-core)**
-+ **REST API**: OpenIMServer offers REST APIs for business systems, aimed at empowering businesses with more functionalities, such as creating groups and sending push messages through backend interfaces.
-+ **Webhooks**: OpenIMServer provides callback capabilities to extend more business forms. A callback means that OpenIMServer sends a request to the business server before or after a certain event, like callbacks before or after sending a message.
+## 🌐 Introduction to OpenIMServer
-👉 **[Learn more](https://docs.openim.io/guides/introduction/product)**
++ **OpenIMServer** features include:
+ - 🌐 Microservices Architecture: Supports cluster mode, including a gateway and multiple rpc services.
+ - 🚀 Diverse Deployment Options: Supports source code, Kubernetes, or Docker deployment.
+ - Massive User Support: Supports large-scale groups with hundreds of thousands, millions of users, and billions of messages.
-## :building_construction: Overall Architecture
+### Enhanced Business Functions:
-Delve into the heart of Open-IM-Server's functionality with our architecture diagram.
++ **REST API**: Provides a REST API for business systems to enhance functionality, such as group creation and message pushing through backend interfaces.
-![Overall Architecture](./docs/images/architecture-layers.png)
++ **Webhooks**: Expands business forms through callbacks, sending requests to business servers before or after certain events.
+ ![Overall Architecture](./docs/images/architecture-layers.png)
## :rocket: Quick Start
-We support many platforms. Here are the addresses for quick experience on the web side:
+Experience online for iOS/Android/H5/PC/Web:
-👉 **[OpenIM online web demo](https://web-enterprise.rentsoft.cn/)**
+👉 **[OpenIM Online Demo](https://www.openim.io/en/commercial)**
-🤲 To facilitate user experience, we offer various deployment solutions. You can choose your deployment method from the list below:
+To facilitate user experience, we offer various deployment solutions. You can choose your preferred deployment method from the list below:
+ **[Source Code Deployment Guide](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)**
+ **[Docker Deployment Guide](https://docs.openim.io/guides/gettingStarted/dockerCompose)**
-+ **[Kubernetes Deployment Guide](https://docs.openim.io/guides/gettingStarted/k8s-deployment)**
-+ **[Mac Developer Deployment Guide](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)**
-
-## :hammer_and_wrench: To Start Developing OpenIM
-
-[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server)
-
-[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/openimsdk/open-im-server)
-
-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).
-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).
+## System Support
-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-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), or if you find an issue, [report it](https://github.com/openimsdk/open-im-server/issues/new/choose) first.
+Supports Linux, Windows, Mac systems, and ARM and AMD CPU architectures.
-- [OpenIM API Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md)
-- [OpenIM Bash Logging](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md)
-- [OpenIM CI/CD Actions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md)
-- [OpenIM Code Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md)
-- [OpenIM Commit Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md)
-- [OpenIM Development Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md)
-- [OpenIM Directory Structure](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md)
-- [OpenIM Environment Setup](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md)
-- [OpenIM Error Code Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md)
-- [OpenIM Git Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md)
-- [OpenIM Git Cherry Pick Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md)
-- [OpenIM GitHub Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md)
-- [OpenIM Go Code Standards](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md)
-- [OpenIM Image Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md)
-- [OpenIM Initial Configuration](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md)
-- [OpenIM Docker Installation Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md)
-- [OpenIM OpenIM Linux System Installation](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md)
-- [OpenIM Linux Development Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md)
-- [OpenIM Local Actions Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md)
-- [OpenIM Logging Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md)
-- [OpenIM Offline Deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md)
-- [OpenIM Protoc Tools](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md)
-- [OpenIM Testing Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md)
-- [OpenIM Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md)
-- [OpenIM Makefile Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md)
-- [OpenIM Script Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md)
-- [OpenIM Versioning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md)
-- [Manage backend and monitor deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md)
-- [Mac Developer Deployment Guide for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md)
+## :link: Links
-## :calendar: Community Meetings
+ + **[Developer Manual](https://docs.openim.io/)**
+ + **[Changelog](https://github.com/openimsdk/open-im-server/blob/main/CHANGELOG.md)**
-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.
+## :writing_hand: How to Contribute
-Our conference is in the [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, then you can search the Open-IM-Server pipeline to join
+We welcome contributions of any kind! Please make sure to read our [Contributor Documentation](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md) before submitting a Pull Request.
-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).
+ + **[Report a Bug](https://github.com/openimsdk/open-im-server/issues/new?assignees=&labels=bug&template=bug_report.md&title=)**
+ + **[Suggest a Feature](https://github.com/openimsdk/open-im-server/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)**
+ + **[Submit a Pull Request](https://github.com/openimsdk/open-im-server/pulls)**
-## :eyes: Who Are Using OpenIM
+Thank you for contributing to building a powerful instant messaging solution!
-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.
+## :closed_book: License
-## :page_facing_up: License
+OpenIMSDK is available under the Apache License 2.0. See the [LICENSE file](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) for more information.
-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!
diff --git a/README_zh_CN.md b/README_zh_CN.md
index f42031165f..65aac9ebc6 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -51,31 +51,29 @@
-## 🟢 扫描微信进群交流
-
+## :busts_in_silhouette: 加入我们的社区
++ 💬 [关注我们的 Twitter](https://twitter.com/founder_im63606)
++ 🚀 [加入我们的 Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-2hljfom5u-9ZuzP3NfEKW~BJKbpLm0Hw)
++ :eyes: [加入我们的微信群](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg)
## Ⓜ️ 关于 OpenIM
-OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通话、通知以及AI聊天机器人等通信功能的服务平台。它通过提供一系列强大的API和Webhooks,使开发者可以轻松地在他们的应用中加入这些交互特性。OpenIM 本身并不是一个独立运行的聊天应用,而是作为一个平台,为其他应用提供支持,实现丰富的通信功能。下图展示 AppServer、AppClient、OpenIMServer 和 OpenIMSDK 之间的交互关系来具体说明。
-
-
+与Telegram、Signal、Rocket.Chat等独立聊天应用不同,OpenIM提供了专为开发者设计的开源即时通讯解决方案,而不是直接安装使用的独立聊天应用。OpenIM由OpenIM SDK和OpenIM Server两大部分组成,为开发者提供了一整套集成即时通讯功能的工具和服务,包括消息发送接收、用户管理和群组管理等。总体来说,OpenIM旨在为开发者提供必要的工具和框架,帮助他们在自己的应用中实现高效的即时通讯解决方案。
![App-OpenIM 关系](./docs/images/oepnim-design.png)
-## 🚀 关于 OpenIMSDK
+## 🚀 OpenIMSDK 介绍
-**OpenIMSDK** 是为 **OpenIMServer** 设计的IM SDK,专为嵌入客户端应用而生。其主要功能及模块如下:
+**OpenIMSDK** 是为 **OpenIMServer** 设计的IM SDK,专为集成到客户端应用而生。它支持多种功能和模块:
+ 🌟 主要功能:
-
- 📦 本地存储
- 🔔 监听器回调
- 🛡️ API封装
- 🌐 连接管理
- ## 📚 主要模块:
-
++ 📚 主要模块:
1. 🚀 初始化及登录
2. 👤 用户管理
3. 👫 好友管理
@@ -86,81 +84,52 @@ OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通
👉 **[探索 GO SDK](https://github.com/openimsdk/openim-sdk-core)**
-## 🌐 关于 OpenIMServer
+## 🌐 OpenIMServer 介绍
-+ **OpenIMServer** 具有以下特点:
++ **OpenIMServer** 的特点包括:
- 🌐 微服务架构:支持集群模式,包括网关(gateway)和多个rpc服务。
- - 🚀 部署方式多样:支持源代码、kubernetes或docker部署。
- - 海量用户支持:十万超级大群,千万用户,及百亿消息
+ - 🚀 多样的部署方式:支持源代码、Kubernetes或Docker部署。
+ - 海量用户支持:支持十万级超大群组,千万级用户和百亿级消息。
### 增强的业务功能:
-+ **REST API**:OpenIMServer 提供了REST API供业务系统使用,旨在赋予业务更多功能,例如通过后台接口建立群组、发送推送消息等。
-+ **Webhooks**:OpenIMServer提供了回调能力以扩展更多的业务形态,所谓回调,即OpenIMServer会在某一事件发生之前或者之后,向业务服务器发送请求,如发送消息之前或之后的回调。
++ **REST API**:为业务系统提供REST API,增加群组创建、消息推送等后台接口功能。
+
++ **Webhooks**:通过事件前后的回调,向业务服务器发送请求,扩展更多的业务形态。
-👉 **[了解更多](https://docs.openim.io/guides/introduction/product)**
+ ![整体架构](./docs/images/architecture-layers.png)
-## :rocket: 快速开始
+
+
+## :rocket: 快速入门
在线体验iOS/Android/H5/PC/Web:
-👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)**
+👉 **[OpenIM在线演示](https://www.openim.io/en/commercial)**
-🤲 为了方便用户体验,我们提供了多种部署解决方案,您可以根据下面的列表选择自己的部署方法:
+为了便于用户体验,我们提供了多种部署解决方案,您可以根据以下列表选择适合您的部署方式:
+ **[源代码部署指南](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)**
+ **[Docker 部署指南](https://docs.openim.io/guides/gettingStarted/dockerCompose)**
-+ **[Kubernetes 部署指南](https://docs.openim.io/guides/gettingStarted/k8s-deployment)**
-
-## :hammer_and_wrench: 开始开发 OpenIM
-
-OpenIM 我们的目标是建立一个顶级的开源社区。我们有一套标准,在[社区仓库](https://github.com/OpenIMSDK/community)中。
-
-如果你想为这个 Open-IM-Server 仓库做贡献,请阅读我们的[贡献者文档](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。
-
-在开始之前,请确保你的更改是有需求的。最好的方法是创建一个[新的讨论](https://github.com/openimsdk/open-im-server/discussions/new/choose) 或 [Slack 通信](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q),或者如果你发现一个问题,首先[报告它](https://github.com/openimsdk/open-im-server/issues/new/choose)。
-+ [代码标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md)
+## 系统支持
-+ [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md)
+支持 Linux、Windows、Mac 系统以及 ARM 和 AMD CPU 架构。
-+ [目录标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md)
+## :link: 相关链接
-+ [提交标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md)
-
-+ [版本控制标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md)
-
-+ [接口标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/interface.md)
-
-+ [OpenIM配置和环境变量设置](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md)
-
-> **Note**
-> 针对中国的用户,阅读我们的 [Docker 镜像标准](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) 以便使用国内 aliyun 的镜像地址。OpenIM 也有针对中国的 gitee 同步仓库,你可以在 [gitee.com](https://gitee.com/openimsdk) 上找到它。
-
-## :link: 链接
-
- + **[完整文档](https://doc.rentsoft.cn/)**
+ + **[开发手册](https://docs.openim.io/)**
+ **[更新日志](https://github.com/openimsdk/open-im-server/blob/main/CHANGELOG.md)**
- + **[FAQ](https://github.com/openimsdk/open-im-server/blob/main/FAQ.md)**
- + **[代码示例](https://github.com/openimsdk/open-im-server/blob/main/examples)**
-
-## :handshake: 社区
-
- + **[GitHub Discussions](https://github.com/openimsdk/open-im-server/discussions)**
- + **[Slack 通信](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)**
- + **[GitHub Issues](https://github.com/openimsdk/open-im-server/issues)**
-
- 您可以加入这些平台,讨论问题,提出建议,或分享您的成功故事!
-## :writing_hand: 贡献
+## :writing_hand: 如何贡献
- 我们欢迎任何形式的贡献!请确保在提交 Pull Request 之前阅读我们的[贡献者文档](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。
+我们欢迎任何形式的贡献!在提交 Pull Request 之前,请确保阅读我们的[贡献者文档](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)
+ **[报告 Bug](https://github.com/openimsdk/open-im-server/issues/new?assignees=&labels=bug&template=bug_report.md&title=)**
+ **[提出新特性](https://github.com/openimsdk/open-im-server/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)**
+ **[提交 Pull Request](https://github.com/openimsdk/open-im-server/pulls)**
- 感谢您的贡献,我们一起打造一个强大的即时通信解决方案!
+感谢您的贡献,一起来打造强大的即时通讯解决方案!
## :closed_book: 许可证
diff --git a/bootstrap.bat b/bootstrap.bat
new file mode 100644
index 0000000000..819f19c871
--- /dev/null
+++ b/bootstrap.bat
@@ -0,0 +1,31 @@
+@echo off
+SETLOCAL
+
+mage -version >nul 2>&1
+IF %ERRORLEVEL% EQU 0 (
+ echo Mage is already installed.
+ GOTO DOWNLOAD
+)
+
+go version >nul 2>&1
+IF NOT %ERRORLEVEL% EQU 0 (
+ echo Go is not installed. Please install Go and try again.
+ exit /b 1
+)
+
+echo Installing Mage...
+go install github.com/magefile/mage@latest
+
+mage -version >nul 2>&1
+IF NOT %ERRORLEVEL% EQU 0 (
+ echo Mage installation failed.
+ echo Please ensure that %GOPATH%/bin is in your PATH.
+ exit /b 1
+)
+
+echo Mage installed successfully.
+
+:DOWNLOAD
+go mod download
+
+ENDLOCAL
diff --git a/bootstrap.sh b/bootstrap.sh
new file mode 100644
index 0000000000..f79cd1f110
--- /dev/null
+++ b/bootstrap.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+if [[ ":$PATH:" == *":$HOME/.local/bin:"* ]]; then
+ TARGET_DIR="$HOME/.local/bin"
+else
+ TARGET_DIR="/usr/local/bin"
+ echo "Using /usr/local/bin as the installation directory. Might require sudo permissions."
+fi
+
+if ! command -v mage &> /dev/null; then
+ echo "Installing Mage to $TARGET_DIR ..."
+ GOBIN=$TARGET_DIR go install github.com/magefile/mage@latest
+fi
+
+if ! command -v mage &> /dev/null; then
+ echo "Mage installation failed."
+ echo "Please ensure that $TARGET_DIR is in your \$PATH."
+ exit 1
+fi
+
+echo "Mage installed successfully."
+
+go mod download
diff --git a/build/images/openim-api/Dockerfile b/build/images/openim-api/Dockerfile
index 5832c31a61..6622239569 100644
--- a/build/images/openim-api/Dockerfile
+++ b/build/images/openim-api/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-cmdutils/Dockerfile b/build/images/openim-cmdutils/Dockerfile
index 5afbe6ecec..34bcd41f5d 100644
--- a/build/images/openim-cmdutils/Dockerfile
+++ b/build/images/openim-cmdutils/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-crontask/Dockerfile b/build/images/openim-crontask/Dockerfile
index 4019e16c12..90a5629264 100644
--- a/build/images/openim-crontask/Dockerfile
+++ b/build/images/openim-crontask/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-msggateway/Dockerfile b/build/images/openim-msggateway/Dockerfile
index c9d1ce949c..d3a8694eda 100644
--- a/build/images/openim-msggateway/Dockerfile
+++ b/build/images/openim-msggateway/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-msgtransfer/Dockerfile b/build/images/openim-msgtransfer/Dockerfile
index 08a026c350..f949786488 100644
--- a/build/images/openim-msgtransfer/Dockerfile
+++ b/build/images/openim-msgtransfer/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-push/Dockerfile b/build/images/openim-push/Dockerfile
index 43a3833c2a..faebbe9c05 100644
--- a/build/images/openim-push/Dockerfile
+++ b/build/images/openim-push/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-auth/Dockerfile b/build/images/openim-rpc-auth/Dockerfile
index 6a64c3e79e..1e905d4b2b 100644
--- a/build/images/openim-rpc-auth/Dockerfile
+++ b/build/images/openim-rpc-auth/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-conversation/Dockerfile b/build/images/openim-rpc-conversation/Dockerfile
index 421755094b..5a69aa89f1 100644
--- a/build/images/openim-rpc-conversation/Dockerfile
+++ b/build/images/openim-rpc-conversation/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-friend/Dockerfile b/build/images/openim-rpc-friend/Dockerfile
index ba3a381162..fad21a880f 100644
--- a/build/images/openim-rpc-friend/Dockerfile
+++ b/build/images/openim-rpc-friend/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-group/Dockerfile b/build/images/openim-rpc-group/Dockerfile
index 916c8e848c..d7dede6b9f 100644
--- a/build/images/openim-rpc-group/Dockerfile
+++ b/build/images/openim-rpc-group/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-msg/Dockerfile b/build/images/openim-rpc-msg/Dockerfile
index a0ac98c472..71ad85f378 100644
--- a/build/images/openim-rpc-msg/Dockerfile
+++ b/build/images/openim-rpc-msg/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-third/Dockerfile b/build/images/openim-rpc-third/Dockerfile
index 9fe17dd408..3560ad0d75 100644
--- a/build/images/openim-rpc-third/Dockerfile
+++ b/build/images/openim-rpc-third/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-rpc-user/Dockerfile b/build/images/openim-rpc-user/Dockerfile
index bdd0a42eee..a9891ece80 100644
--- a/build/images/openim-rpc-user/Dockerfile
+++ b/build/images/openim-rpc-user/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-tools/component/Dockerfile b/build/images/openim-tools/component/Dockerfile
index 1f7a1a46a5..6bdfa69422 100644
--- a/build/images/openim-tools/component/Dockerfile
+++ b/build/images/openim-tools/component/Dockerfile
@@ -19,7 +19,6 @@
FROM golang:1.20 AS builder
ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
WORKDIR /openim/openim-server
diff --git a/build/images/openim-tools/openim-web/Dockerfile b/build/images/openim-tools/openim-web/Dockerfile
deleted file mode 100644
index d145011890..0000000000
--- a/build/images/openim-tools/openim-web/Dockerfile
+++ /dev/null
@@ -1,57 +0,0 @@
-# Copyright © 2023 OpenIM. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# OpenIM base image: https://github.com/openim-sigs/openim-base-image
-
-# Set go mod installation source and proxy
-
-FROM golang:1.20 AS builder
-
-ARG GO111MODULE=on
-ARG GOPROXY=https://goproxy.io,direct
-
-WORKDIR /openim/openim-server
-
-ENV GO111MODULE=$GO111MODULE
-ENV GOPROXY=$GOPROXY
-
-RUN apt-get update && apt-get install -y curl unzip
-
-RUN curl -LO https://app-1302656840.cos.ap-nanjing.myqcloud.com/dist.zip \
- && unzip dist.zip -d ./ \
- && rm dist.zip
-
-COPY go.mod go.sum ./
-RUN go mod download
-
-COPY . .
-
-RUN make clean
-RUN make build BINS=openim-web
-
-FROM ghcr.io/openim-sigs/openim-ubuntu-image:latest
-
-WORKDIR /openim/openim-server
-
-COPY --from=builder /openim/openim-server/_output/bin/tools /openim/openim-server/_output/bin/tools/
-COPY --from=builder /openim/openim-server/dist /openim/openim-server/dist
-
-ENV PORT 11001
-ENV DISTPATH /openim/openim-server/dist
-
-EXPOSE 11001
-
-RUN mv ${OPENIM_SERVER_BINDIR}/tools/$(get_os)/$(get_arch)/openim-web /usr/bin/openim-web
-
-ENTRYPOINT ["bash", "-c", "openim-web -port $PORT -distPath $DISTPATH"]
\ No newline at end of file
diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go
index ee19a5c60d..58e540c05f 100644
--- a/cmd/openim-api/main.go
+++ b/cmd/openim-api/main.go
@@ -18,14 +18,12 @@ import (
_ "net/http/pprof"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- apiCmd := cmd.NewApiCmd()
- apiCmd.AddPortFlag()
- apiCmd.AddPrometheusPortFlag()
- if err := apiCmd.Execute(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewApiCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
+
}
diff --git a/cmd/openim-cmdutils/main.go b/cmd/openim-cmdutils/main.go
index f6b7889331..433eefb325 100644
--- a/cmd/openim-cmdutils/main.go
+++ b/cmd/openim-cmdutils/main.go
@@ -16,7 +16,7 @@ package main
import (
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
@@ -29,6 +29,8 @@ func main() {
getCmd.AddCommand(seqCmd.GetSeqCmd(), msgCmd.GetMsgCmd())
getCmd.AddSuperGroupIDFlag()
getCmd.AddUserIDFlag()
+ getCmd.AddConfigDirFlag()
+ getCmd.AddIndexFlag()
getCmd.AddBeginSeqFlag()
getCmd.AddLimitFlag()
// openIM get seq --userID=xxx
@@ -39,6 +41,8 @@ func main() {
fixCmd.AddCommand(seqCmd.FixSeqCmd())
fixCmd.AddSuperGroupIDFlag()
fixCmd.AddUserIDFlag()
+ fixCmd.AddConfigDirFlag()
+ fixCmd.AddIndexFlag()
fixCmd.AddFixAllFlag()
// openIM fix seq --userID=xxx
// openIM fix seq --superGroupID=xxx
@@ -47,6 +51,8 @@ func main() {
clearCmd.AddCommand(msgCmd.ClearMsgCmd())
clearCmd.AddSuperGroupIDFlag()
clearCmd.AddUserIDFlag()
+ clearCmd.AddConfigDirFlag()
+ clearCmd.AddIndexFlag()
clearCmd.AddClearAllFlag()
clearCmd.AddBeginSeqFlag()
clearCmd.AddLimitFlag()
@@ -55,6 +61,6 @@ func main() {
// openIM clear msg --clearAll
msgUtilsCmd.AddCommand(&getCmd.Command, &fixCmd.Command, &clearCmd.Command)
if err := msgUtilsCmd.Execute(); err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-crontask/main.go b/cmd/openim-crontask/main.go
index b52029c64b..6745065187 100644
--- a/cmd/openim-crontask/main.go
+++ b/cmd/openim-crontask/main.go
@@ -16,12 +16,11 @@ package main
import (
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- cronTaskCmd := cmd.NewCronTaskCmd()
- if err := cronTaskCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewCronTaskCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-msggateway/main.go b/cmd/openim-msggateway/main.go
index 01b13560d3..6e3eda6bf6 100644
--- a/cmd/openim-msggateway/main.go
+++ b/cmd/openim-msggateway/main.go
@@ -16,15 +16,11 @@ package main
import (
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- msgGatewayCmd := cmd.NewMsgGatewayCmd()
- msgGatewayCmd.AddWsPortFlag()
- msgGatewayCmd.AddPortFlag()
- msgGatewayCmd.AddPrometheusPortFlag()
- if err := msgGatewayCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewMsgGatewayCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-msgtransfer/main.go b/cmd/openim-msgtransfer/main.go
index 84fbbd2ea1..23b629d69a 100644
--- a/cmd/openim-msgtransfer/main.go
+++ b/cmd/openim-msgtransfer/main.go
@@ -16,14 +16,11 @@ package main
import (
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- msgTransferCmd := cmd.NewMsgTransferCmd()
- msgTransferCmd.AddPrometheusPortFlag()
- msgTransferCmd.AddTransferProgressFlag()
- if err := msgTransferCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewMsgTransferCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go
index c7d29fc970..cd5106d173 100644
--- a/cmd/openim-push/main.go
+++ b/cmd/openim-push/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/push"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- pushCmd := cmd.NewRpcCmd(cmd.RpcPushServer, push.Start)
- pushCmd.AddPortFlag()
- pushCmd.AddPrometheusPortFlag()
- if err := pushCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewPushRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go
index da281b70ec..158b1f4647 100644
--- a/cmd/openim-rpc/openim-rpc-auth/main.go
+++ b/cmd/openim-rpc/openim-rpc-auth/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/auth"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- authCmd := cmd.NewRpcCmd(cmd.RpcAuthServer, auth.Start)
- authCmd.AddPortFlag()
- authCmd.AddPrometheusPortFlag()
- if err := authCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewAuthRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go
index 6e74b32517..5b2e66c954 100644
--- a/cmd/openim-rpc/openim-rpc-conversation/main.go
+++ b/cmd/openim-rpc/openim-rpc-conversation/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- rpcCmd := cmd.NewRpcCmd(cmd.RpcConversationServer, conversation.Start)
- rpcCmd.AddPortFlag()
- rpcCmd.AddPrometheusPortFlag()
- if err := rpcCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewConversationRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go
index a307c01a14..745c405532 100644
--- a/cmd/openim-rpc/openim-rpc-friend/main.go
+++ b/cmd/openim-rpc/openim-rpc-friend/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/friend"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- rpcCmd := cmd.NewRpcCmd(cmd.RpcFriendServer, friend.Start)
- rpcCmd.AddPortFlag()
- rpcCmd.AddPrometheusPortFlag()
- if err := rpcCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewFriendRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go
index 2afb7963cc..5badf934e9 100644
--- a/cmd/openim-rpc/openim-rpc-group/main.go
+++ b/cmd/openim-rpc/openim-rpc-group/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/group"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- rpcCmd := cmd.NewRpcCmd(cmd.RpcGroupServer, group.Start)
- rpcCmd.AddPortFlag()
- rpcCmd.AddPrometheusPortFlag()
- if err := rpcCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewGroupRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go
index bbffbcae7c..37f6cf237a 100644
--- a/cmd/openim-rpc/openim-rpc-msg/main.go
+++ b/cmd/openim-rpc/openim-rpc-msg/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/msg"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- rpcCmd := cmd.NewRpcCmd(cmd.RpcMsgServer, msg.Start)
- rpcCmd.AddPortFlag()
- rpcCmd.AddPrometheusPortFlag()
- if err := rpcCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewMsgRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go
index 09a8409e6f..fcead5f897 100644
--- a/cmd/openim-rpc/openim-rpc-third/main.go
+++ b/cmd/openim-rpc/openim-rpc-third/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/third"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- rpcCmd := cmd.NewRpcCmd(cmd.RpcThirdServer, third.Start)
- rpcCmd.AddPortFlag()
- rpcCmd.AddPrometheusPortFlag()
- if err := rpcCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewThirdRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go
index 18adbfae57..75ab902914 100644
--- a/cmd/openim-rpc/openim-rpc-user/main.go
+++ b/cmd/openim-rpc/openim-rpc-user/main.go
@@ -15,16 +15,12 @@
package main
import (
- "github.com/openimsdk/open-im-server/v3/internal/rpc/user"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/system/program"
)
func main() {
- rpcCmd := cmd.NewRpcCmd(cmd.RpcUserServer, user.Start)
- rpcCmd.AddPortFlag()
- rpcCmd.AddPrometheusPortFlag()
- if err := rpcCmd.Exec(); err != nil {
- util.ExitWithError(err)
+ if err := cmd.NewUserRpcCmd().Exec(); err != nil {
+ program.ExitWithError(err)
}
}
diff --git a/config/README.md b/config/README.md
new file mode 100644
index 0000000000..048d7fe362
--- /dev/null
+++ b/config/README.md
@@ -0,0 +1,63 @@
+---
+title: 'OpenIM Configuration Files and Common Configuration Item Modifications Guide'
+
+## Configuration Files Explanation
+
+| Configuration File | Description |
+| ------------------------------- | ------------------------------------------------------------ |
+| **kafka.yml** | Configurations for Kafka username, password, address, etc. |
+| **redis.yml** | Configurations for Redis password, address, etc. |
+| **minio.yml** | Configurations for MinIO username, password, address, and external IP/domain; failing to modify external IP or domain may cause image file sending failures |
+| **zookeeper.yml** | Configurations for ZooKeeper user, password, address, etc. |
+| **mongodb.yml** | Configurations for MongoDB username, password, address, etc. |
+| **log.yml** | Configurations for log level and storage directory. |
+| **notification.yml** | Configurations for events like adding friends, creating groups, etc. |
+| **share.yml** | Common configurations needed by various OpenIM services, such as secret. |
+| **webhooks.yml** | Configurations for URLs in Webhook. |
+| **local-cache.yml** | Local cache configurations. |
+| **openim-rpc-third.yml** | Configurations for listening IP, port, and storage settings for images and videos in openim-rpc-third service. |
+| **openim-rpc-user.yml** | Configurations for listening IP and port in openim-rpc-user service. |
+| **openim-api.yml** | Configurations for listening IP, port, etc., in openim-api service. |
+| **openim-crontask.yml** | Configurations for openim-crontask service. |
+| **openim-msggateway.yml** | Configurations for listening IP, port, etc., in openim-msggateway service. |
+| **openim-msgtransfer.yml** | Configurations for openim-msgtransfer service. |
+| **openim-push.yml** | Configurations for listening IP, port, and offline push settings in openim-push service. |
+| **openim-rpc-auth.yml** | Configurations for listening IP, port, and token expiration settings in openim-rpc-auth service. |
+| **openim-rpc-conversation.yml** | Configurations for listening IP, port, etc., in openim-rpc-conversation service. |
+| **openim-rpc-friend.yml** | Configurations for listening IP, port, etc., in openim-rpc-friend service. |
+| **openim-rpc-group.yml** | Configurations for listening IP, port, etc., in openim-rpc-group service. |
+| **openim-rpc-msg.yml** | Configurations for listening IP, port, and whether to verify friendship before sending messages in openim-rpc-msg service. |
+
+## Common Configuration Item Modifications
+
+| Configuration Item Modification | Configuration File |
+| ----------------------------------------------------- | ----------------------- |
+| Using MinIO for image and video file object storage | `minio.yml` |
+| Adjusting production environment logs | `log.yml` |
+| Verifying friendship before sending messages | `openim-rpc-msg.yml` |
+| Modifying secret | `share.yml` |
+| Using OSS, COS, AWS, Kodo for image and video storage | `openim-rpc-third.yml` |
+| Setting multiple login policy | `openim-msggateway.yml` |
+| Setting up offline push | `openim-push.yml` |
+
+## Starting Multiple Instances of an OpenIM Service
+
+To start multiple instances of an OpenIM service, simply increase the corresponding port numbers and modify the `start-config.yml` file in the project root directory. Restart the service to take effect. For example, the configuration to start 2 instances of `openim-rpc-user` is as follows:
+
+```yaml
+rpc:
+ registerIP: ''
+ listenIP: 0.0.0.0
+ ports: [ 10110, 10111 ]
+
+prometheus:
+ enable: true
+ ports: [ 20100, 20101 ]
+```
+
+Modify `start-config.yml`:
+
+```yaml
+serviceBinaries:
+ openim-rpc-user: 2
+```
diff --git a/config/README_zh_CN.md b/config/README_zh_CN.md
new file mode 100644
index 0000000000..6ddab06368
--- /dev/null
+++ b/config/README_zh_CN.md
@@ -0,0 +1,65 @@
+# OpenIM配置文件说明以及常用配置修改说明
+
+## 配置文件说明
+
+| Configuration File | Description |
+| ------------------------------- | ------------------------------------------------------------ |
+| **kafka.yml** | Kafka用户名、密码、地址等配置 |
+| **redis.yml** | Redis密码、地址等配置 |
+| **minio.yml** | MinIO用户名、密码、地址及外网IP域名等配置;未修改外网IP或域名可能导致图片文件发送失败 |
+| **zookeeper.yml** | ZooKeeper用户、密码、地址等配置 |
+| **mongodb.yml** | MongoDB用户名、密码、地址等配置 |
+| **log.yml** | 日志级别及存储目录等配置 |
+| **notification.yml** | 添加好友、创建群组等事件通知配置 |
+| **share.yml** | OpenIM各服务所需的公共配置,如secret等 |
+| **webhooks.yml** | Webhook中URL等配置 |
+| **local-cache.yml** | 本地缓存配置 |
+| **openim-rpc-third.yml** | openim-rpc-third服务的监听IP、端口及图片视频对象存储配置 |
+| **openim-rpc-user.yml** | openim-rpc-user服务的监听IP、端口配置 |
+| **openim-api.yml** | openim-api服务的监听IP、端口等配置项 |
+| **openim-crontask.yml** | openim-crontask服务配置 |
+| **openim-msggateway.yml** | openim-msggateway服务的监听IP、端口等配置 |
+| **openim-msgtransfer.yml** | openim-msgtransfer服务配置 |
+| **openim-push.yml** | openim-push服务的监听IP、端口及离线推送配置 |
+| **openim-rpc-auth.yml** | openim-rpc-auth服务的监听IP、端口及token有效期等配置 |
+| **openim-rpc-conversation.yml** | openim-rpc-conversation服务的监听IP、端口等配置 |
+| **openim-rpc-friend.yml** | openim-rpc-friend服务的监听IP、端口等配置 |
+| **openim-rpc-group.yml** | openim-rpc-group服务的监听IP、端口等配置 |
+| **openim-rpc-msg.yml** | openim-rpc-msg服务的监听IP、端口及消息发送是否验证好友关系等配置 |
+
+## 常用配置修改
+
+| 修改配置项 | 配置文件 |
+| ----------------------------------------------- | ----------------------- |
+| 使用minio作为图片视频文件对象存储 | `minio.yml` |
+| 生产环境日志调整 | `log.yml` |
+| 发送消息是否验证好友关系 | `openim-rpc-msg.yml` |
+| 修改secret | `share.yml` |
+| 使用oss, cos, aws, kodo作为图片视频文件对象存储 | `openim-rpc-third.yml` |
+| 设置多端互踢策略 | `openim-msggateway.yml` |
+| 设置离线推送 | `openim-push.yml` |
+
+## 启动某个OpenIM服务的多个实例
+
+若要启动某个OpenIM的多个实例,只需增加对应的端口数,并修改项目根目录下的`start-config.yml`文件,重启服务即可生效。例如,启动2个`openim-rpc-user`实例的配置如下:
+
+```yaml
+rpc:
+ registerIP: ''
+ listenIP: 0.0.0.0
+ ports: [ 10110, 10111 ]
+
+prometheus:
+ enable: true
+ ports: [ 20100, 20101 ]
+```
+
+修改`start-config.yml`:
+
+```yaml
+serviceBinaries:
+ openim-rpc-user: 2
+```
+
+
+
diff --git a/config/Readme.md b/config/Readme.md
deleted file mode 100644
index 72f4577a54..0000000000
--- a/config/Readme.md
+++ /dev/null
@@ -1,243 +0,0 @@
-# OpenIM Configuration Guide
-
-
-* 1. [Directory Structure and File Descriptions](#DirectoryStructureandFileDescriptions)
- * 1.1. [Directory Structure](#DirectoryStructure)
- * 1.2. [Directory Structure Explanation](#DirectoryStructureExplanation)
-* 2. [File Descriptions](#FileDescriptions)
- * 2.1. [Files in the Root Directory](#FilesintheRootDirectory)
- * 2.2. [Files in the `templates/` Directory](#FilesinthetemplatesDirectory)
-* 3. [Configuration File Generation](#ConfigurationFileGeneration)
- * 3.1. [How to Use `init-config.sh` Script](#HowtoUseinit-config.shScript)
- * 3.2. [Examples of Operations](#ExamplesofOperations)
- * 3.3. [Points to Note](#PointstoNote)
-* 4. [Example Directory](#ExampleDirectory)
- * 4.1. [Overview](#Overview)
- * 4.2. [Structure](#Structure)
- * 4.3. [How to Use These Examples](#HowtoUseTheseExamples)
- * 4.4. [Tips for Using Example Files:](#TipsforUsingExampleFiles:)
-* 5. [Configuration Item Descriptions](#ConfigurationItemDescriptions)
-* 6. [Version Management and Upgrading](#VersionManagementandUpgrading)
- * 6.1. [Pulling the Latest Code](#PullingtheLatestCode)
- * 6.2. [Generating the Latest Example Configuration Files](#GeneratingtheLatestExampleConfigurationFiles)
- * 6.3. [Comparing Configuration File Differences](#ComparingConfigurationFileDifferences)
- * 6.4. [Updating Configuration Files](#UpdatingConfigurationFiles)
- * 6.5. [Updating Binary Files and Restarting Services](#UpdatingBinaryFilesandRestartingServices)
- * 6.6. [Best Practices for Version Management](#BestPracticesforVersionManagement)
-* 7. [How to Contribute](#HowtoContribute)
- * 7.1. [OpenIM Configuration Item Descriptions](#OpenIMConfigurationItemDescriptions)
- * 7.2. [Modifying Template Files](#ModifyingTemplateFiles)
- * 7.3. [Updating Configuration Center Scripts](#UpdatingConfigurationCenterScripts)
- * 7.4. [Configuration File Generation Process](#ConfigurationFileGenerationProcess)
- * 7.5. [Contribution Guidelines](#ContributionGuidelines)
- * 7.6. [Submission and Review](#SubmissionandReview)
-
-
-
-
-
-## 1. Directory Structure and File Descriptions
-
-This document details the structure of the `config` directory, aiding users in understanding and managing configuration files.
-
-### 1.1. Directory Structure
-
-```bash
-$ tree config
-├── alertmanager.yml
-├── config.yaml
-├── email.tmpl
-├── instance-down-rules.yml
-├── notification.yaml
-├── prometheus.yml
-├── Readme.md
-└── templates
- ├── alertmanager.yml.template
- ├── config.yaml.template
- ├── email.tmpl.template
- ├── env.template
- ├── instance-down-rules.yml.template
- ├── notification.yaml.template
- ├── open-im-ng-example.conf
- ├── prometheus-dashboard.yaml
- └── prometheus.yml.template
-```
-
-### 1.2. Directory Structure Explanation
-
-- **Root Directory (`config/`)**: Contains actual configuration files and the `templates` subdirectory.
-- **`templates/` Subdirectory**: Stores configuration templates for generating or updating configuration files in the root directory.
-
-## 2. File Descriptions
-
-### 2.1. Files in the Root Directory
-
-- **`alertmanager.yml`**: Configuration file for AlertManager, managing and setting up the alert system.
-- **`config.yaml`**: The main application configuration file, covering service settings.
-- **`email.tmpl`**: Template file for email notifications, defining email format and content.
-- **`instance-down-rules.yml`**: Instance downtime rules configuration file for the monitoring system.
-- **`notification.yaml`**: Configuration file for notification settings, defining different types of notifications.
-- **`prometheus.yml`**: Configuration file for the Prometheus monitoring system, setting monitoring metrics and rules.
-
-### 2.2. Files in the `templates/` Directory
-
-- **`alertmanager.yml.template`**: Template for AlertManager configuration.
-- **`config.yaml.template`**: Main configuration template for the application.
-- **`email.tmpl.template`**: Template for email notifications.
-- **`env.template`**: Template for environmental variable configurations, setting environment-related configurations.
-- **`instance-down-rules.yml.template`**: Template for instance downtime rules.
-- **`notification.yaml.template`**: Template for notification settings.
-- **`open-im-ng-example.conf`**: Example configuration file for the application.
-- **`prometheus-dashboard.yaml`**: Prometheus dashboard configuration file, specific to the OpenIM application.
-- **`prometheus.yml.template`**: Template for Prometheus configuration.
-
-## 3. Configuration File Generation
-
-Configuration files can be automatically generated using the `make init` command or the `./scripts/init-config.sh` script. These scripts conveniently extract templates from the `templates` directory and generate or update actual configuration files in the root directory.
-
-### 3.1. How to Use `init-config.sh` Script
-
-```bash
-$ ./scripts/init-config.sh --help
-Usage: init-config.sh [options]
-Options:
- -h, --help Show this help message
- --force Overwrite existing files without prompt
- --skip Skip generation if file exists
- --examples Generate example files
- --clean-config Clean all configuration files
- --clean-examples Clean all example files
-```
-
-### 3.2. Examples of Operations
-
-- Generate all template configuration files:
-
- ```bash
- $ ./scripts/init-config.sh --examples
- ```
-
-- Force overwrite existing configuration files:
-
- ```bash
- $ ./scripts/init-config.sh --force
- ```
-
-### 3.3. Points to Note
-
-- **Template files should not be directly modified**: Files in the `template` directory are templates included in source code management. Direct modification may lead to version conflicts or management issues.
-- **Operations for Windows Users**: Windows users can use the `cp` command to copy files from the `template` directory to the `config/` directory and then modify the configuration items as needed.
-
-## 4. Example Directory
-
-Welcome to our project's `examples` directory! This directory contains a range of example files, showcasing various configurations and settings of our software. These examples are intended to provide you with templates that can serve as a starting point for your own configurations.
-
-### 4.1. Overview
-
-In this directory, you'll find examples suitable for a variety of use cases. Each file is a template with default values and configurations, demonstrating best practices and typical scenarios. Whether you're just getting started or looking to implement complex settings, these examples should help you get on the right track.
-
-### 4.2. Structure
-
-Here's a quick overview of the contents in this directory:
-
-- `env-example.yaml`: Demonstrates how to set up environmental variables.
-- `openim-example.yaml`: Example configuration file for the OpenIM application.
-- `prometheus-example.yml`: Example configuration for monitoring with Prometheus.
-- `alertmanager-example.yml`: Template for setting up Alertmanager configuration.
-
-### 4.3. How to Use These Examples
-
-To use these examples, simply copy the relevant files to your working directory and rename them as needed (for example, removing the `-example` suffix). Then, modify the files according to your needs.
-
-### 4.4. Tips for Using Example Files:
-
-1. **Read Comments**: Each file contains comments explaining the various sections and settings. Make sure to read these comments for a better understanding of how to customize the file.
-2. **Check Required Changes**: Some examples might require mandatory changes before they can be used effectively (such as setting specific environmental variables).
-3. **Version Compatibility**: Ensure that the example files are compatible with the version of the software you are using.
-
-## 5. Configuration Item Descriptions
-
-## 6. Version Management and Upgrading
-
-When managing and upgrading the `config` directory's versions, it is crucial to ensure that the configuration files in both the local `config/` and `config/templates/` directories are kept in sync. This process can ensure that your configuration files are consistent with the latest standard templates, while also maintaining custom settings.
-
-### 6.1. Pulling the Latest Code
-
-First, ensure that your local repository is in sync with the remote repository. This can be achieved by pulling the latest code:
-
-```bash
-$ git pull
-```
-
-### 6.2. Generating the Latest Example Configuration Files
-
-Next, generate the latest example configuration files. This can be done by running the `init-config.sh` script, using the `--examples` option to generate example files, and the `--skip` option to avoid overwriting existing configuration files:
-
-```bash
-$ ./scripts/init-config.sh --examples --skip
-```
-
-### 6.3. Comparing Configuration File Differences
-
-Once the latest example configuration files are generated, you need to compare the configuration files in the `config/` and `config/templates/` directories to find any potential differences. This step ensures that you can identify and integrate any important updates or changes. Tools like `diff` can be helpful in completing this step:
-
-```bash
-$ diff -ur config/ config/templates/
-```
-
-### 6.4. Updating Configuration Files
-
-Based on the comparison results, manually update the configuration files in the `config/` directory to reflect the latest configurations in `config/templates/`. During this process, ensure to retain any custom configuration settings.
-
-### 6.5. Updating Binary Files and Restarting Services
-
-After updating the configuration files, the next step is to update any related binary files. This typically involves downloading and installing the latest version of the application or service. Depending on the specific application or service, this might involve running specific update scripts or directly downloading the latest version from official sources.
-
-Once the binary files are updated, the services need to be restarted to apply the new configurations. Make sure to conduct necessary checks before restarting to ensure the correctness of the configurations.
-
-### 6.6. Best Practices for Version Management
-
-- **Record Changes**: When committing changes to a version control system, ensure to log detailed change logs.
-- **Stay Synced**: Regularly sync with the remote repository to ensure that your local configurations are in line with the latest developments.
-- **Backup**: Backup your current configurations before making any significant changes, so that you can revert to a previous state if necessary.
-
-By following these steps and best practices, you can ensure effective management and smooth upgrading of your `config` directory.
-
-## 7. How to Contribute
-
-If you have an understanding of the logic behind OpenIM's configuration generation, then you will clearly know where to make modifications to contribute code.
-
-### 7.1. OpenIM Configuration Item Descriptions
-
-First, it is recommended to read the [OpenIM Configuration Items Document](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/environment.md). This will help you understand the roles of various configuration items and how they affect the operation of OpenIM.
-
-### 7.2. Modifying Template Files
-
-To contribute to OpenIM, focus on the `./deployments/templates` directory. This contains various configuration template files, which are the basis for generating the final configuration files.
-
-When making modifications, ensure that your changes align with OpenIM's configuration requirements and logic. This may involve adding new template files or modifying existing files to reflect new configuration options or structural changes.
-
-### 7.3. Updating Configuration Center Scripts
-
-In addition to modifying template files, pay attention to the `./scripts/install/environment.sh` script. In this script, you may need to add or modify environment variables.
-
-This script is responsible for defining environment variables that influence configuration generation. Therefore, any new configuration items or modifications to existing items need to be reflected here.
-
-### 7.4. Configuration File Generation Process
-
-The essence of the `make init` command is to use the environment variables defined in `/scripts/install/environment.sh` to render the template files in the `./deployments/templates` directory, thereby generating the final configuration files.
-
-When contributing code, ensure that your changes work smoothly in this process and do not cause errors during configuration file generation.
-
-### 7.5. Contribution Guidelines
-
-- **Code Review**: Ensure your changes have passed code review. This typically means that the code should be clear, easy to understand, and adhere to the project's coding style and best practices.
-- **Testing**: Before submitting changes, conduct thorough tests to ensure new or modified configurations work as expected and do not negatively impact existing functionalities.
-- **Documentation**: If you have added a new configuration option or made significant changes to an existing one, update the relevant documentation to assist other users and developers in understanding and utilizing these changes.
-
-### 7.6. Submission and Review
-
-After completing your changes, submit your code to the OpenIM repository in the form of a Pull Request (PR). The PR will be reviewed by the project maintainers and you may be asked to make further modifications or provide additional information.
\ No newline at end of file
diff --git a/config/kafka.yml b/config/kafka.yml
new file mode 100644
index 0000000000..d412e1be06
--- /dev/null
+++ b/config/kafka.yml
@@ -0,0 +1,36 @@
+# Username for authentication
+username: ''
+# Password for authentication
+password: ''
+# Producer acknowledgment settings
+producerAck: ""
+# Compression type to use (e.g., none, gzip, snappy)
+compressType: "none"
+# List of Kafka broker addresses
+address: [ localhost:19094 ]
+# Kafka topic for Redis integration
+toRedisTopic: "toRedis"
+# Kafka topic for MongoDB integration
+toMongoTopic: "toMongo"
+# Kafka topic for push notifications
+toPushTopic: "toPush"
+# Consumer group ID for Redis topic
+toRedisGroupID: redis
+# Consumer group ID for MongoDB topic
+toMongoGroupID: mongo
+# Consumer group ID for push notifications topic
+toPushGroupID: push
+# TLS (Transport Layer Security) configuration
+tls:
+ # Enable or disable TLS
+ enableTLS: false
+ # CA certificate file path
+ caCrt: ""
+ # Client certificate file path
+ clientCrt: ""
+ # Client key file path
+ clientKey: ""
+ # Client key password
+ clientKeyPwd: ""
+ # Whether to skip TLS verification (not recommended for production)
+ insecureSkipVerify: false
diff --git a/config/local-cache.yml b/config/local-cache.yml
new file mode 100644
index 0000000000..06e211ebb8
--- /dev/null
+++ b/config/local-cache.yml
@@ -0,0 +1,27 @@
+user:
+ topic: DELETE_CACHE_USER
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
+
+group:
+ topic: DELETE_CACHE_GROUP
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
+
+friend:
+ topic: DELETE_CACHE_FRIEND
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
+
+conversation:
+ topic: DELETE_CACHE_CONVERSATION
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
diff --git a/config/log.yml b/config/log.yml
new file mode 100644
index 0000000000..2194d89174
--- /dev/null
+++ b/config/log.yml
@@ -0,0 +1,13 @@
+# Log storage path, default is acceptable, change to a full path if modification is needed
+storageLocation: ../../../../logs/
+# Log rotation period (in hours), default is acceptable
+rotationTime: 24
+# Number of log files to retain, default is acceptable
+remainRotationCount: 2
+# Log level settings: 3 for production environment; 6 for more verbose logging in debugging environments
+remainLogLevel: 6
+# Whether to output to standard output, default is acceptable
+isStdout: false
+# Whether to log in JSON format, default is acceptable
+isJson: false
+
diff --git a/config/minio.yml b/config/minio.yml
new file mode 100644
index 0000000000..11a9ace354
--- /dev/null
+++ b/config/minio.yml
@@ -0,0 +1,16 @@
+# Name of the bucket in MinIO
+bucket: "openim"
+# Access key ID for MinIO authentication
+accessKeyID: "root"
+# Secret access key for MinIO authentication
+secretAccessKey: "openIM123"
+# Session token for MinIO authentication (optional)
+sessionToken: ''
+# Internal address of the MinIO server
+internalAddress: "localhost:10005"
+# External address of the MinIO server, accessible from outside. Supports both HTTP and HTTPS using a domain name
+externalAddress: "http://external_ip:10005"
+# Flag to enable or disable public read access to the bucket
+publicRead: false
+
+
diff --git a/config/mongodb.yml b/config/mongodb.yml
new file mode 100644
index 0000000000..98f5694e45
--- /dev/null
+++ b/config/mongodb.yml
@@ -0,0 +1,14 @@
+# URI for database connection, leave empty if using address and credential settings directly
+uri: ''
+# List of MongoDB server addresses
+address: [ localhost:37017 ]
+# Name of the database
+database: openim_v3
+# Username for database authentication
+username: openIM
+# Password for database authentication
+password: openIM123
+# Maximum number of connections in the connection pool
+maxPoolSize: 100
+# Maximum number of retry attempts for a failed database connection
+maxRetry: 10
diff --git a/config/notification.yml b/config/notification.yml
new file mode 100644
index 0000000000..278376c244
--- /dev/null
+++ b/config/notification.yml
@@ -0,0 +1,345 @@
+# 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.
+
+# Determines if a message should be sent. If set to false, it triggers a silent sync without a message. If true, it requires triggering a conversation.
+# For rpc notification, send twice: once as a message and once as a notification.
+# The options field 'isNotification' indicates if it's a notification.
+groupCreated:
+ isSendMsg: true
+# Reliability level of the message sending.
+# Set to 1 to send only when online, 2 for guaranteed delivery.
+ reliabilityLevel: 1
+# This setting is effective only when 'isSendMsg' is true.
+# It controls whether to count unread messages.
+ unreadCount: false
+# Configuration for offline push notifications.
+ offlinePush:
+ # Enables or disables offline push notifications.
+ enable: false
+ # Title for the notification when a group is created.
+ title: "create group title"
+ # Description for the notification.
+ desc: "create group desc"
+ # Additional information for the notification.
+ ext: "create group ext"
+
+groupInfoSet:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupInfoSet title"
+ desc: "groupInfoSet desc"
+ ext: "groupInfoSet ext"
+
+
+joinGroupApplication:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "joinGroupApplication title"
+ desc: "joinGroupApplication desc"
+ ext: "joinGroupApplication ext"
+
+memberQuit:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "memberQuit title"
+ desc: "memberQuit desc"
+ ext: "memberQuit ext"
+
+groupApplicationAccepted:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupApplicationAccepted title"
+ desc: "groupApplicationAccepted desc"
+ ext: "groupApplicationAccepted ext"
+
+groupApplicationRejected:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupApplicationRejected title"
+ desc: "groupApplicationRejected desc"
+ ext: "groupApplicationRejected ext"
+
+
+groupOwnerTransferred:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupOwnerTransferred title"
+ desc: "groupOwnerTransferred desc"
+ ext: "groupOwnerTransferred ext"
+
+memberKicked:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "memberKicked title"
+ desc: "memberKicked desc"
+ ext: "memberKicked ext"
+
+memberInvited:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "memberInvited title"
+ desc: "memberInvited desc"
+ ext: "memberInvited ext"
+
+memberEnter:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "memberEnter title"
+ desc: "memberEnter desc"
+ ext: "memberEnter ext"
+
+groupDismissed:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupDismissed title"
+ desc: "groupDismissed desc"
+ ext: "groupDismissed ext"
+
+groupMuted:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupMuted title"
+ desc: "groupMuted desc"
+ ext: "groupMuted ext"
+
+groupCancelMuted:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupCancelMuted title"
+ desc: "groupCancelMuted desc"
+ ext: "groupCancelMuted ext"
+ defaultTips:
+ tips: "group Cancel Muted"
+
+
+groupMemberMuted:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupMemberMuted title"
+ desc: "groupMemberMuted desc"
+ ext: "groupMemberMuted ext"
+
+groupMemberCancelMuted:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupMemberCancelMuted title"
+ desc: "groupMemberCancelMuted desc"
+ ext: "groupMemberCancelMuted ext"
+
+groupMemberInfoSet:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupMemberInfoSet title"
+ desc: "groupMemberInfoSet desc"
+ ext: "groupMemberInfoSet ext"
+
+groupInfoSetAnnouncement:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupInfoSetAnnouncement title"
+ desc: "groupInfoSetAnnouncement desc"
+ ext: "groupInfoSetAnnouncement ext"
+
+
+groupInfoSetName:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "groupInfoSetName title"
+ desc: "groupInfoSetName desc"
+ ext: "groupInfoSetName ext"
+
+
+#############################friend#################################
+friendApplicationAdded:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "Somebody applies to add you as a friend"
+ desc: "Somebody applies to add you as a friend"
+ ext: "Somebody applies to add you as a friend"
+
+friendApplicationApproved:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "Someone applies to add your friend application"
+ desc: "Someone applies to add your friend application"
+ ext: "Someone applies to add your friend application"
+
+friendApplicationRejected:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "Someone rejected your friend application"
+ desc: "Someone rejected your friend application"
+ ext: "Someone rejected your friend application"
+
+friendAdded:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "We have become friends"
+ desc: "We have become friends"
+ ext: "We have become friends"
+
+friendDeleted:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "deleted a friend"
+ desc: "deleted a friend"
+ ext: "deleted a friend"
+
+friendRemarkSet:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "Your friend's profile has been changed"
+ desc: "Your friend's profile has been changed"
+ ext: "Your friend's profile has been changed"
+
+blackAdded:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "blocked a user"
+ desc: "blocked a user"
+ ext: "blocked a user"
+
+blackDeleted:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "Remove a blocked user"
+ desc: "Remove a blocked user"
+ ext: "Remove a blocked user"
+
+friendInfoUpdated:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "friend info updated"
+ desc: "friend info updated"
+ ext: "friend info updated"
+
+#####################user#########################
+userInfoUpdated:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "Remove a blocked user"
+ desc: "Remove a blocked user"
+ ext: "Remove a blocked user"
+
+userStatusChanged:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: false
+ title: "user status changed"
+ desc: "user status changed"
+ ext: "user status changed"
+
+#####################conversation#########################
+conversationChanged:
+ isSendMsg: false
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "conversation changed"
+ desc: "conversation changed"
+ ext: "conversation changed"
+
+conversationSetPrivate:
+ isSendMsg: true
+ reliabilityLevel: 1
+ unreadCount: false
+ offlinePush:
+ enable: true
+ title: "burn after reading"
+ desc: "burn after reading"
+ ext: "burn after reading"
diff --git a/config/openim-api.yml b/config/openim-api.yml
new file mode 100644
index 0000000000..78a688fcd6
--- /dev/null
+++ b/config/openim-api.yml
@@ -0,0 +1,13 @@
+api:
+ # Listening IP; 0.0.0.0 means both internal and external IPs are listened to, default is recommended
+ listenIP: 0.0.0.0
+ # Listening ports; if multiple are configured, multiple instances will be launched, must be consistent with the number of prometheus.ports
+ ports: [ 10002 ]
+
+prometheus:
+ # Whether to enable prometheus
+ enable: true
+ # Prometheus listening ports, must match the number of api.ports
+ ports: [ 20113 ]
+ # This address can be accessed via a browser
+ grafanaURL: http://127.0.0.1:13000/
diff --git a/config/openim-crontask.yml b/config/openim-crontask.yml
new file mode 100644
index 0000000000..9bbccfd252
--- /dev/null
+++ b/config/openim-crontask.yml
@@ -0,0 +1,2 @@
+chatRecordsClearTime: "0 2 * * *"
+retainChatRecords: 365
diff --git a/config/openim-msggateway.yml b/config/openim-msggateway.yml
new file mode 100644
index 0000000000..0c92d83278
--- /dev/null
+++ b/config/openim-msggateway.yml
@@ -0,0 +1,30 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10140 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20112 ]
+
+# IP address that the RPC/WebSocket service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+listenIP: 0.0.0.0
+
+longConnSvr:
+ # WebSocket listening ports, must match the number of rpc.ports
+ ports: [ 10001 ]
+ # Maximum number of WebSocket connections
+ websocketMaxConnNum: 100000
+ # Maximum length of the entire WebSocket message packet
+ websocketMaxMsgLen: 4096
+ # WebSocket connection handshake timeout in seconds
+ websocketTimeout: 10
+
+# 1: For Android, iOS, Windows, Mac, and web platforms, only one instance can be online at a time
+multiLoginPolicy: 1
+
+
+
diff --git a/config/openim-msgtransfer.yml b/config/openim-msgtransfer.yml
new file mode 100644
index 0000000000..07a7dc1ab1
--- /dev/null
+++ b/config/openim-msgtransfer.yml
@@ -0,0 +1,6 @@
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; each port corresponds to an instance of monitoring. Ensure these are managed accordingly
+ # Because four instances have been launched, four ports need to be specified
+ ports: [ 20108, 20109, 20110, 20111 ]
diff --git a/config/openim-push.yml b/config/openim-push.yml
new file mode 100644
index 0000000000..a1abfcf889
--- /dev/null
+++ b/config/openim-push.yml
@@ -0,0 +1,44 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10170 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20107 ]
+
+maxConcurrentWorkers: 3
+#"Use geTui for offline push notifications, or choose fcm or jpns; corresponding configuration settings must be specified."
+enable: "geTui"
+geTui:
+ pushUrl: "https://restapi.getui.com/v2/$appId"
+ masterSecret: ''
+ appKey: ''
+ intent: ''
+ channelID: ''
+ channelName: ''
+fcm:
+ serviceAccount: "x.json"
+jpns:
+ appKey: ''
+ masterSecret: ''
+ pushURL: ''
+ pushIntent: ''
+
+# iOS system push sound and badge count
+iosPush:
+ pushSound: "xxx"
+ badgeCount: true
+ production: false
+
+
+
+
+
+
+
diff --git a/config/openim-rpc-auth.yml b/config/openim-rpc-auth.yml
new file mode 100644
index 0000000000..2d861cd5ab
--- /dev/null
+++ b/config/openim-rpc-auth.yml
@@ -0,0 +1,18 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10160 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20106 ]
+
+tokenPolicy:
+ # Token validity period, in days
+ expire: 90
+
diff --git a/config/openim-rpc-conversation.yml b/config/openim-rpc-conversation.yml
new file mode 100644
index 0000000000..a094bfac10
--- /dev/null
+++ b/config/openim-rpc-conversation.yml
@@ -0,0 +1,13 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10180 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20105 ]
diff --git a/config/openim-rpc-friend.yml b/config/openim-rpc-friend.yml
new file mode 100644
index 0000000000..7b829f971c
--- /dev/null
+++ b/config/openim-rpc-friend.yml
@@ -0,0 +1,13 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10120 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20104 ]
diff --git a/config/openim-rpc-group.yml b/config/openim-rpc-group.yml
new file mode 100644
index 0000000000..78b44030e0
--- /dev/null
+++ b/config/openim-rpc-group.yml
@@ -0,0 +1,13 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10150 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20103 ]
diff --git a/config/openim-rpc-msg.yml b/config/openim-rpc-msg.yml
new file mode 100644
index 0000000000..17ce26e9b2
--- /dev/null
+++ b/config/openim-rpc-msg.yml
@@ -0,0 +1,20 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10130 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20102 ]
+
+
+# Does sending messages require friend verification
+friendVerify: false
+
+
+
diff --git a/config/openim-rpc-third.yml b/config/openim-rpc-third.yml
new file mode 100644
index 0000000000..bb41c93aee
--- /dev/null
+++ b/config/openim-rpc-third.yml
@@ -0,0 +1,47 @@
+rpc:
+ # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP
+ registerIP: ''
+ # IP address that the RPC service listens on; setting to 0.0.0.0 listens on both internal and external IPs. If left blank, it automatically uses the internal network IP
+ listenIP: 0.0.0.0
+ # List of ports that the RPC service listens on; configuring multiple ports will launch multiple instances. These must match the number of configured prometheus ports
+ ports: [ 10190 ]
+
+prometheus:
+ # Enable or disable Prometheus monitoring
+ enable: true
+ # List of ports that Prometheus listens on; these must match the number of rpc.ports to ensure correct monitoring setup
+ ports: [ 20101 ]
+
+
+object:
+ # Use MinIO as object storage, or set to "cos", "oss", "kodo", "aws", while also configuring the corresponding settings
+ enable: "minio"
+ cos:
+ bucketURL: https://temp-1252357374.cos.ap-chengdu.myqcloud.com
+ secretID: ''
+ secretKey: ''
+ sessionToken: ''
+ publicRead: false
+ oss:
+ endpoint: "https://oss-cn-chengdu.aliyuncs.com"
+ bucket: "demo-9999999"
+ bucketURL: "https://demo-9999999.oss-cn-chengdu.aliyuncs.com"
+ accessKeyID: ''
+ accessKeySecret: ''
+ sessionToken: ''
+ publicRead: false
+ kodo:
+ endpoint: "webhook://s3.cn-east-1.qiniucs.com"
+ bucket: "demo-9999999"
+ bucketURL: "webhook://your.domain.com"
+ accessKeyID: ''
+ accessKeySecret: ''
+ sessionToken: ''
+ publicRead: false
+ aws:
+ endpoint: "''"
+ region: "us-east-1"
+ bucket: "demo-9999999"
+ accessKeyID: ''
+ accessKeySecret: ''
+ publicRead: false
diff --git a/config/openim-rpc-user.yml b/config/openim-rpc-user.yml
new file mode 100644
index 0000000000..cbfb55b6c7
--- /dev/null
+++ b/config/openim-rpc-user.yml
@@ -0,0 +1,17 @@
+rpc:
+ # API or other RPCs can access this RPC through this IP; if left blank, the internal network IP is obtained by default
+ registerIP: ''
+ # Listening IP; 0.0.0.0 means both internal and external IPs are listened to, if blank, the internal network IP is automatically obtained by default
+ listenIP: 0.0.0.0
+ # Listening ports; if multiple are configured, multiple instances will be launched, and must be consistent with the number of prometheus.ports
+ ports: [ 10110 ]
+
+prometheus:
+ # Whether to enable prometheus
+ enable: true
+ # Prometheus listening ports, must be consistent with the number of rpc.ports
+ ports: [ 20100 ]
+
+
+
+
diff --git a/config/redis.yml b/config/redis.yml
new file mode 100644
index 0000000000..26becd887d
--- /dev/null
+++ b/config/redis.yml
@@ -0,0 +1,7 @@
+address: [ localhost:16379 ]
+username: ''
+password: openIM123
+enablePipeline: false
+clusterMode: false
+db: 0
+maxRetry: 10
\ No newline at end of file
diff --git a/config/share.yml b/config/share.yml
new file mode 100644
index 0000000000..2abbb77a0f
--- /dev/null
+++ b/config/share.yml
@@ -0,0 +1,15 @@
+secret: openIM123
+env: zookeeper
+rpcRegisterName:
+ user: user
+ friend: friend
+ msg: msg
+ push: push
+ messageGateway: messageGateway
+ group: group
+ auth: auth
+ conversation: conversation
+ third: third
+
+imAdminUserID: [ "imAdmin" ]
+
diff --git a/config/templates/config.yaml.template b/config/templates/config.yaml.template
index 03413c595c..5c5cfda787 100644
--- a/config/templates/config.yaml.template
+++ b/config/templates/config.yaml.template
@@ -1,24 +1,3 @@
-# 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.
-
-# -----------------------------------------------------------------
-# TODO: This config file is the template file
-# --| source: deployments/templates/config.yaml
-# --| env: scripts/install/environment
-# --| target: config/config.yaml
-# -----------------------------------------------------------------
-
envs:
discovery: zookeeper
@@ -122,14 +101,14 @@ api:
# minio.signEndpoint is minio public network address
object:
enable: "minio"
- apiURL: "http://172.28.0.1:10002"
+ apiURL: "http://127.0.0.1:10002"
minio:
bucket: "openim"
endpoint: "http://172.28.0.1:10005"
accessKeyID: "root"
secretAccessKey: "openIM123"
sessionToken: ''
- signEndpoint: "http://172.28.0.1:10005"
+ signEndpoint: "http://127.0.0.1:10005"
publicRead: false
cos:
bucketURL: https://temp-1252357374.cos.ap-chengdu.myqcloud.com
@@ -154,8 +133,8 @@ object:
sessionToken: ''
publicRead: false
aws:
- endpoint: ""
- region: ""
+ endpoint: "''" # This might not be necessary unless you're using a custom endpoint
+ region: "us-east-1"
bucket: "demo-9999999"
accessKeyID: ''
accessKeySecret: ''
@@ -200,7 +179,7 @@ rpcRegisterName:
# Whether to output in json format
# Whether to include stack trace in logs
log:
- storageLocation: /workspaces/open-im-server/logs/
+ storageLocation: /data/workspaces/open-im-server/_output/logs/
rotationTime: 24
remainRotationCount: 2
remainLogLevel: 6
@@ -250,9 +229,10 @@ push:
#
# Built-in app manager user IDs
# Built-in app manager nicknames
+# Attention, this configure is discarded. If you have used him before, configure your own
manager:
- userID: [ "openIM123456", "openIM654321", "openIMAdmin" ]
- nickname: [ "system1", "system2", "system3" ]
+ userID:
+ nickname:
# chatAdmin, use for send notification
#
@@ -336,7 +316,7 @@ callback:
timeout: 5
failedContinue: true
afterSendSingleMsg:
- enable: true
+ enable: false
timeout: 5
failedContinue: true
beforeSendGroupMsg:
@@ -521,7 +501,7 @@ callback:
# The number of ports needs to be consistent with msg_transfer_service_num in script/path_info.sh
prometheus:
enable: true
- grafanaUrl: http://172.28.0.1:13000/
+ grafanaUrl: http://127.0.0.1:13000/
apiPrometheusPort: [20100]
userPrometheusPort: [ 20110 ]
friendPrometheusPort: [ 20120 ]
@@ -534,3 +514,39 @@ prometheus:
rtcPrometheusPort: [ 21300 ]
thirdPrometheusPort: [ 21301 ]
messageTransferPrometheusPort: [ 21400, 21401, 21402, 21403 ] # List of ports
+
+###################### LocalCache configuration information ######################
+# topic: redis subscriber channel
+# slotNum: number of slots, multiple slots can prevent too many keys from competing for a lock
+# slotSize: number of slots, the number of cached keys per slot, the overall cache quantity is slotNum * slotSize
+# successExpire: successful cache time seconds
+# failedExpire: failed cache time seconds
+# disable local caching and annotate topic, slotNum, and slotSize
+localCache:
+ user:
+ topic: DELETE_CACHE_USER
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
+
+ group:
+ topic: DELETE_CACHE_GROUP
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
+
+ friend:
+ topic: DELETE_CACHE_FRIEND
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
+
+ conversation:
+ topic: DELETE_CACHE_CONVERSATION
+ slotNum: 100
+ slotSize: 2000
+ successExpire: 300
+ failedExpire: 5
diff --git a/config/templates/env.template b/config/templates/env.template
index 1441f8af9f..5a232b2aef 100644
--- a/config/templates/env.template
+++ b/config/templates/env.template
@@ -25,7 +25,7 @@
# Data storage directory for persistent data.
# Example: DATA_DIR=/path/to/data
-DATA_DIR=/workspaces/open-im-server
+DATA_DIR=/data/workspaces/open-im-server
# Docker image registry. Uncomment the preferred one.
# Options: ghcr.io/openimsdk, openim, registry.cn-hangzhou.aliyuncs.com/openimsdk
@@ -91,7 +91,7 @@ ALERT_MANAGER_NETWORK_ADDRESS=172.28.0.14
# ==============================================================================
# Local IP address of the service. Modify if necessary.
# Example: OPENIM_IP=172.28.0.1,
-OPENIM_IP=172.28.0.1
+OPENIM_IP=127.0.0.1
# ----- ZooKeeper Configuration -----
# Port for ZooKeeper service.
@@ -100,7 +100,7 @@ ZOOKEEPER_PORT=12181
# MongoDB service port configuration.
# Default: MONGO_PORT=37017
-# MONGO_PORT=37017
+MONGO_PORT=37017
# Password for MongoDB admin user. Used for service authentication.
# Default: MONGO_PASSWORD=openIM123
@@ -143,7 +143,7 @@ KAFKA_LATESTMSG_REDIS_TOPIC=latestMsgToRedis
# MINIO_PORT
# ----------
# MINIO_PORT sets the port for the MinIO object storage service.
-# Upon changing this port, the MinIO endpoint URLs in the file must be updated
+# Upon changing this port, the MinIO endpoint URLs in the config/config.yaml file must be updated
# to reflect this change. The endpoints include both the 'endpoint' and 'signEndpoint'
# under the MinIO configuration.
#
diff --git a/config/templates/open-im-ng-example.conf b/config/templates/open-im-ng-example.conf
index 62befa6385..2598347773 100644
--- a/config/templates/open-im-ng-example.conf
+++ b/config/templates/open-im-ng-example.conf
@@ -66,6 +66,7 @@ server {
proxy_set_header Connection "Upgrade";
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
+ proxy_set_header X-Request-Api $scheme://$host/api;
proxy_pass http://im_api/;
}
location ^~/grafana/ { #10007 prometheus
diff --git a/config/templates/prometheus-dashboard.yaml b/config/templates/prometheus-dashboard.yaml
index 2e1ae7760c..1dccbd6921 100644
--- a/config/templates/prometheus-dashboard.yaml
+++ b/config/templates/prometheus-dashboard.yaml
@@ -1213,7 +1213,7 @@
"editorMode": "code",
"expr": "sum(rate(app_requests_total{job=~\"^($job)$\"}[$interval])) by (job)",
"instant": false,
- "legendFormat": "{{job}}-http",
+ "legendFormat": "{{job}}-webhook",
"range": true,
"refId": "A"
},
diff --git a/config/webhooks.yml b/config/webhooks.yml
new file mode 100644
index 0000000000..c7839d4f21
--- /dev/null
+++ b/config/webhooks.yml
@@ -0,0 +1,156 @@
+url: "webhook://127.0.0.1:10008/callbackExample"
+beforeSendSingleMsg:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeUpdateUserInfoEx:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterUpdateUserInfoEx:
+ enable: false
+ timeout: 5
+afterSendSingleMsg:
+ enable: false
+ timeout: 5
+beforeSendGroupMsg:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeMsgModify:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterSendGroupMsg:
+ enable: false
+ timeout: 5
+afterUserOnline:
+ enable: false
+ timeout: 5
+afterUserOffline:
+ enable: false
+ timeout: 5
+afterUserKickOff:
+ enable: false
+ timeout: 5
+beforeOfflinePush:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeOnlinePush:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeGroupOnlinePush:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeAddFriend:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeUpdateUserInfo:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterUpdateUserInfo:
+ enable: false
+ timeout: 5
+beforeCreateGroup:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterCreateGroup:
+ enable: false
+ timeout: 5
+beforeMemberJoinGroup:
+ enable: false
+ timeout: 5
+ failedContinue: true
+beforeSetGroupMemberInfo:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterSetGroupMemberInfo:
+ enable: false
+ timeout: 5
+afterQuitGroup:
+ enable: false
+ timeout: 5
+afterKickGroupMember:
+ enable: false
+ timeout: 5
+afterDismissGroup:
+ enable: false
+ timeout: 5
+beforeApplyJoinGroup:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterGroupMsgRead:
+ enable: false
+ timeout: 5
+afterSingleMsgRead:
+ enable: false
+ timeout: 5
+beforeUserRegister:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterUserRegister:
+ enable: false
+ timeout: 5
+afterTransferGroupOwner:
+ enable: false
+ timeout: 5
+beforeSetFriendRemark:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterSetFriendRemark:
+ enable: false
+ timeout: 5
+afterGroupMsgRevoke:
+ enable: false
+ timeout: 5
+afterJoinGroup:
+ enable: false
+ timeout: 5
+beforeInviteUserToGroup:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterSetGroupInfo:
+ enable: false
+ timeout: 5
+beforeSetGroupInfo:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterRevokeMsg:
+ enable: false
+ timeout: 5
+beforeAddBlack:
+ enable: false
+ timeout: 5
+ failedContinue:
+afterAddFriend:
+ enable: false
+ timeout: 5
+beforeAddFriendAgree:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterDeleteFriend:
+ enable: false
+ timeout: 5
+beforeImportFriends:
+ enable: false
+ timeout: 5
+ failedContinue: true
+afterImportFriends:
+ enable: false
+ timeout: 5
+afterRemoveBlack:
+ enable: false
+ timeout: 5
diff --git a/config/zookeeper.yml b/config/zookeeper.yml
new file mode 100644
index 0000000000..33f52d7ca7
--- /dev/null
+++ b/config/zookeeper.yml
@@ -0,0 +1,6 @@
+
+schema: openim
+address: [ localhost:12181 ]
+username: ''
+password: ''
+
diff --git a/deployments/charts/openim-api/templates/deployment.yaml b/deployments/charts/openim-api/templates/deployment.yaml
index 3b4bf57a24..b0076393f1 100644
--- a/deployments/charts/openim-api/templates/deployment.yaml
+++ b/deployments/charts/openim-api/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-api/templates/service.yaml b/deployments/charts/openim-api/templates/service.yaml
index 3704bf35c8..74f75a25e0 100644
--- a/deployments/charts/openim-api/templates/service.yaml
+++ b/deployments/charts/openim-api/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-api.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-msggateway/templates/deployment.yaml b/deployments/charts/openim-msggateway/templates/deployment.yaml
index 0f5f9d06fa..e938fa9bf4 100644
--- a/deployments/charts/openim-msggateway/templates/deployment.yaml
+++ b/deployments/charts/openim-msggateway/templates/deployment.yaml
@@ -48,7 +48,7 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
- name: rpc
@@ -57,11 +57,11 @@ spec:
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-msggateway/templates/service.yaml b/deployments/charts/openim-msggateway/templates/service.yaml
index e33fce6db7..e914ee1d47 100644
--- a/deployments/charts/openim-msggateway/templates/service.yaml
+++ b/deployments/charts/openim-msggateway/templates/service.yaml
@@ -22,9 +22,9 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
- port: 88
targetPort: rpc
protocol: TCP
diff --git a/deployments/charts/openim-msgtransfer/templates/deployment.yaml b/deployments/charts/openim-msgtransfer/templates/deployment.yaml
index 2c9e24bddd..019e307d52 100644
--- a/deployments/charts/openim-msgtransfer/templates/deployment.yaml
+++ b/deployments/charts/openim-msgtransfer/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-msgtransfer/templates/service.yaml b/deployments/charts/openim-msgtransfer/templates/service.yaml
index e657f8c18f..467f7d13c5 100644
--- a/deployments/charts/openim-msgtransfer/templates/service.yaml
+++ b/deployments/charts/openim-msgtransfer/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-msgtransfer.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-push/templates/deployment.yaml b/deployments/charts/openim-push/templates/deployment.yaml
index 34de33e10b..86c27d14c2 100644
--- a/deployments/charts/openim-push/templates/deployment.yaml
+++ b/deployments/charts/openim-push/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-push/templates/service.yaml b/deployments/charts/openim-push/templates/service.yaml
index b38c353753..c2ef8db358 100644
--- a/deployments/charts/openim-push/templates/service.yaml
+++ b/deployments/charts/openim-push/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-push.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-auth/templates/deployment.yaml b/deployments/charts/openim-rpc-auth/templates/deployment.yaml
index 2021627754..98c43ecb75 100644
--- a/deployments/charts/openim-rpc-auth/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-auth/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-auth/templates/service.yaml b/deployments/charts/openim-rpc-auth/templates/service.yaml
index 3674da014e..7855123470 100644
--- a/deployments/charts/openim-rpc-auth/templates/service.yaml
+++ b/deployments/charts/openim-rpc-auth/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-auth.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-conversation/templates/deployment.yaml b/deployments/charts/openim-rpc-conversation/templates/deployment.yaml
index 01721aa30a..6dcb001f4b 100644
--- a/deployments/charts/openim-rpc-conversation/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-conversation/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-conversation/templates/service.yaml b/deployments/charts/openim-rpc-conversation/templates/service.yaml
index f906735846..8559c4d11d 100644
--- a/deployments/charts/openim-rpc-conversation/templates/service.yaml
+++ b/deployments/charts/openim-rpc-conversation/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-conversation.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-friend/templates/deployment.yaml b/deployments/charts/openim-rpc-friend/templates/deployment.yaml
index a571888284..01251cdfac 100644
--- a/deployments/charts/openim-rpc-friend/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-friend/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-friend/templates/service.yaml b/deployments/charts/openim-rpc-friend/templates/service.yaml
index e445d561f5..892a007dd7 100644
--- a/deployments/charts/openim-rpc-friend/templates/service.yaml
+++ b/deployments/charts/openim-rpc-friend/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-friend.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-group/templates/deployment.yaml b/deployments/charts/openim-rpc-group/templates/deployment.yaml
index 406d0b3427..e738f33be2 100644
--- a/deployments/charts/openim-rpc-group/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-group/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-group/templates/service.yaml b/deployments/charts/openim-rpc-group/templates/service.yaml
index fc6f760603..42e1f78ca5 100644
--- a/deployments/charts/openim-rpc-group/templates/service.yaml
+++ b/deployments/charts/openim-rpc-group/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-group.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-msg/templates/deployment.yaml b/deployments/charts/openim-rpc-msg/templates/deployment.yaml
index d764294ea0..f7267fabbc 100644
--- a/deployments/charts/openim-rpc-msg/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-msg/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-msg/templates/service.yaml b/deployments/charts/openim-rpc-msg/templates/service.yaml
index 953b89d5dd..ba403d5abf 100644
--- a/deployments/charts/openim-rpc-msg/templates/service.yaml
+++ b/deployments/charts/openim-rpc-msg/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-msg.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-third/templates/deployment.yaml b/deployments/charts/openim-rpc-third/templates/deployment.yaml
index e4f47de576..7794155359 100644
--- a/deployments/charts/openim-rpc-third/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-third/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-third/templates/service.yaml b/deployments/charts/openim-rpc-third/templates/service.yaml
index f467992a21..af112794e8 100644
--- a/deployments/charts/openim-rpc-third/templates/service.yaml
+++ b/deployments/charts/openim-rpc-third/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-third.selectorLabels" . | nindent 4 }}
diff --git a/deployments/charts/openim-rpc-user/templates/deployment.yaml b/deployments/charts/openim-rpc-user/templates/deployment.yaml
index 62106e5a25..26497d837e 100644
--- a/deployments/charts/openim-rpc-user/templates/deployment.yaml
+++ b/deployments/charts/openim-rpc-user/templates/deployment.yaml
@@ -48,17 +48,17 @@ spec:
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- - name: http
+ - name: webhook
containerPort: 80
protocol: TCP
#livenessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
#readinessProbe:
# httpGet:
# path: /
- # port: http
+ # port: webhook
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumeMounts:
diff --git a/deployments/charts/openim-rpc-user/templates/service.yaml b/deployments/charts/openim-rpc-user/templates/service.yaml
index f89be1c440..af8a53e195 100644
--- a/deployments/charts/openim-rpc-user/templates/service.yaml
+++ b/deployments/charts/openim-rpc-user/templates/service.yaml
@@ -22,8 +22,8 @@ spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
- targetPort: http
+ targetPort: webhook
protocol: TCP
- name: http
+ name: webhook
selector:
{{- include "openim-rpc-user.selectorLabels" . | nindent 4 }}
diff --git a/deployments/templates/config.yaml b/deployments/templates/config.yaml
index 5da6d5d0b1..fee0bf90a4 100644
--- a/deployments/templates/config.yaml
+++ b/deployments/templates/config.yaml
@@ -323,7 +323,7 @@ iosPush:
# Timeout in seconds
# Whether to continue execution if callback fails
callback:
- url: "http://127.0.0.1:10008/callbackExample"
+ url: "webhook://127.0.0.1:10008/callbackExample"
beforeSendSingleMsg:
enable: ${CALLBACK_ENABLE}
timeout: ${CALLBACK_TIMEOUT}
diff --git a/docker-compose-1.yml b/docker-compose-1.yml
deleted file mode 100644
index ed852fd299..0000000000
--- a/docker-compose-1.yml
+++ /dev/null
@@ -1,298 +0,0 @@
-#fixme Clone openIM Server project before using docker-compose,project address:https://github.com/openimsdk/open-im-server.git
-# The command that triggers this file to pull the image is "docker compose up -f"
-version: '3'
-
-networks:
- server:
- driver: bridge
- ipam:
- driver: default
- config:
- - subnet: '${DOCKER_BRIDGE_SUBNET:-172.28.0.0/16}'
- gateway: '${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}'
-
-services:
- mongodb:
- image: mongo:${MONGODB_IMAGE_VERSION-6.0.2}
- ports:
- - "${MONGO_PORT:-37017}:27017"
- container_name: mongo
- command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh || true; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"]
- volumes:
- - "${DATA_DIR:-./}/components/mongodb/data/db:/data/db"
- - "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs"
- - "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo"
- - "./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro"
- environment:
- - TZ=Asia/Shanghai
- - wiredTigerCacheSizeGB=1
- - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
- - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
- - MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openim_v3}
- - MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username
- - MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password
- restart: always
- networks:
- server:
- ipv4_address: ${MONGO_NETWORK_ADDRESS:-172.28.0.2}
-
- redis:
- image: redis:${REDIS_IMAGE_VERSION:-7.0.0}
- container_name: redis
- ports:
- - "${REDIS_PORT:-16379}:6379"
- volumes:
- - "${DATA_DIR:-./}/components/redis/data:/data"
- - "${DATA_DIR:-./}/components/redis/config/redis.conf:/usr/local/redis/config/redis.conf"
- environment:
- TZ: Asia/Shanghai
- restart: always
- sysctls:
- net.core.somaxconn: 1024
- command: redis-server --requirepass ${REDIS_PASSWORD:-openIM123} --appendonly yes
- networks:
- server:
- ipv4_address: ${REDIS_NETWORK_ADDRESS:-172.28.0.3}
-
- zookeeper:
- image: bitnami/zookeeper:${ZOOKEEPER_IMAGE_VERSION:-3.8}
- container_name: zookeeper
- ports:
- - "${ZOOKEEPER_PORT:-12181}:2181"
- volumes:
- - "/etc/localtime:/etc/localtime"
- environment:
- - ALLOW_ANONYMOUS_LOGIN=yes
- - TZ="Asia/Shanghai"
- restart: always
- networks:
- server:
- ipv4_address: ${ZOOKEEPER_NETWORK_ADDRESS:-172.28.0.5}
-
- kafka:
- image: 'bitnami/kafka:${KAFKA_IMAGE_VERSION:-3.5.1}'
- container_name: kafka
- restart: always
- user: ${KAFKA_USER:-root}
- ports:
- - "${KAFKA_PORT:-19094}:9094"
- volumes:
- - ./scripts/create-topic.sh:/opt/bitnami/kafka/create-topic.sh
- - "${DATA_DIR:-./}/components/kafka:/bitnami/kafka"
- command: >
- bash -c "/opt/bitnami/scripts/kafka/run.sh & sleep 5; /opt/bitnami/kafka/create-topic.sh; wait"
- environment:
- - TZ=Asia/Shanghai
- - KAFKA_CFG_NODE_ID=0
- - KAFKA_CFG_PROCESS_ROLES=controller,broker
- - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@:9093
- - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094
- - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}:${KAFKA_PORT:-19094}
- # - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://127.0.0.1:${KAFKA_PORT:-19094} # Mac Deployment
- - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
- - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- networks:
- server:
- ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4}
-
- minio:
- image: minio/minio:${MINIO_IMAGE_VERSION:-RELEASE.2024-01-11T07-46-16Z}
- ports:
- - "${MINIO_PORT:-10005}:9000"
- - "9090:9090"
- container_name: minio
- volumes:
- - "${DATA_DIR:-./}/components/mnt/data:/data"
- - "${DATA_DIR:-./}/components/mnt/config:/root/.minio"
- environment:
- MINIO_ROOT_USER: "${MINIO_ACCESS_KEY:-root}"
- MINIO_ROOT_PASSWORD: "${MINIO_SECRET_KEY:-openIM123}"
- restart: always
- command: minio server /data --console-address ':9090'
- networks:
- server:
- ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6}
-
- openim-web:
- image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker}
- container_name: openim-web
- platform: linux/amd64
- restart: always
- ports:
- - "${OPENIM_WEB_PORT:-11001}:80"
- networks:
- server:
- ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7}
-
- openim-admin:
- # https://github.com/openimsdk/open-im-server/issues/1662
- image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35}
- container_name: openim-admin
- platform: linux/amd64
- restart: always
- ports:
- - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80"
- networks:
- server:
- ipv4_address: ${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS:-172.28.0.13}
-
- prometheus:
- image: prom/prometheus
- container_name: prometheus
- hostname: prometheus
- restart: always
- volumes:
- - "${DATA_DIR:-./}/config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml"
- - "${DATA_DIR:-./}/config/prometheus.yml:/etc/prometheus/prometheus.yml"
- ports:
- - "${PROMETHEUS_PORT:-19090}:9090"
- networks:
- server:
- ipv4_address: ${PROMETHEUS_NETWORK_ADDRESS:-172.28.0.10}
-
- alertmanager:
- image: prom/alertmanager
- container_name: alertmanager
- hostname: alertmanager
- restart: always
- volumes:
- - ${DATA_DIR:-./}/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
- - ${DATA_DIR:-./}/config/email.tmpl:/etc/alertmanager/email.tmpl
- ports:
- - "${ALERT_MANAGER_PORT:-19093}:9093"
- networks:
- server:
- ipv4_address: ${ALERT_MANAGER_NETWORK_ADDRESS:-172.28.0.14}
-
- grafana:
- image: grafana/grafana
- container_name: grafana
- hostname: grafana
- user: root
- restart: always
- ports:
- - "${GRAFANA_PORT:-13000}:3000"
- volumes:
- - "${DATA_DIR:-./}/components/grafana:/var/lib/grafana"
- networks:
- server:
- ipv4_address: ${GRAFANA_NETWORK_ADDRESS:-172.28.0.11}
-
- node-exporter:
- image: quay.io/prometheus/node-exporter
- container_name: node-exporter
- hostname: node-exporter
- restart: always
- ports:
- - "${NODE_EXPORTER_PORT:-19100}:9100"
- networks:
- server:
- ipv4_address: ${NODE_EXPORTER_NETWORK_ADDRESS:-172.28.0.12}
-
-### Source code deployment does not require pulling the following mirrors
-
- # openim-server:
- # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main}
- # container_name: openim-server
- # ports:
- # - "${OPENIM_WS_PORT:-10001}:${OPENIM_WS_PORT:-10001}"
- # - "${API_OPENIM_PORT:-10002}:${API_OPENIM_PORT:-10002}"
- # - "${API_PROM_PORT:-20100}:${API_PROM_PORT:-20100}"
- # - "${USER_PROM_PORT:-20110}:${USER_PROM_PORT:-20110}"
- # - "${FRIEND_PROM_PORT:-20120}:${FRIEND_PROM_PORT:-20120}"
- # - "${MESSAGE_PROM_PORT:-20130}:${MESSAGE_PROM_PORT:-20130}"
- # - "${MSG_GATEWAY_PROM_PORT:-20140}:${MSG_GATEWAY_PROM_PORT:-20140}"
- # - "${GROUP_PROM_PORT:-20150}:${GROUP_PROM_PORT:-20150}"
- # - "${AUTH_PROM_PORT:-20160}:${AUTH_PROM_PORT:-20160}"
- # - "${PUSH_PROM_PORT:-20170}:${PUSH_PROM_PORT:-20170}"
- # - "${CONVERSATION_PROM_PORT:-20230}:${CONVERSATION_PROM_PORT:-20230}"
- # - "${RTC_PROM_PORT:-21300}:${RTC_PROM_PORT:-21300}"
- # - "${THIRD_PROM_PORT:-21301}:${THIRD_PROM_PORT:-21301}"
- # - "21400-21403:21400-21403"
- # healthcheck:
- # test: ["CMD", "/openim/openim-server/scripts/check-all.sh"]
- # interval: 120s
- # timeout: 30s
- # retries: 5
- # env_file:
- # - .env
- # environment:
- # - OPENIM_IP=${OPENIM_IP:-127.0.0.1}
- # volumes:
- # - "${DATA_DIR:-./}/openim-server/logs:/openim/openim-server/logs"
- # - "${DATA_DIR:-./}/openim-server/_output/logs:/openim/openim-server/_output/logs"
- # - "${DATA_DIR:-./}/openim-server/config:/openim/openim-server/config"
- # restart: always
- # depends_on:
- # - kafka
- # - mysql
- # - mongodb
- # - redis
- # - minio
- # logging:
- # driver: json-file
- # options:
- # max-size: "1g"
- # max-file: "2"
- # networks:
- # server:
- # ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8}
-
- ### TODO: mysql is required to deploy the openim-chat component
- # mysql:
- # image: mysql:${MYSQL_IMAGE_VERSION:-5.7}
- # platform: linux/amd64
- # ports:
- # - "${MYSQL_PORT:-13306}:3306"
- # container_name: mysql
- # volumes:
- # - "${DATA_DIR:-./}/components/mysql/data:/var/lib/mysql"
- # - "/etc/localtime:/etc/localtime"
- # environment:
- # MYSQL_ROOT_PASSWORD: "${MYSQL_PASSWORD:-openIM123}"
- # restart: always
- # networks:
- # server:
- # ipv4_address: ${MYSQL_NETWORK_ADDRESS:-172.28.0.15}
-
- # openim-chat:
- # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main}
- # container_name: openim-chat
- # healthcheck:
- # test: ["CMD", "/openim/openim-chat/scripts/check_all.sh"]
- # interval: 60s
- # timeout: 30s
- # retries: 5
- # env_file:
- # - .env
- # environment:
- # - ZOOKEEPER_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
- # - ZOOKEEPER_PORT=${ZOOKEEPER_PORT:-12181}
- # - OPENIM_SERVER_ADDRESS=http://${OPENIM_SERVER_ADDRESS:-172.28.0.1}
- # - API_OPENIM_PORT=${API_OPENIM_PORT:-10002}
- # - MYSQL_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
- # - MYSQL_PORT=${MYSQL_PORT:-13306}
- # - REDIS_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
- # - REDIS_PORT=${REDIS_PORT:-16379}
- # ports:
- # - "${OPENIM_CHAT_API_PORT:-10008}:10008"
- # - "${OPENIM_ADMIN_API_PORT:-10009}:10009"
- # volumes:
- # - "${DATA_DIR:-./}/components/openim-chat/logs:/openim/openim-chat/logs"
- # - "${DATA_DIR:-./}/components/openim-chat/_output/logs:/openim/openim-chat/_output/logs"
- # - "${DATA_DIR:-./}/components/openim-chat/config:/openim/openim-chat/config"
- # restart: always
- # # user: root:root
- # depends_on:
- # - mysql
- # - kafka
- # - redis
- # - zookeeper
- # logging:
- # driver: json-file
- # options:
- # max-size: "1g"
- # max-file: "2"
- # networks:
- # server:
- # ipv4_address: ${OPENIM_CHAT_NETWORK_ADDRESS:-172.28.0.9}
diff --git a/docker-compose.yml b/docker-compose.yml
index a983acbaf1..4acd00cdd5 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,282 +1,121 @@
-#fixme Clone openIM Server project before using docker-compose,project address:https://github.com/openimsdk/open-im-server.git
-# The command that triggers this file to pull the image is "docker compose up -d".
version: '3'
networks:
- server:
+ openim:
driver: bridge
- ipam:
- driver: default
- config:
- - subnet: '${DOCKER_BRIDGE_SUBNET:-172.28.0.0/16}'
- gateway: '${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}'
services:
mongodb:
- image: mongo:${MONGODB_IMAGE_VERSION-6.0.2}
+ image: "${MONGO_IMAGE}"
ports:
- - "${MONGO_PORT:-37017}:27017"
+ - "37017:27017"
container_name: mongo
- command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh || true; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"]
+ command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"]
volumes:
- - "${DATA_DIR:-./}/components/mongodb/data/db:/data/db"
- - "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs"
- - "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo"
+ - "${DATA_DIR}/components/mongodb/data/db:/data/db"
+ - "${DATA_DIR}/components/mongodb/data/logs:/data/logs"
+ - "${DATA_DIR}/components/mongodb/data/conf:/etc/mongo"
- "./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro"
environment:
- TZ=Asia/Shanghai
- wiredTigerCacheSizeGB=1
- - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root}
- - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123}
- - MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openim_v3}
- - MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username
- - MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password
+ - MONGO_INITDB_ROOT_USERNAME=root
+ - MONGO_INITDB_ROOT_PASSWORD=openIM123
+ - MONGO_INITDB_DATABASE=openim_v3
+ - MONGO_OPENIM_USERNAME=openIM
+ - MONGO_OPENIM_PASSWORD=openIM123
restart: always
networks:
- server:
- ipv4_address: ${MONGO_NETWORK_ADDRESS:-172.28.0.2}
+ - openim
redis:
- image: redis:${REDIS_IMAGE_VERSION:-7.0.0}
+ image: "${REDIS_IMAGE}"
container_name: redis
ports:
- - "${REDIS_PORT:-16379}:6379"
+ - "16379:6379"
volumes:
- - "${DATA_DIR:-./}/components/redis/data:/data"
- - "${DATA_DIR:-./}/components/redis/config/redis.conf:/usr/local/redis/config/redis.conf"
+ - "${DATA_DIR}/components/redis/data:/data"
+ - "${DATA_DIR}/components/redis/config/redis.conf:/usr/local/redis/config/redis.conf"
environment:
TZ: Asia/Shanghai
restart: always
sysctls:
net.core.somaxconn: 1024
- command: redis-server --requirepass ${REDIS_PASSWORD:-openIM123} --appendonly yes
+ command: redis-server /usr/local/redis/config/redis.conf --requirepass openIM123 --appendonly yes
networks:
- server:
- ipv4_address: ${REDIS_NETWORK_ADDRESS:-172.28.0.3}
+ - openim
zookeeper:
- image: bitnami/zookeeper:${ZOOKEEPER_IMAGE_VERSION:-3.8}
+ image: "${ZOOKEEPER_IMAGE}"
container_name: zookeeper
ports:
- - "${ZOOKEEPER_PORT:-12181}:2181"
- volumes:
- - "/etc/localtime:/etc/localtime"
+ - "12181:2181"
environment:
- - ALLOW_ANONYMOUS_LOGIN=yes
- - TZ="Asia/Shanghai"
+ TZ: "Asia/Shanghai"
+ ALLOW_ANONYMOUS_LOGIN: "yes"
restart: always
networks:
- server:
- ipv4_address: ${ZOOKEEPER_NETWORK_ADDRESS:-172.28.0.5}
+ - openim
kafka:
- image: 'bitnami/kafka:${KAFKA_IMAGE_VERSION:-3.5.1}'
+ image: "${KAFKA_IMAGE}"
container_name: kafka
+ user: root
restart: always
- user: ${KAFKA_USER:-root}
ports:
- - "${KAFKA_PORT:-19094}:9094"
+ - "19094:9094"
volumes:
- ./scripts/create-topic.sh:/opt/bitnami/kafka/create-topic.sh
- - "${DATA_DIR:-./}/components/kafka:/bitnami/kafka"
+ - "${DATA_DIR}/components/kafka:/bitnami/kafka"
command: >
- bash -c "/opt/bitnami/scripts/kafka/run.sh & sleep 5; /opt/bitnami/kafka/create-topic.sh; wait"
+ bash -c "/opt/bitnami/scripts/kafka/run.sh & /opt/bitnami/kafka/create-topic.sh; wait"
environment:
- - TZ=Asia/Shanghai
- - KAFKA_CFG_NODE_ID=0
- - KAFKA_CFG_PROCESS_ROLES=controller,broker
- - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@:9093
- - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094
- - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}:${KAFKA_PORT:-19094}
- # - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://127.0.0.1:${KAFKA_PORT:-19094} # Mac Deployment
- - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
- - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
+ TZ: Asia/Shanghai
+ KAFKA_CFG_NODE_ID: 0
+ KAFKA_CFG_PROCESS_ROLES: controller,broker
+ KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: 0@kafka:9093
+ KAFKA_CFG_LISTENERS: PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094
+ KAFKA_CFG_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://localhost:19094
+ KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT
+ KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER
networks:
- server:
- ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4}
+ - openim
+
minio:
- image: minio/minio:${MINIO_IMAGE_VERSION:-RELEASE.2024-01-11T07-46-16Z}
+ image: "${MINIO_IMAGE}"
ports:
- - "${MINIO_PORT:-10005}:9000"
- - "9090:9090"
+ - "10005:9000"
+ - "19090:9090"
container_name: minio
volumes:
- - "${DATA_DIR:-./}/components/mnt/data:/data"
- - "${DATA_DIR:-./}/components/mnt/config:/root/.minio"
+ - "${DATA_DIR}/components/mnt/data:/data"
+ - "${DATA_DIR}/components/mnt/config:/root/.minio"
environment:
- MINIO_ROOT_USER: "${MINIO_ACCESS_KEY:-root}"
- MINIO_ROOT_PASSWORD: "${MINIO_SECRET_KEY:-openIM123}"
+ TZ: Asia/Shanghai
+ MINIO_ROOT_USER: root
+ MINIO_ROOT_PASSWORD: openIM123
restart: always
command: minio server /data --console-address ':9090'
networks:
- server:
- ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6}
+ - openim
- openim-web:
- image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker}
- container_name: openim-web
- platform: linux/amd64
+ openim-web-front:
+ image: ${OPENIM_WEB_FRONT_IMAGE}
+ container_name: openim-web-front
restart: always
ports:
- - "${OPENIM_WEB_PORT:-11001}:80"
+ - "11001:80"
networks:
- server:
- ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7}
+ - openim
- openim-admin:
- # https://github.com/openimsdk/open-im-server/issues/1662
- image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35}
- container_name: openim-admin
- platform: linux/amd64
+ openim-admin-front:
+ image: ${OPENIM_ADMIN_FRONT_IMAGE}
+ container_name: openim-admin-front
restart: always
ports:
- - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80"
+ - "11002:80"
networks:
- server:
- ipv4_address: ${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS:-172.28.0.13}
-
-### TODO: Uncomment, or deploy using openim docker: https://github.com/openimsdk/openim-docker
-### Uncomment and configure the following services as needed
-
- # openim-server:
- # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main}
- # container_name: openim-server
- # ports:
- # - "${OPENIM_WS_PORT:-10001}:${OPENIM_WS_PORT:-10001}"
- # - "${API_OPENIM_PORT:-10002}:${API_OPENIM_PORT:-10002}"
- # - "${API_PROM_PORT:-20100}:${API_PROM_PORT:-20100}"
- # - "${USER_PROM_PORT:-20110}:${USER_PROM_PORT:-20110}"
- # - "${FRIEND_PROM_PORT:-20120}:${FRIEND_PROM_PORT:-20120}"
- # - "${MESSAGE_PROM_PORT:-20130}:${MESSAGE_PROM_PORT:-20130}"
- # - "${MSG_GATEWAY_PROM_PORT:-20140}:${MSG_GATEWAY_PROM_PORT:-20140}"
- # - "${GROUP_PROM_PORT:-20150}:${GROUP_PROM_PORT:-20150}"
- # - "${AUTH_PROM_PORT:-20160}:${AUTH_PROM_PORT:-20160}"
- # - "${PUSH_PROM_PORT:-20170}:${PUSH_PROM_PORT:-20170}"
- # - "${CONVERSATION_PROM_PORT:-20230}:${CONVERSATION_PROM_PORT:-20230}"
- # - "${RTC_PROM_PORT:-21300}:${RTC_PROM_PORT:-21300}"
- # - "${THIRD_PROM_PORT:-21301}:${THIRD_PROM_PORT:-21301}"
- # - "21400-21403:21400-21403"
- # healthcheck:
- # test: ["CMD", "/openim/openim-server/scripts/check-all.sh"]
- # interval: 120s
- # timeout: 30s
- # retries: 5
- # env_file:
- # - .env
- # environment:
- # - OPENIM_IP=${OPENIM_IP:-127.0.0.1}
- # volumes:
- # - "${DATA_DIR:-./}/openim-server/logs:/openim/openim-server/logs"
- # - "${DATA_DIR:-./}/openim-server/_output/logs:/openim/openim-server/_output/logs"
- # - "${DATA_DIR:-./}/openim-server/config:/openim/openim-server/config"
- # restart: always
- # depends_on:
- # - kafka
- # - mysql
- # - mongodb
- # - redis
- # - minio
- # logging:
- # driver: json-file
- # options:
- # max-size: "1g"
- # max-file: "2"
- # networks:
- # server:
- # ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8}
-
- # openim-chat:
- # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main}
- # container_name: openim-chat
- # healthcheck:
- # test: ["CMD", "/openim/openim-chat/scripts/check_all.sh"]
- # interval: 60s
- # timeout: 30s
- # retries: 5
- # env_file:
- # - .env
- # environment:
- # - ZOOKEEPER_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
- # - ZOOKEEPER_PORT=${ZOOKEEPER_PORT:-12181}
- # - OPENIM_SERVER_ADDRESS=http://${OPENIM_SERVER_ADDRESS:-172.28.0.1}
- # - API_OPENIM_PORT=${API_OPENIM_PORT:-10002}
- # - MYSQL_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
- # - MYSQL_PORT=${MYSQL_PORT:-13306}
- # - REDIS_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}
- # - REDIS_PORT=${REDIS_PORT:-16379}
- # ports:
- # - "${OPENIM_CHAT_API_PORT:-10008}:10008"
- # - "${OPENIM_ADMIN_API_PORT:-10009}:10009"
- # volumes:
- # - "${DATA_DIR:-./}/components/openim-chat/logs:/openim/openim-chat/logs"
- # - "${DATA_DIR:-./}/components/openim-chat/_output/logs:/openim/openim-chat/_output/logs"
- # - "${DATA_DIR:-./}/components/openim-chat/config:/openim/openim-chat/config"
- # restart: always
- # # user: root:root
- # depends_on:
- # - mysql
- # - kafka
- # - redis
- # - zookeeper
- # logging:
- # driver: json-file
- # options:
- # max-size: "1g"
- # max-file: "2"
- # networks:
- # server:
- # ipv4_address: ${OPENIM_CHAT_NETWORK_ADDRESS:-172.28.0.9}
-
- # prometheus:
- # image: prom/prometheus
- # container_name: prometheus
- # hostname: prometheus
- # restart: always
- # volumes:
- # - "${DATA_DIR:-./}/config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml"
- # - "${DATA_DIR:-./}/config/prometheus.yml:/etc/prometheus/prometheus.yml"
- # ports:
- # - "${PROMETHEUS_PORT:-19090}:9090"
- # networks:
- # server:
- # ipv4_address: ${PROMETHEUS_NETWORK_ADDRESS:-172.28.0.10}
-
- # alertmanager:
- # image: prom/alertmanager
- # container_name: alertmanager
- # hostname: alertmanager
- # restart: always
- # volumes:
- # - ${DATA_DIR:-./}/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml
- # - ${DATA_DIR:-./}/config/email.tmpl:/etc/alertmanager/email.tmpl
- # ports:
- # - "${ALERT_MANAGER_PORT:-19093}:9093"
- # networks:
- # server:
- # ipv4_address: ${ALERT_MANAGER_NETWORK_ADDRESS:-172.28.0.14}
+ - openim
- # grafana:
- # image: grafana/grafana
- # container_name: grafana
- # hostname: grafana
- # user: root
- # restart: always
- # ports:
- # - "${GRAFANA_PORT:-13000}:3000"
- # volumes:
- # - "${DATA_DIR:-./}/components/grafana:/var/lib/grafana"
- # networks:
- # server:
- # ipv4_address: ${GRAFANA_NETWORK_ADDRESS:-172.28.0.11}
- # node-exporter:
- # image: quay.io/prometheus/node-exporter
- # container_name: node-exporter
- # hostname: node-exporter
- # restart: always
- # ports:
- # - "${NODE_EXPORTER_PORT:-19100}:9100"
- # networks:
- # server:
- # ipv4_address: ${NODE_EXPORTER_NETWORK_ADDRESS:-172.28.0.12}
diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS
index d1119eb611..5c0d904a46 100644
--- a/docs/CODEOWNERS
+++ b/docs/CODEOWNERS
@@ -1,87 +1,4 @@
-# This is a comment.
-# Each line is a file pattern followed by one or more owners.
+# CODEOWNERS file
+# This file is used to specify the individuals who are required to review changes in this repository.
-# README files
-README.md @openimsdk/openim @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# Contributing guidelines
-CONTRIBUTING.md @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# License files
-LICENSE @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# Makefile
-Makefile @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# These owners will be the default owners for everything in
-# the repo. Unless a later match takes precedence,
-# @cubxxw and @openimsdk/bot will be requested for
-# review when someone opens a pull request.
-* @openimsdk/openim @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# Order is important; the last matching pattern takes the most
-# precedence. When someone opens a pull request that only
-# modifies JS files, only @js-owner and not the global
-# owner(s) will be requested for a review.
-*.js @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# You can also use email addresses if you prefer. They'll be
-# used to look up users just like we do for commit author
-# emails.
-*.go 3293172751nss@gmail.com
-*.py 3293172751nss@gmail.com
-
-# Teams can be specified as code owners as well. Teams should
-# be identified in the format @org/team-name. Teams must have
-# explicit write access to the repository. In this example,
-# the OpenIMSDK team in the github organization owns all .txt files.
-*.txt @cubxxw @openimsdk/bot @Bloomingg @FGadvancer @skiffer-git @rfyiamcool @withchao
-
-# The `docs/*` pattern will match files like
-# `docs/getting-started.md` but not further nested files like
-# `docs/build-app/troubleshooting.md`.
-docs/* 3293172751nss@gmail.com @openimsdk/bot @skiffer-git
-
-# In this example, @octocat owns any file in an apps directory
-# anywhere in your repository.
-api/ @openimsdk/openim @cubxxw @skiffer-git
-
-# This is a comment.
-# Each line is a file pattern followed by one or more owners.
-
-# CHANGELOG file
-CHANGELOG/* @cubxxw @skiffer-git
-
-# _output directory
-_output/* @skiffer-git
-
-# bin directory
-bin/* @skiffer-git @FGadvancer
-
-# cmd directory
-cmd/*
-
-# config directory
-config/* @skiffer-git
-
-# internal directory
-internal/ @openimsdk/openim @skiffer-git @FGadvancer
-
-tools @openimsdk/openim @openimsdk/bot @cubxxw @skiffer-git @FGadvancer
-
-# logs directory
-logs/* @skiffer-git @FGadvancer
-
-# pkg directory
-pkg/a2r @openimsdk/openim @skiffer-git @cubxxw @openimsdk/bot
-
-# scripts directory
-scripts/template/* @openimsdk/openim @cubxxw @skiffer-git @FGadvancer
-scripts/enterprise/* @openimsdk/openim @FGadvancer @cubxxw @skiffer-git @openimsdk/bot
-scripts/githooks/* @openimsdk/openim @cubxxw @skiffer-git @FGadvancer
-scripts/lib/* @openimsdk/openim @FGadvancer @cubxxw @skiffer-git @openimsdk/bot
-scripts/make-rules/* @openimsdk/openim @FGadvancer @cubxxw @skiffer-git @openimsdk/bot
-
-# test directory
-test/mongo @FGadvancer @cubxxw @skiffer-git @openimsdk/bot
-test/mysql @FGadvancer @cubxxw @skiffer-git @openimsdk/bot
+* @Bloomingg @FGadvancer @skiffer-git @withchao
\ No newline at end of file
diff --git a/docs/contrib/go-code.md b/docs/contrib/go-code.md
index 5c02127251..df74dec1bb 100644
--- a/docs/contrib/go-code.md
+++ b/docs/contrib/go-code.md
@@ -115,22 +115,83 @@ var s = F()
func F() string { return "A" }
```
-- Use `_` as a prefix for unexported top-level constants and variables.
+- This example emphasizes using PascalCase for exported constants and camelCase for unexported ones, avoiding all caps and underscores.
```go
// bad
const (
- defaultHost = "127.0.0.1"
- defaultPort = 8080
+ MAX_COUNT = 100
+ timeout = 30
)
// good
const (
- _defaultHost = "127.0.0.1"
- _defaultPort = 8080
+ MaxCount = 100 // Exported constants should use PascalCase.
+ defaultTimeout = 30 // Unexported constants should use camelCase.
)
```
+- Grouping related constants enhances organization and readability, especially when there are multiple constants related to a particular feature or configuration.
+
+```go
+// bad
+const apiVersion = "v1"
+const retryInterval = 5
+
+// good
+const (
+ ApiVersion = "v1" // Group related constants together for better organization.
+ RetryInterval = 5
+)
+```
+
+- The "good" practice utilizes iota for a clear, concise, and auto-incrementing way to define enumerations, reducing the potential for errors and improving maintainability.
+
+```go
+// bad
+const (
+ StatusActive = 0
+ StatusInactive = 1
+ StatusUnknown = 2
+)
+
+// good
+const (
+ StatusActive = iota // Use iota for simple and efficient constant enumerations.
+ StatusInactive
+ StatusUnknown
+)
+```
+
+- Specifying types explicitly improves clarity, especially when the purpose or type of a constant might not be immediately obvious. Additionally, adding comments to exported constants or those whose purpose isn't clear from the name alone can greatly aid in understanding the code.
+
+```go
+// bad
+const serverAddress = "localhost:8080"
+const debugMode = 1 // Is this supposed to be a boolean or an int?
+
+// good
+const ServerAddress string = "localhost:8080" // Specify type for clarity.
+// DebugMode indicates if the application should run in debug mode (true for debug mode).
+const DebugMode bool = true
+```
+
+- By defining a contextKey type and making userIDKey of this type, you avoid potential collisions with other context keys. This approach leverages Go's type system to provide compile-time checks against misuse.
+
+```go
+// bad
+const userIDKey = "userID"
+
+// In this example, userIDKey is a string type, which can lead to conflicts or accidental misuse because string keys are prone to typos and collisions in a global namespace.
+
+
+// good
+type contextKey string
+
+const userIDKey contextKey = "userID"
+```
+
+
- 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
@@ -274,8 +335,6 @@ The use of `panic` should be carefully controlled in Go applications to ensure p
- **Restricted Use in Main Package:** In the main package, the use of `panic` should be reserved for situations where the program is entirely inoperable, such as failure to open essential files, inability to connect to the database, or other critical startup issues. Even in these scenarios, prefer using structured error handling to terminate the program.
-- **Use `log.Fatal` for Critical Errors:** Instead of panicking, use `log.Fatal` to log critical errors that prevent the program from operating normally. This approach allows the program to terminate while ensuring the error is properly logged for troubleshooting.
-
- **Prohibition on Exportable Interfaces:** Exportable interfaces must not invoke `panic`. They should handle errors gracefully and return errors as part of their contract.
- **Prefer Errors Over Panic:** It is recommended to use error returns instead of panic to convey errors within a package. This approach promotes error handling that integrates smoothly with Go's error handling idioms.
@@ -303,7 +362,7 @@ func SIGTERMExit() {
```go
import (
- _ "net/http/pprof"
+ _ "net/webhook/pprof"
"github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
@@ -357,27 +416,31 @@ The naming convention is a very important part of the code specification. A unif
- Don't use broad, meaningless package names like common, util, shared or lib.
- The package name should be simple and clear, such as net, time, log.
-### 2.2 Function Naming
-- 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`.
-- 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.2 Function Naming Conventions
+
+Function names should adhere to the following guidelines, inspired by OpenIM’s standards and Google’s Go Style Guide:
+
+- Use camel case for function names. Start with an uppercase letter for public functions (`MixedCaps`) and a lowercase letter for private functions (`mixedCaps`).
+- Exceptions to this rule include code automatically generated by tools (e.g., `xxxx.pb.go`) and test functions that use underscores for clarity (e.g., `TestMyFunction_WhatIsBeingTested`).
+
+### 2.3 File and Directory Naming Practices
+
+To maintain consistency and readability across the OpenIM project, observe the following naming practices:
+
+**File Names:**
+- Use underscores (`_`) as the default separator in filenames, keeping them short and descriptive.
+- Both hyphens (`-`) and underscores (`_`) are allowed, but underscores are preferred for general use.
-In accordance with the naming conventions adopted by OpenIM and drawing reference from the Google Naming Conventions as per the guidelines available at https://google.github.io/styleguide/go/, the following expectations for naming practices within the project are set forth:
+**Script and Markdown Files:**
+- Prefer hyphens (`-`) for shell scripts and Markdown (`.md`) files to enhance searchability and web compatibility.
-1. **File Names:**
- + Both hyphens (`-`) and underscores (`_`) are permitted when naming files.
- + A preference is established for the use of underscores (`_`), suggesting it as the best practice in general scenarios.
-2. **Script and Markdown Files:**
- + For shell scripts (bash files) and Markdown (`.md`) documents, the use of hyphens (`-`) is recommended.
- + This recommendation is based on the improved searchability and compatibility in web browsers when hyphens are used in names.
-3. **Directories:**
- + A consistent approach is mandated for naming directories, exclusively using hyphens (`-`) to separate words within directory names.
+**Directories:**
+- Name directories with hyphens (`-`) exclusively to separate words, ensuring consistency and readability.
+Remember to keep filenames lowercase and use meaningful, concise identifiers to facilitate better organization and navigation within the project.
-### 2.3 File Naming
-- Keep the filename short and meaningful.
-- Filenames should be lowercase and use underscores to separate words.
### 2.4 Structure Naming
@@ -478,9 +541,9 @@ var LintGonicMapper = GonicMapper{
- If the variable type is bool, the name should start with Has, Is, Can or Allow, for example:
```go
-var has Conflict bool
+var hasConflict bool
var isExist bool
-var can Manage bool
+var canManage bool
var allowGitHook bool
```
diff --git a/docs/contrib/go-code1.md b/docs/contrib/go-code1.md
new file mode 100644
index 0000000000..2206a153e3
--- /dev/null
+++ b/docs/contrib/go-code1.md
@@ -0,0 +1,1554 @@
+## OpenIM development specification
+We have very high standards for code style and specification, and we want our products to be polished and perfect
+
+## 1. Code style
+
+### 1.1 Code format
+
+- Code must be formatted with `gofmt`.
+- Leave spaces between operators and operands.
+- 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.
+- Function length cannot exceed 80 lines.
+- import specification
+- All code must be formatted with `goimports` (it is recommended to set the code Go code editor to: run `goimports` on save).
+- Do not use relative paths to import packages, such as `import ../util/net`.
+- 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.
+
+```go
+// bad
+"github.com/dgrijalva/jwt-go/v4"
+
+//good
+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
+import (
+ // go standard package
+ "fmt"
+
+ // third party package
+ "github.com/jinzhu/gorm"
+ "github.com/spf13/cobra"
+ "github.com/spf13/viper"
+
+ // Anonymous packages are grouped separately, and anonymous package references are explained
+ // import mysql driver
+ _ "github.com/jinzhu/gorm/dialects/mysql"
+
+ // inner package
+)
+```
+
+### 1.2 Declaration, initialization and definition
+
+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
+var (
+ Width int
+ Height int
+)
+```
+
+- When initializing a structure reference, please use `&T{}` instead of `new(T)` to make it consistent with structure initialization.
+
+```go
+ // bad
+ sptr := new(T)
+ sptr.Name = "bar"
+
+ // good
+ sptr := &T{Name: "bar"}
+```
+
+- The struct declaration and initialization format takes multiple lines and is defined as follows.
+
+```go
+ type User struct{
+ Username string
+ Email string
+ }
+
+ user := User{
+ Username: "belm",
+ Email: "nosbelm@qq.com",
+}
+```
+
+- Similar declarations are grouped together, and the same applies to constant, variable, and type declarations.
+
+```go
+// bad
+import "a"
+import "b"
+
+//good
+import (
+ "a"
+ "b"
+)
+```
+
+- Specify container capacity where possible to pre-allocate memory for the container, for example:
+
+```go
+v := make(map[int]string, 4)
+v := make([]string, 0, 4)
+```
+
+- 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
+// bad
+var s string = F()
+
+func F() string { return "A" }
+
+// good
+var s = F()
+// 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" }
+```
+
+- This example emphasizes using PascalCase for exported constants and camelCase for unexported ones, avoiding all caps and underscores.
+
+```go
+// bad
+const (
+ MAX_COUNT = 100
+ timeout = 30
+)
+
+// good
+const (
+ MaxCount = 100 // Exported constants should use PascalCase.
+ defaultTimeout = 30 // Unexported constants should use camelCase.
+)
+```
+
+- Grouping related constants enhances organization and readability, especially when there are multiple constants related to a particular feature or configuration.
+
+```go
+// bad
+const apiVersion = "v1"
+const retryInterval = 5
+
+// good
+const (
+ ApiVersion = "v1" // Group related constants together for better organization.
+ RetryInterval = 5
+)
+```
+
+- The "good" practice utilizes iota for a clear, concise, and auto-incrementing way to define enumerations, reducing the potential for errors and improving maintainability.
+
+```go
+// bad
+const (
+ StatusActive = 0
+ StatusInactive = 1
+ StatusUnknown = 2
+)
+
+// good
+const (
+ StatusActive = iota // Use iota for simple and efficient constant enumerations.
+ StatusInactive
+ StatusUnknown
+)
+```
+
+- Specifying types explicitly improves clarity, especially when the purpose or type of a constant might not be immediately obvious. Additionally, adding comments to exported constants or those whose purpose isn't clear from the name alone can greatly aid in understanding the code.
+
+```go
+// bad
+const serverAddress = "localhost:8080"
+const debugMode = 1 // Is this supposed to be a boolean or an int?
+
+// good
+const ServerAddress string = "localhost:8080" // Specify type for clarity.
+// DebugMode indicates if the application should run in debug mode (true for debug mode).
+const DebugMode bool = true
+```
+
+- By defining a contextKey type and making userIDKey of this type, you avoid potential collisions with other context keys. This approach leverages Go's type system to provide compile-time checks against misuse.
+
+```go
+// bad
+const userIDKey = "userID"
+
+// In this example, userIDKey is a string type, which can lead to conflicts or accidental misuse because string keys are prone to typos and collisions in a global namespace.
+
+
+// good
+type contextKey string
+
+const userIDKey contextKey = "userID"
+```
+
+
+- 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
+// bad
+type Client struct {
+ version int
+ http.Client
+}
+
+//good
+type Client struct {
+ http.Client
+
+ version int
+}
+```
+
+
+### 1.5 Unit Tests
+
+- 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.
+- If `func (b *Bar) Foo` exists, the single test function can be `func TestBar_Foo`.
+
+## 2. Naming convention
+
+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 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.
+- 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`.
+- Don't use broad, meaningless package names like common, util, shared or lib.
+- The package name should be simple and clear, such as net, time, log.
+
+
+### 2.2 Function Naming Conventions
+
+Function names should adhere to the following guidelines, inspired by OpenIM’s standards and Google’s Go Style Guide:
+
+- Use camel case for function names. Start with an uppercase letter for public functions (`MixedCaps`) and a lowercase letter for private functions (`mixedCaps`).
+- Exceptions to this rule include code automatically generated by tools (e.g., `xxxx.pb.go`) and test functions that use underscores for clarity (e.g., `TestMyFunction_WhatIsBeingTested`).
+
+### 2.3 File and Directory Naming Practices
+
+To maintain consistency and readability across the OpenIM project, observe the following naming practices:
+
+**File Names:**
+- Use underscores (`_`) as the default separator in filenames, keeping them short and descriptive.
+- Both hyphens (`-`) and underscores (`_`) are allowed, but underscores are preferred for general use.
+
+**Script and Markdown Files:**
+- Prefer hyphens (`-`) for shell scripts and Markdown (`.md`) files to enhance searchability and web compatibility.
+
+**Directories:**
+- Name directories with hyphens (`-`) exclusively to separate words, ensuring consistency and readability.
+
+Remember to keep filenames lowercase and use meaningful, concise identifiers to facilitate better organization and navigation within the project.
+
+### 2.4 Structure Naming
+
+- The camel case is adopted, and the first letter is uppercase or lowercase according to the access control, such as `MixedCaps` or `mixedCaps`.
+- Struct names should not be verbs, but should be nouns, such as `Node`, `NodeSpec`.
+- Avoid using meaningless structure names such as Data and Info.
+- The declaration and initialization of the structure should take multiple lines, for example:
+
+```go
+// User multi-line declaration
+type User struct {
+ name string
+ Email string
+}
+
+// multi-line initialization
+u := User{
+ UserName: "belm",
+ Email: "nosbelm@qq.com",
+}
+```
+
+### 2.5 Interface Naming
+
+- The interface naming rules are basically consistent with the structure naming rules:
+- Interface names of individual functions suffixed with "er"" (e.g. Reader, Writer) can sometimes lead to broken English, but that's okay.
+- 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:
+
+```go
+// 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
+// I/O operations on the underlying object are implementation-dependent.
+type Seeker interface {
+ Seek(offset int64, whence int) (int64, error)
+}
+
+// ReadWriter is the interface that groups the basic Read and Write methods.
+type ReadWriter interface {
+ reader
+ Writer
+}
+```
+
+### 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 can be abbreviated as u;
+- userID can be abbreviated as uid.
+- When using proper nouns, the following rules need to be followed:
+- If the variable is private and the proper noun is the first word, use lowercase, such as apiClient.
+- In other cases, the original wording of the noun should be used, such as APIClient, repoID, UserID.
+
+Some common nouns are listed below.
+
+```go
+// A GonicMapper that contains a list of common initialisms taken from golang/lint
+var LintGonicMapper = GonicMapper{
+ "API": true,
+ "ASCII": true,
+ "CPU": true,
+ "CSS": true,
+ "DNS": true,
+ "EOF": true,
+ "GUID": true,
+ "HTML": true,
+ "HTTP": true,
+ "HTTPS": true,
+ "ID": true,
+ "IP": true,
+ "JSON": true,
+ "LHS": true,
+ "QPS": true,
+ "RAM": true,
+ "RHS": true,
+ "RPC": true,
+ "SLA": true,
+ "SMTP": true,
+ "SSH": true,
+ "TLS": true,
+ "TTL": true,
+ "UI": true,
+ "UID": true,
+ "UUID": true,
+ "URI": true,
+ "URL": true,
+ "UTF8": true,
+ "VM": true,
+ "XML": true,
+ "XSRF": true,
+ "XSS": true,
+}
+```
+
+- If the variable type is bool, the name should start with Has, Is, Can or Allow, for example:
+
+```go
+var hasConflict bool
+var isExist bool
+var canManage bool
+var allowGitHook bool
+```
+
+- Local variables should be as short as possible, for example, use buf to refer to buffer, and use i to refer to index.
+- The code automatically generated by the code generation tool can exclude this rule (such as the Id in `xxx.pb.go`)
+
+### 2.7 Constant Naming
+
+In Go, constants play a critical role in defining values that do not change throughout the execution of a program. Adhering to best practices in naming constants can significantly improve the readability and maintainability of your code. Here are some guidelines for constant naming:
+
+- **Camel Case Naming:** The name of a constant must follow the camel case notation. The initial letter should be uppercase or lowercase based on the access control requirements. Uppercase indicates that the constant is exported (visible outside the package), while lowercase indicates package-private visibility (visible only within its own package).
+
+- **Enumeration Type Constants:** For constants that represent a set of enumerated values, it's recommended to define a corresponding type first. This approach not only enhances type safety but also improves code readability by clearly indicating the purpose of the enumeration.
+
+**Example:**
+
+```go
+// Code defines an error code type.
+type Code int
+
+// Internal errors.
+const (
+ // ErrUnknown - 0: An unknown error occurred.
+ ErrUnknown Code = iota
+ // ErrFatal - 1: A fatal error occurred.
+ ErrFatal
+)
+```
+
+In the example above, `Code` is defined as a new type based on `int`. The enumerated constants `ErrUnknown` and `ErrFatal` are then defined with explicit comments to indicate their purpose and values. This pattern is particularly useful for grouping related constants and providing additional context.
+
+### Global Variables and Constants Across Packages
+
+- **Use Constants for Global Variables:** When defining variables that are intended to be accessed across packages, prefer using constants to ensure immutability. This practice avoids unintended modifications to the value, which can lead to unpredictable behavior or hard-to-track bugs.
+
+- **Lowercase for Package-Private Usage:** If a global variable or constant is intended for use only within its own package, it should start with a lowercase letter. This clearly signals its limited scope of visibility, adhering to Go's access control mechanism based on naming conventions.
+
+**Guideline:**
+
+- For global constants that need to be accessed across packages, declare them with an uppercase initial letter. This makes them exported, adhering to Go's visibility rules.
+- For constants used within the same package, start their names with a lowercase letter to limit their scope to the package.
+
+**Example:**
+
+```go
+package config
+
+// MaxConnections - the maximum number of allowed connections. Visible across packages.
+const MaxConnections int = 100
+
+// minIdleTime - the minimum idle time before a connection is considered stale. Only visible within the config package.
+const minIdleTime int = 30
+```
+
+In this example, `MaxConnections` is a global constant meant to be accessed across packages, hence it starts with an uppercase letter. On the other hand, `minIdleTime` is intended for use only within the `config` package, so it starts with a lowercase letter.
+
+Following these guidelines ensures that your Go code is more readable, maintainable, and consistent with Go's design philosophy and access control mechanisms.
+
+
+
+
+### 2.10 Using Context with IO or Inter-Process Communication (IPC)
+
+In Go, `context.Context` is a powerful construct for managing deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. It is particularly important in I/O operations or inter-process communication (IPC), where operations might need to be cancelled or timed out.
+
+#### Guideline: Use Context for IO and IPC
+
+- **Mandatory Use of Context:** When performing I/O operations or inter-process communication, it's crucial to use `context.Context` to manage the lifecycle of these operations. This includes setting deadlines, handling cancellation signals, and passing request-scoped values.
+
+#### Incorrect Example: Ignoring Context in an HTTP Call
+
+```go
+package main
+
+import (
+ "io/ioutil"
+ "net/http"
+ "log"
+)
+
+// FetchData makes an HTTP GET request to the specified URL and returns the response body.
+// This function does not use context, making it impossible to cancel the request or set a deadline.
+func FetchData(url string) (string, error) {
+ resp, err := http.Get(url) // Incorrect: Ignoring context
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+
+ return string(body), nil
+}
+
+func main() {
+ data, err := FetchData("http://example.com")
+ if err != nil {
+ log.Fatalf("Failed to fetch data: %v", err)
+ }
+ log.Println(data)
+}
+```
+
+In this incorrect example, the `FetchData` function makes an HTTP GET request without using a `context`. This approach does not allow the request to be cancelled or a timeout to be set, potentially leading to resources being wasted if the server takes too long to respond or if the operation needs to be aborted for any reason.
+
+#### Correct Example: Using Context in an HTTP Call
+
+```go
+package main
+
+import (
+ "context"
+ "io/ioutil"
+ "net/http"
+ "log"
+ "time"
+)
+
+// FetchDataWithContext makes an HTTP GET request to the specified URL using the provided context.
+// This allows the request to be cancelled or timed out according to the context's deadline.
+func FetchDataWithContext(ctx context.Context, url string) (string, error) {
+ req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+ if err != nil {
+ return "", err
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+
+ return string(body), nil
+}
+
+func main() {
+ // Create a context with a 5-second timeout
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ data, err := FetchDataWithContext(ctx, "http://example.com")
+ if err != nil {
+ log.Fatalf("Failed to fetch data: %v", err)
+ }
+ log.Println(data)
+}
+```
+
+In the correct example, `FetchDataWithContext` uses a context to make the HTTP GET request. This allows the operation to be cancelled or subjected to a timeout, as dictated by the context passed to it. The `context.WithTimeout` function is used in `main` to create a context that cancels the request if it takes longer than 5 seconds, demonstrating a practical use of context to manage operation lifecycle.
+
+### Best Practices for Using Context
+
+1. **Pass context as the first parameter of a function**, following the convention `func(ctx context.Context, ...)`.
+2. **Never ignore the context** provided to you in functions that support it. Always use it in your I/O or IPC operations.
+3. **Avoid storing context in a struct**. Contexts are meant to be passed around within the call stack, not stored.
+4. **Use context's cancellation and deadline features** to control the lifecycle of blocking operations, especially in network I/O and IPC scenarios.
+5. **Propagate context down the call stack** to any function that supports it, ensuring that your application can respond to cancellation signals and deadlines effectively.
+
+By adhering to these guidelines and examples, you can ensure that your Go applications handle I/O and IPC operations more reliably and efficiently, with proper support for cancellation, timeouts, and request-scoped values.
+
+
+
+## 3. 日志规范
+
+启动时正常日志,打印流程日志,如链接mongo成功,注意不要打印密码等敏感信息。
+
+启动时以及运行中异常终止日志,如果需要终止程序,调用ExitWithError
+
+运行时日志打印,对于错误日志,使用日志库打印,仅在最上层调用打印;对于debug日志,可以随意打印;对于关键日志打印 info;
+
+## 5.异常及错误处理
+
+任何情况禁止使用panic
+
+错误需要wrap,并带上message和key value,用户排查问题;错误wrap仅一次,及函数本身出现的错误,或者调用项目之外的函数产生的错误。
+
+用errs.New()替代errors.New()
+
+
+
+
+
+### 1.4 Panic Processing
+
+The use of `panic` should be carefully controlled in Go applications to ensure program stability and predictable error handling. Following are revised guidelines emphasizing the restriction on using `panic` and promoting alternative strategies for error handling and program termination.
+
+- **Prohibited in Business Logic:** Using `panic` within business logic processing is strictly prohibited. Business logic should handle errors gracefully and use error returns to propagate issues up the call stack.
+
+- **Restricted Use in Main Package:** In the main package, the use of `panic` should be reserved for situations where the program is entirely inoperable, such as failure to open essential files, inability to connect to the database, or other critical startup issues. Even in these scenarios, prefer using structured error handling to terminate the program.
+
+- **Prohibition on Exportable Interfaces:** Exportable interfaces must not invoke `panic`. They should handle errors gracefully and return errors as part of their contract.
+
+- **Prefer Errors Over Panic:** It is recommended to use error returns instead of panic to convey errors within a package. This approach promotes error handling that integrates smoothly with Go's error handling idioms.
+
+#### Alternative to Panic: Structured Program Termination
+
+To enforce these guidelines, consider implementing structured functions to terminate the program gracefully in the face of unrecoverable errors, while providing clear error messages. Here are two recommended functions:
+
+```go
+// ExitWithError logs an error message and exits the program with a non-zero status.
+func ExitWithError(err error) {
+ progName := filepath.Base(os.Args[0])
+ fmt.Fprintf(os.Stderr, "%s exit -1: %+v\n", progName, err)
+ os.Exit(-1)
+}
+
+// SIGTERMExit logs a warning message when the program receives a SIGTERM signal and exits with status 0.
+func SIGTERMExit() {
+ progName := filepath.Base(os.Args[0])
+ fmt.Fprintf(os.Stderr, "Warning %s receive process terminal SIGTERM exit 0\n", progName)
+}
+```
+
+#### Example Usage:
+
+```go
+import (
+ _ "net/webhook/pprof"
+
+ "github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
+ util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+)
+
+func main() {
+ apiCmd := cmd.NewApiCmd()
+ apiCmd.AddPortFlag()
+ apiCmd.AddPrometheusPortFlag()
+ if err := apiCmd.Execute(); err != nil {
+ util.ExitWithError(err)
+ }
+}
+```
+
+In this example, `ExitWithError` is used to terminate the program when an unrecoverable error occurs, providing a clear error message to stderr and exiting with a non-zero status. This approach ensures that critical errors are logged and the program exits in a controlled manner, facilitating troubleshooting and maintaining the stability of the application.
+
+
+
+### 1.3 Error Handling
+
+- `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
+func load() error {
+// normal code
+}
+
+// bad
+load()
+
+//good
+ _ = load()
+```
+
+- When `error` is returned as the value of a function and there are multiple return values, `error` must be the last parameter.
+
+```go
+// bad
+func load() (error, int) {
+// normal code
+}
+
+//good
+func load() (int, error) {
+// normal code
+}
+```
+
+- Perform error handling as early as possible and return as early as possible to reduce nesting.
+
+```go
+// bad
+if err != nil {
+// error code
+} else {
+// normal code
+}
+
+//good
+if err != nil {
+// error handling
+return err
+}
+// normal code
+```
+
+- If you need to use the result of the function call outside if, you should use the following method.
+
+```go
+// bad
+if v, err := foo(); err != nil {
+// error handling
+}
+
+// good
+v, err := foo()
+if err != nil {
+// error handling
+}
+```
+
+- Errors should be judged independently, not combined with other logic.
+
+```go
+// bad
+v, err := foo()
+if err != nil || v == nil {
+ // error handling
+ return err
+}
+
+//good
+v, err := foo()
+if err != nil {
+ // error handling
+ return err
+}
+
+if v == nil {
+ // error handling
+ return errors. New("invalid value v")
+}
+```
+
+- If the return value needs to be initialized, use the following method.
+
+```go
+v, err := f()
+if err != nil {
+ // error handling
+ return // or continue.
+}
+```
+
+- Bug description suggestions
+- Error descriptions start with a lowercase letter and do not end with punctuation, for example:
+
+```go
+// bad
+errors.New("Redis connection failed")
+errors.New("redis connection failed.")
+
+// good
+errors.New("redis connection failed")
+```
+
+- Tell users what they can do, not what they can't.
+- When declaring a requirement, use must instead of should. For example, `must be greater than 0, must match regex '[a-z]+'`.
+- When declaring that a format is incorrect, use must not. For example, `must not contain`.
+- Use may not when declaring an action. For example, `may not be specified when otherField is empty, only name may be specified`.
+- When quoting a literal string value, indicate the literal in single quotes. For example, `ust not contain '..'`.
+- When referencing another field name, specify that name in backticks. For example, must be greater than `request`.
+- 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 or above is recommended, and the error generation method is `fmt.Errorf("module xxx: %w", err)`.
+
+### 1.6 Type assertion failure handling
+
+- A single return value from a type assertion will panic for an incorrect type. Always use the "comma ok" idiom.
+
+```go
+// bad
+t := n.(int)
+
+//good
+t, ok := n.(int)
+if !ok {
+// error handling
+}
+```
+
+
+
+### 2.8 Error naming
+
+- The Error type should be written in the form of FooError.
+
+```go
+type ExitError struct {
+// ....
+}
+```
+
+- The Error variable is written in the form of ErrFoo.
+
+```go
+var ErrFormat = errors. New("unknown format")
+```
+
+For non-standard Err naming, CICD will report an error
+
+
+### 2.9 Handling Errors Properly
+
+In Go, proper error handling is crucial for creating reliable and maintainable applications. It's important to ensure that errors are not ignored or discarded, as this can lead to unpredictable behavior and difficult-to-debug issues. Here are the guidelines and examples regarding the proper handling of errors.
+
+#### Guideline: Do Not Discard Errors
+
+- **Mandatory Error Propagation:** When calling a function that returns an error, the calling function must handle or propagate the error, instead of ignoring it. This approach ensures that errors are not silently ignored, allowing higher-level logic to make informed decisions about error handling.
+
+#### Incorrect Example: Discarding an Error
+
+```go
+package main
+
+import (
+ "io/ioutil"
+ "log"
+)
+
+func ReadFileContent(filename string) string {
+ content, _ := ioutil.ReadFile(filename) // Incorrect: Error is ignored
+ return string(content)
+}
+
+func main() {
+ content := ReadFileContent("example.txt")
+ log.Println(content)
+}
+```
+
+In this incorrect example, the error returned by `ioutil.ReadFile` is ignored. This can lead to situations where the program continues execution even if the file doesn't exist or cannot be accessed, potentially causing more cryptic errors downstream.
+
+#### Correct Example: Propagating an Error
+
+```go
+package main
+
+import (
+ "io/ioutil"
+ "log"
+)
+
+// ReadFileContent attempts to read and return the content of the specified file.
+// It returns an error if reading fails.
+func ReadFileContent(filename string) (string, error) {
+ content, err := ioutil.ReadFile(filename)
+ if err != nil {
+ // Correct: Propagate the error
+ return "", err
+ }
+ return string(content), nil
+}
+
+func main() {
+ content, err := ReadFileContent("example.txt")
+ if err != nil {
+ log.Fatalf("Failed to read file: %v", err)
+ }
+ log.Println(content)
+}
+```
+
+In the correct example, the error returned by `ioutil.ReadFile` is propagated back to the caller. The `main` function then checks the error and terminates the program with an appropriate error message if an error occurred. This approach ensures that errors are handled appropriately, and the program does not proceed with invalid state.
+
+### Best Practices for Error Handling
+
+1. **Always check the error returned by a function.** Do not ignore it.
+2. **Propagate errors up the call stack unless they can be handled gracefully at the current level.**
+3. **Provide context for errors when propagating them, making it easier to trace the source of the error.** This can be achieved using `fmt.Errorf` with the `%w` verb or dedicated wrapping functions provided by some error handling packages.
+4. **Log the error at the point where it is handled or makes the program to terminate, to provide insight into the failure.**
+
+By following these guidelines, you ensure that your Go applications handle errors in a consistent and effective manner, improving their reliability and maintainability.
+
+
+
+
+
+
+
+
+
+
+
+
+
+## Suggestions
+
+
+
+## 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.
+- 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
+// bad
+// logs the flags in the flagset.
+func PrintFlags(flags *pflag. FlagSet) {
+// normal code
+}
+
+//good
+// PrintFlags logs the flags in the flagset.
+func PrintFlags(flags *pflag. FlagSet) {
+// normal code
+}
+```
+
+- 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
+// Package superman implements methods for saving the world.
+//
+// Experience has shown that a small number of procedures can prove
+// helpful when attempting to save the world.
+package superman
+```
+
+### 3.1 Package Notes
+
+- Each package has one and only one package-level annotation.
+- Package comments are uniformly commented with // in the format of `// Package package description`, for example:
+
+```go
+// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce
+// useful helper functions.
+package genericclioptions
+```
+
+### 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
+// ErrSigningMethod defines invalid signing method error.
+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
+// Code must start with 1xxxxx.
+const (
+ // ErrSuccess - 200: OK.
+ ErrSuccess int = iota + 100001
+
+ // ErrUnknown - 500: Internal server error.
+ ErrUnknown
+
+ // ErrBind - 400: Error occurred while binding the request body to the struct.
+ ErrBind
+
+ // ErrValidation - 400: Validation failed.
+ ErrValidation
+)
+```
+
+### 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
+// User represents a user restful resource. It is also used as gorm model.
+type User struct {
+ // Standard object's metadata.
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Nickname string `json:"nickname" gorm:"column:nickname"`
+ Password string `json:"password" gorm:"column:password"`
+ Email string `json:"email" gorm:"column:email"`
+ Phone string `json:"phone" gorm:"column:phone"`
+ IsAdmin int `json:"isAdmin,omitempty" gorm:"column:isAdmin"`
+}
+```
+
+### 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
+// BeforeUpdate run before update database record.
+func (p *Policy) BeforeUpdate() (err error) {
+// normal code
+ return nil
+}
+```
+
+### 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
+// Code defines an error code type.
+type Code int
+```
+
+## 4. Type
+
+### 4.1 Strings
+
+- Empty string judgment.
+
+```go
+// bad
+if s == "" {
+ // normal code
+}
+
+//good
+if len(s) == 0 {
+ // normal code
+}
+```
+
+- `[]byte`/`string` equality comparison.
+
+```go
+// bad
+var s1 []byte
+var s2 []byte
+...
+bytes.Equal(s1, s2) == 0
+bytes.Equal(s1, s2) != 0
+
+//good
+var s1 []byte
+var s2 []byte
+...
+bytes. Compare(s1, s2) == 0
+bytes. Compare(s1, s2) != 0
+```
+
+- Complex strings use raw strings to avoid character escaping.
+
+```go
+// bad
+regexp.MustCompile("\\.")
+
+//good
+regexp.MustCompile(`\.`)
+```
+
+### 4.2 Slicing
+
+- Empty slice judgment.
+
+```go
+// bad
+if len(slice) = 0 {
+ // normal code
+}
+
+//good
+if slice != nil && len(slice) == 0 {
+ // normal code
+}
+```
+
+The above judgment also applies to map and channel.
+
+- Declare a slice.
+
+```go
+// bad
+s := []string{}
+s := make([]string, 0)
+
+//good
+var s[]string
+```
+
+- slice copy.
+
+```go
+// bad
+var b1, b2 []byte
+for i, v := range b1 {
+ b2[i] = v
+}
+for i := range b1 {
+ b2[i] = b1[i]
+}
+
+//good
+copy(b2, b1)
+```
+
+- slice added.
+
+```go
+// bad
+var a, b []int
+for _, v := range a {
+ b = append(b, v)
+}
+
+//good
+var a, b []int
+b = append(b, a...)
+```
+
+### 4.3 Structure
+
+- struct initialization.
+
+The struct is initialized in multi-line format.
+
+```go
+type user struct {
+Id int64
+name string
+}
+
+u1 := user{100, "Colin"}
+
+u2 := user{
+ Id: 200,
+ Name: "Lex",
+}
+```
+
+-
+
+
+
+## 5. Control Structure
+
+### 5.1 if
+
+- if accepts the initialization statement, the convention is to create local variables in the following way.
+
+```go
+if err := loadConfig(); err != nil {
+// error handling
+return err
+}
+```
+
+- if For variables of bool type, true and false judgments should be made directly.
+
+```go
+var isAllow bool
+if isAllow {
+// normal code
+}
+```
+
+### 5.2 for
+
+- Create local variables using short declarations.
+
+```go
+sum := 0
+for i := 0; i < 10; i++ {
+ sum += 1
+}
+```
+
+- Don't use defer in for loop, defer will only be executed when the function exits.
+
+```go
+// bad
+for file := range files {
+ fd, err := os. Open(file)
+ if err != nil {
+ return err
+}
+defer fd. Close()
+// normal code
+}
+
+//good
+for file := range files {
+ func() {
+ fd, err := os. Open(file)
+ if err != nil {
+ return err
+ }
+ defer fd. Close()
+ // normal code
+ }()
+}
+```
+
+### 5.3 range
+
+- If only the first item (key) is needed, discard the second.
+
+```go
+for keyIndex := range keys {
+// normal code
+}
+```
+
+- If only the second item is required, underline the first item.
+
+```go
+sum := 0
+for _, value := range array {
+ sum += value
+}
+```
+
+### 5.4 switch
+
+- must have default.
+
+```go
+switch os := runtime.GOOS; os {
+ case "linux":
+ fmt.Println("Linux.")
+ case "darwin":
+ fmt.Println("OS X.")
+ default:
+ fmt.Printf("%s.\n", os)
+}
+```
+
+### 5.5 goto
+
+- Business code prohibits the use of goto.
+- Try not to use frameworks or other low-level source code.
+
+## 6. Functions
+
+- Incoming variables and return variables start with a lowercase letter.
+- 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.
+- The incoming parameters are map, slice, chan, interface, do not pass pointers.
+
+### 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
+func coordinate() (x, y float64, err error) {
+// normal code
+}
+```
+
+- Both incoming and returned variables start with a lowercase letter.
+- Try to pass by value instead of pointer.
+- The number of parameters cannot exceed 5.
+- Multiple return values can return up to three, and if there are more than three, please use struct.
+
+### 6.2 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).
+- First judge whether there is an error, and then defer to release resources, for example:
+
+```go
+rep, err := http. Get(url)
+if err != nil {
+ return err
+}
+
+defer resp.Body.Close()
+```
+
+### 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.
+- Don't use a single character in the name of the receiver when the function exceeds 20 lines.
+- The name of the receiver cannot use confusing names such as me, this, and self.
+
+### 6.4 Nesting
+
+- The nesting depth cannot exceed 4 levels.
+
+### 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
+// PI...
+const Price = 3.14
+
+func getAppleCost(n float64) float64 {
+return Price * n
+}
+
+func getOrangeCost(n float64) float64 {
+return Price * n
+}
+```
+
+## 7. GOPATH setting specification
+
+- 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.
+- 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. Dependency Management
+
+- Go 1.11 and above must use Go Modules.
+- When using Go Modules as a dependency management project, it is not recommended to submit the vendor directory.
+- When using Go Modules as a dependency management project, the go.sum file must be submitted.
+
+### 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
+type LogHandler struct {
+ h http.Handler
+ log *zap. Logger
+}
+var_http.Handler = LogHandler{}
+```
+
+- 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 Performance
+
+- 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.
+- Prefer strconv over fmt.
+
+### 9.2 Precautions
+
+- append Be careful about automatically allocating memory, append may return a newly allocated address.
+- 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 needs to be locked during concurrency.
+- The conversion of interface{} cannot be checked during compilation, it can only be checked at runtime, be careful to cause panic.
+
+
+
+
+
+## 10 Golang CI Lint
+
+- Golang CI Lint is a fast Go linters runner. It runs linters in parallel, uses caching, and works well with all environments, including CI.
+
+**In local development, you can use the following command to install Golang CI Lint: **
+
+```bash
+make lint
+```
+
+**In CI/CD, Check the Github Actions status code below after you submit the code directly**
+
+[![OpenIM golangci-lint](https://github.com/openimsdk/open-im-server/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/openimsdk/open-im-server/actions/workflows/golangci-lint.yml)
+
+golangci lint can select the types of tools, refer to the official documentation: [https://golangci-lint.run/usage/linters/](https://golangci-lint.run/usage/linters/)
+
+The types of comments we currently use include: [https://github.com/openimsdk/open-im-server/blob/main/.golangci.yml](https://github.com/openimsdk/open-im-server/blob/main/.golangci.yml) the `linters.enable` field in the file.
+
+e.g:
+
+```yaml
+linters:
+ # please, do not use `enable-all`: it's deprecated and will be removed soon.
+ # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
+ # enable-all: true
+ disable-all: true
+ enable:
+ - typecheck # Basic type checking
+ - gofmt # Format check
+ - govet # Go's standard linting tool
+ - gosimple # Suggestions for simplifying code
+ - misspell # Spelling mistakes
+ - staticcheck # Static analysis
+ - unused # Checks for unused code
+ - goimports # Checks if imports are correctly sorted and formatted
+ - godot # Checks for comment punctuation
+ - bodyclose # Ensures HTTP response body is closed
+ - errcheck # Checks for missed error returns
+ fast: true
+```
+
+Add that Chinese comments are not allowed in go code, please write a complete golangci lint specification on the basis of the above.
+
+
+### 10.1 Configuration Document
+
+This configuration document is designed to configure the operational parameters of OpenIM (a hypothetical or specific code analysis tool), customize output formats, and provide detailed settings for specific code checkers (linters). Below is a summary of the document drafted based on the provided configuration information.
+
+#### 10.1 Runtime Options
+
+- **Concurrency** (`concurrency`): Default to use the available CPU count, can be manually set to 4 for parallel analysis.
+- **Timeout** (`timeout`): Timeout duration for analysis operations, default is 1 minute, set here to 5 minutes.
+- **Issue Exit Code** (`issues-exit-code`): Exit code defaults to 1 if at least one issue is found.
+- **Test Files** (`tests`): Whether to include test files, defaults to true.
+- **Build Tags** (`build-tags`): Specify build tags used by all linters, defaults to an empty list. Example adds `mytag`.
+- **Skip Directories** (`skip-dirs`): Configure which directories' issues are not reported, defaults to empty, but some default directories are independently skipped.
+- **Skip Files** (`skip-files`): Specify files where issues should not be reported, supports regular expressions.
+
+#### 10.2 Output Configuration
+
+- **Format** (`format`): Set output format, default is "colored-line-number".
+- **Print Issued Lines** (`print-issued-lines`): Whether to print the lines where issues occur, defaults to true.
+- **Print Linter Name** (`print-linter-name`): Whether to print the linter name at the end of issue text, defaults to true.
+- **Uniqueness Filter** (`uniq-by-line`): Whether to make issue outputs unique per line, defaults to true.
+- **Path Prefix** (`path-prefix`): Prefix to add to output file references, defaults to no prefix.
+- **Sort Results** (`sort-results`): Sort results by file path, line number, and column number.
+
+#### 10.3 Linters Settings
+
+In the configuration file, the `linters-settings` section allows detailed configuration of individual linters. Below are examples of specific linters settings and their purposes:
+
+- **bidichk**: Used to check bidirectional text characters, ensuring correct display direction of text, especially when dealing with mixed left-to-right (LTR) and right-to-left (RTL) text.
+
+- **dogsled**: Monitors excessive use of blank identifiers (`_`) in assignment operations, which may obscure data processing errors or unclear logic.
+
+- **dupl**: Identifies duplicate code blocks, helping developers avoid code redundancy. The `threshold` parameter in settings allows adjustment of code similarity threshold triggering warnings.
+
+- **errcheck**: Checks for unhandled errors. In Go, error handling is achieved by checking function return values. This linter helps ensure all errors are properly handled.
+
+- **exhaustive**: Checks if `switch` statements include all possible values of an enum type, ensuring exhaustiveness of code. This helps avoid forgetting to handle certain cases.
+
+#### 10.4 Example: `errcheck`
+
+**Incorrect Code Example**:
+
+```go
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ f, _ := os.Open("filename.ext")
+ defer f.Close()
+}
+```
+
+**Issue**: In the above code, the error return value of `os.Open` function is explicitly ignored. This is a common mistake as it may lead to unhandled errors and hard-to-trace bugs.
+
+**Correct Form**:
+
+```go
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ f, err := os.Open("filename.ext")
+ if err != nil {
+ fmt.Printf("error opening file: %v\n", err)
+ return
+ }
+ defer f.Close()
+}
+```
+
+In the correct form, by checking the error (`err`) returned by `os.Open`, we gracefully handle error cases rather than simply ignoring them.
+
+#### 10.5 Example: `gofmt`
+
+**Incorrect Code Example**:
+
+```go
+package main
+import "fmt"
+func main() {
+fmt.Println("Hello, world!")
+}
+```
+
+**Issue**: This code snippet doesn't follow Go's standard formatting rules, for example, incorrect indentation of `fmt.Println`.
+
+**Correct Form**:
+
+```go
+package main
+
+import "fmt"
+
+func main() {
+ fmt.Println("Hello, world!")
+}
+```
+
+Using `gofmt` tool can automatically fix such formatting issues, ensuring the code adheres to the coding standards of the Go community.
+
+#### 10.6 Example: `unused`
+
+**Incorrect Code Example**:
+
+```go
+package main
+
+func helper() {}
+
+func main() {}
+```
+
+**Issue**: The `helper` function is defined but not called anywhere, indicating potential redundant code or missing functionality implementation.
+
+**Correct Form**:
+
+```go
+package main
+
+// If the helper function is indeed needed, ensure it's used properly.
+func helper() {
+ // Implement the function's functionality or ensure it's called elsewhere
+}
+
+func main() {
+ helper()
+}
+```
+
+To improve the section on Linters settings in the document, we'll expand with more detailed explanations and reinforce understanding through examples.
+
+#### 10.7 Example: `dogsled`
+
+**Incorrect Code Example**:
+
+```go
+func getValues() (int, int, int) {
+ return 1, 2, 3
+}
+
+func main() {
+ _, _, val := getValues()
+ fmt.Println(val) // Only interested in the third return value
+}
+```
+
+**Explanation**: In the above code, we use two blank identifiers to ignore the first two return values. Excessive use of blank identifiers can make code reading difficult.
+
+**Improved Code**:
+Consider refactoring the function or the usage of return values to reduce the need for blank identifiers or explicitly comment why ignoring certain values is safe.
+
+#### 10.8: `exhaustive`
+
+**Incorrect Code Example**:
+
+```go
+type Fruit int
+
+const (
+ Apple Fruit = iota
+ Banana
+ Orange
+)
+
+func getFruitName(f Fruit) string {
+ switch f {
+ case Apple:
+ return "Apple"
+ case Banana:
+ return "Banana"
+ // Missing handling for Orange
+ }
+ return "Unknown"
+}
+```
+
+**Explanation**: In this code, the `switch` statement doesn't cover all possible values of the `Fruit` type; the case for `Orange` is missing.
+
+**Improved Code**:
+
+```go
+func getFruitName(f Fruit) string {
+ switch f {
+ case Apple:
+ return "Apple"
+ case Banana:
+ return "Banana"
+ case Orange:
+ return "Orange"
+ }
+ return "Unknown"
+}
+```
+
+By adding the missing `case`, we ensure the `switch` statement is exhaustive, handling every possible enum value.
+
+#### 10.9 Optimization of Configuration Files and Application of Code Analysis Tools
+
+Through these examples, we demonstrate how to improve code quality by identifying and fixing common coding issues. OpenIM's configuration files allow developers to customize linters' behavior according to project requirements, ensuring code compliance with predefined quality standards and style guidelines.
+
+By employing these tools and configuration strategies, teams can reduce the number of bugs, enhance code maintainability, and facilitate efficient collaboration during code review processes.
+
+
+
+
+
+
+
diff --git a/docs/contrib/go-doc.md b/docs/contrib/go-doc.md
new file mode 100644
index 0000000000..65b88514e4
--- /dev/null
+++ b/docs/contrib/go-doc.md
@@ -0,0 +1,50 @@
+# Go Language Documentation for OpenIM
+
+In the realm of software development, especially within Go language projects, documentation plays a crucial role in ensuring code maintainability and ease of use. Properly written and accurate documentation is not only essential for understanding and utilizing software effectively but also needs to be easy to write and maintain. This principle is at the heart of OpenIM's approach to supporting commands and generating documentation.
+
+## Supported Commands in OpenIM
+
+OpenIM leverages Go language's documentation standards to facilitate clear and maintainable code documentation. Below are some of the key commands used in OpenIM for documentation purposes:
+
+### `go doc` Command
+
+The `go doc` command is used to print documentation for Go language entities such as variables, constants, functions, structures, and interfaces. This command allows specifying the identifier of the program entity to tailor the output. Examples of `go doc` command usage include:
+
+- `go doc sync.WaitGroup.Add` prints the documentation for a specific method of a type in a package.
+- `go doc -u -all sync.WaitGroup` displays all program entities, including unexported ones, for a specified type.
+- `go doc -u sync` outputs all program entities for a specified package, focusing on exported ones without detailed comments.
+
+### `godoc` Command
+
+For environments lacking internet access, the `godoc` command serves to view the Go language standard library and project dependency library documentation in a web format. Notably, post-Go 1.12 versions, `godoc` is not part of the Go compiler suite. It can be installed using:
+
+```shell
+go get -u -v golang.org/x/tools/cmd/godoc
+```
+
+The `godoc` command, once running, hosts a local web server (by default on port 6060) to facilitate documentation browsing at http://127.0.0.1:6060. It generates documentation based on the GOROOT and GOPATH directories, showcasing both the project's own documentation and that of third-party packages installed via `go get`.
+
+### Custom Documentation Generation Commands in OpenIM
+
+OpenIM includes a suite of commands aimed at initializing, generating, and maintaining project documentation and associated files. Some notable commands are:
+
+- `gen.init`: Initializes the OpenIM server project.
+- `gen.docgo`: Generates missing `doc.go` files for Go packages, crucial for package-level documentation.
+- `gen.errcode.doc`: Generates markdown documentation for OpenIM error codes.
+- `gen.ca`: Generates CA files for all certificates, enhancing security documentation.
+
+These commands underscore the project's commitment to thorough and accessible documentation, supporting both developers and users alike.
+
+## Writing Your Own Documentation
+
+When creating documentation for Go projects, including OpenIM, it's important to follow certain practices:
+
+1. **Commenting**: Use single-line (`//`) and block (`/* */`) comments to provide detailed documentation within the code. Block comments are especially useful for package documentation, which should immediately precede the package statement without any intervening blank lines.
+
+2. **Overview Section**: To create an overview section in the documentation, place a block comment directly before the package statement. This section should succinctly describe the package's purpose and functionality.
+
+3. **Detailed Descriptions**: Comments placed before functions, structures, or variables will be used to generate detailed descriptions in the documentation. Follow the same commenting rules as for the overview section.
+
+4. **Examples**: Include example functions prefixed with `Example` to demonstrate usage. Output from these examples can be documented at the end of the function, starting with `// Output:` followed by the expected result.
+
+Through adherence to these documentation practices, OpenIM ensures that its codebase remains accessible, maintainable, and easy to use for developers and users alike.
\ No newline at end of file
diff --git a/docs/contrib/logging.md b/docs/contrib/logging.md
index c44f6f3c74..03628f427c 100644
--- a/docs/contrib/logging.md
+++ b/docs/contrib/logging.md
@@ -66,7 +66,7 @@ The `CodeError` interface is designed to provide a unified mechanism for error h
## Logging Standards and Code Examples
-In the OpenIM project, we use the unified logging package `github.com/OpenIMSDK/tools/log` for logging to achieve efficient log management and analysis. This logging package supports structured logging formats, making it easier for developers to handle log information.
+In the OpenIM project, we use the unified logging package `github.com/openimsdk/tools/log` for logging to achieve efficient log management and analysis. This logging package supports structured logging formats, making it easier for developers to handle log information.
### Logger Interface and Implementation
@@ -96,7 +96,7 @@ func main() {
## Error Handling and Code Examples
-We use the `github.com/OpenIMSDK/tools/errs` package for unified error handling and wrapping.
+We use the `github.com/openimsdk/tools/errs` package for unified error handling and wrapping.
### CodeError Interface and Implementation
@@ -121,7 +121,7 @@ package main
import (
"fmt"
- "github.com/OpenIMSDK/tools/errs"
+ "github.com/openimsdk/tools/errs"
)
func main() {
@@ -164,7 +164,7 @@ More details")
import (
"context"
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/log"
)
func main() {
@@ -226,7 +226,7 @@ More details")
package main
import (
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/log"
"context"
)
@@ -337,7 +337,19 @@ More details")
// Suppose an error occurs here
err, _ := someFunc()
if err != nil {
- return errs.Wrap(err, "doSomething failed")
+ return errs.WrapMsg(err, "doSomething failed")
+ }
+ }
+ ```
+
+ It just works if the package is wrong:
+
+ ```go
+ func doSomething() error {
+ // Suppose an error occurs here
+ err, _ := someFunc()
+ if err != nil {
+ return errs.Wrap(err)
}
}
```
@@ -383,4 +395,113 @@ More details")
}
return nil
}
- ```
\ No newline at end of file
+ ```
+
+
+### About WrapMsg Use
+
+```go
+// "github.com/openimsdk/tools/errs"
+func WrapMsg(err error, msg string, kv ...any) error {
+ if len(kv) == 0 {
+ if len(msg) == 0 {
+ return errors.WithStack(err)
+ } else {
+ return errors.WithMessage(err, msg)
+ }
+ }
+ var buf bytes.Buffer
+ if len(msg) > 0 {
+ buf.WriteString(msg)
+ buf.WriteString(" ")
+ }
+ for i := 0; i < len(kv); i += 2 {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(toString(kv[i]))
+ buf.WriteString("=")
+ buf.WriteString(toString(kv[i+1]))
+ }
+ return errors.WithMessage(err, buf.String())
+}
+```
+
+1. **Function Signature**:
+ - `err error`: The original error object.
+ - `msg string`: The message text to append to the error.
+ - `kv ...any`: A variable number of parameters used to pass key-value pairs. `any` was introduced in Go 1.18 and is equivalent to `interface{}`, meaning any type.
+
+2. **Logic**:
+ - If there are no key-value pairs (`kv` is empty):
+ - If `msg` is also empty, use `errors.WithStack(err)` to return the original error with the call stack appended.
+ - If `msg` is not empty, use `errors.WithMessage(err, msg)` to append the message text to the original error.
+ - If there are key-value pairs, the function constructs a string containing the message text and all key-value pairs. The key-value pairs are added in the format `"key=value"`, separated by commas. If a message text is provided, it is added first, followed by a space.
+
+3. **Key-Value Pair Formatting**:
+ - A loop iterates over all the key-value pairs, processing one pair at a time.
+ - The `toString` function (although not provided in the code, we can assume it converts any type to a string) is used to convert both keys and values to strings, and they are added to a `bytes.Buffer` in the format `"key=value"`.
+
+4. **Result**:
+ - Use `errors.WithMessage(err, buf.String())` to append the constructed message text to the original error, and return this new error object.
+
+Next, let's demonstrate several ways to use the `WrapMsg` function:
+
+**Example 1: No Additional Information**
+
+```go
+// "github.com/openimsdk/tools/errs"
+err := errors.New("original error")
+wrappedErr := errs.WrapMsg(err, "")
+// wrappedErr will contain the original error and its call stack
+```
+
+**Example 2: Message Text Only**
+
+```go
+// "github.com/openimsdk/tools/errs"
+err := errors.New("original error")
+wrappedErr := errs.WrapMsg(err, "additional error information")
+// wrappedErr will contain the original error, call stack, and "additional error information"
+```
+
+**Example 3: Message Text and Key-Value Pairs**
+
+```go
+// "github.com/openimsdk/tools/errs"
+err := errors.New("original error")
+wrappedErr := errs.WrapMsg(err, "problem occurred", "code", 404, "url", "webhook://example.com")
+// wrappedErr will contain the original error, call stack, and "problem occurred code=404, url=http://example.com"
+```
+
+**Example 4: Key-Value Pairs Only**
+
+```go
+// "github.com/openimsdk/tools/errs"
+err := errors.New("original error")
+wrappedErr := errs.WrapMsg(err, "", "user", "john_doe", "action", "login")
+// wrappedErr will contain the original error, call stack, and "user=john_doe, action=login"
+```
+
+> [!TIP] WThese examples demonstrate how the `errs.WrapMsg` function can flexibly handle error messages and context data, helping developers to more effectively track and debug their programs.
+
+
+### Example 5: Dynamic Key-Value Pairs from Context
+Suppose we have some runtime context variables, such as a user ID and the type of operation being performed, and we want to include these variables in the error message. This can help with later debugging and identifying the specific environment of the issue.
+
+```go
+// Define some context variables
+userID := "user123"
+operation := "update profile"
+errorCode := 500
+requestURL := "webhook://example.com/updateProfile"
+
+// Create a new error
+err := errors.New("original error")
+
+// Wrap the error, including dynamic key-value pairs from the context
+wrappedErr := errs.WrapMsg(err, "operation failed", "user", userID, "action", operation, "code", errorCode, "url", requestURL)
+// wrappedErr will contain the original error, call stack, and "operation failed user=user123, action=update profile, code=500, url=http://example.com/updateProfile"
+```
+
+> [!TIP]In this example, the `WrapMsg` function accepts not just a static error message and additional information, but also dynamic key-value pairs generated from the code's execution context, such as the user ID, operation type, error code, and the URL of the request. Including this contextual information in the error message makes it easier for developers to understand and resolve the issue.
\ No newline at end of file
diff --git a/go.mod b/go.mod
index da28155712..21eca956e3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,12 +1,9 @@
module github.com/openimsdk/open-im-server/v3
-go 1.19
+go 1.21
require (
firebase.google.com/go v3.13.0+incompatible
- github.com/OpenIMSDK/protocol v0.0.56
- github.com/OpenIMSDK/tools v0.0.37
- github.com/bwmarrin/snowflake v0.3.0 // indirect
github.com/dtm-labs/rockscache v0.1.1
github.com/gin-gonic/gin v1.9.1
github.com/go-playground/validator/v10 v10.18.0
@@ -15,36 +12,36 @@ require (
github.com/gorilla/websocket v1.5.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
- github.com/minio/minio-go/v7 v7.0.67
github.com/mitchellh/mapstructure v1.5.0
- github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
- github.com/openimsdk/localcache v0.0.1
+ github.com/openimsdk/protocol v0.0.65
+ github.com/openimsdk/tools v0.0.49-alpha.2
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.18.0
- github.com/robfig/cron/v3 v3.0.1
- github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/stretchr/testify v1.8.4
+ github.com/stretchr/testify v1.9.0
go.mongodb.org/mongo-driver v1.14.0
- golang.org/x/image v0.15.0
google.golang.org/api v0.165.0
- google.golang.org/grpc v1.61.0
- google.golang.org/protobuf v1.32.0
+ google.golang.org/grpc v1.62.1
+ google.golang.org/protobuf v1.33.0
gopkg.in/yaml.v3 v3.0.1
)
require github.com/google/uuid v1.6.0
require (
- github.com/IBM/sarama v1.42.2
- github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
+ github.com/IBM/sarama v1.43.0
+ github.com/fatih/color v1.14.1
github.com/go-redis/redis v6.15.9+incompatible
+ github.com/hashicorp/golang-lru/v2 v2.0.7
+ github.com/kelindar/bitmap v1.5.2
+ github.com/likexian/gokit v0.25.13
+ github.com/openimsdk/gomake v0.0.9
github.com/redis/go-redis/v9 v9.4.0
- github.com/spf13/pflag v1.0.5
+ github.com/robfig/cron/v3 v3.0.1
+ github.com/shirou/gopsutil v3.21.11+incompatible
+ github.com/spf13/viper v1.18.2
github.com/stathat/consistent v1.0.0
- github.com/tencentyun/cos-go-sdk-v5 v0.7.46
+ go.uber.org/automaxprocs v1.5.3
golang.org/x/sync v0.6.0
- gopkg.in/src-d/go-git.v4 v4.13.1
- gotest.tools v2.2.0+incompatible
)
require (
@@ -55,101 +52,112 @@ require (
cloud.google.com/go/iam v1.1.5 // indirect
cloud.google.com/go/longrunning v0.5.4 // indirect
cloud.google.com/go/storage v1.36.0 // indirect
+ github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
- github.com/eapache/go-resiliency v1.5.0 // indirect
+ github.com/eapache/go-resiliency v1.6.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect
github.com/eapache/queue v1.1.0 // indirect
- github.com/emirpasic/gods v1.12.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
+ github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-zookeeper/zk v1.0.3 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/snappy v0.0.4 // indirect
- github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
- github.com/hashicorp/errwrap v1.0.0 // indirect
+ github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
- github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
- github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.4 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
- github.com/jinzhu/copier v0.3.5 // indirect
+ github.com/jinzhu/copier v0.4.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
- github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect
- github.com/klauspost/compress v1.17.4 // indirect
+ github.com/kelindar/simd v1.1.2 // indirect
+ github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lithammer/shortuuid v3.0.0+incompatible // indirect
+ github.com/magefile/mage v1.15.0 // indirect
+ github.com/magiconair/properties v1.8.7 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
+ github.com/minio/minio-go/v7 v7.0.69 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
- github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
github.com/mozillazg/go-httpheader v0.4.0 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.18.1 // indirect
- github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+ github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rs/xid v1.5.0 // indirect
- github.com/sergi/go-diff v1.0.0 // indirect
- github.com/src-d/gcfg v1.4.0 // indirect
- github.com/stretchr/objx v0.5.0 // indirect
+ github.com/sagikazarmark/locafero v0.4.0 // indirect
+ github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.11.0 // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/stretchr/objx v0.5.2 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
+ github.com/tencentyun/cos-go-sdk-v5 v0.7.47 // indirect
+ github.com/tklauser/go-sysconf v0.3.13 // indirect
+ github.com/tklauser/numcpus v0.7.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
- github.com/xanzy/ssh-agent v0.2.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+ github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.23.0 // indirect
go.opentelemetry.io/otel/metric v1.23.0 // indirect
go.opentelemetry.io/otel/trace v1.23.0 // indirect
- go.uber.org/atomic v1.7.0 // indirect
+ go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.3.0 // indirect
- golang.org/x/net v0.21.0 // indirect
+ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
+ golang.org/x/image v0.15.0 // indirect
+ golang.org/x/net v0.22.0 // indirect
golang.org/x/oauth2 v0.17.0 // indirect
- golang.org/x/sys v0.17.0 // indirect
+ golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect
- gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect
- gopkg.in/warnings.v0 v0.1.2 // indirect
- gorm.io/gorm v1.25.4 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
+ gorm.io/gorm v1.25.8 // indirect
stathat.com/c/consistent v1.0.0 // indirect
)
@@ -161,8 +169,6 @@ require (
github.com/spf13/cobra v1.8.0
github.com/ugorji/go/codec v1.2.11 // indirect
go.uber.org/zap v1.24.0 // indirect
- golang.org/x/crypto v0.19.0 // indirect
+ golang.org/x/crypto v0.21.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
-
-replace github.com/openimsdk/localcache => ./pkg/localcache
diff --git a/go.sum b/go.sum
index c3803ef581..aa851c74b8 100644
--- a/go.sum
+++ b/go.sum
@@ -16,28 +16,19 @@ cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYE
firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4=
firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/IBM/sarama v1.42.2 h1:VoY4hVIZ+WQJ8G9KNY/SQlWguBQXQ9uvFPOnrcu8hEw=
-github.com/IBM/sarama v1.42.2/go.mod h1:FLPGUGwYqEs62hq2bVG6Io2+5n+pS6s/WOXVKWSLFtE=
-github.com/OpenIMSDK/protocol v0.0.56 h1:mbVFyDBachEsmJLfYW5AU1z2KL8AUEpoHG8RPCIxjgg=
-github.com/OpenIMSDK/protocol v0.0.56/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
-github.com/OpenIMSDK/tools v0.0.37 h1:qvDqmA4RbEJtPjZouWCkVuf/pjm6Y8nUrG5iH2gcnOg=
-github.com/OpenIMSDK/tools v0.0.37/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE=
+github.com/IBM/sarama v1.43.0 h1:YFFDn8mMI2QL0wOrG0J2sFoVIAFl7hS9JQi2YZsXtJc=
+github.com/IBM/sarama v1.43.0/go.mod h1:zlE6HEbC/SMQ9mhEYaF7nNLYOUyrs0obySKCckWP9BM=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
-github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
+github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
+github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
-github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
-github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
+github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@@ -54,12 +45,13 @@ github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I=
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY=
+github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ=
+github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
@@ -67,40 +59,45 @@ github.com/dtm-labs/rockscache v0.1.1 h1:6S1vgaHvGqrLd8Ka4hRTKeKPV7v+tT0MSkTIX81
github.com/dtm-labs/rockscache v0.1.1/go.mod h1:c76WX0kyIibmQ2ACxUXvDvaLykoPakivMqIxt+UzE7A=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
-github.com/eapache/go-resiliency v1.5.0 h1:dRsaR00whmQD+SgVKlq/vCRFNgtEb5yppyeVos3Yce0=
-github.com/eapache/go-resiliency v1.5.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho=
+github.com/eapache/go-resiliency v1.6.0 h1:CqGDTLtpwuWKn6Nj3uNUdflaq+/kIPsg0gfNzHton30=
+github.com/eapache/go-resiliency v1.6.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho=
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws=
github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
-github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA=
+github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
+github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
+github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
+github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
-github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
-github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
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=
@@ -154,6 +151,7 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw=
+github.com/google/martian/v3 v3.3.2/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
@@ -171,8 +169,9 @@ github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -180,12 +179,12 @@ github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/C
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
@@ -198,32 +197,32 @@ github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh6
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
-github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
-github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
-github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
+github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
+github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
+github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
-github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
+github.com/kelindar/bitmap v1.5.2 h1:XwX7CTvJtetQZ64zrOkApoZZHBJRkjE23NfqUALA/HE=
+github.com/kelindar/bitmap v1.5.2/go.mod h1:j3qZjxH9s4OtvsnFTP2bmPkjqil9Y2xQlxPYHexasEA=
+github.com/kelindar/simd v1.1.2 h1:KduKb+M9cMY2HIH8S/cdJyD+5n5EGgq+Aeeleos55To=
+github.com/kelindar/simd v1.1.2/go.mod h1:inq4DFudC7W8L5fhxoeZflLRNpWSs0GNx6MlWFvuvr0=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
-github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
+github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg=
+github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
@@ -232,20 +231,27 @@ github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkL
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
+github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
+github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5nwm/V115vj2YXfhS0w=
github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w=
+github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
+github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
-github.com/minio/minio-go/v7 v7.0.67 h1:BeBvZWAS+kRJm1vGTMJYVjKUNoo0FoEt/wUWdUtfmh8=
-github.com/minio/minio-go/v7 v7.0.67/go.mod h1:+UXocnUeZ3wHvVh5s95gcrA4YjMIbccT6ubB+1m054A=
+github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=
+github.com/minio/minio-go/v7 v7.0.69/go.mod h1:XAvOPJQ5Xlzk5o3o/ArO2NMbhSGkimC+bpW/ngRKDmQ=
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
@@ -259,8 +265,6 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/mozillazg/go-httpheader v0.4.0 h1:aBn6aRXtFzyDLZ4VIRLsZbbJloagQfMnCiYgOq6hK4w=
github.com/mozillazg/go-httpheader v0.4.0/go.mod h1:PuT8h0pw6efvp8ZeUec1Rs7dwjK08bt6gKSReGMqtdA=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
@@ -275,16 +279,23 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
-github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
-github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
-github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/openimsdk/gomake v0.0.9 h1:ouf25ygN2PMQ68Gfgns/EQRPiLPnp+77SIr68GfE+n4=
+github.com/openimsdk/gomake v0.0.9/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI=
+github.com/openimsdk/protocol v0.0.65 h1:SPT9qyUsFRTTKSKb/FjpS+xr6sxz/Kbnu+su1bxYagc=
+github.com/openimsdk/protocol v0.0.65/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8=
+github.com/openimsdk/tools v0.0.49-alpha.2 h1:8IfV6o2ySU7C54sh/MG7ctEp1h3lSNe03OCUDWSk5Ws=
+github.com/openimsdk/tools v0.0.49-alpha.2/go.mod h1:P4oGP1Pd+d4ctbLD5U/XQTgl8yu8Hd3skx640Fr69ko=
+github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
+github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
+github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -301,26 +312,35 @@ github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
+github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
-github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
+github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
-github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
+github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
+github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stathat/consistent v1.0.0 h1:ZFJ1QTRn8npNBKW065raSZ8xfOqhpb8vLOkfp4CcL/U=
github.com/stathat/consistent v1.0.0/go.mod h1:uajTPbgSygZBJ+V+0mY7meZ8i0XAcZs7AQ6V121XSxw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -328,19 +348,23 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0=
-github.com/tencentyun/cos-go-sdk-v5 v0.7.46 h1:IeTiMR8qZ7iQWhAGb1niw5vt0T1TfAwPeB8Gn/oTkuk=
-github.com/tencentyun/cos-go-sdk-v5 v0.7.46/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.47 h1:uoS4Sob16qEYoapkqJq1D1Vnsy9ira9BfNUMtoFYTI4=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.47/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE=
+github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
+github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
+github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
+github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
-github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
-github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
@@ -352,6 +376,8 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
+github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
@@ -365,11 +391,15 @@ go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFu
go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV+9Cnpo=
go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo=
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
+go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI=
go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk=
-go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
+go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
+go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
@@ -377,16 +407,16 @@ go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
-golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -402,7 +432,6 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -413,8 +442,8 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
-golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
+golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
@@ -429,10 +458,9 @@ golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -444,18 +472,16 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
-golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
+golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
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=
@@ -470,7 +496,6 @@ golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -481,6 +506,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/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/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.165.0 h1:zd5d4JIIIaYYsfVy1HzoXYZ9rWCSBxxAglbczzo7Bgc=
google.golang.org/api v0.165.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
@@ -492,17 +518,17 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe h1:USL2DhxfgRchafRvt/wYyyQNzwgL7ZiURcozOE/Pkvo=
google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
-google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s=
-google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4=
+google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU=
+google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
-google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
+google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
+google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -514,24 +540,16 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
-google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
-gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
-gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
-gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
-gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
-gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@@ -540,10 +558,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw=
-gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
+gorm.io/gorm v1.25.8 h1:WAGEZ/aEcznN4D03laj8DKnehe1e9gYQAjW8xyPRdeo=
+gorm.io/gorm v1.25.8/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
diff --git a/go.work b/go.work
deleted file mode 100644
index 7cc1b80a11..0000000000
--- a/go.work
+++ /dev/null
@@ -1,14 +0,0 @@
-go 1.19
-
-use (
- .
- ./tools/changelog
- ./tools/component
- ./tools/imctl
- ./tools/infra
- ./tools/ncpu
- ./tools/openim-web
- ./tools/url2im
- ./tools/versionchecker
- ./tools/yamlfmt
-)
diff --git a/internal/api/auth.go b/internal/api/auth.go
index 0f7a3933b3..f0790ce984 100644
--- a/internal/api/auth.go
+++ b/internal/api/auth.go
@@ -15,10 +15,10 @@
package api
import (
- "github.com/OpenIMSDK/protocol/auth"
- "github.com/OpenIMSDK/tools/a2r"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/tools/a2r"
)
type AuthApi rpcclient.Auth
diff --git a/internal/api/conversation.go b/internal/api/conversation.go
index fe6c67001e..f273eaa4a7 100644
--- a/internal/api/conversation.go
+++ b/internal/api/conversation.go
@@ -15,10 +15,10 @@
package api
import (
- "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/tools/a2r"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/tools/a2r"
)
type ConversationApi rpcclient.Conversation
diff --git a/internal/api/custom_validator.go b/internal/api/custom_validator.go
index d135b0dc47..99c84f0746 100644
--- a/internal/api/custom_validator.go
+++ b/internal/api/custom_validator.go
@@ -15,8 +15,8 @@
package api
import (
- "github.com/OpenIMSDK/protocol/constant"
"github.com/go-playground/validator/v10"
+ "github.com/openimsdk/protocol/constant"
)
// RequiredIf validates if the specified field is required based on the session type.
@@ -26,7 +26,7 @@ func RequiredIf(fl validator.FieldLevel) bool {
switch sessionType {
case constant.SingleChatType, constant.NotificationChatType:
return fl.FieldName() != "RecvID" || fl.Field().String() != ""
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
return fl.FieldName() != "GroupID" || fl.Field().String() != ""
default:
return true
diff --git a/internal/api/friend.go b/internal/api/friend.go
index 24bcbf8990..1fea38b313 100644
--- a/internal/api/friend.go
+++ b/internal/api/friend.go
@@ -15,10 +15,10 @@
package api
import (
- "github.com/OpenIMSDK/protocol/friend"
- "github.com/OpenIMSDK/tools/a2r"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/friend"
+ "github.com/openimsdk/tools/a2r"
)
type FriendApi rpcclient.Friend
diff --git a/internal/api/group.go b/internal/api/group.go
index c18ded64b1..6079c53437 100644
--- a/internal/api/group.go
+++ b/internal/api/group.go
@@ -15,10 +15,10 @@
package api
import (
- "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/tools/a2r"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/tools/a2r"
)
type GroupApi rpcclient.Group
@@ -115,22 +115,14 @@ func (o *GroupApi) GetGroupAbstractInfo(c *gin.Context) {
a2r.Call(group.GroupClient.GetGroupAbstractInfo, o.Client, c)
}
-//func (g *Group) SetGroupMemberNickname(c *gin.Context) {
+// func (g *Group) SetGroupMemberNickname(c *gin.Context) {
// a2r.Call(group.GroupClient.SetGroupMemberNickname, g.userClient, c)
//}
//
-//func (g *Group) GetGroupAllMemberList(c *gin.Context) {
+// func (g *Group) GetGroupAllMemberList(c *gin.Context) {
// a2r.Call(group.GroupClient.GetGroupAllMember, g.userClient, c)
//}
-func (o *GroupApi) GetJoinedSuperGroupList(c *gin.Context) {
- a2r.Call(group.GroupClient.GetJoinedSuperGroupList, o.Client, c)
-}
-
-func (o *GroupApi) GetSuperGroupsInfo(c *gin.Context) {
- a2r.Call(group.GroupClient.GetSuperGroupsInfo, o.Client, c)
-}
-
func (o *GroupApi) GroupCreateCount(c *gin.Context) {
a2r.Call(group.GroupClient.GroupCreateCount, o.Client, c)
}
diff --git a/internal/api/init.go b/internal/api/init.go
new file mode 100644
index 0000000000..6e784da9a9
--- /dev/null
+++ b/internal/api/init.go
@@ -0,0 +1,114 @@
+// 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 api
+
+import (
+ "context"
+ "fmt"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/network"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+ "strconv"
+ "syscall"
+ "time"
+
+ kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
+ ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/system/program"
+)
+
+type Config struct {
+ RpcConfig config.API
+ MongodbConfig config.Mongo
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ MinioConfig config.Minio
+}
+
+func Start(ctx context.Context, index int, config *Config) error {
+ apiPort, err := datautil.GetElemByIndex(config.RpcConfig.Api.Ports, index)
+ if err != nil {
+ return err
+ }
+ prometheusPort, err := datautil.GetElemByIndex(config.RpcConfig.Prometheus.Ports, index)
+ if err != nil {
+ return err
+ }
+
+ var client discovery.SvcDiscoveryRegistry
+
+ // Determine whether zk is passed according to whether it is a clustered deployment
+ client, err = kdisc.NewDiscoveryRegister(&config.ZookeeperConfig, &config.Share)
+ if err != nil {
+ return errs.WrapMsg(err, "failed to register discovery service")
+ }
+
+ var (
+ netDone = make(chan struct{}, 1)
+ netErr error
+ )
+
+ router := newGinRouter(client, config)
+ if config.RpcConfig.Prometheus.Enable {
+ go func() {
+ p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
+ p.SetListenAddress(fmt.Sprintf(":%d", prometheusPort))
+ if err = p.Use(router); err != nil && err != http.ErrServerClosed {
+ netErr = errs.WrapMsg(err, fmt.Sprintf("prometheus start err: %d", prometheusPort))
+ netDone <- struct{}{}
+ }
+ }()
+
+ }
+ address := net.JoinHostPort(network.GetListenIP(config.RpcConfig.Api.ListenIP), strconv.Itoa(apiPort))
+
+ server := http.Server{Addr: address, Handler: router}
+ log.CInfo(ctx, "API server is initializing", "address", address, "apiPort", apiPort, "prometheusPort", prometheusPort)
+ go func() {
+ err = server.ListenAndServe()
+ if err != nil && err != http.ErrServerClosed {
+ netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr))
+ netDone <- struct{}{}
+
+ }
+ }()
+
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGTERM)
+
+ ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
+ defer cancel()
+ select {
+ case <-sigs:
+ program.SIGTERMExit()
+ err := server.Shutdown(ctx)
+ if err != nil {
+ return errs.WrapMsg(err, "shutdown err")
+ }
+ case <-netDone:
+ close(netDone)
+ return netErr
+ }
+ return nil
+}
diff --git a/internal/api/msg.go b/internal/api/msg.go
index ad5001459e..180342e591 100644
--- a/internal/api/msg.go
+++ b/internal/api/msg.go
@@ -15,15 +15,6 @@
package api
import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/a2r"
- "github.com/OpenIMSDK/tools/apiresp"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/mitchellh/mapstructure"
@@ -31,23 +22,38 @@ import (
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/a2r"
+ "github.com/openimsdk/tools/apiresp"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/idutil"
+ "github.com/openimsdk/tools/utils/jsonutil"
+ "github.com/openimsdk/tools/utils/timeutil"
)
type MessageApi struct {
*rpcclient.Message
validate *validator.Validate
userRpcClient *rpcclient.UserRpcClient
+ imAdminUserID []string
}
-func NewMessageApi(msgRpcClient *rpcclient.Message, userRpcClient *rpcclient.User) MessageApi {
- return MessageApi{Message: msgRpcClient, validate: validator.New(), userRpcClient: rpcclient.NewUserRpcClientByUser(userRpcClient)}
+func NewMessageApi(msgRpcClient *rpcclient.Message, userRpcClient *rpcclient.User,
+ imAdminUserID []string) MessageApi {
+ return MessageApi{Message: msgRpcClient, validate: validator.New(),
+ userRpcClient: rpcclient.NewUserRpcClientByUser(userRpcClient), imAdminUserID: imAdminUserID}
}
func (MessageApi) SetOptions(options map[string]bool, value bool) {
- utils.SetSwitchFromOptions(options, constant.IsHistory, value)
- utils.SetSwitchFromOptions(options, constant.IsPersistent, value)
- utils.SetSwitchFromOptions(options, constant.IsSenderSync, value)
- utils.SetSwitchFromOptions(options, constant.IsConversationUpdate, value)
+ datautil.SetSwitchFromOptions(options, constant.IsHistory, value)
+ datautil.SetSwitchFromOptions(options, constant.IsPersistent, value)
+ datautil.SetSwitchFromOptions(options, constant.IsSenderSync, value)
+ datautil.SetSwitchFromOptions(options, constant.IsConversationUpdate, value)
}
func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg) *msg.SendMsgReq {
@@ -56,8 +62,8 @@ func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg)
switch params.ContentType {
case constant.OANotification:
notification := sdkws.NotificationElem{}
- notification.Detail = utils.StructToJsonString(params.Content)
- newContent = utils.StructToJsonString(¬ification)
+ notification.Detail = jsonutil.StructToJsonString(params.Content)
+ newContent = jsonutil.StructToJsonString(¬ification)
case constant.Text:
fallthrough
case constant.Picture:
@@ -71,19 +77,19 @@ func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg)
case constant.File:
fallthrough
default:
- newContent = utils.StructToJsonString(params.Content)
+ newContent = jsonutil.StructToJsonString(params.Content)
}
if params.IsOnlineOnly {
m.SetOptions(options, false)
}
if params.NotOfflinePush {
- utils.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
+ datautil.SetSwitchFromOptions(options, constant.IsOfflinePush, false)
}
pbData := msg.SendMsgReq{
MsgData: &sdkws.MsgData{
SendID: params.SendID,
GroupID: params.GroupID,
- ClientMsgID: utils.GetMsgID(params.SendID),
+ ClientMsgID: idutil.GetMsgIDByMD5(params.SendID),
SenderPlatformID: params.SenderPlatformID,
SenderNickname: params.SenderNickname,
SenderFaceURL: params.SenderFaceURL,
@@ -91,7 +97,7 @@ func (m MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg)
MsgFrom: constant.SysMsgType,
ContentType: params.ContentType,
Content: []byte(newContent),
- CreateTime: utils.GetCurrentTimestampByMill(),
+ CreateTime: timeutil.GetCurrentTimestampByMill(),
SendTime: params.SendTime,
Options: options,
OfflinePushInfo: params.OfflinePushInfo,
@@ -173,14 +179,14 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM
return nil, err
}
default:
- return nil, errs.ErrArgs.WithDetail("not support err contentType")
+ return nil, errs.WrapMsg(errs.ErrArgs, "unsupported content type", "contentType", req.ContentType)
}
if err := mapstructure.WeakDecode(req.Content, &data); err != nil {
- return nil, err
+ return nil, errs.WrapMsg(err, "failed to decode message content")
}
- log.ZDebug(c, "getSendMsgReq", "req", req.Content)
+ log.ZDebug(c, "getSendMsgReq", "decodedContent", data)
if err := m.validate.Struct(data); err != nil {
- return nil, err
+ return nil, errs.WrapMsg(err, "validation error")
}
return m.newUserSendMsgReq(c, &req), nil
}
@@ -198,9 +204,9 @@ func (m *MessageApi) SendMessage(c *gin.Context) {
}
// Check if the user has the app manager role.
- if !authverify.IsAppManagerUid(c, m.Config) {
+ if !authverify.IsAppManagerUid(c, m.imAdminUserID) {
// Respond with a permission error if the user is not an app manager.
- apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
+ apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
return
}
@@ -253,16 +259,16 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
return
}
- if !authverify.IsAppManagerUid(c, m.Config) {
- apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
+ if !authverify.IsAppManagerUid(c, m.imAdminUserID) {
+ apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
return
}
sendMsgReq := msg.SendMsgReq{
MsgData: &sdkws.MsgData{
SendID: req.SendUserID,
RecvID: req.RecvUserID,
- Content: []byte(utils.StructToJsonString(&sdkws.NotificationElem{
- Detail: utils.StructToJsonString(&struct {
+ Content: []byte(jsonutil.StructToJsonString(&sdkws.NotificationElem{
+ Detail: jsonutil.StructToJsonString(&struct {
Key string `json:"key"`
Data string `json:"data"`
}{Key: req.Key, Data: req.Data}),
@@ -270,9 +276,9 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) {
MsgFrom: constant.SysMsgType,
ContentType: constant.BusinessNotification,
SessionType: constant.SingleChatType,
- CreateTime: utils.GetCurrentTimestampByMill(),
- ClientMsgID: utils.GetMsgID(mcontext.GetOpUserID(c)),
- Options: config.GetOptionsByNotification(config.NotificationConf{
+ CreateTime: timeutil.GetCurrentTimestampByMill(),
+ ClientMsgID: idutil.GetMsgIDByMD5(mcontext.GetOpUserID(c)),
+ Options: config.GetOptionsByNotification(config.NotificationConfig{
IsSendMsg: false,
ReliabilityLevel: 1,
UnreadCount: false,
@@ -296,9 +302,8 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) {
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return
}
- log.ZInfo(c, "BatchSendMsg", "req", req)
- if err := authverify.CheckAdmin(c, m.Config); err != nil {
- apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message"))
+ if err := authverify.CheckAdmin(c, m.imAdminUserID); err != nil {
+ apiresp.GinError(c, errs.ErrNoPermission.WrapMsg("only app manager can send message"))
return
}
diff --git a/internal/api/route.go b/internal/api/router.go
similarity index 59%
rename from internal/api/route.go
rename to internal/api/router.go
index ca0b6829ef..bd2de99db7 100644
--- a/internal/api/route.go
+++ b/internal/api/router.go
@@ -1,135 +1,25 @@
-// 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 api
import (
- "context"
"fmt"
- "net"
- "net/http"
- "os"
- "os/signal"
- "strconv"
- "syscall"
- "time"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/apiresp"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mw"
- "github.com/OpenIMSDK/tools/tokenverify"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"github.com/go-playground/validator/v10"
- "github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
- ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
- "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
- "github.com/redis/go-redis/v9"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/apiresp"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mw"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
+ "net/http"
)
-func Start(config *config.GlobalConfig, port int, proPort int) error {
- if port == 0 || proPort == 0 {
- err := "port or proPort is empty:" + strconv.Itoa(port) + "," + strconv.Itoa(proPort)
- return errs.Wrap(fmt.Errorf(err))
- }
- rdb, err := cache.NewRedis(config)
- if err != nil {
- return err
- }
-
- var client discoveryregistry.SvcDiscoveryRegistry
-
- // Determine whether zk is passed according to whether it is a clustered deployment
- client, err = kdisc.NewDiscoveryRegister(config)
- if err != nil {
- return errs.Wrap(err, "register discovery err")
- }
-
- if err = client.CreateRpcRootNodes(config.GetServiceNames()); err != nil {
- return errs.Wrap(err, "create rpc root nodes error")
- }
-
- if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.EncodeConfig()); err != nil {
- return errs.Wrap(err)
- }
- var (
- netDone = make(chan struct{}, 1)
- netErr error
- )
- router := newGinRouter(client, rdb, config)
- if config.Prometheus.Enable {
- go func() {
- p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api"))
- p.SetListenAddress(fmt.Sprintf(":%d", proPort))
- if err = p.Use(router); err != nil && err != http.ErrServerClosed {
- netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", proPort))
- netDone <- struct{}{}
- }
- }()
-
- }
-
- var address string
- if config.Api.ListenIP != "" {
- address = net.JoinHostPort(config.Api.ListenIP, strconv.Itoa(port))
- } else {
- address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port))
- }
-
- server := http.Server{Addr: address, Handler: router}
-
- go func() {
- err = server.ListenAndServe()
- if err != nil && err != http.ErrServerClosed {
- netErr = errs.Wrap(err, fmt.Sprintf("api start err: %s", server.Addr))
- netDone <- struct{}{}
-
- }
- }()
-
- sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGTERM)
-
- ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
- defer cancel()
- select {
- case <-sigs:
- util.SIGTERMExit()
- err := server.Shutdown(ctx)
- if err != nil {
- return errs.Wrap(err, "shutdown err")
- }
- case <-netDone:
- close(netDone)
- return netErr
- }
- return nil
-}
-
-func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient, config *config.GlobalConfig) *gin.Engine {
- disCov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
+func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.Engine {
+ disCov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
+ grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
gin.SetMode(gin.ReleaseMode)
r := gin.New()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
@@ -137,17 +27,18 @@ func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
}
r.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID())
// init rpc client here
- userRpc := rpcclient.NewUser(disCov, config)
- groupRpc := rpcclient.NewGroup(disCov, config)
- friendRpc := rpcclient.NewFriend(disCov, config)
- messageRpc := rpcclient.NewMessage(disCov, config)
- conversationRpc := rpcclient.NewConversation(disCov, config)
- authRpc := rpcclient.NewAuth(disCov, config)
- thirdRpc := rpcclient.NewThird(disCov, config)
+ userRpc := rpcclient.NewUser(disCov, config.Share.RpcRegisterName.User, config.Share.RpcRegisterName.MessageGateway,
+ config.Share.IMAdminUserID)
+ groupRpc := rpcclient.NewGroup(disCov, config.Share.RpcRegisterName.Group)
+ friendRpc := rpcclient.NewFriend(disCov, config.Share.RpcRegisterName.Friend)
+ messageRpc := rpcclient.NewMessage(disCov, config.Share.RpcRegisterName.Msg)
+ conversationRpc := rpcclient.NewConversation(disCov, config.Share.RpcRegisterName.Conversation)
+ authRpc := rpcclient.NewAuth(disCov, config.Share.RpcRegisterName.Auth)
+ thirdRpc := rpcclient.NewThird(disCov, config.Share.RpcRegisterName.Third, config.RpcConfig.Prometheus.GrafanaURL)
u := NewUserApi(*userRpc)
- m := NewMessageApi(messageRpc, userRpc)
- ParseToken := GinParseToken(rdb, config)
+ m := NewMessageApi(messageRpc, userRpc, config.Share.IMAdminUserID)
+ ParseToken := GinParseToken(authRpc)
userRouterGroup := r.Group("/user")
{
userRouterGroup.POST("/user_register", u.UserRegister)
@@ -224,11 +115,6 @@ func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
groupRouterGroup.POST("/get_groups", g.GetGroups)
groupRouterGroup.POST("/get_group_member_user_id", g.GetGroupMemberUserIDs)
}
- superGroupRouterGroup := r.Group("/super_group", ParseToken)
- {
- superGroupRouterGroup.POST("/get_joined_group_list", g.GetJoinedSuperGroupList)
- superGroupRouterGroup.POST("/get_groups_info", g.GetSuperGroupsInfo)
- }
// certificate
authRouterGroup := r.Group("/auth")
{
@@ -309,68 +195,26 @@ func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive
return r
}
-func GinParseToken(rdb redis.UniversalClient, config *config.GlobalConfig) gin.HandlerFunc {
- dataBase := controller.NewAuthDatabase(
- cache.NewMsgCacheModel(rdb, config),
- config.Secret,
- config.TokenPolicy.Expire,
- config,
- )
+func GinParseToken(authRPC *rpcclient.Auth) gin.HandlerFunc {
return func(c *gin.Context) {
switch c.Request.Method {
case http.MethodPost:
token := c.Request.Header.Get(constant.Token)
if token == "" {
- log.ZWarn(c, "header get token error", errs.ErrArgs.Wrap("header must have token"))
- apiresp.GinError(c, errs.ErrArgs.Wrap("header must have token"))
- c.Abort()
- return
- }
- claims, err := tokenverify.GetClaimFromToken(token, authverify.Secret(config.Secret))
- if err != nil {
- log.ZWarn(c, "jwt get token error", errs.ErrTokenUnknown.Wrap())
- apiresp.GinError(c, errs.ErrTokenUnknown.Wrap())
+ log.ZWarn(c, "header get token error", servererrs.ErrArgs.WrapMsg("header must have token"))
+ apiresp.GinError(c, servererrs.ErrArgs.WrapMsg("header must have token"))
c.Abort()
return
}
- m, err := dataBase.GetTokensWithoutError(c, claims.UserID, claims.PlatformID)
+ resp, err := authRPC.ParseToken(c, token)
if err != nil {
- apiresp.GinError(c, errs.ErrTokenNotExist.Wrap())
- c.Abort()
- return
- }
- if len(m) == 0 {
- apiresp.GinError(c, errs.ErrTokenNotExist.Wrap())
+ apiresp.GinError(c, err)
c.Abort()
return
}
- if v, ok := m[token]; ok {
- switch v {
- case constant.NormalToken:
- case constant.KickedToken:
- apiresp.GinError(c, errs.ErrTokenKicked.Wrap())
- c.Abort()
- return
- default:
- apiresp.GinError(c, errs.ErrTokenUnknown.Wrap())
- c.Abort()
- return
- }
- } else {
- apiresp.GinError(c, errs.ErrTokenNotExist.Wrap())
- c.Abort()
- return
- }
- c.Set(constant.OpUserPlatform, constant.PlatformIDToName(claims.PlatformID))
- c.Set(constant.OpUserID, claims.UserID)
+ c.Set(constant.OpUserPlatform, constant.PlatformIDToName(int(resp.PlatformID)))
+ c.Set(constant.OpUserID, resp.UserID)
c.Next()
}
}
}
-
-// // handleGinError logs and returns an error response through Gin context.
-// func handleGinError(c *gin.Context, logMessage string, errType errs.CodeError, detail string) {
-// wrappedErr := errType.Wrap(detail)
-// apiresp.GinError(c, wrappedErr)
-// c.Abort()
-// }
diff --git a/internal/api/statistics.go b/internal/api/statistics.go
index 2b80a15857..f5ee99f733 100644
--- a/internal/api/statistics.go
+++ b/internal/api/statistics.go
@@ -15,10 +15,10 @@
package api
import (
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/a2r"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/a2r"
)
type StatisticsApi rpcclient.User
diff --git a/internal/api/third.go b/internal/api/third.go
index 30448ae4dd..6baa70ee5d 100644
--- a/internal/api/third.go
+++ b/internal/api/third.go
@@ -15,16 +15,20 @@
package api
import (
+ "context"
+ "google.golang.org/grpc"
"math/rand"
"net/http"
+ "net/url"
"strconv"
+ "strings"
- "github.com/OpenIMSDK/protocol/third"
- "github.com/OpenIMSDK/tools/a2r"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mcontext"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/a2r"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/mcontext"
)
type ThirdApi rpcclient.Third
@@ -43,6 +47,35 @@ func (o *ThirdApi) SetAppBadge(c *gin.Context) {
// #################### s3 ####################
+func setURLPrefixOption[A, B, C any](_ func(client C, ctx context.Context, req *A, options ...grpc.CallOption) (*B, error), fn func(*A) error) *a2r.Option[A, B] {
+ return &a2r.Option[A, B]{
+ BindAfter: fn,
+ }
+}
+
+func setURLPrefix(c *gin.Context, urlPrefix *string) error {
+ host := c.GetHeader("X-Request-Api")
+ if host != "" {
+ if strings.HasSuffix(host, "/") {
+ *urlPrefix = host + "object/"
+ return nil
+ } else {
+ *urlPrefix = host + "/object/"
+ return nil
+ }
+ }
+ u := url.URL{
+ Scheme: "http",
+ Host: c.Request.Host,
+ Path: "/object/",
+ }
+ if c.Request.TLS != nil {
+ u.Scheme = "https"
+ }
+ *urlPrefix = u.String()
+ return nil
+}
+
func (o *ThirdApi) PartLimit(c *gin.Context) {
a2r.Call(third.ThirdClient.PartLimit, o.Client, c)
}
@@ -52,7 +85,10 @@ func (o *ThirdApi) PartSize(c *gin.Context) {
}
func (o *ThirdApi) InitiateMultipartUpload(c *gin.Context) {
- a2r.Call(third.ThirdClient.InitiateMultipartUpload, o.Client, c)
+ opt := setURLPrefixOption(third.ThirdClient.InitiateMultipartUpload, func(req *third.InitiateMultipartUploadReq) error {
+ return setURLPrefix(c, &req.UrlPrefix)
+ })
+ a2r.Call(third.ThirdClient.InitiateMultipartUpload, o.Client, c, opt)
}
func (o *ThirdApi) AuthSign(c *gin.Context) {
@@ -60,7 +96,10 @@ func (o *ThirdApi) AuthSign(c *gin.Context) {
}
func (o *ThirdApi) CompleteMultipartUpload(c *gin.Context) {
- a2r.Call(third.ThirdClient.CompleteMultipartUpload, o.Client, c)
+ opt := setURLPrefixOption(third.ThirdClient.CompleteMultipartUpload, func(req *third.CompleteMultipartUploadReq) error {
+ return setURLPrefix(c, &req.UrlPrefix)
+ })
+ a2r.Call(third.ThirdClient.CompleteMultipartUpload, o.Client, c, opt)
}
func (o *ThirdApi) AccessURL(c *gin.Context) {
@@ -72,7 +111,10 @@ func (o *ThirdApi) InitiateFormData(c *gin.Context) {
}
func (o *ThirdApi) CompleteFormData(c *gin.Context) {
- a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c)
+ opt := setURLPrefixOption(third.ThirdClient.CompleteFormData, func(req *third.CompleteFormDataReq) error {
+ return setURLPrefix(c, &req.UrlPrefix)
+ })
+ a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c, opt)
}
func (o *ThirdApi) ObjectRedirect(c *gin.Context) {
@@ -126,5 +168,5 @@ func (o *ThirdApi) SearchLogs(c *gin.Context) {
}
func (o *ThirdApi) GetPrometheus(c *gin.Context) {
- c.Redirect(http.StatusFound, o.Config.Prometheus.GrafanaUrl)
+ c.Redirect(http.StatusFound, o.GrafanaUrl)
}
diff --git a/internal/api/user.go b/internal/api/user.go
index 16b453e467..d48111b9eb 100644
--- a/internal/api/user.go
+++ b/internal/api/user.go
@@ -15,15 +15,15 @@
package api
import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msggateway"
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/a2r"
- "github.com/OpenIMSDK/tools/apiresp"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
"github.com/gin-gonic/gin"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msggateway"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/a2r"
+ "github.com/openimsdk/tools/apiresp"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
)
type UserApi rpcclient.User
@@ -69,7 +69,7 @@ func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) {
apiresp.GinError(c, err)
return
}
- conns, err := u.Discov.GetConns(c, u.Config.RpcRegisterName.OpenImMessageGatewayName)
+ conns, err := u.Discov.GetConns(c, u.MessageGateWayRpcName)
if err != nil {
apiresp.GinError(c, err)
return
@@ -133,7 +133,7 @@ func (u *UserApi) GetUsersOnlineTokenDetail(c *gin.Context) {
apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap())
return
}
- conns, err := u.Discov.GetConns(c, u.Config.RpcRegisterName.OpenImMessageGatewayName)
+ conns, err := u.Discov.GetConns(c, u.MessageGateWayRpcName)
if err != nil {
apiresp.GinError(c, err)
return
diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go
index afb83bcc48..1750f779bf 100644
--- a/internal/msggateway/callback.go
+++ b/internal/msggateway/callback.go
@@ -18,21 +18,17 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/mcontext"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/mcontext"
)
-func CallbackUserOnline(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int, isAppBackground bool, connID string) error {
- if !globalConfig.Callback.CallbackUserOnline.Enable {
- return nil
- }
+func (ws *WsServer) webhookAfterUserOnline(ctx context.Context, after *config.AfterConfig, userID string, platformID int, isAppBackground bool, connID string) {
req := cbapi.CallbackUserOnlineReq{
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
- CallbackCommand: cbapi.CallbackUserOnlineCommand,
+ CallbackCommand: cbapi.CallbackAfterUserOnlineCommand,
OperationID: mcontext.GetOperationID(ctx),
PlatformID: platformID,
Platform: constant.PlatformIDToName(platformID),
@@ -43,21 +39,14 @@ func CallbackUserOnline(ctx context.Context, globalConfig *config.GlobalConfig,
IsAppBackground: isAppBackground,
ConnID: connID,
}
- resp := cbapi.CommonCallbackResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, &req, &resp, globalConfig.Callback.CallbackUserOnline); err != nil {
- return err
- }
- return nil
+ ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CommonCallbackResp{}, after)
}
-func CallbackUserOffline(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int, connID string) error {
- if !globalConfig.Callback.CallbackUserOffline.Enable {
- return nil
- }
+func (ws *WsServer) webhookAfterUserOffline(ctx context.Context, after *config.AfterConfig, userID string, platformID int, connID string) {
req := &cbapi.CallbackUserOfflineReq{
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
- CallbackCommand: cbapi.CallbackUserOfflineCommand,
+ CallbackCommand: cbapi.CallbackAfterUserOfflineCommand,
OperationID: mcontext.GetOperationID(ctx),
PlatformID: platformID,
Platform: constant.PlatformIDToName(platformID),
@@ -67,21 +56,14 @@ func CallbackUserOffline(ctx context.Context, globalConfig *config.GlobalConfig,
Seq: time.Now().UnixMilli(),
ConnID: connID,
}
- resp := &cbapi.CallbackUserOfflineResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackUserOffline); err != nil {
- return err
- }
- return nil
+ ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackUserOfflineResp{}, after)
}
-func CallbackUserKickOff(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int) error {
- if !globalConfig.Callback.CallbackUserKickOff.Enable {
- return nil
- }
+func (ws *WsServer) webhookAfterUserKickOff(ctx context.Context, after *config.AfterConfig, userID string, platformID int) {
req := &cbapi.CallbackUserKickOffReq{
UserStatusCallbackReq: cbapi.UserStatusCallbackReq{
UserStatusBaseCallback: cbapi.UserStatusBaseCallback{
- CallbackCommand: cbapi.CallbackUserKickOffCommand,
+ CallbackCommand: cbapi.CallbackAfterUserKickOffCommand,
OperationID: mcontext.GetOperationID(ctx),
PlatformID: platformID,
Platform: constant.PlatformIDToName(platformID),
@@ -90,93 +72,5 @@ func CallbackUserKickOff(ctx context.Context, globalConfig *config.GlobalConfig,
},
Seq: time.Now().UnixMilli(),
}
- resp := &cbapi.CommonCallbackResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackUserOffline); err != nil {
- return err
- }
- return nil
+ ws.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CommonCallbackResp{}, after)
}
-
-// func callbackUserOnline(operationID, userID string, platformID int, token string, isAppBackground bool, connID
-// string) cbApi.CommonCallbackResp {
-// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
-// if !config.Config.Callback.CallbackUserOnline.WithEnable {
-// return callbackResp
-// }
-// callbackUserOnlineReq := cbApi.CallbackUserOnlineReq{
-// Token: token,
-// UserStatusCallbackReq: cbApi.UserStatusCallbackReq{
-// UserStatusBaseCallback: cbApi.UserStatusBaseCallback{
-// CallbackCommand: constant.CallbackUserOnlineCommand,
-// OperationID: operationID,
-// PlatformID: int32(platformID),
-// Platform: constant.PlatformIDToName(platformID),
-// },
-// UserID: userID,
-// },
-// Seq: int(time.Now().UnixNano() / 1e6),
-// IsAppBackground: isAppBackground,
-// ConnID: connID,
-// }
-// callbackUserOnlineResp := &cbApi.CallbackUserOnlineResp{CommonCallbackResp: &callbackResp}
-// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOnlineCommand,
-// callbackUserOnlineReq, callbackUserOnlineResp, config.Config.Callback.CallbackUserOnline.CallbackTimeOut); err != nil
-// {
-// callbackResp.ErrCode = http2.StatusInternalServerError
-// callbackResp.ErrMsg = err.Error()
-// }
-// return callbackResp
-//}
-//func callbackUserOffline(operationID, userID string, platformID int, connID string) cbApi.CommonCallbackResp {
-// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
-// if !config.Config.Callback.CallbackUserOffline.WithEnable {
-// return callbackResp
-// }
-// callbackOfflineReq := cbApi.CallbackUserOfflineReq{
-// UserStatusCallbackReq: cbApi.UserStatusCallbackReq{
-// UserStatusBaseCallback: cbApi.UserStatusBaseCallback{
-// CallbackCommand: constant.CallbackUserOfflineCommand,
-// OperationID: operationID,
-// PlatformID: int32(platformID),
-// Platform: constant.PlatformIDToName(platformID),
-// },
-// UserID: userID,
-// },
-// Seq: int(time.Now().UnixNano() / 1e6),
-// ConnID: connID,
-// }
-// callbackUserOfflineResp := &cbApi.CallbackUserOfflineResp{CommonCallbackResp: &callbackResp}
-// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserOfflineCommand,
-// callbackOfflineReq, callbackUserOfflineResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err != nil
-// {
-// callbackResp.ErrCode = http2.StatusInternalServerError
-// callbackResp.ErrMsg = err.Error()
-// }
-// return callbackResp
-//}
-//func callbackUserKickOff(operationID string, userID string, platformID int) cbApi.CommonCallbackResp {
-// callbackResp := cbApi.CommonCallbackResp{OperationID: operationID}
-// if !config.Config.Callback.CallbackUserKickOff.WithEnable {
-// return callbackResp
-// }
-// callbackUserKickOffReq := cbApi.CallbackUserKickOffReq{
-// UserStatusCallbackReq: cbApi.UserStatusCallbackReq{
-// UserStatusBaseCallback: cbApi.UserStatusBaseCallback{
-// CallbackCommand: constant.CallbackUserKickOffCommand,
-// OperationID: operationID,
-// PlatformID: int32(platformID),
-// Platform: constant.PlatformIDToName(platformID),
-// },
-// UserID: userID,
-// },
-// Seq: int(time.Now().UnixNano() / 1e6),
-// }
-// callbackUserKickOffResp := &cbApi.CallbackUserKickOffResp{CommonCallbackResp: &callbackResp}
-// if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, constant.CallbackUserKickOffCommand,
-// callbackUserKickOffReq, callbackUserKickOffResp, config.Config.Callback.CallbackUserOffline.CallbackTimeOut); err !=
-// nil {
-// callbackResp.ErrCode = http2.StatusInternalServerError
-// callbackResp.ErrMsg = err.Error()
-// }
-// return callbackResp
-//}
diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go
index 4e843821e2..af869dd850 100644
--- a/internal/msggateway/client.go
+++ b/internal/msggateway/client.go
@@ -16,28 +16,27 @@ package msggateway
import (
"context"
- "errors"
"fmt"
"runtime/debug"
"sync"
"sync/atomic"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/apiresp"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/apiresp"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/stringutil"
"google.golang.org/protobuf/proto"
)
var (
- ErrConnClosed = errors.New("conn has closed")
- ErrNotSupportMessageProtocol = errors.New("not support message protocol")
- ErrClientClosed = errors.New("client actively close the connection")
- ErrPanic = errors.New("panic error")
+ ErrConnClosed = errs.New("conn has closed")
+ ErrNotSupportMessageProtocol = errs.New("not support message protocol")
+ ErrClientClosed = errs.New("client actively close the connection")
+ ErrPanic = errs.New("panic error")
)
const (
@@ -75,32 +74,20 @@ type Client struct {
token string
}
-// function not used
-// func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client {
-// return &Client{
-// w: new(sync.Mutex),
-// conn: conn,
-// PlatformID: utils.StringToInt(ctx.GetPlatformID()),
-// IsCompress: isCompress,
-// UserID: ctx.GetUserID(),
-// ctx: ctx,
-// }
-// }
-
// ResetClient updates the client's state with new connection and context information.
-func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, isBackground, isCompress bool, longConnServer LongConnServer, token string) {
+func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, longConnServer LongConnServer) {
c.w = new(sync.Mutex)
c.conn = conn
- c.PlatformID = utils.StringToInt(ctx.GetPlatformID())
- c.IsCompress = isCompress
- c.IsBackground = isBackground
+ c.PlatformID = stringutil.StringToInt(ctx.GetPlatformID())
+ c.IsCompress = ctx.GetCompression()
+ c.IsBackground = ctx.GetBackground()
c.UserID = ctx.GetUserID()
c.ctx = ctx
c.longConnServer = longConnServer
c.IsBackground = false
c.closed.Store(false)
c.closedErr = nil
- c.token = token
+ c.token = ctx.GetToken()
}
func (c *Client) pingHandler(_ string) error {
@@ -126,6 +113,7 @@ func (c *Client) readMessage() {
c.conn.SetPingHandler(c.pingHandler)
for {
+ log.ZDebug(c.ctx, "readMessage")
messageType, message, returnErr := c.conn.ReadMessage()
if returnErr != nil {
log.ZWarn(c.ctx, "readMessage", returnErr, "messageType", messageType)
@@ -187,7 +175,7 @@ func (c *Client) handleMessage(message []byte) error {
}
if binaryReq.SendID != c.UserID {
- return errs.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String())
+ return errs.New("exception conn userID not same to req userID", "binaryReq", binaryReq.String())
}
ctx := mcontext.WithMustInfoCtx(
@@ -267,7 +255,7 @@ func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, re
}
if binaryReq.ReqIdentifier == WsLogoutMsg {
- return errs.Wrap(errors.New("user logout"))
+ return errs.New("user logout", "operationID", binaryReq.OperationID).Wrap()
}
return nil
}
diff --git a/internal/msggateway/compressor.go b/internal/msggateway/compressor.go
index 140aac4d86..52d315b79b 100644
--- a/internal/msggateway/compressor.go
+++ b/internal/msggateway/compressor.go
@@ -20,7 +20,7 @@ import (
"io"
"sync"
- "github.com/OpenIMSDK/tools/errs"
+ "github.com/openimsdk/tools/errs"
)
var (
@@ -34,6 +34,7 @@ type Compressor interface {
DeCompress(compressedData []byte) ([]byte, error)
DecompressWithPool(compressedData []byte) ([]byte, error)
}
+
type GzipCompressor struct {
compressProtocol string
}
@@ -47,11 +48,11 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) {
gz := gzip.NewWriter(&gzipBuffer)
if _, err := gz.Write(rawData); err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.Compress: writing to gzip writer failed")
+ return nil, errs.WrapMsg(err, "GzipCompressor.Compress: writing to gzip writer failed")
}
if err := gz.Close(); err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.Compress: closing gzip writer failed")
+ return nil, errs.WrapMsg(err, "GzipCompressor.Compress: closing gzip writer failed")
}
return gzipBuffer.Bytes(), nil
@@ -65,10 +66,10 @@ func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) {
gz.Reset(&gzipBuffer)
if _, err := gz.Write(rawData); err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.CompressWithPool: error writing data")
+ return nil, errs.WrapMsg(err, "GzipCompressor.CompressWithPool: error writing data")
}
if err := gz.Close(); err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.CompressWithPool: error closing gzip writer")
+ return nil, errs.WrapMsg(err, "GzipCompressor.CompressWithPool: error closing gzip writer")
}
return gzipBuffer.Bytes(), nil
}
@@ -77,16 +78,16 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) {
buff := bytes.NewBuffer(compressedData)
reader, err := gzip.NewReader(buff)
if err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.DeCompress: NewReader creation failed")
+ return nil, errs.WrapMsg(err, "GzipCompressor.DeCompress: NewReader creation failed")
}
decompressedData, err := io.ReadAll(reader)
if err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.DeCompress: reading from gzip reader failed")
+ return nil, errs.WrapMsg(err, "GzipCompressor.DeCompress: reading from gzip reader failed")
}
if err = reader.Close(); err != nil {
// Even if closing the reader fails, we've successfully read the data,
// so we return the decompressed data and an error indicating the close failure.
- return decompressedData, errs.Wrap(err, "GzipCompressor.DeCompress: closing gzip reader failed")
+ return decompressedData, errs.WrapMsg(err, "GzipCompressor.DeCompress: closing gzip reader failed")
}
return decompressedData, nil
}
@@ -97,16 +98,16 @@ func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, erro
err := reader.Reset(bytes.NewReader(compressedData))
if err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.DecompressWithPool: resetting gzip reader failed")
+ return nil, errs.WrapMsg(err, "GzipCompressor.DecompressWithPool: resetting gzip reader failed")
}
decompressedData, err := io.ReadAll(reader)
if err != nil {
- return nil, errs.Wrap(err, "GzipCompressor.DecompressWithPool: reading from pooled gzip reader failed")
+ return nil, errs.WrapMsg(err, "GzipCompressor.DecompressWithPool: reading from pooled gzip reader failed")
}
if err = reader.Close(); err != nil {
// Similar to DeCompress, return the data and error for close failure.
- return decompressedData, errs.Wrap(err, "GzipCompressor.DecompressWithPool: closing pooled gzip reader failed")
+ return decompressedData, errs.WrapMsg(err, "GzipCompressor.DecompressWithPool: closing pooled gzip reader failed")
}
return decompressedData, nil
}
diff --git a/internal/msggateway/constant.go b/internal/msggateway/constant.go
index 045629b4ee..64664ac0ab 100644
--- a/internal/msggateway/constant.go
+++ b/internal/msggateway/constant.go
@@ -26,7 +26,7 @@ const (
Compression = "compression"
GzipCompressionProtocol = "gzip"
BackgroundStatus = "isBackground"
- MsgResp = "isMsgResp"
+ SendResponse = "isMsgResp"
)
const (
diff --git a/internal/msggateway/context.go b/internal/msggateway/context.go
index ad679c1a14..6c80ece1ba 100644
--- a/internal/msggateway/context.go
+++ b/internal/msggateway/context.go
@@ -15,13 +15,16 @@
package msggateway
import (
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"net/http"
"net/url"
"strconv"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/utils"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/utils/encrypt"
+ "github.com/openimsdk/tools/utils/stringutil"
+ "github.com/openimsdk/tools/utils/timeutil"
)
type UserConnContext struct {
@@ -54,7 +57,7 @@ func (c *UserConnContext) Value(key any) any {
case constant.ConnID:
return c.GetConnID()
case constant.OpUserPlatform:
- return constant.PlatformIDToName(utils.StringToInt(c.GetPlatformID()))
+ return constant.PlatformIDToName(stringutil.StringToInt(c.GetPlatformID()))
case constant.RemoteAddr:
return c.RemoteAddr
default:
@@ -69,7 +72,7 @@ func newContext(respWriter http.ResponseWriter, req *http.Request) *UserConnCont
Path: req.URL.Path,
Method: req.Method,
RemoteAddr: req.RemoteAddr,
- ConnID: utils.Md5(req.RemoteAddr + "_" + strconv.Itoa(int(utils.GetCurrentTimestampByMill()))),
+ ConnID: encrypt.Md5(req.RemoteAddr + "_" + strconv.Itoa(int(timeutil.GetCurrentTimestampByMill()))),
}
}
@@ -133,6 +136,32 @@ func (c *UserConnContext) GetToken() string {
return c.Req.URL.Query().Get(Token)
}
+func (c *UserConnContext) GetCompression() bool {
+ compression, exists := c.Query(Compression)
+ if exists && compression == GzipCompressionProtocol {
+ return true
+ } else {
+ compression, exists := c.GetHeader(Compression)
+ if exists && compression == GzipCompressionProtocol {
+ return true
+ }
+ }
+ return false
+}
+
+func (c *UserConnContext) ShouldSendResp() bool {
+ errResp, exists := c.Query(SendResponse)
+ if exists {
+ b, err := strconv.ParseBool(errResp)
+ if err != nil {
+ return false
+ } else {
+ return b
+ }
+ }
+ return false
+}
+
func (c *UserConnContext) SetToken(token string) {
c.Req.URL.RawQuery = Token + "=" + token
}
@@ -144,3 +173,23 @@ func (c *UserConnContext) GetBackground() bool {
}
return b
}
+func (c *UserConnContext) ParseEssentialArgs() error {
+ _, exists := c.Query(Token)
+ if !exists {
+ return servererrs.ErrConnArgsErr.WrapMsg("token is empty")
+ }
+ _, exists = c.Query(WsUserID)
+ if !exists {
+ return servererrs.ErrConnArgsErr.WrapMsg("sendID is empty")
+ }
+ platformIDStr, exists := c.Query(PlatformID)
+ if !exists {
+ return servererrs.ErrConnArgsErr.WrapMsg("platformID is empty")
+ }
+ _, err := strconv.Atoi(platformIDStr)
+ if err != nil {
+ return servererrs.ErrConnArgsErr.WrapMsg("platformID is not int")
+
+ }
+ return nil
+}
diff --git a/internal/msggateway/encoder.go b/internal/msggateway/encoder.go
index cd2c50d964..3af2663748 100644
--- a/internal/msggateway/encoder.go
+++ b/internal/msggateway/encoder.go
@@ -18,7 +18,7 @@ import (
"bytes"
"encoding/gob"
- "github.com/OpenIMSDK/tools/errs"
+ "github.com/openimsdk/tools/errs"
)
type Encoder interface {
@@ -35,9 +35,8 @@ func NewGobEncoder() *GobEncoder {
func (g *GobEncoder) Encode(data any) ([]byte, error) {
buff := bytes.Buffer{}
enc := gob.NewEncoder(&buff)
- err := enc.Encode(data)
- if err != nil {
- return nil, errs.Wrap(err, "GobEncoder.Encode failed")
+ if err := enc.Encode(data); err != nil {
+ return nil, errs.WrapMsg(err, "GobEncoder.Encode failed", "action", "encode")
}
return buff.Bytes(), nil
}
@@ -45,9 +44,8 @@ func (g *GobEncoder) Encode(data any) ([]byte, error) {
func (g *GobEncoder) Decode(encodeData []byte, decodeData any) error {
buff := bytes.NewBuffer(encodeData)
dec := gob.NewDecoder(buff)
- err := dec.Decode(decodeData)
- if err != nil {
- return errs.Wrap(err, "GobEncoder.Decode failed")
+ if err := dec.Decode(decodeData); err != nil {
+ return errs.WrapMsg(err, "GobEncoder.Decode failed", "action", "decode")
}
return nil
}
diff --git a/internal/msggateway/http_error.go b/internal/msggateway/http_error.go
index 03881cf270..8d9d035226 100644
--- a/internal/msggateway/http_error.go
+++ b/internal/msggateway/http_error.go
@@ -14,8 +14,12 @@
package msggateway
-import "github.com/OpenIMSDK/tools/apiresp"
+import (
+ "github.com/openimsdk/tools/apiresp"
+ "github.com/openimsdk/tools/log"
+)
func httpError(ctx *UserConnContext, err error) {
+ log.ZWarn(ctx, "ws connection error", err)
apiresp.HttpError(ctx.RespWriter, err)
}
diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go
index 1465655615..bfe81b6024 100644
--- a/internal/msggateway/hub_server.go
+++ b/internal/msggateway/hub_server.go
@@ -16,38 +16,30 @@ package msggateway
import (
"context"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msggateway"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msggateway"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
"google.golang.org/grpc"
)
-func (s *Server) InitServer(config *config.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- rdb, err := cache.NewRedis(config)
- if err != nil {
- return err
- }
-
- msgModel := cache.NewMsgCacheModel(rdb, config)
+func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
s.LongConnServer.SetDiscoveryRegistry(disCov, config)
- s.LongConnServer.SetCacheHandler(msgModel)
msggateway.RegisterMsgGatewayServer(server, s)
return nil
}
-func (s *Server) Start(conf *config.GlobalConfig) error {
- return startrpc.Start(
- s.rpcPort,
- conf.RpcRegisterName.OpenImMessageGatewayName,
- s.prometheusPort,
+func (s *Server) Start(ctx context.Context, index int, conf *Config) error {
+ return startrpc.Start(ctx, &conf.ZookeeperConfig, &conf.MsgGateway.Prometheus, conf.MsgGateway.ListenIP,
+ conf.MsgGateway.RPC.RegisterIP,
+ conf.MsgGateway.RPC.Ports, index,
+ conf.Share.RpcRegisterName.MessageGateway,
+ &conf.Share,
conf,
s.InitServer,
)
@@ -57,7 +49,7 @@ type Server struct {
rpcPort int
prometheusPort int
LongConnServer LongConnServer
- config *config.GlobalConfig
+ config *Config
pushTerminal map[int]struct{}
}
@@ -65,7 +57,7 @@ func (s *Server) SetLongConnServer(LongConnServer LongConnServer) {
s.LongConnServer = LongConnServer
}
-func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, conf *config.GlobalConfig) *Server {
+func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, conf *Config) *Server {
s := &Server{
rpcPort: rpcPort,
prometheusPort: proPort,
@@ -89,8 +81,8 @@ func (s *Server) GetUsersOnlineStatus(
ctx context.Context,
req *msggateway.GetUsersOnlineStatusReq,
) (*msggateway.GetUsersOnlineStatusResp, error) {
- if !authverify.IsAppManagerUid(ctx, s.config) {
- return nil, errs.ErrNoPermission.Wrap("only app manager")
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
+ return nil, errs.ErrNoPermission.WrapMsg("only app manager")
}
var resp msggateway.GetUsersOnlineStatusResp
for _, userID := range req.UserIDs {
@@ -122,11 +114,9 @@ func (s *Server) GetUsersOnlineStatus(
return &resp, nil
}
-func (s *Server) OnlineBatchPushOneMsg(
- ctx context.Context,
- req *msggateway.OnlineBatchPushOneMsgReq,
-) (*msggateway.OnlineBatchPushOneMsgResp, error) {
- panic("implement me")
+func (s *Server) OnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq) (*msggateway.OnlineBatchPushOneMsgResp, error) {
+ // todo implement
+ return nil, nil
}
func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq,
@@ -158,7 +148,7 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga
(client.IsBackground && client.PlatformID != constant.IOSPlatformID) {
err := client.PushMessage(ctx, req.MsgData)
if err != nil {
- userPlatform.ResultCode = int64(errs.ErrPushMsgErr.Code())
+ userPlatform.ResultCode = int64(servererrs.ErrPushMsgErr.Code())
resp = append(resp, userPlatform)
} else {
if _, ok := s.pushTerminal[client.PlatformID]; ok {
@@ -167,7 +157,7 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga
}
}
} else {
- userPlatform.ResultCode = int64(errs.ErrIOSBackgroundPushErr.Code())
+ userPlatform.ResultCode = int64(servererrs.ErrIOSBackgroundPushErr.Code())
resp = append(resp, userPlatform)
}
}
@@ -187,7 +177,7 @@ func (s *Server) KickUserOffline(
for _, v := range req.KickUserIDList {
clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID))
if !ok {
- log.ZInfo(ctx, "conn not exist", "userID", v, "platformID", req.PlatformID)
+ log.ZDebug(ctx, "conn not exist", "userID", v, "platformID", req.PlatformID)
continue
}
@@ -203,10 +193,7 @@ func (s *Server) KickUserOffline(
return &msggateway.KickUserOfflineResp{}, nil
}
-func (s *Server) MultiTerminalLoginCheck(
- ctx context.Context,
- req *msggateway.MultiTerminalLoginCheckReq,
-) (*msggateway.MultiTerminalLoginCheckResp, error) {
+func (s *Server) MultiTerminalLoginCheck(ctx context.Context, req *msggateway.MultiTerminalLoginCheckReq) (*msggateway.MultiTerminalLoginCheckResp, error) {
if oldClients, userOK, clientOK := s.LongConnServer.GetUserPlatformCons(req.UserID, int(req.PlatformID)); userOK {
tempUserCtx := newTempContext()
tempUserCtx.SetToken(req.Token)
diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go
index 4efbb7cdf5..727ade0afd 100644
--- a/internal/msggateway/init.go
+++ b/internal/msggateway/init.go
@@ -15,22 +15,43 @@
package msggateway
import (
- "fmt"
+ "context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/utils/datautil"
"time"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/log"
)
-// RunWsAndServer run ws server.
-func RunWsAndServer(conf *config.GlobalConfig, rpcPort, wsPort, prometheusPort int) error {
- fmt.Println("start rpc/msg_gateway server, port: ", rpcPort, wsPort, prometheusPort, ", OpenIM version: ", config.Version)
+type Config struct {
+ MsgGateway config.MsgGateway
+ ZookeeperConfig config.ZooKeeper
+ Share config.Share
+ WebhooksConfig config.Webhooks
+}
+
+// Start run ws server.
+func Start(ctx context.Context, index int, conf *Config) error {
+ log.CInfo(ctx, "MSG-GATEWAY server is initializing", "rpcPorts", conf.MsgGateway.RPC.Ports,
+ "wsPort", conf.MsgGateway.LongConnSvr.Ports, "prometheusPorts", conf.MsgGateway.Prometheus.Ports)
+ wsPort, err := datautil.GetElemByIndex(conf.MsgGateway.LongConnSvr.Ports, index)
+ if err != nil {
+ return err
+ }
+ prometheusPort, err := datautil.GetElemByIndex(conf.MsgGateway.Prometheus.Ports, index)
+ if err != nil {
+ return err
+ }
+ rpcPort, err := datautil.GetElemByIndex(conf.MsgGateway.RPC.Ports, index)
+ if err != nil {
+ return err
+ }
longServer, err := NewWsServer(
conf,
WithPort(wsPort),
- WithMaxConnNum(int64(conf.LongConnSvr.WebsocketMaxConnNum)),
- WithHandshakeTimeout(time.Duration(conf.LongConnSvr.WebsocketTimeout)*time.Second),
- WithMessageMaxMsgLength(conf.LongConnSvr.WebsocketMaxMsgLen),
- WithWriteBufferSize(conf.LongConnSvr.WebsocketWriteBufferSize),
+ WithMaxConnNum(int64(conf.MsgGateway.LongConnSvr.WebsocketMaxConnNum)),
+ WithHandshakeTimeout(time.Duration(conf.MsgGateway.LongConnSvr.WebsocketTimeout)*time.Second),
+ WithMessageMaxMsgLength(conf.MsgGateway.LongConnSvr.WebsocketMaxMsgLen),
)
if err != nil {
return err
@@ -39,7 +60,7 @@ func RunWsAndServer(conf *config.GlobalConfig, rpcPort, wsPort, prometheusPort i
hubServer := NewServer(rpcPort, prometheusPort, longServer, conf)
netDone := make(chan error)
go func() {
- err = hubServer.Start(conf)
+ err = hubServer.Start(ctx, index, conf)
netDone <- err
}()
return hubServer.LongConnServer.Run(netDone)
diff --git a/internal/msggateway/long_conn.go b/internal/msggateway/long_conn.go
index 7dc79c834f..7d5bef4c3a 100644
--- a/internal/msggateway/long_conn.go
+++ b/internal/msggateway/long_conn.go
@@ -15,12 +15,13 @@
package msggateway
import (
- "errors"
+ "encoding/json"
+ "github.com/openimsdk/tools/apiresp"
"net/http"
"time"
- "github.com/OpenIMSDK/tools/errs"
"github.com/gorilla/websocket"
+ "github.com/openimsdk/tools/errs"
)
type LongConn interface {
@@ -75,7 +76,7 @@ func (d *GWebSocket) GenerateLongConn(w http.ResponseWriter, r *http.Request) er
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
// The upgrader.Upgrade method usually returns enough error messages to diagnose problems that may occur during the upgrade
- return errs.Wrap(err, "GenerateLongConn: WebSocket upgrade failed")
+ return errs.WrapMsg(err, "GenerateLongConn: WebSocket upgrade failed")
}
d.conn = conn
return nil
@@ -86,7 +87,7 @@ func (d *GWebSocket) WriteMessage(messageType int, message []byte) error {
return d.conn.WriteMessage(messageType, message)
}
-//func (d *GWebSocket) setSendConn(sendConn *websocket.Conn) {
+// func (d *GWebSocket) setSendConn(sendConn *websocket.Conn) {
// d.sendConn = sendConn
//}
@@ -99,24 +100,24 @@ func (d *GWebSocket) SetReadDeadline(timeout time.Duration) error {
}
func (d *GWebSocket) SetWriteDeadline(timeout time.Duration) error {
- // TODO add error
if timeout <= 0 {
- return errs.Wrap(errors.New("timeout must be greater than 0"))
+ return errs.New("timeout must be greater than 0")
}
// TODO SetWriteDeadline Future add error handling
if err := d.conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
- return errs.Wrap(err, "GWebSocket.SetWriteDeadline failed")
+ return errs.WrapMsg(err, "GWebSocket.SetWriteDeadline failed")
}
return nil
}
func (d *GWebSocket) Dial(urlStr string, requestHeader http.Header) (*http.Response, error) {
conn, httpResp, err := websocket.DefaultDialer.Dial(urlStr, requestHeader)
- if err == nil {
- d.conn = conn
+ if err != nil {
+ return httpResp, errs.WrapMsg(err, "GWebSocket.Dial failed", "url", urlStr)
}
- return httpResp, err
+ d.conn = conn
+ return httpResp, nil
}
func (d *GWebSocket) IsNil() bool {
@@ -144,6 +145,34 @@ func (d *GWebSocket) SetPingHandler(handler PingPongHandler) {
d.conn.SetPingHandler(handler)
}
-//func (d *GWebSocket) CheckSendConnDiffNow() bool {
-// return d.conn == d.sendConn
-//}
+func (d *GWebSocket) RespondWithError(err error, w http.ResponseWriter, r *http.Request) error {
+ if err := d.GenerateLongConn(w, r); err != nil {
+ return err
+ }
+ data, err := json.Marshal(apiresp.ParseError(err))
+ if err != nil {
+ _ = d.Close()
+ return errs.WrapMsg(err, "json marshal failed")
+ }
+
+ if err := d.WriteMessage(MessageText, data); err != nil {
+ _ = d.Close()
+ return errs.WrapMsg(err, "WriteMessage failed")
+ }
+ _ = d.Close()
+ return nil
+}
+
+func (d *GWebSocket) RespondWithSuccess() error {
+ data, err := json.Marshal(apiresp.ParseError(nil))
+ if err != nil {
+ _ = d.Close()
+ return errs.WrapMsg(err, "json marshal failed")
+ }
+
+ if err := d.WriteMessage(MessageText, data); err != nil {
+ _ = d.Close()
+ return errs.WrapMsg(err, "WriteMessage failed")
+ }
+ return nil
+}
diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go
index 2fbdd5683e..8a11e6ab3c 100644
--- a/internal/msggateway/message_handler.go
+++ b/internal/msggateway/message_handler.go
@@ -18,15 +18,15 @@ import (
"context"
"sync"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/push"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/utils"
"github.com/go-playground/validator/v10"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/push"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/utils/jsonutil"
"google.golang.org/protobuf/proto"
)
@@ -46,7 +46,7 @@ func (r *Req) String() string {
tReq.SendID = r.SendID
tReq.OperationID = r.OperationID
tReq.MsgIncr = r.MsgIncr
- return utils.StructToJsonString(tReq)
+ return jsonutil.StructToJsonString(tReq)
}
var reqPool = sync.Pool{
@@ -86,7 +86,7 @@ func (r *Resp) String() string {
tResp.OperationID = r.OperationID
tResp.ErrCode = r.ErrCode
tResp.ErrMsg = r.ErrMsg
- return utils.StructToJsonString(tResp)
+ return jsonutil.StructToJsonString(tResp)
}
type MessageHandler interface {
@@ -106,30 +106,30 @@ type GrpcHandler struct {
validate *validator.Validate
}
-func NewGrpcHandler(validate *validator.Validate, client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *GrpcHandler {
- msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
- pushRpcClient := rpcclient.NewPushRpcClient(client, config)
+func NewGrpcHandler(validate *validator.Validate, client discovery.SvcDiscoveryRegistry, rpcRegisterName *config.RpcRegisterName) *GrpcHandler {
+ msgRpcClient := rpcclient.NewMessageRpcClient(client, rpcRegisterName.Msg)
+ pushRpcClient := rpcclient.NewPushRpcClient(client, rpcRegisterName.Push)
return &GrpcHandler{
msgRpcClient: &msgRpcClient,
pushClient: &pushRpcClient, validate: validate,
}
}
-func (g GrpcHandler) GetSeq(context context.Context, data *Req) ([]byte, error) {
+func (g GrpcHandler) GetSeq(ctx context.Context, data *Req) ([]byte, error) {
req := sdkws.GetMaxSeqReq{}
if err := proto.Unmarshal(data.Data, &req); err != nil {
- return nil, errs.Wrap(err, "GetSeq: error unmarshaling request")
+ return nil, errs.WrapMsg(err, "GetSeq: error unmarshaling request", "action", "unmarshal", "dataType", "GetMaxSeqReq")
}
if err := g.validate.Struct(&req); err != nil {
- return nil, errs.Wrap(err, "GetSeq: validation failed")
+ return nil, errs.WrapMsg(err, "GetSeq: validation failed", "action", "validate", "dataType", "GetMaxSeqReq")
}
- resp, err := g.msgRpcClient.GetMaxSeq(context, &req)
+ resp, err := g.msgRpcClient.GetMaxSeq(ctx, &req)
if err != nil {
return nil, err
}
c, err := proto.Marshal(resp)
if err != nil {
- return nil, errs.Wrap(err, "GetSeq: error marshaling response")
+ return nil, errs.WrapMsg(err, "GetSeq: error marshaling response", "action", "marshal", "dataType", "GetMaxSeqResp")
}
return c, nil
}
@@ -137,19 +137,16 @@ func (g GrpcHandler) GetSeq(context context.Context, data *Req) ([]byte, error)
// SendMessage handles the sending of messages through gRPC. It unmarshals the request data,
// validates the message, and then sends it using the message RPC client.
func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error) {
- // Unmarshal the message data from the request.
var msgData sdkws.MsgData
if err := proto.Unmarshal(data.Data, &msgData); err != nil {
- return nil, errs.Wrap(err, "error unmarshalling message data")
+ return nil, errs.WrapMsg(err, "SendMessage: error unmarshaling message data", "action", "unmarshal", "dataType", "MsgData")
}
- // Validate the message data structure.
if err := g.validate.Struct(&msgData); err != nil {
- return nil, errs.Wrap(err, "message data validation failed")
+ return nil, errs.WrapMsg(err, "SendMessage: message data validation failed", "action", "validate", "dataType", "MsgData")
}
req := msg.SendMsgReq{MsgData: &msgData}
-
resp, err := g.msgRpcClient.SendMsg(ctx, &req)
if err != nil {
return nil, err
@@ -157,7 +154,7 @@ func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error)
c, err := proto.Marshal(resp)
if err != nil {
- return nil, errs.Wrap(err, "error marshaling response")
+ return nil, errs.WrapMsg(err, "SendMessage: error marshaling response", "action", "marshal", "dataType", "SendMsgResp")
}
return c, nil
@@ -170,7 +167,7 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by
}
c, err := proto.Marshal(resp)
if err != nil {
- return nil, errs.Wrap(err, "error marshaling response")
+ return nil, errs.WrapMsg(err, "error marshaling response", "action", "marshal", "dataType", "SendMsgResp")
}
return c, nil
}
@@ -178,10 +175,10 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by
func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([]byte, error) {
req := sdkws.PullMessageBySeqsReq{}
if err := proto.Unmarshal(data.Data, &req); err != nil {
- return nil, errs.Wrap(err, "error unmarshaling request")
+ return nil, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "PullMessageBySeqsReq")
}
if err := g.validate.Struct(data); err != nil {
- return nil, errs.Wrap(err, "validation failed")
+ return nil, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "PullMessageBySeqsReq")
}
resp, err := g.msgRpcClient.PullMessageBySeqList(context, &req)
if err != nil {
@@ -189,7 +186,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([
}
c, err := proto.Marshal(resp)
if err != nil {
- return nil, errs.Wrap(err, "error marshaling response")
+ return nil, errs.WrapMsg(err, "error marshaling response", "action", "marshal", "dataType", "PullMessageBySeqsResp")
}
return c, nil
}
@@ -197,7 +194,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([
func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, error) {
req := push.DelUserPushTokenReq{}
if err := proto.Unmarshal(data.Data, &req); err != nil {
- return nil, errs.Wrap(err, "error unmarshaling request")
+ return nil, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "DelUserPushTokenReq")
}
resp, err := g.pushClient.DelUserPushToken(context, &req)
if err != nil {
@@ -205,7 +202,7 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err
}
c, err := proto.Marshal(resp)
if err != nil {
- return nil, errs.Wrap(err, "error marshaling response")
+ return nil, errs.WrapMsg(err, "error marshaling response", "action", "marshal", "dataType", "DelUserPushTokenResp")
}
return c, nil
}
@@ -213,31 +210,10 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err
func (g GrpcHandler) SetUserDeviceBackground(_ context.Context, data *Req) ([]byte, bool, error) {
req := sdkws.SetAppBackgroundStatusReq{}
if err := proto.Unmarshal(data.Data, &req); err != nil {
- return nil, false, errs.Wrap(err, "error unmarshaling request")
+ return nil, false, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "SetAppBackgroundStatusReq")
}
if err := g.validate.Struct(data); err != nil {
- return nil, false, errs.Wrap(err, "validation failed")
+ return nil, false, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "SetAppBackgroundStatusReq")
}
return nil, req.IsBackground, nil
}
-
-// func (g GrpcHandler) call[T any](ctx context.Context, data Req, m proto.Message, rpc func(ctx context.Context, req
-// proto.Message)) ([]byte, error) {
-// if err := proto.Unmarshal(data.Data, m); err != nil {
-// return nil, err
-// }
-// if err := g.validate.Struct(m); err != nil {
-// return nil, err
-// }
-// rpc(ctx, m)
-// req := msg.SendMsgReq{MsgData: &msgData}
-// resp, err := g.notification.Msg.SendMsg(context, &req)
-// if err != nil {
-// return nil, err
-// }
-// c, err := proto.Marshal(resp)
-// if err != nil {
-// return nil, err
-// }
-// return c, nil
-//}
diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go
index f5838c7036..cf607d4707 100644
--- a/internal/msggateway/n_ws_server.go
+++ b/internal/msggateway/n_ws_server.go
@@ -16,29 +16,25 @@ package msggateway
import (
"context"
- "encoding/json"
- "errors"
"fmt"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ pbAuth "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/tools/mcontext"
"net/http"
- "strconv"
"sync"
"sync/atomic"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msggateway"
- "github.com/OpenIMSDK/tools/apiresp"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/go-playground/validator/v10"
- "github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "github.com/redis/go-redis/v9"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msggateway"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/stringutil"
"golang.org/x/sync/errgroup"
)
@@ -48,8 +44,7 @@ type LongConnServer interface {
GetUserAllCons(userID string) ([]*Client, bool)
GetUserPlatformCons(userID string, platform int) ([]*Client, bool, bool)
Validate(s any) error
- SetCacheHandler(cache cache.MsgModel)
- SetDiscoveryRegistry(client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig)
+ SetDiscoveryRegistry(client discovery.SvcDiscoveryRegistry, config *Config)
KickUserConn(client *Client) error
UnRegister(c *Client)
SetKickHandlerInfo(i *kickHandler)
@@ -58,15 +53,8 @@ type LongConnServer interface {
MessageHandler
}
-// bufferPool is unused
-// var bufferPool = sync.Pool{
-// New: func() any {
-// return make([]byte, 1024)
-// },
-// }
-
type WsServer struct {
- globalConfig *config.GlobalConfig
+ msgGatewayConfig *Config
port int
wsMaxConnNum int64
registerChan chan *Client
@@ -79,12 +67,13 @@ type WsServer struct {
handshakeTimeout time.Duration
writeBufferSize int
validate *validator.Validate
- cache cache.MsgModel
userClient *rpcclient.UserRpcClient
- disCov discoveryregistry.SvcDiscoveryRegistry
+ authClient *rpcclient.Auth
+ disCov discovery.SvcDiscoveryRegistry
Compressor
Encoder
MessageHandler
+ webhookClient *webhook.Client
}
type kickHandler struct {
@@ -93,9 +82,10 @@ type kickHandler struct {
newClient *Client
}
-func (ws *WsServer) SetDiscoveryRegistry(disCov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) {
- ws.MessageHandler = NewGrpcHandler(ws.validate, disCov, config)
- u := rpcclient.NewUserRpcClient(disCov, config)
+func (ws *WsServer) SetDiscoveryRegistry(disCov discovery.SvcDiscoveryRegistry, config *Config) {
+ ws.MessageHandler = NewGrpcHandler(ws.validate, disCov, &config.Share.RpcRegisterName)
+ u := rpcclient.NewUserRpcClient(disCov, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
+ ws.authClient = rpcclient.NewAuth(disCov, config.Share.RpcRegisterName.Auth)
ws.userClient = &u
ws.disCov = disCov
}
@@ -107,30 +97,17 @@ func (ws *WsServer) SetUserOnlineStatus(ctx context.Context, client *Client, sta
}
switch status {
case constant.Online:
- err := CallbackUserOnline(ctx, ws.globalConfig, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID())
- if err != nil {
- log.ZWarn(ctx, "CallbackUserOnline err", err)
- }
+ ws.webhookAfterUserOnline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOnline, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID())
case constant.Offline:
- err := CallbackUserOffline(ctx, ws.globalConfig, client.UserID, client.PlatformID, client.ctx.GetConnID())
- if err != nil {
- log.ZWarn(ctx, "CallbackUserOffline err", err)
- }
+ ws.webhookAfterUserOffline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOffline, client.UserID, client.PlatformID, client.ctx.GetConnID())
}
}
-func (ws *WsServer) SetCacheHandler(cache cache.MsgModel) {
- ws.cache = cache
-}
-
func (ws *WsServer) UnRegister(c *Client) {
ws.unregisterChan <- c
}
-func (ws *WsServer) Validate(s any) error {
- if s == nil {
- return errs.Wrap(errors.New("input cannot be nil"))
- }
+func (ws *WsServer) Validate(_ any) error {
return nil
}
@@ -142,14 +119,14 @@ func (ws *WsServer) GetUserPlatformCons(userID string, platform int) ([]*Client,
return ws.clients.Get(userID, platform)
}
-func NewWsServer(globalConfig *config.GlobalConfig, opts ...Option) (*WsServer, error) {
+func NewWsServer(msgGatewayConfig *Config, opts ...Option) (*WsServer, error) {
var config configs
for _, o := range opts {
o(&config)
}
v := validator.New()
return &WsServer{
- globalConfig: globalConfig,
+ msgGatewayConfig: msgGatewayConfig,
port: config.port,
wsMaxConnNum: config.maxConnNum,
writeBufferSize: config.writeBufferSize,
@@ -166,6 +143,7 @@ func NewWsServer(globalConfig *config.GlobalConfig, opts ...Option) (*WsServer,
clients: newUserMap(),
Compressor: NewGzipCompressor(),
Encoder: NewGobEncoder(),
+ webhookClient: webhook.NewWebhookClient(msgGatewayConfig.WebhooksConfig.URL),
}, nil
}
@@ -176,7 +154,7 @@ func (ws *WsServer) Run(done chan error) error {
shutdownDone = make(chan struct{}, 1)
)
- server := http.Server{Addr: ":" + utils.IntToString(ws.port), Handler: nil}
+ server := http.Server{Addr: ":" + stringutil.IntToString(ws.port), Handler: nil}
go func() {
for {
@@ -196,9 +174,9 @@ func (ws *WsServer) Run(done chan error) error {
go func() {
http.HandleFunc("/", ws.wsHandler)
err := server.ListenAndServe()
+ defer close(netDone)
if err != nil && err != http.ErrServerClosed {
- netErr = errs.Wrap(err, "ws start err", server.Addr)
- close(netDone)
+ netErr = errs.WrapMsg(err, "ws start err", server.Addr)
}
}()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
@@ -208,7 +186,7 @@ func (ws *WsServer) Run(done chan error) error {
case err = <-done:
sErr := server.Shutdown(ctx)
if sErr != nil {
- return errs.Wrap(sErr, "shutdown err")
+ return errs.WrapMsg(sErr, "shutdown err")
}
close(shutdownDone)
if err != nil {
@@ -223,7 +201,7 @@ func (ws *WsServer) Run(done chan error) error {
var concurrentRequest = 3
func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error {
- conns, err := ws.disCov.GetConns(ctx, ws.globalConfig.RpcRegisterName.OpenImMessageGatewayName)
+ conns, err := ws.disCov.GetConns(ctx, ws.msgGatewayConfig.Share.RpcRegisterName.MessageGateway)
if err != nil {
return err
}
@@ -279,7 +257,8 @@ func (ws *WsServer) registerClient(client *Client) {
if clientOK {
ws.clients.Set(client.UserID, client)
// There is already a connection to the platform
- log.ZInfo(client.ctx, "repeat login", "userID", client.UserID, "platformID", client.PlatformID, "old remote addr", getRemoteAdders(oldClients))
+ log.ZInfo(client.ctx, "repeat login", "userID", client.UserID, "platformID",
+ client.PlatformID, "old remote addr", getRemoteAdders(oldClients))
ws.onlineUserConnNum.Add(1)
} else {
ws.clients.Set(client.UserID, client)
@@ -288,13 +267,14 @@ func (ws *WsServer) registerClient(client *Client) {
}
wg := sync.WaitGroup{}
- if ws.globalConfig.Envs.Discovery == "zookeeper" {
+ if ws.msgGatewayConfig.Share.Env == "zookeeper" {
wg.Add(1)
go func() {
defer wg.Done()
_ = ws.sendUserOnlineInfoToOtherNode(client.ctx, client)
}()
}
+
wg.Add(1)
go func() {
defer wg.Done()
@@ -331,7 +311,7 @@ func (ws *WsServer) KickUserConn(client *Client) error {
}
func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Client, newClient *Client) {
- switch ws.globalConfig.MultiLoginPolicy {
+ switch ws.msgGatewayConfig.MsgGateway.MultiLoginPolicy {
case constant.DefalutNotKick:
case constant.PCAndOther:
if constant.PlatformIDToClass(newClient.PlatformID) == constant.TerminalPC {
@@ -349,57 +329,13 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien
log.ZWarn(c.ctx, "KickOnlineMessage", err)
}
}
- m, err := ws.cache.GetTokensWithoutError(
- newClient.ctx,
- newClient.UserID,
- newClient.PlatformID,
- )
- if err != nil && err != redis.Nil {
- log.ZWarn(
- newClient.ctx,
- "get token from redis err",
- err,
- "userID",
- newClient.UserID,
- "platformID",
- newClient.PlatformID,
- )
- return
- }
- if m == nil {
- log.ZWarn(
- newClient.ctx,
- "m is nil",
- errors.New("m is nil"),
- "userID",
- newClient.UserID,
- "platformID",
- newClient.PlatformID,
- )
- return
- }
- log.ZDebug(
- newClient.ctx,
- "get token from redis",
- "userID",
- newClient.UserID,
- "platformID",
- newClient.PlatformID,
- "tokenMap",
- m,
+ ctx := mcontext.WithMustInfoCtx(
+ []string{newClient.ctx.GetOperationID(), newClient.ctx.GetUserID(),
+ constant.PlatformIDToName(newClient.PlatformID), newClient.ctx.GetConnID()},
)
-
- for k := range m {
- if k != newClient.ctx.GetToken() {
- m[k] = constant.KickedToken
- }
- }
- log.ZDebug(newClient.ctx, "set token map is ", "token map", m, "userID",
- newClient.UserID, "token", newClient.ctx.GetToken())
- err = ws.cache.SetTokenMapByUidPid(newClient.ctx, newClient.UserID, newClient.PlatformID, m)
- if err != nil {
- log.ZWarn(newClient.ctx, "SetTokenMapByUidPid err", err, "userID", newClient.UserID, "platformID", newClient.PlatformID)
- return
+ if _, err := ws.authClient.InvalidateToken(ctx, newClient.token, newClient.UserID, newClient.PlatformID); err != nil {
+ log.ZWarn(newClient.ctx, "InvalidateToken err", err, "userID", newClient.UserID,
+ "platformID", newClient.PlatformID)
}
}
}
@@ -413,107 +349,94 @@ func (ws *WsServer) unregisterClient(client *Client) {
}
ws.onlineUserConnNum.Add(-1)
ws.SetUserOnlineStatus(client.ctx, client, constant.Offline)
- log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num", ws.onlineUserNum.Load(), "online user conn Num",
+ log.ZInfo(client.ctx, "user offline", "close reason", client.closedErr, "online user Num",
+ ws.onlineUserNum.Load(), "online user conn Num",
ws.onlineUserConnNum.Load(),
)
}
-func (ws *WsServer) ParseWSArgs(r *http.Request) (args *WSArgs, err error) {
- var v WSArgs
- defer func() {
- args = &v
- }()
- query := r.URL.Query()
- v.MsgResp, _ = strconv.ParseBool(query.Get(MsgResp))
- if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
- return nil, errs.ErrConnOverMaxNumLimit.Wrap("over max conn num limit")
- }
- if v.Token = query.Get(Token); v.Token == "" {
- return nil, errs.ErrConnArgsErr.Wrap("token is empty")
+// validateRespWithRequest checks if the response matches the expected userID and platformID.
+func (ws *WsServer) validateRespWithRequest(ctx *UserConnContext, resp *pbAuth.ParseTokenResp) error {
+ userID := ctx.GetUserID()
+ platformID := stringutil.StringToInt32(ctx.GetPlatformID())
+ if resp.UserID != userID {
+ return servererrs.ErrTokenInvalid.WrapMsg(fmt.Sprintf("token uid %s != userID %s", resp.UserID, userID))
}
- if v.UserID = query.Get(WsUserID); v.UserID == "" {
- return nil, errs.ErrConnArgsErr.Wrap("sendID is empty")
+ if resp.PlatformID != platformID {
+ return servererrs.ErrTokenInvalid.WrapMsg(fmt.Sprintf("token platform %d != platformID %d", resp.PlatformID, platformID))
}
- platformIDStr := query.Get(PlatformID)
- if platformIDStr == "" {
- return nil, errs.ErrConnArgsErr.Wrap("platformID is empty")
+ return nil
+}
+
+func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
+ // Create a new connection context
+ connContext := newContext(w, r)
+
+ // Check if the current number of online user connections exceeds the maximum limit
+ if ws.onlineUserConnNum.Load() >= ws.wsMaxConnNum {
+ // If it exceeds the maximum connection number, return an error via HTTP and stop processing
+ httpError(connContext, servererrs.ErrConnOverMaxNumLimit.WrapMsg("over max conn num limit"))
+ return
}
- platformID, err := strconv.Atoi(platformIDStr)
+
+ // Parse essential arguments (e.g., user ID, Token)
+ err := connContext.ParseEssentialArgs()
if err != nil {
- return nil, errs.ErrConnArgsErr.Wrap("platformID is not int")
- }
- v.PlatformID = platformID
- if err = authverify.WsVerifyToken(v.Token, v.UserID, ws.globalConfig.Secret, platformID); err != nil {
- return nil, err
- }
- if query.Get(Compression) == GzipCompressionProtocol {
- v.Compression = true
- }
- if r.Header.Get(Compression) == GzipCompressionProtocol {
- v.Compression = true
+ // If there's an error during parsing, return an error via HTTP and stop processing
+
+ httpError(connContext, err)
+ return
}
- m, err := ws.cache.GetTokensWithoutError(context.Background(), v.UserID, platformID)
+
+ // Call the authentication client to parse the Token obtained from the context
+ resp, err := ws.authClient.ParseToken(connContext, connContext.GetToken())
if err != nil {
- return nil, err
- }
- if v, ok := m[v.Token]; ok {
- switch v {
- case constant.NormalToken:
- case constant.KickedToken:
- return nil, errs.ErrTokenKicked.Wrap()
- default:
- return nil, errs.ErrTokenUnknown.Wrap(fmt.Sprintf("token status is %d", v))
+ // If there's an error parsing the Token, decide whether to send the error message via WebSocket based on the context flag
+ shouldSendError := connContext.ShouldSendResp()
+ if shouldSendError {
+ // Create a WebSocket connection object and attempt to send the error message via WebSocket
+ wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
+ if err := wsLongConn.RespondWithError(err, w, r); err == nil {
+ // If the error message is successfully sent via WebSocket, stop processing
+ return
+ }
}
- } else {
- return nil, errs.ErrTokenNotExist.Wrap()
+ // If sending via WebSocket is not required or fails, return the error via HTTP and stop processing
+ httpError(connContext, err)
+ return
}
- return &v, nil
-}
-type WSArgs struct {
- Token string
- UserID string
- PlatformID int
- Compression bool
- MsgResp bool
-}
+ // Validate the authentication response matches the request (e.g., user ID and platform ID)
+ err = ws.validateRespWithRequest(connContext, resp)
+ if err != nil {
+ // If validation fails, return an error via HTTP and stop processing
+ httpError(connContext, err)
+ return
+ }
-func (ws *WsServer) wsHandler(w http.ResponseWriter, r *http.Request) {
- connContext := newContext(w, r)
- args, pErr := ws.ParseWSArgs(r)
- var wsLongConn *GWebSocket
- if args.MsgResp {
- wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
- if err := wsLongConn.GenerateLongConn(w, r); err != nil {
- httpError(connContext, err)
- return
- }
- data, err := json.Marshal(apiresp.ParseError(pErr))
- if err != nil {
- _ = wsLongConn.Close()
- return
- }
- if err := wsLongConn.WriteMessage(MessageText, data); err != nil {
- _ = wsLongConn.Close()
- return
- }
- if pErr != nil {
- _ = wsLongConn.Close()
- return
- }
+ // Create a WebSocket long connection object
+ wsLongConn := newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
+ if err := wsLongConn.GenerateLongConn(w, r); err != nil {
+ //If the creation of the long connection fails, the error is handled internally during the handshake process.
+ log.ZWarn(connContext, "long connection fails", err)
+ return
} else {
- if pErr != nil {
- httpError(connContext, pErr)
- return
- }
- wsLongConn = newGWebSocket(WebSocket, ws.handshakeTimeout, ws.writeBufferSize)
- if err := wsLongConn.GenerateLongConn(w, r); err != nil {
- httpError(connContext, err)
- return
+ // Check if a normal response should be sent via WebSocket
+ shouldSendSuccessResp := connContext.ShouldSendResp()
+ if shouldSendSuccessResp {
+ // Attempt to send a success message through WebSocket
+ if err := wsLongConn.RespondWithSuccess(); err != nil {
+ // If the success message is successfully sent, end further processing
+ return
+ }
}
}
+
+ // Retrieve a client object from the client pool, reset its state, and associate it with the current WebSocket long connection
client := ws.clientPool.Get().(*Client)
- client.ResetClient(connContext, wsLongConn, connContext.GetBackground(), args.Compression, ws, args.Token)
+ client.ResetClient(connContext, wsLongConn, ws)
+
+ // Register the client with the server and start message processing
ws.registerChan <- client
go client.readMessage()
}
diff --git a/internal/msggateway/user_map.go b/internal/msggateway/user_map.go
index 89eb20de6c..79cc53d1bc 100644
--- a/internal/msggateway/user_map.go
+++ b/internal/msggateway/user_map.go
@@ -18,7 +18,8 @@ import (
"context"
"sync"
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
)
type UserMap struct {
@@ -54,6 +55,7 @@ func (u *UserMap) Get(key string, platformID int) ([]*Client, bool, bool) {
return nil, userExisted, false
}
+// Set adds a client to the map.
func (u *UserMap) Set(key string, v *Client) {
allClients, existed := u.m.Load(key)
if existed {
@@ -63,6 +65,7 @@ func (u *UserMap) Set(key string, v *Client) {
u.m.Store(key, oldClients)
} else {
log.ZDebug(context.Background(), "Set not existed", "user_id", key, "client_user_id", v.UserID)
+
var clients []*Client
clients = append(clients, v)
u.m.Store(key, clients)
@@ -98,14 +101,10 @@ func (u *UserMap) delete(key string, connRemoteAddr string) (isDeleteUser bool)
return false
}
-func (u *UserMap) deleteClients(key string, clientsToDelete []*Client) (isDeleteUser bool) {
- // Convert the slice of clients to delete into a map for efficient lookup.
- deleteMap := make(map[string]struct{})
- for _, client := range clientsToDelete {
- deleteMap[client.ctx.GetRemoteAddr()] = struct{}{}
- }
-
- // Load the current clients associated with the key.
+func (u *UserMap) deleteClients(key string, clients []*Client) (isDeleteUser bool) {
+ m := datautil.SliceToMapAny(clients, func(c *Client) (string, struct{}) {
+ return c.ctx.GetRemoteAddr(), struct{}{}
+ })
allClients, existed := u.m.Load(key)
if !existed {
// If the key doesn't exist, return false.
@@ -116,7 +115,7 @@ func (u *UserMap) deleteClients(key string, clientsToDelete []*Client) (isDelete
oldClients := allClients.([]*Client)
var remainingClients []*Client
for _, client := range oldClients {
- if _, shouldBeDeleted := deleteMap[client.ctx.GetRemoteAddr()]; !shouldBeDeleted {
+ if _, shouldBeDeleted := m[client.ctx.GetRemoteAddr()]; !shouldBeDeleted {
remainingClients = append(remainingClients, client)
}
}
diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go
index ee62db9c1a..68d953e902 100644
--- a/internal/msgtransfer/init.go
+++ b/internal/msgtransfer/init.go
@@ -16,23 +16,26 @@ package msgtransfer
import (
"context"
- "errors"
"fmt"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/openimsdk/tools/utils/datautil"
"net/http"
"os"
"os/signal"
"syscall"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mw"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mw"
+ "github.com/openimsdk/tools/system/program"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
@@ -50,59 +53,62 @@ type MsgTransfer struct {
// and handle the deletion notification message deleted subscriptions topic: msg_to_mongo
ctx context.Context
cancel context.CancelFunc
- config *config.GlobalConfig
}
-func StartTransfer(config *config.GlobalConfig, prometheusPort int) error {
- rdb, err := cache.NewRedis(config)
- if err != nil {
- return err
- }
+type Config struct {
+ MsgTransfer config.MsgTransfer
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ KafkaConfig config.Kafka
+ ZookeeperConfig config.ZooKeeper
+ Share config.Share
+ WebhooksConfig config.Webhooks
+}
- mongo, err := unrelation.NewMongo(config)
+func Start(ctx context.Context, index int, config *Config) error {
+ log.CInfo(ctx, "MSG-TRANSFER server is initializing", "prometheusPorts",
+ config.MsgTransfer.Prometheus.Ports, "index", index)
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
-
- if err = mongo.CreateMsgIndex(); err != nil {
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
+ if err != nil {
return err
}
- client, err := kdisc.NewDiscoveryRegister(config)
+ client, err := kdisc.NewDiscoveryRegister(&config.ZookeeperConfig, &config.Share)
if err != nil {
return err
}
-
- if err := client.CreateRpcRootNodes(config.GetServiceNames()); err != nil {
+ client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()),
+ grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
+ //todo MsgCacheTimeout
+ msgModel := cache.NewMsgCache(rdb, config.RedisConfig.EnablePipeline)
+ seqModel := cache.NewSeqCache(rdb)
+ msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB())
+ if err != nil {
return err
}
-
- client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
- msgModel := cache.NewMsgCacheModel(rdb, config)
- msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase(config.Mongo.Database))
- msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, config)
+ msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, seqModel, &config.KafkaConfig)
if err != nil {
return err
}
- conversationRpcClient := rpcclient.NewConversationRpcClient(client, config)
- groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
- msgTransfer, err := NewMsgTransfer(config, msgDatabase, &conversationRpcClient, &groupRpcClient)
+ conversationRpcClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
+ groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
+ msgTransfer, err := NewMsgTransfer(&config.KafkaConfig, msgDatabase, &conversationRpcClient, &groupRpcClient)
if err != nil {
return err
}
- return msgTransfer.Start(prometheusPort, config)
+ return msgTransfer.Start(index, config)
}
-func NewMsgTransfer(
- config *config.GlobalConfig,
- msgDatabase controller.CommonMsgDatabase,
- conversationRpcClient *rpcclient.ConversationRpcClient,
- groupRpcClient *rpcclient.GroupRpcClient,
-) (*MsgTransfer, error) {
- historyCH, err := NewOnlineHistoryRedisConsumerHandler(config, msgDatabase, conversationRpcClient, groupRpcClient)
+func NewMsgTransfer(kafkaConf *config.Kafka, msgDatabase controller.CommonMsgDatabase,
+ conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) {
+ historyCH, err := NewOnlineHistoryRedisConsumerHandler(kafkaConf, msgDatabase, conversationRpcClient, groupRpcClient)
if err != nil {
return nil, err
}
- historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(config, msgDatabase)
+ historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(kafkaConf, msgDatabase)
if err != nil {
return nil, err
}
@@ -110,14 +116,13 @@ func NewMsgTransfer(
return &MsgTransfer{
historyCH: historyCH,
historyMongoCH: historyMongoCH,
- config: config,
}, nil
}
-func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) error {
- fmt.Println("start msg transfer", "prometheusPort:", prometheusPort)
- if prometheusPort <= 0 {
- return errs.Wrap(errors.New("prometheusPort not correct"))
+func (m *MsgTransfer) Start(index int, config *Config) error {
+ prometheusPort, err := datautil.GetElemByIndex(config.MsgTransfer.Prometheus.Ports, index)
+ if err != nil {
+ return err
}
m.ctx, m.cancel = context.WithCancel(context.Background())
@@ -129,17 +134,17 @@ func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) err
go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH)
go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH)
- if config.Prometheus.Enable {
+ if config.MsgTransfer.Prometheus.Enable {
go func() {
proreg := prometheus.NewRegistry()
proreg.MustRegister(
collectors.NewGoCollector(),
)
- proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer", config)...)
+ proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer", &config.Share)...)
http.Handle("/metrics", promhttp.HandlerFor(proreg, promhttp.HandlerOpts{Registry: proreg}))
err := http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil)
if err != nil && err != http.ErrServerClosed {
- netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", prometheusPort))
+ netErr = errs.WrapMsg(err, "prometheus start error", "prometheusPort", prometheusPort)
netDone <- struct{}{}
}
}()
@@ -149,7 +154,7 @@ func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) err
signal.Notify(sigs, syscall.SIGTERM)
select {
case <-sigs:
- util.SIGTERMExit()
+ program.SIGTERMExit()
// graceful close kafka client.
m.cancel()
m.historyCH.historyConsumerGroup.Close()
diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go
index 50fc933690..8691e92ab6 100644
--- a/internal/msgtransfer/online_history_msg_handler.go
+++ b/internal/msgtransfer/online_history_msg_handler.go
@@ -23,18 +23,19 @@ import (
"time"
"github.com/IBM/sarama"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/go-redis/redis"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/mq/kafka"
+ "github.com/openimsdk/tools/utils/idutil"
+ "github.com/openimsdk/tools/utils/stringutil"
"google.golang.org/protobuf/proto"
)
@@ -80,12 +81,12 @@ type OnlineHistoryRedisConsumerHandler struct {
groupRpcClient *rpcclient.GroupRpcClient
}
-func NewOnlineHistoryRedisConsumerHandler(
- config *config.GlobalConfig,
- database controller.CommonMsgDatabase,
- conversationRpcClient *rpcclient.ConversationRpcClient,
- groupRpcClient *rpcclient.GroupRpcClient,
-) (*OnlineHistoryRedisConsumerHandler, error) {
+func NewOnlineHistoryRedisConsumerHandler(kafkaConf *config.Kafka, database controller.CommonMsgDatabase,
+ conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*OnlineHistoryRedisConsumerHandler, error) {
+ historyConsumerGroup, err := kafka.NewMConsumerGroup(kafkaConf.Build(), kafkaConf.ToRedisGroupID, []string{kafkaConf.ToRedisTopic})
+ if err != nil {
+ return nil, err
+ }
var och OnlineHistoryRedisConsumerHandler
och.msgDatabase = database
och.msgDistributionCh = make(chan Cmd2Value) // no buffer channel
@@ -96,32 +97,7 @@ func NewOnlineHistoryRedisConsumerHandler(
}
och.conversationRpcClient = conversationRpcClient
och.groupRpcClient = groupRpcClient
- var err error
-
- var tlsConfig *kafka.TLSConfig
- if config.Kafka.TLS != nil {
- tlsConfig = &kafka.TLSConfig{
- CACrt: config.Kafka.TLS.CACrt,
- ClientCrt: config.Kafka.TLS.ClientCrt,
- ClientKey: config.Kafka.TLS.ClientKey,
- ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
- InsecureSkipVerify: false,
- }
- }
-
- och.historyConsumerGroup, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
- KafkaVersion: sarama.V2_0_0_0,
- OffsetsInitial: sarama.OffsetNewest,
- IsReturnErr: false,
- UserName: config.Kafka.Username,
- Password: config.Kafka.Password,
- }, []string{config.Kafka.LatestMsgToRedis.Topic},
- config.Kafka.Addr,
- config.Kafka.ConsumerGroupID.MsgToRedis,
- tlsConfig,
- )
- // statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d
- // second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval)
+ och.historyConsumerGroup = historyConsumerGroup
return &och, err
}
@@ -265,22 +241,13 @@ func (och *OnlineHistoryRedisConsumerHandler) handleNotification(
}
}
-func (och *OnlineHistoryRedisConsumerHandler) toPushTopic(
- ctx context.Context,
- key, conversationID string,
- msgs []*sdkws.MsgData,
-) {
+func (och *OnlineHistoryRedisConsumerHandler) toPushTopic(ctx context.Context, key, conversationID string, msgs []*sdkws.MsgData) {
for _, v := range msgs {
och.msgDatabase.MsgToPushMQ(ctx, key, conversationID, v) // nolint: errcheck
-
}
}
-func (och *OnlineHistoryRedisConsumerHandler) handleMsg(
- ctx context.Context,
- key, conversationID string,
- storageList, notStorageList []*sdkws.MsgData,
-) {
+func (och *OnlineHistoryRedisConsumerHandler) handleMsg(ctx context.Context, key, conversationID string, storageList, notStorageList []*sdkws.MsgData) {
och.toPushTopic(ctx, key, conversationID, notStorageList)
if len(storageList) > 0 {
lastSeq, isNewConversation, err := och.msgDatabase.BatchInsertChat2Cache(ctx, conversationID, storageList)
@@ -290,7 +257,7 @@ func (och *OnlineHistoryRedisConsumerHandler) handleMsg(
}
if isNewConversation {
switch storageList[0].SessionType {
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
log.ZInfo(ctx, "group chat first create conversation", "conversationID",
conversationID)
userIDs, err := och.groupRpcClient.GetGroupMemberIDs(ctx, storageList[0].GroupID)
@@ -349,14 +316,8 @@ func (och *OnlineHistoryRedisConsumerHandler) MessagesDistributionHandle() {
for i, header := range consumerMessages[i].Headers {
arr = append(arr, strconv.Itoa(i), string(header.Key), string(header.Value))
}
- log.ZInfo(
- ctx,
- "consumer.kafka.GetContextWithMQHeader",
- "len",
- len(consumerMessages[i].Headers),
- "header",
- strings.Join(arr, ", "),
- )
+ log.ZInfo(ctx, "consumer.kafka.GetContextWithMQHeader", "len", len(consumerMessages[i].Headers),
+ "header", strings.Join(arr, ", "))
ctxMsg.ctx = kafka.GetContextWithMQHeader(consumerMessages[i].Headers)
ctxMsg.message = msgFromMQ
log.ZDebug(
@@ -381,7 +342,7 @@ func (och *OnlineHistoryRedisConsumerHandler) MessagesDistributionHandle() {
log.ZDebug(ctx, "generate map list users len", "length", len(aggregationMsgs))
for uniqueKey, v := range aggregationMsgs {
if len(v) >= 0 {
- hashCode := utils.GetHashCode(uniqueKey)
+ hashCode := stringutil.GetHashCode(uniqueKey)
channelID := hashCode % ChannelNum
newCtx := withAggregationCtx(ctx, v)
log.ZDebug(
@@ -433,7 +394,7 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
break
}
}
- log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset",
+ log.ZInfo(context.Background(), "online new session msg come", "highWaterMarkOffset",
claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition())
var (
@@ -472,7 +433,7 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim(
rwLock.Unlock()
start := time.Now()
- ctx := mcontext.WithTriggerIDContext(context.Background(), utils.OperationIDGenerator())
+ ctx := mcontext.WithTriggerIDContext(context.Background(), idutil.OperationIDGenerator())
log.ZDebug(ctx, "timer trigger msg consumer start", "length", len(buffer))
for i := 0; i < len(buffer)/split; i++ {
och.msgDistributionCh <- Cmd2Value{Cmd: ConsumerMsgs, Value: TriggerChannelValue{
diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go
index 045f822206..978302e768 100644
--- a/internal/msgtransfer/online_msg_to_mongo_handler.go
+++ b/internal/msgtransfer/online_msg_to_mongo_handler.go
@@ -18,42 +18,22 @@ import (
"context"
"github.com/IBM/sarama"
- pbmsg "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/tools/log"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ pbmsg "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mq/kafka"
"google.golang.org/protobuf/proto"
)
type OnlineHistoryMongoConsumerHandler struct {
- historyConsumerGroup *kfk.MConsumerGroup
+ historyConsumerGroup *kafka.MConsumerGroup
msgDatabase controller.CommonMsgDatabase
}
-func NewOnlineHistoryMongoConsumerHandler(config *config.GlobalConfig, database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) {
- var tlsConfig *kfk.TLSConfig
- if config.Kafka.TLS != nil {
- tlsConfig = &kfk.TLSConfig{
- CACrt: config.Kafka.TLS.CACrt,
- ClientCrt: config.Kafka.TLS.ClientCrt,
- ClientKey: config.Kafka.TLS.ClientKey,
- ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
- InsecureSkipVerify: false,
- }
- }
- historyConsumerGroup, err := kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
- KafkaVersion: sarama.V2_0_0_0,
- OffsetsInitial: sarama.OffsetNewest,
- IsReturnErr: false,
- UserName: config.Kafka.Username,
- Password: config.Kafka.Password,
- }, []string{config.Kafka.MsgToMongo.Topic},
- config.Kafka.Addr,
- config.Kafka.ConsumerGroupID.MsgToMongo,
- tlsConfig,
- )
+func NewOnlineHistoryMongoConsumerHandler(kafkaConf *config.Kafka, database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) {
+ historyConsumerGroup, err := kafka.NewMConsumerGroup(kafkaConf.Build(), kafkaConf.ToMongoGroupID, []string{kafkaConf.ToMongoTopic})
if err != nil {
return nil, err
}
@@ -65,12 +45,7 @@ func NewOnlineHistoryMongoConsumerHandler(config *config.GlobalConfig, database
return mc, nil
}
-func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(
- ctx context.Context,
- cMsg *sarama.ConsumerMessage,
- key string,
- session sarama.ConsumerGroupSession,
-) {
+func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Context, cMsg *sarama.ConsumerMessage, key string, session sarama.ConsumerGroupSession) {
msg := cMsg.Value
msgFromMQ := pbmsg.MsgDataToMongoByMQ{}
err := proto.Unmarshal(msg, &msgFromMQ)
diff --git a/internal/push/callback.go b/internal/push/callback.go
index 6415d63d6b..8897295827 100644
--- a/internal/push/callback.go
+++ b/internal/push/callback.go
@@ -16,122 +16,135 @@ package push
import (
"context"
+ "encoding/json"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
)
-func callbackOfflinePush(
- ctx context.Context,
- config *config.GlobalConfig,
- userIDs []string,
- msg *sdkws.MsgData,
- offlinePushUserIDs *[]string,
-) error {
- if !config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing {
- return nil
- }
- req := &callbackstruct.CallbackBeforePushReq{
- UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
- UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
- CallbackCommand: callbackstruct.CallbackOfflinePushCommand,
- OperationID: mcontext.GetOperationID(ctx),
- PlatformID: int(msg.SenderPlatformID),
- Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
+func (c *ConsumerHandler) webhookBeforeOfflinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ if msg.ContentType == constant.Typing {
+ return nil
+ }
+ req := &callbackstruct.CallbackBeforePushReq{
+ UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
+ UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
+ CallbackCommand: callbackstruct.CallbackBeforeOfflinePushCommand,
+ OperationID: mcontext.GetOperationID(ctx),
+ PlatformID: int(msg.SenderPlatformID),
+ Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
+ },
+ UserIDList: userIDs,
},
- UserIDList: userIDs,
- },
- OfflinePushInfo: msg.OfflinePushInfo,
- ClientMsgID: msg.ClientMsgID,
- SendID: msg.SendID,
- GroupID: msg.GroupID,
- ContentType: msg.ContentType,
- SessionType: msg.SessionType,
- AtUserIDs: msg.AtUserIDList,
- Content: GetContent(msg),
- }
+ OfflinePushInfo: msg.OfflinePushInfo,
+ ClientMsgID: msg.ClientMsgID,
+ SendID: msg.SendID,
+ GroupID: msg.GroupID,
+ ContentType: msg.ContentType,
+ SessionType: msg.SessionType,
+ AtUserIDs: msg.AtUserIDList,
+ Content: GetContent(msg),
+ }
- resp := &callbackstruct.CallbackBeforePushResp{}
- if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackOfflinePush); err != nil {
- return err
- }
+ resp := &callbackstruct.CallbackBeforePushResp{}
- if len(resp.UserIDs) != 0 {
- *offlinePushUserIDs = resp.UserIDs
- }
- if resp.OfflinePushInfo != nil {
- msg.OfflinePushInfo = resp.OfflinePushInfo
- }
- return nil
-}
+ if err := c.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
+ return err
+ }
-func callbackOnlinePush(ctx context.Context, config *config.GlobalConfig, userIDs []string, msg *sdkws.MsgData) error {
- if !config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing {
+ if len(resp.UserIDs) != 0 {
+ *offlinePushUserIDs = resp.UserIDs
+ }
+ if resp.OfflinePushInfo != nil {
+ msg.OfflinePushInfo = resp.OfflinePushInfo
+ }
return nil
- }
- req := callbackstruct.CallbackBeforePushReq{
- UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
- UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
- CallbackCommand: callbackstruct.CallbackOnlinePushCommand,
- OperationID: mcontext.GetOperationID(ctx),
- PlatformID: int(msg.SenderPlatformID),
- Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
+ })
+}
+
+func (c *ConsumerHandler) webhookBeforeOnlinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ if datautil.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing {
+ return nil
+ }
+ req := callbackstruct.CallbackBeforePushReq{
+ UserStatusBatchCallbackReq: callbackstruct.UserStatusBatchCallbackReq{
+ UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
+ CallbackCommand: callbackstruct.CallbackBeforeOnlinePushCommand,
+ OperationID: mcontext.GetOperationID(ctx),
+ PlatformID: int(msg.SenderPlatformID),
+ Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
+ },
+ UserIDList: userIDs,
},
- UserIDList: userIDs,
- },
- ClientMsgID: msg.ClientMsgID,
- SendID: msg.SendID,
- GroupID: msg.GroupID,
- ContentType: msg.ContentType,
- SessionType: msg.SessionType,
- AtUserIDs: msg.AtUserIDList,
- Content: GetContent(msg),
- }
- resp := &callbackstruct.CallbackBeforePushResp{}
- if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackOnlinePush); err != nil {
- return err
- }
- return nil
+ ClientMsgID: msg.ClientMsgID,
+ SendID: msg.SendID,
+ GroupID: msg.GroupID,
+ ContentType: msg.ContentType,
+ SessionType: msg.SessionType,
+ AtUserIDs: msg.AtUserIDList,
+ Content: GetContent(msg),
+ }
+ resp := &callbackstruct.CallbackBeforePushResp{}
+ if err := c.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
+ return err
+ }
+ return nil
+ })
}
-func callbackBeforeSuperGroupOnlinePush(
+func (c *ConsumerHandler) webhookBeforeGroupOnlinePush(
ctx context.Context,
- config *config.GlobalConfig,
+ before *config.BeforeConfig,
groupID string,
msg *sdkws.MsgData,
pushToUserIDs *[]string,
) error {
- if !config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable || msg.ContentType == constant.Typing {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ if msg.ContentType == constant.Typing {
+ return nil
+ }
+ req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
+ UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
+ CallbackCommand: callbackstruct.CallbackBeforeGroupOnlinePushCommand,
+ OperationID: mcontext.GetOperationID(ctx),
+ PlatformID: int(msg.SenderPlatformID),
+ Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
+ },
+ ClientMsgID: msg.ClientMsgID,
+ SendID: msg.SendID,
+ GroupID: groupID,
+ ContentType: msg.ContentType,
+ SessionType: msg.SessionType,
+ AtUserIDs: msg.AtUserIDList,
+ Content: GetContent(msg),
+ Seq: msg.Seq,
+ }
+ resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{}
+ if err := c.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
+ return err
+ }
+ if len(resp.UserIDs) != 0 {
+ *pushToUserIDs = resp.UserIDs
+ }
return nil
- }
- req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{
- UserStatusBaseCallback: callbackstruct.UserStatusBaseCallback{
- CallbackCommand: callbackstruct.CallbackSuperGroupOnlinePushCommand,
- OperationID: mcontext.GetOperationID(ctx),
- PlatformID: int(msg.SenderPlatformID),
- Platform: constant.PlatformIDToName(int(msg.SenderPlatformID)),
- },
- ClientMsgID: msg.ClientMsgID,
- SendID: msg.SendID,
- GroupID: groupID,
- ContentType: msg.ContentType,
- SessionType: msg.SessionType,
- AtUserIDs: msg.AtUserIDList,
- Content: GetContent(msg),
- Seq: msg.Seq,
- }
- resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{}
- if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackBeforeSuperGroupOnlinePush); err != nil {
- return err
- }
+ })
+}
- if len(resp.UserIDs) != 0 {
- *pushToUserIDs = resp.UserIDs
+func GetContent(msg *sdkws.MsgData) string {
+ if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd {
+ var notification sdkws.NotificationElem
+ if err := json.Unmarshal(msg.Content, ¬ification); err != nil {
+ return ""
+ }
+ return notification.Detail
+ } else {
+ return string(msg.Content)
}
- return nil
}
diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go
deleted file mode 100644
index 351b63f46d..0000000000
--- a/internal/push/consumer_init.go
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright © 2023 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package push
-
-import (
- "context"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-type Consumer struct {
- pushCh ConsumerHandler
- // successCount is unused
- // successCount uint64
-}
-
-func NewConsumer(config *config.GlobalConfig, pusher *Pusher) (*Consumer, error) {
- c, err := NewConsumerHandler(config, pusher)
- if err != nil {
- return nil, err
- }
- return &Consumer{
- pushCh: *c,
- }, nil
-}
-
-func (c *Consumer) Start() {
- go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh)
-}
diff --git a/internal/push/offlinepush/dummy/push.go b/internal/push/offlinepush/dummy/push.go
index f147886d9a..028e7edd34 100644
--- a/internal/push/offlinepush/dummy/push.go
+++ b/internal/push/offlinepush/dummy/push.go
@@ -16,8 +16,7 @@ package dummy
import (
"context"
-
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
)
func NewClient() *Dummy {
@@ -27,6 +26,6 @@ func NewClient() *Dummy {
type Dummy struct {
}
-func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
+func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
return nil
}
diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go
index ed65a5af60..34ad1c0d6c 100644
--- a/internal/push/offlinepush/fcm/push.go
+++ b/internal/push/offlinepush/fcm/push.go
@@ -16,14 +16,15 @@ package fcm
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
"path/filepath"
firebase "firebase.google.com/go"
"firebase.google.com/go/messaging"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/errs"
"github.com/redis/go-redis/v9"
"google.golang.org/api/option"
)
@@ -34,29 +35,32 @@ var Terminal = []int{constant.IOSPlatformID, constant.AndroidPlatformID, constan
type Fcm struct {
fcmMsgCli *messaging.Client
- cache cache.MsgModel
+ cache cache.ThirdCache
}
// NewClient initializes a new FCM client using the Firebase Admin SDK.
// It requires the FCM service account credentials file located within the project's configuration directory.
-func NewClient(globalConfig *config.GlobalConfig, cache cache.MsgModel) *Fcm {
- projectRoot := config.GetProjectRoot()
- credentialsFilePath := filepath.Join(projectRoot, "config", globalConfig.Push.Fcm.ServiceAccount)
+func NewClient(pushConf *config.Push, cache cache.ThirdCache) (*Fcm, error) {
+ projectRoot, err := config.GetProjectRoot()
+ if err != nil {
+ return nil, err
+ }
+ credentialsFilePath := filepath.Join(projectRoot, "config", pushConf.FCM.ServiceAccount)
opt := option.WithCredentialsFile(credentialsFilePath)
fcmApp, err := firebase.NewApp(context.Background(), nil, opt)
if err != nil {
- return nil
+ return nil, errs.Wrap(err)
}
ctx := context.Background()
fcmMsgClient, err := fcmApp.Messaging(ctx)
if err != nil {
- return nil
+ return nil, errs.Wrap(err)
}
- return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}
+ return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache}, nil
}
-func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
+func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
// accounts->registrationToken
allTokens := make(map[string][]string, 0)
for _, account := range userIDs {
diff --git a/internal/push/offlinepush/getui/body.go b/internal/push/offlinepush/getui/body.go
index 46479163ff..a96ff4efcd 100644
--- a/internal/push/offlinepush/getui/body.go
+++ b/internal/push/offlinepush/getui/body.go
@@ -133,13 +133,13 @@ type Payload struct {
IsSignal bool `json:"isSignal"`
}
-func newPushReq(config *config.GlobalConfig, title, content string) PushReq {
+func newPushReq(pushConf *config.Push, title, content string) PushReq {
pushReq := PushReq{PushMessage: &PushMessage{Notification: &Notification{
Title: title,
Body: content,
ClickType: "startapp",
- ChannelID: config.Push.GeTui.ChannelID,
- ChannelName: config.Push.GeTui.ChannelName,
+ ChannelID: pushConf.GeTui.ChannelID,
+ ChannelName: pushConf.GeTui.ChannelName,
}}}
return pushReq
}
diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go
index 1a95727e5c..8ecea3a622 100644
--- a/internal/push/offlinepush/getui/push.go
+++ b/internal/push/offlinepush/getui/push.go
@@ -18,25 +18,24 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
- "errors"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
"strconv"
"sync"
"time"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils/splitter"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/httputil"
+ "github.com/openimsdk/tools/utils/splitter"
"github.com/redis/go-redis/v9"
)
var (
- ErrTokenExpire = errors.New("token expire")
- ErrUserIDEmpty = errors.New("userIDs is empty")
+ ErrTokenExpire = errs.New("token expire")
+ ErrUserIDEmpty = errs.New("userIDs is empty")
)
const (
@@ -45,32 +44,34 @@ const (
taskURL = "/push/list/message"
batchPushURL = "/push/list/alias"
- // codes.
+ // Codes.
tokenExpireCode = 10001
tokenExpireTime = 60 * 60 * 23
taskIDTTL = 1000 * 60 * 60 * 24
)
type Client struct {
- cache cache.MsgModel
+ cache cache.ThirdCache
tokenExpireTime int64
taskIDTTL int64
- config *config.GlobalConfig
+ pushConf *config.Push
+ httpClient *httputil.HTTPClient
}
-func NewClient(config *config.GlobalConfig, cache cache.MsgModel) *Client {
+func NewClient(pushConf *config.Push, cache cache.ThirdCache) *Client {
return &Client{cache: cache,
tokenExpireTime: tokenExpireTime,
taskIDTTL: taskIDTTL,
- config: config,
+ pushConf: pushConf,
+ httpClient: httputil.NewHTTPClient(httputil.NewClientConfig()),
}
}
-func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
+func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
token, err := g.cache.GetGetuiToken(ctx)
if err != nil {
if errs.Unwrap(err) == redis.Nil {
- log.ZInfo(ctx, "getui token not exist in redis")
+ log.ZDebug(ctx, "getui token not exist in redis")
token, err = g.getTokenAndSave2Redis(ctx)
if err != nil {
return err
@@ -79,7 +80,7 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri
return err
}
}
- pushReq := newPushReq(g.config, title, content)
+ pushReq := newPushReq(g.pushConf, title, content)
pushReq.setPushChannel(title, content)
if len(userIDs) > 1 {
maxNum := 999
@@ -114,13 +115,13 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri
func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) {
h := sha256.New()
h.Write(
- []byte(g.config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + g.config.Push.GeTui.MasterSecret),
+ []byte(g.pushConf.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + g.pushConf.GeTui.MasterSecret),
)
sign := hex.EncodeToString(h.Sum(nil))
reqAuth := AuthReq{
Sign: sign,
Timestamp: strconv.Itoa(int(timeStamp)),
- AppKey: g.config.Push.GeTui.AppKey,
+ AppKey: g.pushConf.GeTui.AppKey,
}
respAuth := AuthResp{}
err = g.request(ctx, authURL, reqAuth, "", &respAuth)
@@ -163,7 +164,7 @@ func (g *Client) request(ctx context.Context, url string, input any, token strin
header := map[string]string{"token": token}
resp := &Resp{}
resp.Data = output
- return g.postReturn(ctx, g.config.Push.GeTui.PushUrl+url, header, input, resp, 3)
+ return g.postReturn(ctx, g.pushConf.GeTui.PushUrl+url, header, input, resp, 3)
}
func (g *Client) postReturn(
@@ -174,7 +175,7 @@ func (g *Client) postReturn(
output RespI,
timeout int,
) error {
- err := http2.PostReturn(ctx, url, header, input, output, timeout)
+ err := g.httpClient.PostReturn(ctx, url, header, input, output, timeout)
if err != nil {
return err
}
diff --git a/internal/push/offlinepush/jpush/body/audience.go b/internal/push/offlinepush/jpush/body/audience.go
index 43a7148b84..9db66ffe69 100644
--- a/internal/push/offlinepush/jpush/body/audience.go
+++ b/internal/push/offlinepush/jpush/body/audience.go
@@ -32,8 +32,8 @@ func (a *Audience) set(key string, v []string) {
a.audience = make(map[string][]string)
a.Object = a.audience
}
- //v, ok = this.audience[key]
- //if ok {
+ // v, ok = this.audience[key]
+ // if ok {
// return
//}
a.audience[key] = v
diff --git a/internal/push/offlinepush/jpush/body/notification.go b/internal/push/offlinepush/jpush/body/notification.go
index b25882ea58..42e59c46cf 100644
--- a/internal/push/offlinepush/jpush/body/notification.go
+++ b/internal/push/offlinepush/jpush/body/notification.go
@@ -56,8 +56,8 @@ func (n *Notification) SetExtras(extras Extras) {
n.Android.Extras = extras
}
-func (n *Notification) SetAndroidIntent(config *config.GlobalConfig) {
- n.Android.Intent.URL = config.Push.Jpns.PushIntent
+func (n *Notification) SetAndroidIntent(pushConf *config.Push) {
+ n.Android.Intent.URL = pushConf.JPNS.PushIntent
}
func (n *Notification) IOSEnableMutableContent() {
diff --git a/internal/push/offlinepush/jpush/body/platform.go b/internal/push/offlinepush/jpush/body/platform.go
index 1ef136f2c5..5654165fa4 100644
--- a/internal/push/offlinepush/jpush/body/platform.go
+++ b/internal/push/offlinepush/jpush/body/platform.go
@@ -15,9 +15,9 @@
package body
import (
- "errors"
+ "github.com/openimsdk/tools/errs"
- "github.com/OpenIMSDK/protocol/constant"
+ "github.com/openimsdk/protocol/constant"
)
const (
@@ -39,7 +39,7 @@ func (p *Platform) Set(os string) error {
} else {
switch p.Os.(type) {
case string:
- return errors.New("platform is all")
+ return errs.New("platform is all")
default:
}
}
@@ -61,7 +61,7 @@ func (p *Platform) Set(os string) error {
p.osArry = append(p.osArry, os)
p.Os = p.osArry
default:
- return errors.New("unknow platform")
+ return errs.New("unknow platform")
}
return nil
@@ -74,7 +74,7 @@ func (p *Platform) SetPlatform(platform string) error {
case constant.IOSPlatformStr:
return p.SetIOS()
default:
- return errors.New("platform err")
+ return errs.New("platform err")
}
}
diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go
index 2ced4bfd3d..dac52597f5 100644
--- a/internal/push/offlinepush/jpush/push.go
+++ b/internal/push/offlinepush/jpush/push.go
@@ -18,19 +18,22 @@ import (
"context"
"encoding/base64"
"fmt"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush/body"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ "github.com/openimsdk/tools/utils/httputil"
)
type JPush struct {
- config *config.GlobalConfig
+ pushConf *config.Push
+ httpClient *httputil.HTTPClient
}
-func NewClient(config *config.GlobalConfig) *JPush {
- return &JPush{config: config}
+func NewClient(pushConf *config.Push) *JPush {
+ return &JPush{pushConf: pushConf,
+ httpClient: httputil.NewHTTPClient(httputil.NewClientConfig()),
+ }
}
func (j *JPush) Auth(apiKey, secretKey string, timeStamp int64) (token string, err error) {
@@ -48,7 +51,7 @@ func (j *JPush) getAuthorization(appKey string, masterSecret string) string {
return Authorization
}
-func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error {
+func (j *JPush) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error {
var pf body.Platform
pf.SetAll()
var au body.Audience
@@ -61,12 +64,12 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin
no.IOSEnableMutableContent()
no.SetExtras(extras)
no.SetAlert(title)
- no.SetAndroidIntent(j.config)
+ no.SetAndroidIntent(j.pushConf)
var msg body.Message
msg.SetMsgContent(content)
var opt body.Options
- opt.SetApnsProduction(j.config.IOSPush.Production)
+ opt.SetApnsProduction(j.pushConf.IOSPush.Production)
var pushObj body.PushObj
pushObj.SetPlatform(&pf)
pushObj.SetAudience(&au)
@@ -78,11 +81,11 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin
}
func (j *JPush) request(ctx context.Context, po body.PushObj, resp any, timeout int) error {
- return http2.PostReturn(
+ return j.httpClient.PostReturn(
ctx,
- j.config.Push.Jpns.PushUrl,
+ j.pushConf.JPNS.PushURL,
map[string]string{
- "Authorization": j.getAuthorization(j.config.Push.Jpns.AppKey, j.config.Push.Jpns.MasterSecret),
+ "Authorization": j.getAuthorization(j.pushConf.JPNS.AppKey, j.pushConf.JPNS.MasterSecret),
},
po,
resp,
diff --git a/internal/push/offlinepush/offlinepush_interface.go b/internal/push/offlinepush/offlinepush_interface.go
deleted file mode 100644
index a5d4051f90..0000000000
--- a/internal/push/offlinepush/offlinepush_interface.go
+++ /dev/null
@@ -1,37 +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 offlinepush
-
-import (
- "context"
-)
-
-// OfflinePusher Offline Pusher.
-type OfflinePusher interface {
- Push(ctx context.Context, userIDs []string, title, content string, opts *Opts) error
-}
-
-// Opts opts.
-type Opts struct {
- Signal *Signal
- IOSPushSound string
- IOSBadgeCount bool
- Ex string
-}
-
-// Signal message id.
-type Signal struct {
- ClientMsgID string
-}
diff --git a/internal/push/offlinepush/offlinepusher.go b/internal/push/offlinepush/offlinepusher.go
new file mode 100644
index 0000000000..d4fcae434a
--- /dev/null
+++ b/internal/push/offlinepush/offlinepusher.go
@@ -0,0 +1,52 @@
+// Copyright © 2023 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package offlinepush
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
+)
+
+const (
+ geTUI = "geTui"
+ firebase = "fcm"
+ jPush = "jpush"
+)
+
+// OfflinePusher Offline Pusher.
+type OfflinePusher interface {
+ Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error
+}
+
+func NewOfflinePusher(pushConf *config.Push, cache cache.ThirdCache) (OfflinePusher, error) {
+ var offlinePusher OfflinePusher
+ switch pushConf.Enable {
+ case geTUI:
+ offlinePusher = getui.NewClient(pushConf, cache)
+ case firebase:
+ return fcm.NewClient(pushConf, cache)
+ case jPush:
+ offlinePusher = jpush.NewClient(pushConf)
+ default:
+ offlinePusher = dummy.NewClient()
+ }
+ return offlinePusher, nil
+}
diff --git a/internal/push/offlinepush/options/options.go b/internal/push/offlinepush/options/options.go
new file mode 100644
index 0000000000..056f6b7113
--- /dev/null
+++ b/internal/push/offlinepush/options/options.go
@@ -0,0 +1,14 @@
+package options
+
+// Opts opts.
+type Opts struct {
+ Signal *Signal
+ IOSPushSound string
+ IOSBadgeCount bool
+ Ex string
+}
+
+// Signal message id.
+type Signal struct {
+ ClientMsgID string
+}
diff --git a/internal/push/onlinepusher.go b/internal/push/onlinepusher.go
new file mode 100644
index 0000000000..30bdf3e2ed
--- /dev/null
+++ b/internal/push/onlinepusher.go
@@ -0,0 +1,204 @@
+package push
+
+import (
+ "context"
+ "github.com/openimsdk/protocol/msggateway"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+ "golang.org/x/sync/errgroup"
+ "google.golang.org/grpc"
+ "sync"
+)
+
+const (
+ KUBERNETES = "k8s"
+ ZOOKEEPER = "zookeeper"
+)
+
+type OnlinePusher interface {
+ GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
+ pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error)
+ GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults,
+ pushToUserIDs *[]string) []string
+}
+
+type emptyOnlinePUsher struct{}
+
+func newEmptyOnlinePUsher() *emptyOnlinePUsher {
+ return &emptyOnlinePUsher{}
+}
+
+func (emptyOnlinePUsher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
+ pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
+ log.ZWarn(ctx, "emptyOnlinePUsher GetConnsAndOnlinePush", nil)
+ return nil, nil
+}
+func (u emptyOnlinePUsher) GetOnlinePushFailedUserIDs(ctx context.Context, msg *sdkws.MsgData,
+ wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string {
+ log.ZWarn(ctx, "emptyOnlinePUsher GetOnlinePushFailedUserIDs", nil)
+ return nil
+}
+
+func NewOnlinePusher(disCov discovery.SvcDiscoveryRegistry, config *Config) OnlinePusher {
+ switch config.Share.Env {
+ case KUBERNETES:
+ return NewK8sStaticConsistentHash(disCov, config)
+ case ZOOKEEPER:
+ return NewDefaultAllNode(disCov, config)
+ default:
+ return newEmptyOnlinePUsher()
+ }
+}
+
+type DefaultAllNode struct {
+ disCov discovery.SvcDiscoveryRegistry
+ config *Config
+}
+
+func NewDefaultAllNode(disCov discovery.SvcDiscoveryRegistry, config *Config) *DefaultAllNode {
+ return &DefaultAllNode{disCov: disCov, config: config}
+}
+
+func (d *DefaultAllNode) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
+ pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
+ conns, err := d.disCov.GetConns(ctx, d.config.Share.RpcRegisterName.MessageGateway)
+ log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
+ if err != nil {
+ return nil, err
+ }
+
+ var (
+ mu sync.Mutex
+ wg = errgroup.Group{}
+ input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
+ maxWorkers = d.config.RpcConfig.MaxConcurrentWorkers
+ )
+
+ if maxWorkers < 3 {
+ maxWorkers = 3
+ }
+
+ wg.SetLimit(maxWorkers)
+
+ // Online push message
+ for _, conn := range conns {
+ conn := conn // loop var safe
+ wg.Go(func() error {
+ msgClient := msggateway.NewMsgGatewayClient(conn)
+ reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
+ if err != nil {
+ return nil
+ }
+
+ log.ZDebug(ctx, "push result", "reply", reply)
+ if reply != nil && reply.SinglePushResult != nil {
+ mu.Lock()
+ wsResults = append(wsResults, reply.SinglePushResult...)
+ mu.Unlock()
+ }
+
+ return nil
+ })
+ }
+
+ _ = wg.Wait()
+
+ // always return nil
+ return wsResults, nil
+}
+
+func (d *DefaultAllNode) GetOnlinePushFailedUserIDs(_ context.Context, msg *sdkws.MsgData,
+ wsResults []*msggateway.SingleMsgToUserResults, pushToUserIDs *[]string) []string {
+
+ onlineSuccessUserIDs := []string{msg.SendID}
+ for _, v := range wsResults {
+ //message sender do not need offline push
+ if msg.SendID == v.UserID {
+ continue
+ }
+ // mobile online push success
+ if v.OnlinePush {
+ onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
+ }
+
+ }
+
+ return datautil.SliceSub(*pushToUserIDs, onlineSuccessUserIDs)
+}
+
+type K8sStaticConsistentHash struct {
+ disCov discovery.SvcDiscoveryRegistry
+ config *Config
+}
+
+func NewK8sStaticConsistentHash(disCov discovery.SvcDiscoveryRegistry, config *Config) *K8sStaticConsistentHash {
+ return &K8sStaticConsistentHash{disCov: disCov, config: config}
+}
+
+func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData,
+ pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
+
+ var usersHost = make(map[string][]string)
+ for _, v := range pushToUserIDs {
+ tHost, err := k.disCov.GetUserIdHashGatewayHost(ctx, v)
+ if err != nil {
+ log.ZError(ctx, "get msg gateway hash error", err)
+ return nil, err
+ }
+ tUsers, tbl := usersHost[tHost]
+ if tbl {
+ tUsers = append(tUsers, v)
+ usersHost[tHost] = tUsers
+ } else {
+ usersHost[tHost] = []string{v}
+ }
+ }
+ log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
+ var usersConns = make(map[*grpc.ClientConn][]string)
+ for host, userIds := range usersHost {
+ tconn, _ := k.disCov.GetConn(ctx, host)
+ usersConns[tconn] = userIds
+ }
+ var (
+ mu sync.Mutex
+ wg = errgroup.Group{}
+ maxWorkers = k.config.RpcConfig.MaxConcurrentWorkers
+ )
+ if maxWorkers < 3 {
+ maxWorkers = 3
+ }
+ wg.SetLimit(maxWorkers)
+ for conn, userIds := range usersConns {
+ tcon := conn
+ tuserIds := userIds
+ wg.Go(func() error {
+ input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
+ msgClient := msggateway.NewMsgGatewayClient(tcon)
+ reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
+ if err != nil {
+ return nil
+ }
+ log.ZDebug(ctx, "push result", "reply", reply)
+ if reply != nil && reply.SinglePushResult != nil {
+ mu.Lock()
+ wsResults = append(wsResults, reply.SinglePushResult...)
+ mu.Unlock()
+ }
+ return nil
+ })
+ }
+ _ = wg.Wait()
+ return wsResults, nil
+}
+func (k *K8sStaticConsistentHash) GetOnlinePushFailedUserIDs(_ context.Context, _ *sdkws.MsgData,
+ wsResults []*msggateway.SingleMsgToUserResults, _ *[]string) []string {
+ var needOfflinePushUserIDs []string
+ for _, v := range wsResults {
+ if !v.OnlinePush {
+ needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
+ }
+ }
+ return needOfflinePushUserIDs
+}
diff --git a/internal/push/push.go b/internal/push/push.go
new file mode 100644
index 0000000000..18012a8646
--- /dev/null
+++ b/internal/push/push.go
@@ -0,0 +1,71 @@
+package push
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
+ pbpush "github.com/openimsdk/protocol/push"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/openimsdk/tools/discovery"
+ "google.golang.org/grpc"
+)
+
+type pushServer struct {
+ database controller.PushDatabase
+ disCov discovery.SvcDiscoveryRegistry
+ offlinePusher offlinepush.OfflinePusher
+ pushCh *ConsumerHandler
+}
+
+type Config struct {
+ RpcConfig config.Push
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ KafkaConfig config.Kafka
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ WebhooksConfig config.Webhooks
+ LocalCacheConfig config.LocalCache
+}
+
+func (p pushServer) PushMsg(ctx context.Context, req *pbpush.PushMsgReq) (*pbpush.PushMsgResp, error) {
+ //todo reserved Interface
+ return nil, nil
+}
+
+func (p pushServer) DelUserPushToken(ctx context.Context,
+ req *pbpush.DelUserPushTokenReq) (resp *pbpush.DelUserPushTokenResp, err error) {
+ if err = p.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
+ return nil, err
+ }
+ return &pbpush.DelUserPushTokenResp{}, nil
+}
+
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
+ if err != nil {
+ return err
+ }
+ cacheModel := cache.NewThirdCache(rdb)
+ offlinePusher, err := offlinepush.NewOfflinePusher(&config.RpcConfig, cacheModel)
+ if err != nil {
+ return err
+ }
+ database := controller.NewPushDatabase(cacheModel)
+
+ consumer, err := NewConsumerHandler(config, offlinePusher, rdb, client)
+ if err != nil {
+ return err
+ }
+ pbpush.RegisterPushMsgServiceServer(server, &pushServer{
+ database: database,
+ disCov: client,
+ offlinePusher: offlinePusher,
+ pushCh: consumer,
+ })
+ go consumer.pushConsumerGroup.RegisterHandleAndConsumer(ctx, consumer)
+ return nil
+}
diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go
index 0e68e76b3d..3a9a696f64 100644
--- a/internal/push/push_handler.go
+++ b/internal/push/push_handler.go
@@ -16,49 +16,64 @@ package push
import (
"context"
+ "encoding/json"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
+ "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
+ "github.com/openimsdk/open-im-server/v3/pkg/rpccache"
+ "github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/jsonutil"
+ "github.com/redis/go-redis/v9"
"github.com/IBM/sarama"
- "github.com/OpenIMSDK/protocol/constant"
- pbchat "github.com/OpenIMSDK/protocol/msg"
- pbpush "github.com/OpenIMSDK/protocol/push"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
+ "github.com/openimsdk/protocol/constant"
+ pbchat "github.com/openimsdk/protocol/msg"
+ pbpush "github.com/openimsdk/protocol/push"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mq/kafka"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/timeutil"
"google.golang.org/protobuf/proto"
)
type ConsumerHandler struct {
- pushConsumerGroup *kfk.MConsumerGroup
- pusher *Pusher
+ pushConsumerGroup *kafka.MConsumerGroup
+ offlinePusher offlinepush.OfflinePusher
+ onlinePusher OnlinePusher
+ groupLocalCache *rpccache.GroupLocalCache
+ conversationLocalCache *rpccache.ConversationLocalCache
+ msgRpcClient rpcclient.MessageRpcClient
+ conversationRpcClient rpcclient.ConversationRpcClient
+ groupRpcClient rpcclient.GroupRpcClient
+ webhookClient *webhook.Client
+ config *Config
}
-func NewConsumerHandler(config *config.GlobalConfig, pusher *Pusher) (*ConsumerHandler, error) {
+func NewConsumerHandler(config *Config, offlinePusher offlinepush.OfflinePusher, rdb redis.UniversalClient,
+ client discovery.SvcDiscoveryRegistry) (*ConsumerHandler, error) {
var consumerHandler ConsumerHandler
- consumerHandler.pusher = pusher
var err error
- var tlsConfig *kfk.TLSConfig
- if config.Kafka.TLS != nil {
- tlsConfig = &kfk.TLSConfig{
- CACrt: config.Kafka.TLS.CACrt,
- ClientCrt: config.Kafka.TLS.ClientCrt,
- ClientKey: config.Kafka.TLS.ClientKey,
- ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
- InsecureSkipVerify: false,
- }
- }
- consumerHandler.pushConsumerGroup, err = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{
- KafkaVersion: sarama.V2_0_0_0,
- OffsetsInitial: sarama.OffsetNewest,
- IsReturnErr: false,
- UserName: config.Kafka.Username,
- Password: config.Kafka.Password,
- }, []string{config.Kafka.MsgToPush.Topic}, config.Kafka.Addr,
- config.Kafka.ConsumerGroupID.MsgToPush,
- tlsConfig)
+ consumerHandler.pushConsumerGroup, err = kafka.NewMConsumerGroup(config.KafkaConfig.Build(), config.KafkaConfig.ToPushGroupID,
+ []string{config.KafkaConfig.ToPushTopic})
if err != nil {
return nil, err
}
+ consumerHandler.offlinePusher = offlinePusher
+ consumerHandler.onlinePusher = NewOnlinePusher(client, config)
+ consumerHandler.groupRpcClient = rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
+ consumerHandler.groupLocalCache = rpccache.NewGroupLocalCache(consumerHandler.groupRpcClient, &config.LocalCacheConfig, rdb)
+ consumerHandler.msgRpcClient = rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
+ consumerHandler.conversationRpcClient = rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
+ consumerHandler.conversationLocalCache = rpccache.NewConversationLocalCache(consumerHandler.conversationRpcClient,
+ &config.LocalCacheConfig, rdb)
+ consumerHandler.webhookClient = webhook.NewWebhookClient(config.WebhooksConfig.URL)
+ consumerHandler.config = config
return &consumerHandler, nil
}
@@ -73,38 +88,35 @@ func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) {
ConversationID: msgFromMQ.ConversationID,
}
sec := msgFromMQ.MsgData.SendTime / 1000
- nowSec := utils.GetCurrentTimestampBySecond()
+ nowSec := timeutil.GetCurrentTimestampBySecond()
log.ZDebug(ctx, "push msg", "msg", pbData.String(), "sec", sec, "nowSec", nowSec)
if nowSec-sec > 10 {
return
}
var err error
switch msgFromMQ.MsgData.SessionType {
- case constant.SuperGroupChatType:
- err = c.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
+ case constant.ReadGroupChatType:
+ err = c.Push2Group(ctx, pbData.MsgData.GroupID, pbData.MsgData)
default:
var pushUserIDList []string
- isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
+ isSenderSync := datautil.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
if !isSenderSync || pbData.MsgData.SendID == pbData.MsgData.RecvID {
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID)
} else {
pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
}
- err = c.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
+ err = c.Push2User(ctx, pushUserIDList, pbData.MsgData)
}
if err != nil {
- if err == errNoOfflinePusher {
- log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
- } else {
- log.ZError(ctx, "push failed", err, "msg", pbData.String())
- }
+ log.ZWarn(ctx, "push failed", err, "msg", pbData.String())
}
}
-func (ConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil }
-func (ConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
-func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
- claim sarama.ConsumerGroupClaim,
-) error {
+
+func (*ConsumerHandler) Setup(sarama.ConsumerGroupSession) error { return nil }
+
+func (*ConsumerHandler) Cleanup(sarama.ConsumerGroupSession) error { return nil }
+
+func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
for msg := range claim.Messages() {
ctx := c.pushConsumerGroup.GetContextFromMsg(msg)
c.handleMs2PsChat(ctx, msg.Value)
@@ -112,3 +124,243 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession,
}
return nil
}
+
+// Push2User Suitable for two types of conversations, one is SingleChatType and the other is NotificationChatType.
+func (c *ConsumerHandler) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
+ log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String())
+ if err := c.webhookBeforeOnlinePush(ctx, &c.config.WebhooksConfig.BeforeOnlinePush, userIDs, msg); err != nil {
+ return err
+ }
+ wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, userIDs)
+ if err != nil {
+ return err
+ }
+
+ log.ZDebug(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs)
+
+ if !c.shouldPushOffline(ctx, msg) {
+ return nil
+ }
+
+ for _, v := range wsResults {
+ //message sender do not need offline push
+ if msg.SendID == v.UserID {
+ continue
+ }
+ //receiver online push success
+ if v.OnlinePush {
+ return nil
+ }
+ }
+ offlinePUshUserID := []string{msg.RecvID}
+
+ //receiver offline push
+ if err = c.webhookBeforeOfflinePush(ctx, &c.config.WebhooksConfig.BeforeOfflinePush,
+ offlinePUshUserID, msg, nil); err != nil {
+ return err
+ }
+
+ err = c.offlinePushMsg(ctx, msg, offlinePUshUserID)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgData) bool {
+ isOfflinePush := datautil.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
+ if !isOfflinePush {
+ return false
+ }
+ if msg.ContentType == constant.SignalingNotification {
+ return false
+ }
+ return true
+}
+
+func (c *ConsumerHandler) Push2Group(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
+ log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
+ var pushToUserIDs []string
+ if err = c.webhookBeforeGroupOnlinePush(ctx, &c.config.WebhooksConfig.BeforeGroupOnlinePush, groupID, msg,
+ &pushToUserIDs); err != nil {
+ return err
+ }
+
+ err = c.groupMessagesHandler(ctx, groupID, &pushToUserIDs, msg)
+ if err != nil {
+ return err
+ }
+
+ wsResults, err := c.onlinePusher.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
+ if err != nil {
+ return err
+ }
+
+ log.ZDebug(ctx, "group push result", "result", wsResults, "msg", msg)
+
+ if !c.shouldPushOffline(ctx, msg) {
+ return nil
+ }
+ needOfflinePushUserIDs := c.onlinePusher.GetOnlinePushFailedUserIDs(ctx, msg, wsResults, &pushToUserIDs)
+
+ //filter some user, like don not disturb or don't need offline push etc.
+ needOfflinePushUserIDs, err = c.filterGroupMessageOfflinePush(ctx, groupID, msg, needOfflinePushUserIDs)
+ if err != nil {
+ return err
+ }
+ // Use offline push messaging
+ if len(needOfflinePushUserIDs) > 0 {
+ var offlinePushUserIDs []string
+ err = c.webhookBeforeOfflinePush(ctx, &c.config.WebhooksConfig.BeforeOfflinePush, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
+ if err != nil {
+ return err
+ }
+
+ if len(offlinePushUserIDs) > 0 {
+ needOfflinePushUserIDs = offlinePushUserIDs
+ }
+
+ err = c.offlinePushMsg(ctx, msg, needOfflinePushUserIDs)
+ if err != nil {
+ log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
+ return err
+ }
+
+ }
+
+ return nil
+}
+func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID string, pushToUserIDs *[]string, msg *sdkws.MsgData) (err error) {
+ if len(*pushToUserIDs) == 0 {
+ *pushToUserIDs, err = c.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
+ if err != nil {
+ return err
+ }
+ switch msg.ContentType {
+ case constant.MemberQuitNotification:
+ var tips sdkws.MemberQuitTips
+ if unmarshalNotificationElem(msg.Content, &tips) != nil {
+ return err
+ }
+ if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, []string{tips.QuitUser.UserID}); err != nil {
+ log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userID", tips.QuitUser.UserID)
+ }
+ *pushToUserIDs = append(*pushToUserIDs, tips.QuitUser.UserID)
+ case constant.MemberKickedNotification:
+ var tips sdkws.MemberKickedTips
+ if unmarshalNotificationElem(msg.Content, &tips) != nil {
+ return err
+ }
+ kickedUsers := datautil.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
+ if err = c.DeleteMemberAndSetConversationSeq(ctx, groupID, kickedUsers); err != nil {
+ log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", kickedUsers)
+ }
+
+ *pushToUserIDs = append(*pushToUserIDs, kickedUsers...)
+ case constant.GroupDismissedNotification:
+ if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) {
+ var tips sdkws.GroupDismissedTips
+ if unmarshalNotificationElem(msg.Content, &tips) != nil {
+ return err
+ }
+ log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(*pushToUserIDs), "list", pushToUserIDs)
+ if len(c.config.Share.IMAdminUserID) > 0 {
+ ctx = mcontext.WithOpUserIDContext(ctx, c.config.Share.IMAdminUserID[0])
+ }
+ defer func(groupID string) {
+ if err = c.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
+ log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
+ }
+ }(groupID)
+ }
+ }
+ }
+ return err
+}
+
+func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
+ title, content, opts, err := c.getOfflinePushInfos(msg)
+ if err != nil {
+ return err
+ }
+ err = c.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
+ if err != nil {
+ prommetrics.MsgOfflinePushFailedCounter.Inc()
+ return err
+ }
+ return nil
+}
+
+func (c *ConsumerHandler) filterGroupMessageOfflinePush(ctx context.Context, groupID string, msg *sdkws.MsgData,
+ offlinePushUserIDs []string) (userIDs []string, err error) {
+
+ //todo local cache Obtain the difference set through local comparison.
+ needOfflinePushUserIDs, err := c.conversationRpcClient.GetConversationOfflinePushUserIDs(
+ ctx, conversationutil.GenGroupConversationID(groupID), offlinePushUserIDs)
+ if err != nil {
+ return nil, err
+ }
+ return needOfflinePushUserIDs, nil
+}
+
+func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, content string, opts *options.Opts, err error) {
+ type AtTextElem struct {
+ Text string `json:"text,omitempty"`
+ AtUserList []string `json:"atUserList,omitempty"`
+ IsAtSelf bool `json:"isAtSelf"`
+ }
+
+ opts = &options.Opts{Signal: &options.Signal{}}
+ if msg.OfflinePushInfo != nil {
+ opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
+ opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
+ opts.Ex = msg.OfflinePushInfo.Ex
+ }
+
+ if msg.OfflinePushInfo != nil {
+ title = msg.OfflinePushInfo.Title
+ content = msg.OfflinePushInfo.Desc
+ }
+ if title == "" {
+ switch msg.ContentType {
+ case constant.Text:
+ fallthrough
+ case constant.Picture:
+ fallthrough
+ case constant.Voice:
+ fallthrough
+ case constant.Video:
+ fallthrough
+ case constant.File:
+ title = constant.ContentType2PushContent[int64(msg.ContentType)]
+ case constant.AtText:
+ ac := AtTextElem{}
+ _ = jsonutil.JsonStringToStruct(string(msg.Content), &ac)
+ case constant.SignalingNotification:
+ title = constant.ContentType2PushContent[constant.SignalMsg]
+ default:
+ title = constant.ContentType2PushContent[constant.Common]
+ }
+ }
+ if content == "" {
+ content = title
+ }
+ return
+}
+func (c *ConsumerHandler) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
+ conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID)
+ maxSeq, err := c.msgRpcClient.GetConversationMaxSeq(ctx, conversationID)
+ if err != nil {
+ return err
+ }
+ return c.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conversationID, maxSeq)
+}
+func unmarshalNotificationElem(bytes []byte, t any) error {
+ var notification sdkws.NotificationElem
+ if err := json.Unmarshal(bytes, ¬ification); err != nil {
+ return err
+ }
+
+ return json.Unmarshal([]byte(notification.Detail), t)
+}
diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go
deleted file mode 100644
index b68b06666e..0000000000
--- a/internal/push/push_rpc_server.go
+++ /dev/null
@@ -1,107 +0,0 @@
-// Copyright © 2023 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package push
-
-import (
- "context"
-
- "github.com/OpenIMSDK/protocol/constant"
- pbpush "github.com/OpenIMSDK/protocol/push"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- "github.com/openimsdk/open-im-server/v3/pkg/rpccache"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "google.golang.org/grpc"
-)
-
-type pushServer struct {
- pusher *Pusher
- config *config.GlobalConfig
-}
-
-func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- rdb, err := cache.NewRedis(config)
- if err != nil {
- return err
- }
- cacheModel := cache.NewMsgCacheModel(rdb, config)
- offlinePusher := NewOfflinePusher(config, cacheModel)
- database := controller.NewPushDatabase(cacheModel)
- groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
- conversationRpcClient := rpcclient.NewConversationRpcClient(client, config)
- msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
- pusher := NewPusher(
- config,
- client,
- offlinePusher,
- database,
- rpccache.NewGroupLocalCache(groupRpcClient, rdb),
- rpccache.NewConversationLocalCache(conversationRpcClient, rdb),
- &conversationRpcClient,
- &groupRpcClient,
- &msgRpcClient,
- )
-
- pbpush.RegisterPushMsgServiceServer(server, &pushServer{
- pusher: pusher,
- config: config,
- })
-
- consumer, err := NewConsumer(config, pusher)
- if err != nil {
- return err
- }
-
- consumer.Start()
-
- return nil
-}
-
-func (r *pushServer) PushMsg(ctx context.Context, pbData *pbpush.PushMsgReq) (resp *pbpush.PushMsgResp, err error) {
- switch pbData.MsgData.SessionType {
- case constant.SuperGroupChatType:
- err = r.pusher.Push2SuperGroup(ctx, pbData.MsgData.GroupID, pbData.MsgData)
- default:
- var pushUserIDList []string
- isSenderSync := utils.GetSwitchFromOptions(pbData.MsgData.Options, constant.IsSenderSync)
- if !isSenderSync {
- pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID)
- } else {
- pushUserIDList = append(pushUserIDList, pbData.MsgData.RecvID, pbData.MsgData.SendID)
- }
- err = r.pusher.Push2User(ctx, pushUserIDList, pbData.MsgData)
- }
- if err != nil {
- if err != errNoOfflinePusher {
- return nil, err
- }
- log.ZWarn(ctx, "offline push failed", err, "msg", pbData.String())
- }
- return &pbpush.PushMsgResp{}, nil
-}
-
-func (r *pushServer) DelUserPushToken(
- ctx context.Context,
- req *pbpush.DelUserPushTokenReq,
-) (resp *pbpush.DelUserPushTokenResp, err error) {
- if err = r.pusher.database.DelFcmToken(ctx, req.UserID, int(req.PlatformID)); err != nil {
- return nil, err
- }
- return &pbpush.DelUserPushTokenResp{}, nil
-}
diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go
deleted file mode 100644
index d7f30ebce0..0000000000
--- a/internal/push/push_to_client.go
+++ /dev/null
@@ -1,522 +0,0 @@
-// Copyright © 2023 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package push
-
-import (
- "context"
- "encoding/json"
- "errors"
- "sync"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/protocol/msggateway"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/getui"
- "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/jpush"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
- "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
- "github.com/openimsdk/open-im-server/v3/pkg/rpccache"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "golang.org/x/sync/errgroup"
- "google.golang.org/grpc"
-)
-
-type Pusher struct {
- config *config.GlobalConfig
- database controller.PushDatabase
- discov discoveryregistry.SvcDiscoveryRegistry
- offlinePusher offlinepush.OfflinePusher
- groupLocalCache *rpccache.GroupLocalCache
- conversationLocalCache *rpccache.ConversationLocalCache
- msgRpcClient *rpcclient.MessageRpcClient
- conversationRpcClient *rpcclient.ConversationRpcClient
- groupRpcClient *rpcclient.GroupRpcClient
-}
-
-var errNoOfflinePusher = errors.New("no offlinePusher is configured")
-
-func NewPusher(config *config.GlobalConfig, discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase,
- groupLocalCache *rpccache.GroupLocalCache, conversationLocalCache *rpccache.ConversationLocalCache,
- conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient,
-) *Pusher {
- return &Pusher{
- config: config,
- discov: discov,
- database: database,
- offlinePusher: offlinePusher,
- groupLocalCache: groupLocalCache,
- conversationLocalCache: conversationLocalCache,
- msgRpcClient: msgRpcClient,
- conversationRpcClient: conversationRpcClient,
- groupRpcClient: groupRpcClient,
- }
-}
-
-func NewOfflinePusher(config *config.GlobalConfig, cache cache.MsgModel) offlinepush.OfflinePusher {
- var offlinePusher offlinepush.OfflinePusher
- switch config.Push.Enable {
- case "getui":
- offlinePusher = getui.NewClient(config, cache)
- case "fcm":
- offlinePusher = fcm.NewClient(config, cache)
- case "jpush":
- offlinePusher = jpush.NewClient(config)
- default:
- offlinePusher = dummy.NewClient()
- }
- return offlinePusher
-}
-
-func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
- conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
- maxSeq, err := p.msgRpcClient.GetConversationMaxSeq(ctx, conevrsationID)
- if err != nil {
- return err
- }
- return p.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conevrsationID, maxSeq)
-}
-
-func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error {
- log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String())
- if err := callbackOnlinePush(ctx, p.config, userIDs, msg); err != nil {
- return err
- }
- // push
- wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, userIDs)
- if err != nil {
- return err
- }
-
- isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
- log.ZDebug(ctx, "push_result", "ws push result", wsResults, "sendData", msg, "isOfflinePush", isOfflinePush, "push_to_userID", userIDs)
-
- if !isOfflinePush {
- return nil
- }
-
- if len(wsResults) == 0 {
- return nil
- }
- onlinePushSuccUserIDSet := utils.SliceSet(utils.Filter(wsResults, func(e *msggateway.SingleMsgToUserResults) (string, bool) {
- return e.UserID, e.OnlinePush && e.UserID != ""
- }))
- offlinePushUserIDList := utils.Filter(wsResults, func(e *msggateway.SingleMsgToUserResults) (string, bool) {
- _, exist := onlinePushSuccUserIDSet[e.UserID]
- return e.UserID, !exist && e.UserID != "" && e.UserID != msg.SendID
- })
-
- if len(offlinePushUserIDList) > 0 {
- if err = callbackOfflinePush(ctx, p.config, offlinePushUserIDList, msg, &[]string{}); err != nil {
- return err
- }
- err = p.offlinePushMsg(ctx, msg.SendID, msg, offlinePushUserIDList)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func (p *Pusher) UnmarshalNotificationElem(bytes []byte, t any) error {
- var notification sdkws.NotificationElem
- if err := json.Unmarshal(bytes, ¬ification); err != nil {
- return err
- }
-
- return json.Unmarshal([]byte(notification.Detail), t)
-}
-
-/*
-k8s deployment,offline push group messages function.
-*/
-func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData, wsResults []*msggateway.SingleMsgToUserResults) error {
-
- var needOfflinePushUserIDs []string
- for _, v := range wsResults {
- if !v.OnlinePush {
- needOfflinePushUserIDs = append(needOfflinePushUserIDs, v.UserID)
- }
- }
- if len(needOfflinePushUserIDs) > 0 {
- var offlinePushUserIDs []string
- err := callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
- if err != nil {
- return err
- }
-
- if len(offlinePushUserIDs) > 0 {
- needOfflinePushUserIDs = offlinePushUserIDs
- }
- if msg.ContentType != constant.SignalingNotification {
- resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
- ctx,
- &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
- )
- if err != nil {
- return err
- }
- if len(resp.UserIDs) > 0 {
- err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
- if err != nil {
- log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
- return err
- }
- }
- }
-
- }
- return nil
-}
-func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) {
- log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID)
- var pushToUserIDs []string
- if err = callbackBeforeSuperGroupOnlinePush(ctx, p.config, groupID, msg, &pushToUserIDs); err != nil {
- return err
- }
-
- if len(pushToUserIDs) == 0 {
- pushToUserIDs, err = p.groupLocalCache.GetGroupMemberIDs(ctx, groupID)
- if err != nil {
- return err
- }
-
- switch msg.ContentType {
- case constant.MemberQuitNotification:
- var tips sdkws.MemberQuitTips
- if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
- return err
- }
- defer func(groupID string, userIDs []string) {
- if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
- log.ZError(ctx, "MemberQuitNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
- }
- }(groupID, []string{tips.QuitUser.UserID})
- pushToUserIDs = append(pushToUserIDs, tips.QuitUser.UserID)
- case constant.MemberKickedNotification:
- var tips sdkws.MemberKickedTips
- if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
- return err
- }
- kickedUsers := utils.Slice(tips.KickedUserList, func(e *sdkws.GroupMemberFullInfo) string { return e.UserID })
- defer func(groupID string, userIDs []string) {
- if err = p.DeleteMemberAndSetConversationSeq(ctx, groupID, userIDs); err != nil {
- log.ZError(ctx, "MemberKickedNotification DeleteMemberAndSetConversationSeq", err, "groupID", groupID, "userIDs", userIDs)
- }
- }(groupID, kickedUsers)
- pushToUserIDs = append(pushToUserIDs, kickedUsers...)
- case constant.GroupDismissedNotification:
- // Messages arrive first, notifications arrive later
- if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) {
- var tips sdkws.GroupDismissedTips
- if p.UnmarshalNotificationElem(msg.Content, &tips) != nil {
- return err
- }
- log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs)
- if len(p.config.Manager.UserID) > 0 {
- ctx = mcontext.WithOpUserIDContext(ctx, p.config.Manager.UserID[0])
- }
- if len(p.config.Manager.UserID) == 0 && len(p.config.IMAdmin.UserID) > 0 {
- ctx = mcontext.WithOpUserIDContext(ctx, p.config.IMAdmin.UserID[0])
- }
- defer func(groupID string) {
- if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil {
- log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID)
- }
- }(groupID)
- }
- }
- }
-
- wsResults, err := p.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs)
- if err != nil {
- return err
- }
-
- log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg)
- isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush)
- if isOfflinePush && p.config.Envs.Discovery == "k8s" {
- return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults)
- }
- if isOfflinePush && p.config.Envs.Discovery == "zookeeper" {
- var (
- onlineSuccessUserIDs = []string{msg.SendID}
- webAndPcBackgroundUserIDs []string
- )
-
- for _, v := range wsResults {
- if v.OnlinePush && v.UserID != msg.SendID {
- onlineSuccessUserIDs = append(onlineSuccessUserIDs, v.UserID)
- }
-
- if v.OnlinePush {
- continue
- }
-
- if len(v.Resp) == 0 {
- continue
- }
-
- for _, singleResult := range v.Resp {
- if singleResult.ResultCode != -2 {
- continue
- }
-
- isPC := constant.PlatformIDToName(int(singleResult.RecvPlatFormID)) == constant.TerminalPC
- isWebID := singleResult.RecvPlatFormID == constant.WebPlatformID
-
- if isPC || isWebID {
- webAndPcBackgroundUserIDs = append(webAndPcBackgroundUserIDs, v.UserID)
- }
- }
- }
-
- needOfflinePushUserIDs := utils.DifferenceString(onlineSuccessUserIDs, pushToUserIDs)
-
- // Use offline push messaging
- if len(needOfflinePushUserIDs) > 0 {
- var offlinePushUserIDs []string
- err = callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs)
- if err != nil {
- return err
- }
-
- if len(offlinePushUserIDs) > 0 {
- needOfflinePushUserIDs = offlinePushUserIDs
- }
- if msg.ContentType != constant.SignalingNotification {
- resp, err := p.conversationRpcClient.Client.GetConversationOfflinePushUserIDs(
- ctx,
- &conversation.GetConversationOfflinePushUserIDsReq{ConversationID: utils.GenGroupConversationID(groupID), UserIDs: needOfflinePushUserIDs},
- )
- if err != nil {
- return err
- }
- if len(resp.UserIDs) > 0 {
- err = p.offlinePushMsg(ctx, groupID, msg, resp.UserIDs)
- if err != nil {
- log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg)
- return err
- }
- if _, err := p.GetConnsAndOnlinePush(ctx, msg, utils.IntersectString(resp.UserIDs, webAndPcBackgroundUserIDs)); err != nil {
- log.ZError(ctx, "offlinePushMsg failed", err, "groupID", groupID, "msg", msg, "userIDs", utils.IntersectString(needOfflinePushUserIDs, webAndPcBackgroundUserIDs))
- return err
- }
- }
- }
-
- }
- }
- return nil
-}
-
-func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
- var usersHost = make(map[string][]string)
- for _, v := range pushToUserIDs {
- tHost, err := p.discov.GetUserIdHashGatewayHost(ctx, v)
- if err != nil {
- log.ZError(ctx, "get msggateway hash error", err)
- return nil, err
- }
- tUsers, tbl := usersHost[tHost]
- if tbl {
- tUsers = append(tUsers, v)
- usersHost[tHost] = tUsers
- } else {
- usersHost[tHost] = []string{v}
- }
- }
- log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost)
- var usersConns = make(map[*grpc.ClientConn][]string)
- for host, userIds := range usersHost {
- tconn, _ := p.discov.GetConn(ctx, host)
- usersConns[tconn] = userIds
- }
- var (
- mu sync.Mutex
- wg = errgroup.Group{}
- maxWorkers = p.config.Push.MaxConcurrentWorkers
- )
- if maxWorkers < 3 {
- maxWorkers = 3
- }
- wg.SetLimit(maxWorkers)
- for conn, userIds := range usersConns {
- tcon := conn
- tuserIds := userIds
- wg.Go(func() error {
- input := &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: tuserIds}
- msgClient := msggateway.NewMsgGatewayClient(tcon)
- reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
- if err != nil {
- return nil
- }
- log.ZDebug(ctx, "push result", "reply", reply)
- if reply != nil && reply.SinglePushResult != nil {
- mu.Lock()
- wsResults = append(wsResults, reply.SinglePushResult...)
- mu.Unlock()
- }
- return nil
- })
- }
- _ = wg.Wait()
- return wsResults, nil
-}
-func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) {
- if p.config.Envs.Discovery == "k8s" {
- return p.k8sOnlinePush(ctx, msg, pushToUserIDs)
- }
- conns, err := p.discov.GetConns(ctx, p.config.RpcRegisterName.OpenImMessageGatewayName)
- log.ZDebug(ctx, "get gateway conn", "conn length", len(conns))
- if err != nil {
- return nil, err
- }
-
- var (
- mu sync.Mutex
- wg = errgroup.Group{}
- input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs}
- maxWorkers = p.config.Push.MaxConcurrentWorkers
- )
-
- if maxWorkers < 3 {
- maxWorkers = 3
- }
-
- wg.SetLimit(maxWorkers)
-
- // Online push message
- for _, conn := range conns {
- conn := conn // loop var safe
- wg.Go(func() error {
- msgClient := msggateway.NewMsgGatewayClient(conn)
- reply, err := msgClient.SuperGroupOnlineBatchPushOneMsg(ctx, input)
- if err != nil {
- return nil
- }
-
- log.ZDebug(ctx, "push result", "reply", reply)
- if reply != nil && reply.SinglePushResult != nil {
- mu.Lock()
- wsResults = append(wsResults, reply.SinglePushResult...)
- mu.Unlock()
- }
-
- return nil
- })
- }
-
- _ = wg.Wait()
-
- // always return nil
- return wsResults, nil
-}
-
-func (p *Pusher) offlinePushMsg(ctx context.Context, conversationID string, msg *sdkws.MsgData, offlinePushUserIDs []string) error {
- title, content, opts, err := p.getOfflinePushInfos(conversationID, msg)
- if err != nil {
- return err
- }
- err = p.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts)
- if err != nil {
- prommetrics.MsgOfflinePushFailedCounter.Inc()
- return err
- }
- return nil
-}
-
-func (p *Pusher) GetOfflinePushOpts(msg *sdkws.MsgData) (opts *offlinepush.Opts, err error) {
- opts = &offlinepush.Opts{Signal: &offlinepush.Signal{}}
- // if msg.ContentType > constant.SignalingNotificationBegin && msg.ContentType < constant.SignalingNotificationEnd {
- // req := &sdkws.SignalReq{}
- // if err := proto.Unmarshal(msg.Content, req); err != nil {
- // return nil, utils.Wrap(err, "")
- // }
- // switch req.Payload.(type) {
- // case *sdkws.SignalReq_Invite, *sdkws.SignalReq_InviteInGroup:
- // opts.Signal = &offlinepush.Signal{ClientMsgID: msg.ClientMsgID}
- // }
- // }
- if msg.OfflinePushInfo != nil {
- opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount
- opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound
- opts.Ex = msg.OfflinePushInfo.Ex
- }
- return opts, nil
-}
-
-func (p *Pusher) getOfflinePushInfos(conversationID string, msg *sdkws.MsgData) (title, content string, opts *offlinepush.Opts, err error) {
- if p.offlinePusher == nil {
- err = errNoOfflinePusher
- return
- }
-
- type atContent struct {
- Text string `json:"text"`
- AtUserList []string `json:"atUserList"`
- IsAtSelf bool `json:"isAtSelf"`
- }
-
- opts, err = p.GetOfflinePushOpts(msg)
- if err != nil {
- return
- }
-
- if msg.OfflinePushInfo != nil {
- title = msg.OfflinePushInfo.Title
- content = msg.OfflinePushInfo.Desc
- }
- if title == "" {
- switch msg.ContentType {
- case constant.Text:
- fallthrough
- case constant.Picture:
- fallthrough
- case constant.Voice:
- fallthrough
- case constant.Video:
- fallthrough
- case constant.File:
- title = constant.ContentType2PushContent[int64(msg.ContentType)]
- case constant.AtText:
- ac := atContent{}
- _ = utils.JsonStringToStruct(string(msg.Content), &ac)
- if utils.IsContain(conversationID, ac.AtUserList) {
- title = constant.ContentType2PushContent[constant.AtText] + constant.ContentType2PushContent[constant.Common]
- } else {
- title = constant.ContentType2PushContent[constant.GroupMsg]
- }
- case constant.SignalingNotification:
- title = constant.ContentType2PushContent[constant.SignalMsg]
- default:
- title = constant.ContentType2PushContent[constant.Common]
- }
- }
- if content == "" {
- content = title
- }
- return
-}
diff --git a/internal/push/tools.go b/internal/push/tools.go
deleted file mode 100644
index 760c8c95bb..0000000000
--- a/internal/push/tools.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright © 2023 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package push
-
-import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "google.golang.org/protobuf/proto"
-)
-
-func GetContent(msg *sdkws.MsgData) string {
- if msg.ContentType >= constant.NotificationBegin && msg.ContentType <= constant.NotificationEnd {
- var tips sdkws.TipsComm
- _ = proto.Unmarshal(msg.Content, &tips)
- content := tips.JsonDetail
- return content
- }
- return string(msg.Content)
-}
diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go
index eb1e2f68a0..c6d236b21b 100644
--- a/internal/rpc/auth/auth.go
+++ b/internal/rpc/auth/auth.go
@@ -16,45 +16,54 @@ package auth
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/redis/go-redis/v9"
- pbauth "github.com/OpenIMSDK/protocol/auth"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msggateway"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/tokenverify"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ pbauth "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msggateway"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/tokenverify"
"google.golang.org/grpc"
)
type authServer struct {
authDatabase controller.AuthDatabase
userRpcClient *rpcclient.UserRpcClient
- RegisterCenter discoveryregistry.SvcDiscoveryRegistry
- config *config.GlobalConfig
+ RegisterCenter discovery.SvcDiscoveryRegistry
+ config *Config
+}
+
+type Config struct {
+ RpcConfig config.Auth
+ RedisConfig config.Redis
+ ZookeeperConfig config.ZooKeeper
+ Share config.Share
}
-func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- rdb, err := cache.NewRedis(config)
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
- userRpcClient := rpcclient.NewUserRpcClient(client, config)
+ userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
pbauth.RegisterAuthServer(server, &authServer{
userRpcClient: &userRpcClient,
RegisterCenter: client,
authDatabase: controller.NewAuthDatabase(
- cache.NewMsgCacheModel(rdb, config),
- config.Secret,
- config.TokenPolicy.Expire,
- config,
+ cache.NewTokenCacheModel(rdb),
+ config.Share.Secret,
+ config.RpcConfig.TokenPolicy.Expire,
),
config: config,
})
@@ -63,8 +72,8 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg
func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*pbauth.UserTokenResp, error) {
resp := pbauth.UserTokenResp{}
- if req.Secret != s.config.Secret {
- return nil, errs.ErrNoPermission.Wrap("secret invalid")
+ if req.Secret != s.config.Share.Secret {
+ return nil, errs.ErrNoPermission.WrapMsg("secret invalid")
}
if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil {
return nil, err
@@ -75,20 +84,19 @@ func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*
}
prommetrics.UserLoginCounter.Inc()
resp.Token = token
- resp.ExpireTimeSeconds = s.config.TokenPolicy.Expire * 24 * 60 * 60
+ resp.ExpireTimeSeconds = s.config.RpcConfig.TokenPolicy.Expire * 24 * 60 * 60
return &resp, nil
}
func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenReq) (*pbauth.GetUserTokenResp, error) {
- if err := authverify.CheckAdmin(ctx, s.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
resp := pbauth.GetUserTokenResp{}
- if authverify.IsManagerUserID(req.UserID, s.config) {
- return nil, errs.ErrNoPermission.Wrap("don't get Admin token")
+ if authverify.IsManagerUserID(req.UserID, s.config.Share.IMAdminUserID) {
+ return nil, errs.ErrNoPermission.WrapMsg("don't get Admin token")
}
-
if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil {
return nil, err
}
@@ -97,12 +105,12 @@ func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenR
return nil, err
}
resp.Token = token
- resp.ExpireTimeSeconds = s.config.TokenPolicy.Expire * 24 * 60 * 60
+ resp.ExpireTimeSeconds = s.config.RpcConfig.TokenPolicy.Expire * 24 * 60 * 60
return &resp, nil
}
func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) {
- claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret(s.config.Secret))
+ claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret(s.config.Share.Secret))
if err != nil {
return nil, errs.Wrap(err)
}
@@ -111,19 +119,19 @@ func (s *authServer) parseToken(ctx context.Context, tokensString string) (claim
return nil, err
}
if len(m) == 0 {
- return nil, errs.ErrTokenNotExist.Wrap()
+ return nil, servererrs.ErrTokenNotExist.Wrap()
}
if v, ok := m[tokensString]; ok {
switch v {
case constant.NormalToken:
return claims, nil
case constant.KickedToken:
- return nil, errs.ErrTokenKicked.Wrap()
+ return nil, servererrs.ErrTokenKicked.Wrap()
default:
return nil, errs.Wrap(errs.ErrTokenUnknown)
}
}
- return nil, errs.ErrTokenNotExist.Wrap()
+ return nil, servererrs.ErrTokenNotExist.Wrap()
}
func (s *authServer) ParseToken(
@@ -136,13 +144,13 @@ func (s *authServer) ParseToken(
return nil, err
}
resp.UserID = claims.UserID
- resp.Platform = constant.PlatformIDToName(claims.PlatformID)
+ resp.PlatformID = int32(claims.PlatformID)
resp.ExpireTimeSeconds = claims.ExpiresAt.Unix()
return resp, nil
}
func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq) (*pbauth.ForceLogoutResp, error) {
- if err := authverify.CheckAdmin(ctx, s.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
if err := s.forceKickOff(ctx, req.UserID, req.PlatformID, mcontext.GetOperationID(ctx)); err != nil {
@@ -152,7 +160,7 @@ func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq
}
func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID int32, operationID string) error {
- conns, err := s.RegisterCenter.GetConns(ctx, s.config.RpcRegisterName.OpenImMessageGatewayName)
+ conns, err := s.RegisterCenter.GetConns(ctx, s.config.Share.RpcRegisterName.MessageGateway)
if err != nil {
return err
}
@@ -169,3 +177,27 @@ func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID
}
return nil
}
+func (s *authServer) InvalidateToken(ctx context.Context, req *pbauth.InvalidateTokenReq) (*pbauth.InvalidateTokenResp, error) {
+ m, err := s.authDatabase.GetTokensWithoutError(ctx, req.UserID, int(req.PlatformID))
+ if err != nil && err != redis.Nil {
+ return nil, err
+ }
+ if m == nil {
+ return nil, errs.New("token map is empty").Wrap()
+ }
+ log.ZDebug(ctx, "get token from redis", "userID", req.UserID, "platformID",
+ req.PlatformID, "tokenMap", m)
+
+ for k := range m {
+ if k != req.GetPreservedToken() {
+ m[k] = constant.KickedToken
+ }
+ }
+ log.ZDebug(ctx, "set token map is ", "token map", m, "userID",
+ req.UserID, "token", req.GetPreservedToken())
+ err = s.authDatabase.SetTokenMapByUidPid(ctx, req.UserID, int(req.PlatformID), m)
+ if err != nil {
+ return nil, err
+ }
+ return &pbauth.InvalidateTokenResp{}, nil
+}
diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go
index e78e4a18f0..96d2a403f6 100644
--- a/internal/rpc/conversation/conversaion.go
+++ b/internal/rpc/conversation/conversaion.go
@@ -16,26 +16,25 @@ package conversation
import (
"context"
- "errors"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/db/redisutil"
"sort"
- "github.com/OpenIMSDK/protocol/constant"
- pbconversation "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
+ "github.com/openimsdk/protocol/constant"
+ pbconversation "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
"google.golang.org/grpc"
)
@@ -44,33 +43,43 @@ type conversationServer struct {
user *rpcclient.UserRpcClient
groupRpcClient *rpcclient.GroupRpcClient
conversationDatabase controller.ConversationDatabase
- conversationNotificationSender *notification.ConversationNotificationSender
- config *config.GlobalConfig
+ conversationNotificationSender *ConversationNotificationSender
+ config *Config
+}
+
+type Config struct {
+ RpcConfig config.Conversation
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ LocalCacheConfig config.LocalCache
}
-func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- rdb, err := cache.NewRedis(config)
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
- mongo, err := unrelation.NewMongo(config)
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
- conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase(config.Mongo.Database))
+ conversationDB, err := mgo.NewConversationMongo(mgocli.GetDB())
if err != nil {
return err
}
- groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
- msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
- userRpcClient := rpcclient.NewUserRpcClient(client, config)
+ groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
+ msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
+ userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
+ cache.InitLocalCache(&config.LocalCacheConfig)
pbconversation.RegisterConversationServer(server, &conversationServer{
msgRpcClient: &msgRpcClient,
user: &userRpcClient,
- conversationNotificationSender: notification.NewConversationNotificationSender(config, &msgRpcClient),
+ conversationNotificationSender: NewConversationNotificationSender(&config.NotificationConfig, &msgRpcClient),
groupRpcClient: &groupRpcClient,
- conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewMongo(mongo.GetClient())),
- config: config,
+ conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, &config.LocalCacheConfig, cache.GetDefaultOpt(), conversationDB), mgocli.GetTx()),
})
return nil
}
@@ -81,7 +90,7 @@ func (c *conversationServer) GetConversation(ctx context.Context, req *pbconvers
return nil, err
}
if len(conversations) < 1 {
- return nil, errs.ErrRecordNotFound.Wrap("conversation not found")
+ return nil, errs.ErrRecordNotFound.WrapMsg("conversation not found")
}
resp := &pbconversation.GetConversationResp{Conversation: &pbconversation.Conversation{}}
resp.Conversation = convert.ConversationDB2Pb(conversations[0])
@@ -158,7 +167,7 @@ func (c *conversationServer) GetSortedConversationList(ctx context.Context, req
c.conversationSort(conversation_isPinTime, resp, conversation_unreadCount, conversationMsg)
c.conversationSort(conversation_notPinTime, resp, conversation_unreadCount, conversationMsg)
- resp.ConversationElems = utils.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
+ resp.ConversationElems = datautil.Paginate(resp.ConversationElems, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
return resp, nil
}
@@ -184,32 +193,30 @@ func (c *conversationServer) GetConversations(ctx context.Context, req *pbconver
func (c *conversationServer) SetConversation(ctx context.Context, req *pbconversation.SetConversationReq) (*pbconversation.SetConversationResp, error) {
var conversation tablerelation.ConversationModel
- if err := utils.CopyStructFields(&conversation, req.Conversation); err != nil {
+ if err := datautil.CopyStructFields(&conversation, req.Conversation); err != nil {
return nil, err
}
err := c.conversationDatabase.SetUserConversations(ctx, req.Conversation.OwnerUserID, []*tablerelation.ConversationModel{&conversation})
if err != nil {
return nil, err
}
- _ = c.conversationNotificationSender.ConversationChangeNotification(ctx, req.Conversation.OwnerUserID, []string{req.Conversation.ConversationID})
+ c.conversationNotificationSender.ConversationChangeNotification(ctx, req.Conversation.OwnerUserID, []string{req.Conversation.ConversationID})
resp := &pbconversation.SetConversationResp{}
return resp, nil
}
// nolint
-func (c *conversationServer) SetConversations(ctx context.Context,
- req *pbconversation.SetConversationsReq,
-) (*pbconversation.SetConversationsResp, error) {
+func (c *conversationServer) SetConversations(ctx context.Context, req *pbconversation.SetConversationsReq) (*pbconversation.SetConversationsResp, error) {
if req.Conversation == nil {
- return nil, errs.ErrArgs.Wrap("conversation must not be nil")
+ return nil, errs.ErrArgs.WrapMsg("conversation must not be nil")
}
- if req.Conversation.ConversationType == constant.GroupChatType {
+ if req.Conversation.ConversationType == constant.WriteGroupChatType {
groupInfo, err := c.groupRpcClient.GetGroupInfo(ctx, req.Conversation.GroupID)
if err != nil {
return nil, err
}
if groupInfo.Status == constant.GroupStatusDismissed {
- return nil, errs.ErrDismissedAlready.Wrap("group dismissed")
+ return nil, servererrs.ErrDismissedAlready.WrapMsg("group dismissed")
}
}
var unequal int
@@ -220,7 +227,7 @@ func (c *conversationServer) SetConversations(ctx context.Context,
return nil, err
}
if len(cs) == 0 {
- return nil, errs.ErrRecordNotFound.Wrap("conversation not found")
+ return nil, errs.ErrRecordNotFound.WrapMsg("conversation not found")
}
conv = *cs[0]
}
@@ -272,7 +279,7 @@ func (c *conversationServer) SetConversations(ctx context.Context,
unequal++
}
}
- if req.Conversation.IsPrivateChat != nil && req.Conversation.ConversationType != constant.SuperGroupChatType {
+ if req.Conversation.IsPrivateChat != nil && req.Conversation.ConversationType != constant.ReadGroupChatType {
var conversations []*tablerelation.ConversationModel
for _, ownerUserID := range req.UserIDs {
conversation2 := conversation
@@ -280,45 +287,39 @@ func (c *conversationServer) SetConversations(ctx context.Context,
conversation2.IsPrivateChat = req.Conversation.IsPrivateChat.Value
conversations = append(conversations, &conversation2)
}
+
if err := c.conversationDatabase.SyncPeerUserPrivateConversationTx(ctx, conversations); err != nil {
return nil, err
}
for _, userID := range req.UserIDs {
- err := c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, userID, req.Conversation.UserID,
+ c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, userID, req.Conversation.UserID,
req.Conversation.IsPrivateChat.Value, req.Conversation.ConversationID)
- if err != nil {
- log.ZWarn(ctx, "send conversation set private notification failed", err,
- "userID", userID, "conversationID", req.Conversation.ConversationID)
-
- continue
- }
}
}
+
if req.Conversation.BurnDuration != nil {
m["burn_duration"] = req.Conversation.BurnDuration.Value
if req.Conversation.BurnDuration.Value != conv.BurnDuration {
unequal++
}
}
+
if err := c.conversationDatabase.SetUsersConversationFieldTx(ctx, req.UserIDs, &conversation, m); err != nil {
return nil, err
}
+
if unequal > 0 {
for _, v := range req.UserIDs {
c.conversationNotificationSender.ConversationChangeNotification(ctx, v, []string{req.Conversation.ConversationID})
}
}
+
return &pbconversation.SetConversationsResp{}, nil
}
// Get user IDs with "Do Not Disturb" enabled in super large groups.
func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbconversation.GetRecvMsgNotNotifyUserIDsReq) (*pbconversation.GetRecvMsgNotNotifyUserIDsResp, error) {
- //userIDs, err := c.conversationDatabase.FindRecvMsgNotNotifyUserIDs(ctx, req.GroupID)
- //if err != nil {
- // return nil, err
- //}
- //return &pbconversation.GetRecvMsgNotNotifyUserIDsResp{UserIDs: userIDs}, nil
- return nil, errors.New("deprecated")
+ return nil, errs.New("deprecated")
}
// create conversation without notification for msg redis transfer.
@@ -402,12 +403,9 @@ func (c *conversationServer) GetConversationsByConversationID(
return &pbconversation.GetConversationsByConversationIDResp{Conversations: convert.ConversationsDB2Pb(conversations)}, nil
}
-func (c *conversationServer) GetConversationOfflinePushUserIDs(
- ctx context.Context,
- req *pbconversation.GetConversationOfflinePushUserIDsReq,
-) (*pbconversation.GetConversationOfflinePushUserIDsResp, error) {
+func (c *conversationServer) GetConversationOfflinePushUserIDs(ctx context.Context, req *pbconversation.GetConversationOfflinePushUserIDsReq) (*pbconversation.GetConversationOfflinePushUserIDsResp, error) {
if req.ConversationID == "" {
- return nil, errs.ErrArgs.Wrap("conversationID is empty")
+ return nil, errs.ErrArgs.WrapMsg("conversationID is empty")
}
if len(req.UserIDs) == 0 {
return &pbconversation.GetConversationOfflinePushUserIDsResp{}, nil
@@ -426,21 +424,16 @@ func (c *conversationServer) GetConversationOfflinePushUserIDs(
for _, userID := range userIDs {
delete(userIDSet, userID)
}
- return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: utils.Keys(userIDSet)}, nil
+ return &pbconversation.GetConversationOfflinePushUserIDsResp{UserIDs: datautil.Keys(userIDSet)}, nil
}
-func (c *conversationServer) conversationSort(
- conversations map[int64]string,
- resp *pbconversation.GetSortedConversationListResp,
- conversation_unreadCount map[string]int64,
- conversationMsg map[string]*pbconversation.ConversationElem,
-) {
+func (c *conversationServer) conversationSort(conversations map[int64]string, resp *pbconversation.GetSortedConversationListResp, conversation_unreadCount map[string]int64, conversationMsg map[string]*pbconversation.ConversationElem) {
keys := []int64{}
for key := range conversations {
keys = append(keys, key)
}
- sort.Slice(keys[:], func(i, j int) bool {
+ sort.Slice(keys, func(i, j int) bool {
return keys[i] > keys[j]
})
index := 0
@@ -474,7 +467,7 @@ func (c *conversationServer) getConversationInfo(
sendIDs = append(sendIDs, chatLog.RecvID)
}
sendIDs = append(sendIDs, chatLog.SendID)
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
groupIDs = append(groupIDs, chatLog.GroupID)
sendIDs = append(sendIDs, chatLog.SendID)
}
@@ -500,7 +493,7 @@ func (c *conversationServer) getConversationInfo(
for conversationID, chatLog := range chatLogs {
pbchatLog := &pbconversation.ConversationElem{}
msgInfo := &pbconversation.MsgInfo{}
- if err := utils.CopyStructFields(msgInfo, chatLog); err != nil {
+ if err := datautil.CopyStructFields(msgInfo, chatLog); err != nil {
return nil, err
}
switch chatLog.SessionType {
@@ -516,7 +509,7 @@ func (c *conversationServer) getConversationInfo(
msgInfo.FaceURL = send.FaceURL
msgInfo.SenderName = send.Nickname
}
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
msgInfo.GroupID = chatLog.GroupID
if group, ok := groupMap[chatLog.GroupID]; ok {
msgInfo.GroupName = group.GroupName
@@ -536,13 +529,53 @@ func (c *conversationServer) getConversationInfo(
return conversationMsg, nil
}
-func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(
- ctx context.Context,
- req *pbconversation.GetConversationNotReceiveMessageUserIDsReq,
-) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) {
+func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) {
userIDs, err := c.conversationDatabase.GetConversationNotReceiveMessageUserIDs(ctx, req.ConversationID)
if err != nil {
return nil, err
}
return &pbconversation.GetConversationNotReceiveMessageUserIDsResp{UserIDs: userIDs}, nil
}
+
+func (c *conversationServer) UpdateConversation(ctx context.Context, req *pbconversation.UpdateConversationReq) (*pbconversation.UpdateConversationResp, error) {
+ m := make(map[string]any)
+ if req.RecvMsgOpt != nil {
+ m["recv_msg_opt"] = req.RecvMsgOpt.Value
+ }
+ if req.AttachedInfo != nil {
+ m["attached_info"] = req.AttachedInfo.Value
+ }
+ if req.Ex != nil {
+ m["ex"] = req.Ex.Value
+ }
+ if req.IsPinned != nil {
+ m["is_pinned"] = req.IsPinned.Value
+ }
+ if req.GroupAtType != nil {
+ m["group_at_type"] = req.GroupAtType.Value
+ }
+ if req.MsgDestructTime != nil {
+ m["msg_destruct_time"] = req.MsgDestructTime.Value
+ }
+ if req.IsMsgDestruct != nil {
+ m["is_msg_destruct"] = req.IsMsgDestruct.Value
+ }
+ if req.BurnDuration != nil {
+ m["burn_duration"] = req.BurnDuration.Value
+ }
+ if req.IsPrivateChat != nil {
+ m["is_private_chat"] = req.IsPrivateChat.Value
+ }
+ if req.MinSeq != nil {
+ m["min_seq"] = req.MinSeq.Value
+ }
+ if req.MaxSeq != nil {
+ m["max_seq"] = req.MaxSeq.Value
+ }
+ if len(m) > 0 {
+ if err := c.conversationDatabase.UpdateUsersConversationField(ctx, req.UserIDs, req.ConversationID, m); err != nil {
+ return nil, err
+ }
+ }
+ return &pbconversation.UpdateConversationResp{}, nil
+}
diff --git a/pkg/rpcclient/notification/conversation.go b/internal/rpc/conversation/notification.go
similarity index 74%
rename from pkg/rpcclient/notification/conversation.go
rename to internal/rpc/conversation/notification.go
index 1544d6a1f7..994e1d57ae 100644
--- a/pkg/rpcclient/notification/conversation.go
+++ b/internal/rpc/conversation/notification.go
@@ -12,29 +12,29 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notification
+package conversation
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
)
type ConversationNotificationSender struct {
*rpcclient.NotificationSender
}
-func NewConversationNotificationSender(config *config.GlobalConfig, msgRpcClient *rpcclient.MessageRpcClient) *ConversationNotificationSender {
- return &ConversationNotificationSender{rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient))}
+func NewConversationNotificationSender(conf *config.Notification, msgRpcClient *rpcclient.MessageRpcClient) *ConversationNotificationSender {
+ return &ConversationNotificationSender{rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(msgRpcClient))}
}
// SetPrivate invote.
func (c *ConversationNotificationSender) ConversationSetPrivateNotification(ctx context.Context, sendID, recvID string,
isPrivateChat bool, conversationID string,
-) error {
+) {
tips := &sdkws.ConversationSetPrivateTips{
RecvID: recvID,
SendID: sendID,
@@ -42,23 +42,23 @@ func (c *ConversationNotificationSender) ConversationSetPrivateNotification(ctx
ConversationID: conversationID,
}
- return c.Notification(ctx, sendID, recvID, constant.ConversationPrivateChatNotification, tips)
+ c.Notification(ctx, sendID, recvID, constant.ConversationPrivateChatNotification, tips)
}
-func (c *ConversationNotificationSender) ConversationChangeNotification(ctx context.Context, userID string, conversationIDs []string) error {
+func (c *ConversationNotificationSender) ConversationChangeNotification(ctx context.Context, userID string, conversationIDs []string) {
tips := &sdkws.ConversationUpdateTips{
UserID: userID,
ConversationIDList: conversationIDs,
}
- return c.Notification(ctx, userID, userID, constant.ConversationChangeNotification, tips)
+ c.Notification(ctx, userID, userID, constant.ConversationChangeNotification, tips)
}
func (c *ConversationNotificationSender) ConversationUnreadChangeNotification(
ctx context.Context,
userID, conversationID string,
unreadCountTime, hasReadSeq int64,
-) error {
+) {
tips := &sdkws.ConversationHasReadTips{
UserID: userID,
ConversationID: conversationID,
@@ -66,5 +66,5 @@ func (c *ConversationNotificationSender) ConversationUnreadChangeNotification(
UnreadCountTime: unreadCountTime,
}
- return c.Notification(ctx, userID, userID, constant.ConversationUnreadNotification, tips)
+ c.Notification(ctx, userID, userID, constant.ConversationUnreadNotification, tips)
}
diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go
index 4e130360cd..1f52286f34 100644
--- a/internal/rpc/friend/black.go
+++ b/internal/rpc/friend/black.go
@@ -18,11 +18,11 @@ import (
"context"
"time"
- pbfriend "github.com/OpenIMSDK/protocol/friend"
- "github.com/OpenIMSDK/tools/mcontext"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ pbfriend "github.com/openimsdk/protocol/friend"
+ "github.com/openimsdk/tools/mcontext"
)
func (s *friendServer) GetPaginationBlacks(ctx context.Context, req *pbfriend.GetPaginationBlacksReq) (resp *pbfriend.GetPaginationBlacksResp, err error) {
@@ -57,15 +57,18 @@ func (s *friendServer) RemoveBlack(ctx context.Context, req *pbfriend.RemoveBlac
if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil {
return nil, err
}
+
if err := s.blackDatabase.Delete(ctx, []*relation.BlackModel{{OwnerUserID: req.OwnerUserID, BlockUserID: req.BlackUserID}}); err != nil {
return nil, err
}
+
s.notificationSender.BlackDeletedNotification(ctx, req)
+
return &pbfriend.RemoveBlackResp{}, nil
}
func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq) (*pbfriend.AddBlackResp, error) {
- if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
_, err := s.userRpcClient.GetUsersInfo(ctx, []string{req.OwnerUserID, req.BlackUserID})
@@ -83,10 +86,6 @@ func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq)
if err := s.blackDatabase.Create(ctx, []*relation.BlackModel{&black}); err != nil {
return nil, err
}
-
- if err := s.notificationSender.BlackAddedNotification(ctx, req); err != nil {
- return nil, err
- }
-
+ s.notificationSender.BlackAddedNotification(ctx, req)
return &pbfriend.AddBlackResp{}, nil
}
diff --git a/internal/rpc/friend/callback.go b/internal/rpc/friend/callback.go
index 78d4fc926f..0610cdb78a 100644
--- a/internal/rpc/friend/callback.go
+++ b/internal/rpc/friend/callback.go
@@ -16,85 +16,41 @@ package friend
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
- pbfriend "github.com/OpenIMSDK/protocol/friend"
- "github.com/OpenIMSDK/tools/utils"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ pbfriend "github.com/openimsdk/protocol/friend"
)
-func CallbackBeforeAddFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ApplyToAddFriendReq) error {
- if !globalConfig.Callback.CallbackBeforeAddFriend.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackBeforeAddFriendReq{
- CallbackCommand: cbapi.CallbackBeforeAddFriendCommand,
- FromUserID: req.FromUserID,
- ToUserID: req.ToUserID,
- ReqMsg: req.ReqMsg,
- Ex: req.Ex,
- }
- resp := &cbapi.CallbackBeforeAddFriendResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil {
- return err
- }
- return nil
-}
-
-func CallbackBeforeSetFriendRemark(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.SetFriendRemarkReq) error {
- if !globalConfig.Callback.CallbackBeforeSetFriendRemark.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{
- CallbackCommand: cbapi.CallbackBeforeSetFriendRemark,
+func (s *friendServer) webhookAfterDeleteFriend(ctx context.Context, after *config.AfterConfig, req *pbfriend.DeleteFriendReq) {
+ cbReq := &cbapi.CallbackAfterDeleteFriendReq{
+ CallbackCommand: cbapi.CallbackAfterDeleteFriendCommand,
OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID,
- Remark: req.Remark,
- }
- resp := &cbapi.CallbackBeforeSetFriendRemarkResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil {
- return err
}
- utils.NotNilReplace(&req.Remark, &resp.Remark)
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterDeleteFriendResp{}, after)
}
-func CallbackAfterSetFriendRemark(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.SetFriendRemarkReq) error {
- if !globalConfig.Callback.CallbackAfterSetFriendRemark.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{
- CallbackCommand: cbapi.CallbackAfterSetFriendRemark,
- OwnerUserID: req.OwnerUserID,
- FriendUserID: req.FriendUserID,
- Remark: req.Remark,
- }
- resp := &cbapi.CallbackAfterSetFriendRemarkResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil {
- return err
- }
- return nil
-}
-func CallbackBeforeAddBlack(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.AddBlackReq) error {
- if !globalConfig.Callback.CallbackBeforeAddBlack.Enable {
+func (s *friendServer) webhookBeforeAddFriend(ctx context.Context, before *config.BeforeConfig, req *pbfriend.ApplyToAddFriendReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeAddFriendReq{
+ CallbackCommand: cbapi.CallbackBeforeAddFriendCommand,
+ FromUserID: req.FromUserID,
+ ToUserID: req.ToUserID,
+ ReqMsg: req.ReqMsg,
+ Ex: req.Ex,
+ }
+ resp := &cbapi.CallbackBeforeAddFriendResp{}
+
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
return nil
- }
- cbReq := &cbapi.CallbackBeforeAddBlackReq{
- CallbackCommand: cbapi.CallbackBeforeAddBlackCommand,
- OwnerUserID: req.OwnerUserID,
- BlackUserID: req.BlackUserID,
- }
- resp := &cbapi.CallbackBeforeAddBlackResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddBlack); err != nil {
- return err
- }
- return nil
+ })
}
-func CallbackAfterAddFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ApplyToAddFriendReq) error {
- if !globalConfig.Callback.CallbackAfterAddFriend.Enable {
- return nil
- }
+
+func (s *friendServer) webhookAfterAddFriend(ctx context.Context, after *config.AfterConfig, req *pbfriend.ApplyToAddFriendReq) {
cbReq := &cbapi.CallbackAfterAddFriendReq{
CallbackCommand: cbapi.CallbackAfterAddFriendCommand,
FromUserID: req.FromUserID,
@@ -102,90 +58,100 @@ func CallbackAfterAddFriend(ctx context.Context, globalConfig *config.GlobalConf
ReqMsg: req.ReqMsg,
}
resp := &cbapi.CallbackAfterAddFriendResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterAddFriend); err != nil {
- return err
- }
-
- return nil
-}
-func CallbackBeforeAddFriendAgree(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.RespondFriendApplyReq) error {
- if !globalConfig.Callback.CallbackBeforeAddFriendAgree.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{
- CallbackCommand: cbapi.CallbackBeforeAddFriendAgreeCommand,
- FromUserID: req.FromUserID,
- ToUserID: req.ToUserID,
- HandleMsg: req.HandleMsg,
- HandleResult: req.HandleResult,
- }
- resp := &cbapi.CallbackBeforeAddFriendAgreeResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriendAgree); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
}
-func CallbackAfterDeleteFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.DeleteFriendReq) error {
- if !globalConfig.Callback.CallbackAfterDeleteFriend.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackAfterDeleteFriendReq{
- CallbackCommand: cbapi.CallbackAfterDeleteFriendCommand,
+
+func (s *friendServer) webhookAfterSetFriendRemark(ctx context.Context, after *config.AfterConfig, req *pbfriend.SetFriendRemarkReq) {
+
+ cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{
+ CallbackCommand: cbapi.CallbackAfterSetFriendRemarkCommand,
OwnerUserID: req.OwnerUserID,
FriendUserID: req.FriendUserID,
+ Remark: req.Remark,
}
- resp := &cbapi.CallbackAfterDeleteFriendResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterDeleteFriend); err != nil {
- return err
- }
- return nil
-}
-func CallbackBeforeImportFriends(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ImportFriendReq) error {
- if !globalConfig.Callback.CallbackBeforeImportFriends.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackBeforeImportFriendsReq{
- CallbackCommand: cbapi.CallbackBeforeImportFriendsCommand,
- OwnerUserID: req.OwnerUserID,
- FriendUserIDs: req.FriendUserIDs,
- }
- resp := &cbapi.CallbackBeforeImportFriendsResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeImportFriends); err != nil {
- return err
- }
- if len(resp.FriendUserIDs) != 0 {
- req.FriendUserIDs = resp.FriendUserIDs
- }
- return nil
+ resp := &cbapi.CallbackAfterSetFriendRemarkResp{}
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
}
-func CallbackAfterImportFriends(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ImportFriendReq) error {
- if !globalConfig.Callback.CallbackAfterImportFriends.Enable {
- return nil
- }
+
+func (s *friendServer) webhookAfterImportFriends(ctx context.Context, after *config.AfterConfig, req *pbfriend.ImportFriendReq) {
cbReq := &cbapi.CallbackAfterImportFriendsReq{
CallbackCommand: cbapi.CallbackAfterImportFriendsCommand,
OwnerUserID: req.OwnerUserID,
FriendUserIDs: req.FriendUserIDs,
}
resp := &cbapi.CallbackAfterImportFriendsResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterImportFriends); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
}
-func CallbackAfterRemoveBlack(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.RemoveBlackReq) error {
- if !globalConfig.Callback.CallbackAfterRemoveBlack.Enable {
- return nil
- }
+func (s *friendServer) webhookAfterRemoveBlack(ctx context.Context, after *config.AfterConfig, req *pbfriend.RemoveBlackReq) {
cbReq := &cbapi.CallbackAfterRemoveBlackReq{
CallbackCommand: cbapi.CallbackAfterRemoveBlackCommand,
OwnerUserID: req.OwnerUserID,
BlackUserID: req.BlackUserID,
}
resp := &cbapi.CallbackAfterRemoveBlackResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterRemoveBlack); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, after)
+}
+
+func (s *friendServer) webhookBeforeSetFriendRemark(ctx context.Context, before *config.BeforeConfig, req *pbfriend.SetFriendRemarkReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{
+ CallbackCommand: cbapi.CallbackBeforeSetFriendRemarkCommand,
+ OwnerUserID: req.OwnerUserID,
+ FriendUserID: req.FriendUserID,
+ Remark: req.Remark,
+ }
+ resp := &cbapi.CallbackBeforeSetFriendRemarkResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+ if resp.Remark != "" {
+ req.Remark = resp.Remark
+ }
+ return nil
+ })
+}
+
+func (s *friendServer) webhookBeforeAddBlack(ctx context.Context, before *config.BeforeConfig, req *pbfriend.AddBlackReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeAddBlackReq{
+ CallbackCommand: cbapi.CallbackBeforeAddBlackCommand,
+ OwnerUserID: req.OwnerUserID,
+ BlackUserID: req.BlackUserID,
+ }
+ resp := &cbapi.CallbackBeforeAddBlackResp{}
+ return s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before)
+ })
+}
+
+func (s *friendServer) webhookBeforeAddFriendAgree(ctx context.Context, before *config.BeforeConfig, req *pbfriend.RespondFriendApplyReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{
+ CallbackCommand: cbapi.CallbackBeforeAddFriendAgreeCommand,
+ FromUserID: req.FromUserID,
+ ToUserID: req.ToUserID,
+ HandleMsg: req.HandleMsg,
+ HandleResult: req.HandleResult,
+ }
+ resp := &cbapi.CallbackBeforeAddFriendAgreeResp{}
+ return s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before)
+ })
+}
+
+func (s *friendServer) webhookBeforeImportFriends(ctx context.Context, before *config.BeforeConfig, req *pbfriend.ImportFriendReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeImportFriendsReq{
+ CallbackCommand: cbapi.CallbackBeforeImportFriendsCommand,
+ OwnerUserID: req.OwnerUserID,
+ FriendUserIDs: req.FriendUserIDs,
+ }
+ resp := &cbapi.CallbackBeforeImportFriendsResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+ if len(resp.FriendUserIDs) > 0 {
+ req.FriendUserIDs = resp.FriendUserIDs
+ }
+ return nil
+ })
}
diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go
index 4df4085a9c..bffda3c04e 100644
--- a/internal/rpc/friend/friend.go
+++ b/internal/rpc/friend/friend.go
@@ -16,25 +16,25 @@ package friend
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ "github.com/openimsdk/tools/db/redisutil"
- "github.com/OpenIMSDK/protocol/constant"
- pbfriend "github.com/OpenIMSDK/protocol/friend"
- "github.com/OpenIMSDK/protocol/sdkws"
- registry "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
+ "github.com/openimsdk/protocol/constant"
+ pbfriend "github.com/openimsdk/protocol/friend"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/utils/datautil"
"google.golang.org/grpc"
)
@@ -42,67 +42,79 @@ type friendServer struct {
friendDatabase controller.FriendDatabase
blackDatabase controller.BlackDatabase
userRpcClient *rpcclient.UserRpcClient
- notificationSender *notification.FriendNotificationSender
+ notificationSender *FriendNotificationSender
conversationRpcClient rpcclient.ConversationRpcClient
- RegisterCenter registry.SvcDiscoveryRegistry
- config *config.GlobalConfig
+ RegisterCenter discovery.SvcDiscoveryRegistry
+ config *Config
+ webhookClient *webhook.Client
+}
+
+type Config struct {
+ RpcConfig config.Friend
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ WebhooksConfig config.Webhooks
+ LocalCacheConfig config.LocalCache
}
-func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
- // Initialize MongoDB
- mongo, err := unrelation.NewMongo(config)
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
-
- // Initialize Redis
- rdb, err := cache.NewRedis(config)
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
- friendMongoDB, err := mgo.NewFriendMongo(mongo.GetDatabase(config.Mongo.Database))
+ friendMongoDB, err := mgo.NewFriendMongo(mgocli.GetDB())
if err != nil {
return err
}
- friendRequestMongoDB, err := mgo.NewFriendRequestMongo(mongo.GetDatabase(config.Mongo.Database))
+ friendRequestMongoDB, err := mgo.NewFriendRequestMongo(mgocli.GetDB())
if err != nil {
return err
}
- blackMongoDB, err := mgo.NewBlackMongo(mongo.GetDatabase(config.Mongo.Database))
+ blackMongoDB, err := mgo.NewBlackMongo(mgocli.GetDB())
if err != nil {
return err
}
// Initialize RPC clients
- userRpcClient := rpcclient.NewUserRpcClient(client, config)
- msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
+ userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
+ msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
// Initialize notification sender
- notificationSender := notification.NewFriendNotificationSender(
- config,
+ notificationSender := NewFriendNotificationSender(
+ &config.NotificationConfig,
&msgRpcClient,
- notification.WithRpcFunc(userRpcClient.GetUsersInfo),
+ WithRpcFunc(userRpcClient.GetUsersInfo),
)
+ cache.InitLocalCache(&config.LocalCacheConfig)
+
// Register Friend server with refactored MongoDB and Redis integrations
pbfriend.RegisterFriendServer(server, &friendServer{
friendDatabase: controller.NewFriendDatabase(
friendMongoDB,
friendRequestMongoDB,
- cache.NewFriendCacheRedis(rdb, friendMongoDB, cache.GetDefaultOpt()),
- tx.NewMongo(mongo.GetClient()),
+ cache.NewFriendCacheRedis(rdb, &config.LocalCacheConfig, friendMongoDB, cache.GetDefaultOpt()),
+ mgocli.GetTx(),
),
blackDatabase: controller.NewBlackDatabase(
blackMongoDB,
- cache.NewBlackCacheRedis(rdb, blackMongoDB, cache.GetDefaultOpt()),
+ cache.NewBlackCacheRedis(rdb, &config.LocalCacheConfig, blackMongoDB, cache.GetDefaultOpt()),
),
userRpcClient: &userRpcClient,
notificationSender: notificationSender,
RegisterCenter: client,
- conversationRpcClient: rpcclient.NewConversationRpcClient(client, config),
+ conversationRpcClient: rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation),
config: config,
+ webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
})
return nil
@@ -111,18 +123,15 @@ func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, se
// ok.
func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) {
resp = &pbfriend.ApplyToAddFriendResp{}
- if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
-
if req.ToUserID == req.FromUserID {
- return nil, errs.ErrCanNotAddYourself.Wrap("req.ToUserID", req.ToUserID)
+ return nil, servererrs.ErrCanNotAddYourself.WrapMsg("req.ToUserID", req.ToUserID)
}
-
- if err = CallbackBeforeAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
+ if err = s.webhookBeforeAddFriend(ctx, &s.config.WebhooksConfig.BeforeAddFriend, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
-
if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil {
return nil, err
}
@@ -131,41 +140,33 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply
if err != nil {
return nil, err
}
-
if in1 && in2 {
- return nil, errs.ErrRelationshipAlready.Wrap()
+ return nil, servererrs.ErrRelationshipAlready.WrapMsg("already friends has f")
}
-
if err = s.friendDatabase.AddFriendRequest(ctx, req.FromUserID, req.ToUserID, req.ReqMsg, req.Ex); err != nil {
return nil, err
}
-
- if err = s.notificationSender.FriendApplicationAddNotification(ctx, req); err != nil {
- return nil, err
- }
-
- if err = CallbackAfterAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
- return nil, err
- }
+ s.notificationSender.FriendApplicationAddNotification(ctx, req)
+ s.webhookAfterAddFriend(ctx, &s.config.WebhooksConfig.AfterAddFriend, req)
return resp, nil
}
// ok.
func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFriendReq) (resp *pbfriend.ImportFriendResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
- if err := authverify.CheckAdmin(ctx, s.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
if _, err := s.userRpcClient.GetUsersInfo(ctx, append([]string{req.OwnerUserID}, req.FriendUserIDs...)); err != nil {
return nil, err
}
- if utils.Contain(req.OwnerUserID, req.FriendUserIDs...) {
- return nil, errs.ErrCanNotAddYourself.Wrap()
+ if datautil.Contain(req.OwnerUserID, req.FriendUserIDs...) {
+ return nil, servererrs.ErrCanNotAddYourself.WrapMsg("can not add yourself")
}
- if utils.Duplicate(req.FriendUserIDs) {
- return nil, errs.ErrArgs.Wrap("friend userID repeated")
+ if datautil.Duplicate(req.FriendUserIDs) {
+ return nil, errs.ErrArgs.WrapMsg("friend userID repeated")
}
- if err := CallbackBeforeImportFriends(ctx, s.config, req); err != nil {
+
+ if err := s.webhookBeforeImportFriends(ctx, &s.config.WebhooksConfig.BeforeImportFriends, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
@@ -179,17 +180,15 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr
HandleResult: constant.FriendResponseAgree,
})
}
- if err := CallbackAfterImportFriends(ctx, s.config, req); err != nil {
- return nil, err
- }
+
+ s.webhookAfterImportFriends(ctx, &s.config.WebhooksConfig.AfterImportFriends, req)
return &pbfriend.ImportFriendResp{}, nil
}
// ok.
func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.RespondFriendApplyReq) (resp *pbfriend.RespondFriendApplyResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
resp = &pbfriend.RespondFriendApplyResp{}
- if err := authverify.CheckAccessV3(ctx, req.ToUserID, s.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.ToUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
@@ -200,16 +199,14 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res
HandleResult: req.HandleResult,
}
if req.HandleResult == constant.FriendResponseAgree {
- if err := CallbackBeforeAddFriendAgree(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
+ if err := s.webhookBeforeAddFriendAgree(ctx, &s.config.WebhooksConfig.BeforeAddFriendAgree, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
err := s.friendDatabase.AgreeFriendRequest(ctx, &friendRequest)
if err != nil {
return nil, err
}
- if err := s.notificationSender.FriendApplicationAgreedNotification(ctx, req); err != nil {
- return nil, err
- }
+ s.notificationSender.FriendApplicationAgreedNotification(ctx, req)
return resp, nil
}
if req.HandleResult == constant.FriendResponseRefuse {
@@ -220,12 +217,11 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res
s.notificationSender.FriendApplicationRefusedNotification(ctx, req)
return resp, nil
}
- return nil, errs.ErrArgs.Wrap("req.HandleResult != -1/1")
+ return nil, errs.ErrArgs.WrapMsg("req.HandleResult != -1/1")
}
// ok.
func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendReq) (resp *pbfriend.DeleteFriendResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
resp = &pbfriend.DeleteFriendResp{}
if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil {
return nil, err
@@ -238,17 +234,13 @@ func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFri
return nil, err
}
s.notificationSender.FriendDeletedNotification(ctx, req)
- if err := CallbackAfterDeleteFriend(ctx, s.config, req); err != nil {
- return nil, err
- }
+ s.webhookAfterDeleteFriend(ctx, &s.config.WebhooksConfig.AfterDeleteFriend, req)
return resp, nil
}
// ok.
func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) (resp *pbfriend.SetFriendRemarkResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
-
- if err = CallbackBeforeSetFriendRemark(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
+ if err = s.webhookBeforeSetFriendRemark(ctx, &s.config.WebhooksConfig.BeforeSetFriendRemark, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
resp = &pbfriend.SetFriendRemarkResp{}
@@ -262,19 +254,16 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri
if err := s.friendDatabase.UpdateRemark(ctx, req.OwnerUserID, req.FriendUserID, req.Remark); err != nil {
return nil, err
}
- if err := CallbackAfterSetFriendRemark(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue {
- return nil, err
- }
+ s.webhookAfterSetFriendRemark(ctx, &s.config.WebhooksConfig.AfterSetFriendRemark, req)
s.notificationSender.FriendRemarkSetNotification(ctx, req.OwnerUserID, req.FriendUserID)
return resp, nil
}
// ok.
func (s *friendServer) GetDesignatedFriends(ctx context.Context, req *pbfriend.GetDesignatedFriendsReq) (resp *pbfriend.GetDesignatedFriendsResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
resp = &pbfriend.GetDesignatedFriendsResp{}
- if utils.Duplicate(req.FriendUserIDs) {
- return nil, errs.ErrArgs.Wrap("friend userID repeated")
+ if datautil.Duplicate(req.FriendUserIDs) {
+ return nil, errs.ErrArgs.WrapMsg("friend userID repeated")
}
friends, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
if err != nil {
@@ -303,7 +292,6 @@ func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context,
// Get received friend requests (i.e., those initiated by others).
func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyToReq) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
return nil, err
}
@@ -321,7 +309,6 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbf
}
func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyFromReq) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
resp = &pbfriend.GetPaginationFriendsApplyFromResp{}
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
return nil, err
@@ -340,7 +327,6 @@ func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *p
// ok.
func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq) (resp *pbfriend.IsFriendResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
resp = &pbfriend.IsFriendResp{}
resp.InUser1Friends, resp.InUser2Friends, err = s.friendDatabase.CheckIn(ctx, req.UserID1, req.UserID2)
if err != nil {
@@ -350,7 +336,6 @@ func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq)
}
func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.GetPaginationFriendsReq) (resp *pbfriend.GetPaginationFriendsResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
return nil, err
}
@@ -368,7 +353,6 @@ func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.G
}
func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriendIDsReq) (resp *pbfriend.GetFriendIDsResp, err error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
if err := s.userRpcClient.Access(ctx, req.UserID); err != nil {
return nil, err
}
@@ -382,10 +366,10 @@ func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriend
func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfriend.GetSpecifiedFriendsInfoReq) (*pbfriend.GetSpecifiedFriendsInfoResp, error) {
if len(req.UserIDList) == 0 {
- return nil, errs.ErrArgs.Wrap("userIDList is empty")
+ return nil, errs.ErrArgs.WrapMsg("userIDList is empty")
}
- if utils.Duplicate(req.UserIDList) {
- return nil, errs.ErrArgs.Wrap("userIDList repeated")
+ if datautil.Duplicate(req.UserIDList) {
+ return nil, errs.ErrArgs.WrapMsg("userIDList repeated")
}
userMap, err := s.userRpcClient.GetUsersInfoMap(ctx, req.UserIDList)
if err != nil {
@@ -399,10 +383,10 @@ func (s *friendServer) GetSpecifiedFriendsInfo(ctx context.Context, req *pbfrien
if err != nil {
return nil, err
}
- friendMap := utils.SliceToMap(friends, func(e *tablerelation.FriendModel) string {
+ friendMap := datautil.SliceToMap(friends, func(e *tablerelation.FriendModel) string {
return e.FriendUserID
})
- blackMap := utils.SliceToMap(blacks, func(e *tablerelation.BlackModel) string {
+ blackMap := datautil.SliceToMap(blacks, func(e *tablerelation.BlackModel) string {
return e.BlockUserID
})
resp := &pbfriend.GetSpecifiedFriendsInfoResp{
@@ -449,10 +433,10 @@ func (s *friendServer) UpdateFriends(
req *pbfriend.UpdateFriendsReq,
) (*pbfriend.UpdateFriendsResp, error) {
if len(req.FriendUserIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("friendIDList is empty")
+ return nil, errs.ErrArgs.WrapMsg("friendIDList is empty")
}
- if utils.Duplicate(req.FriendUserIDs) {
- return nil, errs.ErrArgs.Wrap("friendIDList repeated")
+ if datautil.Duplicate(req.FriendUserIDs) {
+ return nil, errs.ErrArgs.WrapMsg("friendIDList repeated")
}
_, err := s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, req.FriendUserIDs)
@@ -477,9 +461,6 @@ func (s *friendServer) UpdateFriends(
resp := &pbfriend.UpdateFriendsResp{}
- err = s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs)
- if err != nil {
- return nil, errs.Wrap(err, "FriendsInfoUpdateNotification Error")
- }
+ s.notificationSender.FriendsInfoUpdateNotification(ctx, req.OwnerUserID, req.FriendUserIDs)
return resp, nil
}
diff --git a/pkg/rpcclient/notification/friend.go b/internal/rpc/friend/notification.go
similarity index 72%
rename from pkg/rpcclient/notification/friend.go
rename to internal/rpc/friend/notification.go
index 751bdf4757..f88c9664e6 100644
--- a/pkg/rpcclient/notification/friend.go
+++ b/internal/rpc/friend/notification.go
@@ -12,26 +12,27 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notification
+package friend
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- pbfriend "github.com/OpenIMSDK/protocol/friend"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/mcontext"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
+ "github.com/openimsdk/protocol/constant"
+ pbfriend "github.com/openimsdk/protocol/friend"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/mcontext"
)
type FriendNotificationSender struct {
*rpcclient.NotificationSender
// Target not found err
- getUsersInfo func(ctx context.Context, userIDs []string) ([]CommonUser, error)
+ getUsersInfo func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)
// db controller
db controller.FriendDatabase
}
@@ -48,7 +49,7 @@ func WithDBFunc(
fn func(ctx context.Context, userIDs []string) (users []*relationtb.UserModel, err error),
) friendNotificationSenderOptions {
return func(s *FriendNotificationSender) {
- f := func(ctx context.Context, userIDs []string) (result []CommonUser, err error) {
+ f := func(ctx context.Context, userIDs []string) (result []notification.CommonUser, err error) {
users, err := fn(ctx, userIDs)
if err != nil {
return nil, err
@@ -66,7 +67,7 @@ func WithRpcFunc(
fn func(ctx context.Context, userIDs []string) ([]*sdkws.UserInfo, error),
) friendNotificationSenderOptions {
return func(s *FriendNotificationSender) {
- f := func(ctx context.Context, userIDs []string) (result []CommonUser, err error) {
+ f := func(ctx context.Context, userIDs []string) (result []notification.CommonUser, err error) {
users, err := fn(ctx, userIDs)
if err != nil {
return nil, err
@@ -81,12 +82,12 @@ func WithRpcFunc(
}
func NewFriendNotificationSender(
- config *config.GlobalConfig,
+ conf *config.Notification,
msgRpcClient *rpcclient.MessageRpcClient,
opts ...friendNotificationSenderOptions,
) *FriendNotificationSender {
f := &FriendNotificationSender{
- NotificationSender: rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient)),
+ NotificationSender: rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(msgRpcClient)),
}
for _, opt := range opts {
opt(f)
@@ -121,39 +122,39 @@ func (f *FriendNotificationSender) getFromToUserNickname(
return users[fromUserID].Nickname, users[toUserID].Nickname, nil
}
-func (f *FriendNotificationSender) UserInfoUpdatedNotification(ctx context.Context, changedUserID string) error {
+func (f *FriendNotificationSender) UserInfoUpdatedNotification(ctx context.Context, changedUserID string) {
tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID}
- return f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips)
+ f.Notification(ctx, mcontext.GetOpUserID(ctx), changedUserID, constant.UserInfoUpdatedNotification, &tips)
}
-func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) error {
+func (f *FriendNotificationSender) FriendApplicationAddNotification(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) {
tips := sdkws.FriendApplicationTips{FromToUserID: &sdkws.FromToUserID{
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
}}
- return f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips)
+ f.Notification(ctx, req.FromUserID, req.ToUserID, constant.FriendApplicationNotification, &tips)
}
func (f *FriendNotificationSender) FriendApplicationAgreedNotification(
ctx context.Context,
req *pbfriend.RespondFriendApplyReq,
-) error {
+) {
tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
}, HandleMsg: req.HandleMsg}
- return f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips)
+ f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationApprovedNotification, &tips)
}
func (f *FriendNotificationSender) FriendApplicationRefusedNotification(
ctx context.Context,
req *pbfriend.RespondFriendApplyReq,
-) error {
+) {
tips := sdkws.FriendApplicationApprovedTips{FromToUserID: &sdkws.FromToUserID{
FromUserID: req.FromUserID,
ToUserID: req.ToUserID,
}, HandleMsg: req.HandleMsg}
- return f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips)
+ f.Notification(ctx, req.ToUserID, req.FromUserID, constant.FriendApplicationRejectedNotification, &tips)
}
func (f *FriendNotificationSender) FriendAddedNotification(
@@ -177,34 +178,37 @@ func (f *FriendNotificationSender) FriendAddedNotification(
if err != nil {
return err
}
- return f.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
+ f.Notification(ctx, fromUserID, toUserID, constant.FriendAddedNotification, &tips)
+ return nil
}
-func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *pbfriend.DeleteFriendReq) error {
+func (f *FriendNotificationSender) FriendDeletedNotification(ctx context.Context, req *pbfriend.DeleteFriendReq) {
tips := sdkws.FriendDeletedTips{FromToUserID: &sdkws.FromToUserID{
FromUserID: req.OwnerUserID,
ToUserID: req.FriendUserID,
}}
- return f.Notification(ctx, req.OwnerUserID, req.FriendUserID, constant.FriendDeletedNotification, &tips)
+ f.Notification(ctx, req.OwnerUserID, req.FriendUserID, constant.FriendDeletedNotification, &tips)
}
-func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Context, fromUserID, toUserID string) error {
+func (f *FriendNotificationSender) FriendRemarkSetNotification(ctx context.Context, fromUserID, toUserID string) {
tips := sdkws.FriendInfoChangedTips{FromToUserID: &sdkws.FromToUserID{}}
tips.FromToUserID.FromUserID = fromUserID
tips.FromToUserID.ToUserID = toUserID
- return f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
+ f.Notification(ctx, fromUserID, toUserID, constant.FriendRemarkSetNotification, &tips)
}
-func (f *FriendNotificationSender) FriendsInfoUpdateNotification(ctx context.Context, toUserID string, friendIDs []string) error {
+
+func (f *FriendNotificationSender) FriendsInfoUpdateNotification(ctx context.Context, toUserID string, friendIDs []string) {
tips := sdkws.FriendsInfoUpdateTips{FromToUserID: &sdkws.FromToUserID{}}
tips.FromToUserID.ToUserID = toUserID
tips.FriendIDs = friendIDs
- return f.Notification(ctx, toUserID, toUserID, constant.FriendsInfoUpdateNotification, &tips)
+ f.Notification(ctx, toUserID, toUserID, constant.FriendsInfoUpdateNotification, &tips)
}
-func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbfriend.AddBlackReq) error {
+
+func (f *FriendNotificationSender) BlackAddedNotification(ctx context.Context, req *pbfriend.AddBlackReq) {
tips := sdkws.BlackAddedTips{FromToUserID: &sdkws.FromToUserID{}}
tips.FromToUserID.FromUserID = req.OwnerUserID
tips.FromToUserID.ToUserID = req.BlackUserID
- return f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackAddedNotification, &tips)
+ f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackAddedNotification, &tips)
}
func (f *FriendNotificationSender) BlackDeletedNotification(ctx context.Context, req *pbfriend.RemoveBlackReq) {
@@ -212,19 +216,10 @@ func (f *FriendNotificationSender) BlackDeletedNotification(ctx context.Context,
FromUserID: req.OwnerUserID,
ToUserID: req.BlackUserID,
}}
- if err := f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips); err != nil {
- //err
- }
+ f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips)
}
-func (f *FriendNotificationSender) FriendInfoUpdatedNotification(
- ctx context.Context,
- changedUserID string,
- needNotifiedUserID string,
-) {
+func (f *FriendNotificationSender) FriendInfoUpdatedNotification(ctx context.Context, changedUserID string, needNotifiedUserID string) {
tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID}
- if err := f.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID,
- constant.FriendInfoUpdatedNotification, &tips); err != nil {
- // err
- }
+ f.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, constant.FriendInfoUpdatedNotification, &tips)
}
diff --git a/internal/rpc/group/cache.go b/internal/rpc/group/cache.go
index 35c631effe..022a0f4ef6 100644
--- a/internal/rpc/group/cache.go
+++ b/internal/rpc/group/cache.go
@@ -17,27 +17,27 @@ package group
import (
"context"
- pbgroup "github.com/OpenIMSDK/protocol/group"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
+ pbgroup "github.com/openimsdk/protocol/group"
)
-func (s *groupServer) GetGroupInfoCache(
- ctx context.Context,
- req *pbgroup.GetGroupInfoCacheReq,
-) (resp *pbgroup.GetGroupInfoCacheResp, err error) {
+// GetGroupInfoCache get group info from cache.
+func (s *groupServer) GetGroupInfoCache(ctx context.Context, req *pbgroup.GetGroupInfoCacheReq) (*pbgroup.GetGroupInfoCacheResp, error) {
group, err := s.db.TakeGroup(ctx, req.GroupID)
if err != nil {
return nil, err
}
- resp = &pbgroup.GetGroupInfoCacheResp{GroupInfo: convert.Db2PbGroupInfo(group, "", 0)}
- return resp, nil
+ return &pbgroup.GetGroupInfoCacheResp{
+ GroupInfo: convert.Db2PbGroupInfo(group, "", 0),
+ }, nil
}
-func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (resp *pbgroup.GetGroupMemberCacheResp, err error) {
+func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (*pbgroup.GetGroupMemberCacheResp, error) {
members, err := s.db.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID)
if err != nil {
return nil, err
}
- resp = &pbgroup.GetGroupMemberCacheResp{Member: convert.Db2PbGroupMember(members)}
- return resp, nil
+ return &pbgroup.GetGroupMemberCacheResp{
+ Member: convert.Db2PbGroupMember(members),
+ }, nil
}
diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go
index e82177dde0..1690e3973f 100644
--- a/internal/rpc/group/callback.go
+++ b/internal/rpc/group/callback.go
@@ -16,70 +16,67 @@ package group
import (
"context"
- "time"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/group"
- pbgroup "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/protocol/wrapperspb"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/apistruct"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/wrapperspb"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
+ "time"
)
-func CallbackBeforeCreateGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.CreateGroupReq) (err error) {
- if !globalConfig.Callback.CallbackBeforeCreateGroup.Enable {
- return nil
- }
- cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{
- CallbackCommand: callbackstruct.CallbackBeforeCreateGroupCommand,
- OperationID: mcontext.GetOperationID(ctx),
- GroupInfo: req.GroupInfo,
- }
- cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
- UserID: req.OwnerUserID,
- RoleLevel: constant.GroupOwner,
- })
- for _, userID := range req.AdminUserIDs {
- cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
- UserID: userID,
- RoleLevel: constant.GroupAdmin,
- })
- }
- for _, userID := range req.MemberUserIDs {
+// CallbackBeforeCreateGroup callback before create group.
+func (s *groupServer) webhookBeforeCreateGroup(ctx context.Context, before *config.BeforeConfig, req *group.CreateGroupReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{
+ CallbackCommand: callbackstruct.CallbackBeforeCreateGroupCommand,
+ OperationID: mcontext.GetOperationID(ctx),
+ GroupInfo: req.GroupInfo,
+ }
cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
- UserID: userID,
- RoleLevel: constant.GroupOrdinaryUsers,
+ UserID: req.OwnerUserID,
+ RoleLevel: constant.GroupOwner,
})
- }
- resp := &callbackstruct.CallbackBeforeCreateGroupResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeCreateGroup); err != nil {
- return err
- }
- utils.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID)
- utils.NotNilReplace(&req.GroupInfo.GroupName, resp.GroupName)
- utils.NotNilReplace(&req.GroupInfo.Notification, resp.Notification)
- utils.NotNilReplace(&req.GroupInfo.Introduction, resp.Introduction)
- utils.NotNilReplace(&req.GroupInfo.FaceURL, resp.FaceURL)
- utils.NotNilReplace(&req.GroupInfo.OwnerUserID, resp.OwnerUserID)
- utils.NotNilReplace(&req.GroupInfo.Ex, resp.Ex)
- utils.NotNilReplace(&req.GroupInfo.Status, resp.Status)
- utils.NotNilReplace(&req.GroupInfo.CreatorUserID, resp.CreatorUserID)
- utils.NotNilReplace(&req.GroupInfo.GroupType, resp.GroupType)
- utils.NotNilReplace(&req.GroupInfo.NeedVerification, resp.NeedVerification)
- utils.NotNilReplace(&req.GroupInfo.LookMemberInfo, resp.LookMemberInfo)
- return nil
-}
+ for _, userID := range req.AdminUserIDs {
+ cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
+ UserID: userID,
+ RoleLevel: constant.GroupAdmin,
+ })
+ }
+ for _, userID := range req.MemberUserIDs {
+ cbReq.InitMemberList = append(cbReq.InitMemberList, &apistruct.GroupAddMemberInfo{
+ UserID: userID,
+ RoleLevel: constant.GroupOrdinaryUsers,
+ })
+ }
+ resp := &callbackstruct.CallbackBeforeCreateGroupResp{}
+
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
-func CallbackAfterCreateGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.CreateGroupReq) (err error) {
- if !globalConfig.Callback.CallbackAfterCreateGroup.Enable {
+ datautil.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID)
+ datautil.NotNilReplace(&req.GroupInfo.GroupName, resp.GroupName)
+ datautil.NotNilReplace(&req.GroupInfo.Notification, resp.Notification)
+ datautil.NotNilReplace(&req.GroupInfo.Introduction, resp.Introduction)
+ datautil.NotNilReplace(&req.GroupInfo.FaceURL, resp.FaceURL)
+ datautil.NotNilReplace(&req.GroupInfo.OwnerUserID, resp.OwnerUserID)
+ datautil.NotNilReplace(&req.GroupInfo.Ex, resp.Ex)
+ datautil.NotNilReplace(&req.GroupInfo.Status, resp.Status)
+ datautil.NotNilReplace(&req.GroupInfo.CreatorUserID, resp.CreatorUserID)
+ datautil.NotNilReplace(&req.GroupInfo.GroupType, resp.GroupType)
+ datautil.NotNilReplace(&req.GroupInfo.NeedVerification, resp.NeedVerification)
+ datautil.NotNilReplace(&req.GroupInfo.LookMemberInfo, resp.LookMemberInfo)
return nil
- }
+ })
+}
+
+func (s *groupServer) webhookAfterCreateGroup(ctx context.Context, after *config.AfterConfig, req *group.CreateGroupReq) {
cbReq := &callbackstruct.CallbackAfterCreateGroupReq{
CallbackCommand: callbackstruct.CallbackAfterCreateGroupCommand,
GroupInfo: req.GroupInfo,
@@ -100,239 +97,163 @@ func CallbackAfterCreateGroup(ctx context.Context, globalConfig *config.GlobalCo
RoleLevel: constant.GroupOrdinaryUsers,
})
}
- resp := &callbackstruct.CallbackAfterCreateGroupResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterCreateGroup); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterCreateGroupResp{}, after)
}
-func CallbackBeforeMemberJoinGroup(
- ctx context.Context,
- globalConfig *config.GlobalConfig,
- groupMember *relation.GroupMemberModel,
- groupEx string,
-) (err error) {
- if !globalConfig.Callback.CallbackBeforeMemberJoinGroup.Enable {
+func (s *groupServer) webhookBeforeMemberJoinGroup(ctx context.Context, before *config.BeforeConfig, groupMember *relation.GroupMemberModel, groupEx string) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{
+ CallbackCommand: callbackstruct.CallbackBeforeMemberJoinGroupCommand,
+ GroupID: groupMember.GroupID,
+ UserID: groupMember.UserID,
+ Ex: groupMember.Ex,
+ GroupEx: groupEx,
+ }
+ resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+
+ if resp.MuteEndTime != nil {
+ groupMember.MuteEndTime = time.UnixMilli(*resp.MuteEndTime)
+ }
+ datautil.NotNilReplace(&groupMember.FaceURL, resp.FaceURL)
+ datautil.NotNilReplace(&groupMember.Ex, resp.Ex)
+ datautil.NotNilReplace(&groupMember.Nickname, resp.Nickname)
+ datautil.NotNilReplace(&groupMember.RoleLevel, resp.RoleLevel)
return nil
- }
- callbackReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{
- CallbackCommand: callbackstruct.CallbackBeforeMemberJoinGroupCommand,
- GroupID: groupMember.GroupID,
- UserID: groupMember.UserID,
- Ex: groupMember.Ex,
- GroupEx: groupEx,
- }
- resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{}
- err = http.CallBackPostReturn(
- ctx,
- globalConfig.Callback.CallbackUrl,
- callbackReq,
- resp,
- globalConfig.Callback.CallbackBeforeMemberJoinGroup,
- )
- if err != nil {
- return err
- }
- if resp.MuteEndTime != nil {
- groupMember.MuteEndTime = time.UnixMilli(*resp.MuteEndTime)
- }
- utils.NotNilReplace(&groupMember.FaceURL, resp.FaceURL)
- utils.NotNilReplace(&groupMember.Ex, resp.Ex)
- utils.NotNilReplace(&groupMember.Nickname, resp.Nickname)
- utils.NotNilReplace(&groupMember.RoleLevel, resp.RoleLevel)
- return nil
+ })
}
-func CallbackBeforeSetGroupMemberInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupMemberInfo) (err error) {
- if !globalConfig.Callback.CallbackBeforeSetGroupMemberInfo.Enable {
+func (s *groupServer) webhookBeforeSetGroupMemberInfo(ctx context.Context, before *config.BeforeConfig, req *group.SetGroupMemberInfo) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{
+ CallbackCommand: callbackstruct.CallbackBeforeSetGroupMemberInfoCommand,
+ GroupID: req.GroupID,
+ UserID: req.UserID,
+ }
+ if req.Nickname != nil {
+ cbReq.Nickname = &req.Nickname.Value
+ }
+ if req.FaceURL != nil {
+ cbReq.FaceURL = &req.FaceURL.Value
+ }
+ if req.RoleLevel != nil {
+ cbReq.RoleLevel = &req.RoleLevel.Value
+ }
+ if req.Ex != nil {
+ cbReq.Ex = &req.Ex.Value
+ }
+ resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+ if resp.FaceURL != nil {
+ req.FaceURL = wrapperspb.String(*resp.FaceURL)
+ }
+ if resp.Nickname != nil {
+ req.Nickname = wrapperspb.String(*resp.Nickname)
+ }
+ if resp.RoleLevel != nil {
+ req.RoleLevel = wrapperspb.Int32(*resp.RoleLevel)
+ }
+ if resp.Ex != nil {
+ req.Ex = wrapperspb.String(*resp.Ex)
+ }
return nil
- }
- callbackReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{
- CallbackCommand: callbackstruct.CallbackBeforeSetGroupMemberInfoCommand,
- GroupID: req.GroupID,
- UserID: req.UserID,
- }
- if req.Nickname != nil {
- callbackReq.Nickname = &req.Nickname.Value
- }
- if req.FaceURL != nil {
- callbackReq.FaceURL = &req.FaceURL.Value
- }
- if req.RoleLevel != nil {
- callbackReq.RoleLevel = &req.RoleLevel.Value
- }
- if req.Ex != nil {
- callbackReq.Ex = &req.Ex.Value
- }
- resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{}
- err = http.CallBackPostReturn(
- ctx,
- globalConfig.Callback.CallbackUrl,
- callbackReq,
- resp,
- globalConfig.Callback.CallbackBeforeSetGroupMemberInfo,
- )
- if err != nil {
- return err
- }
- if resp.FaceURL != nil {
- req.FaceURL = wrapperspb.String(*resp.FaceURL)
- }
- if resp.Nickname != nil {
- req.Nickname = wrapperspb.String(*resp.Nickname)
- }
- if resp.RoleLevel != nil {
- req.RoleLevel = wrapperspb.Int32(*resp.RoleLevel)
- }
- if resp.Ex != nil {
- req.Ex = wrapperspb.String(*resp.Ex)
- }
- return nil
+ })
}
-func CallbackAfterSetGroupMemberInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupMemberInfo) (err error) {
- if !globalConfig.Callback.CallbackBeforeSetGroupMemberInfo.Enable {
- return nil
- }
- callbackReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{
+
+func (s *groupServer) webhookAfterSetGroupMemberInfo(ctx context.Context, after *config.AfterConfig, req *group.SetGroupMemberInfo) {
+ cbReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{
CallbackCommand: callbackstruct.CallbackAfterSetGroupMemberInfoCommand,
GroupID: req.GroupID,
UserID: req.UserID,
}
if req.Nickname != nil {
- callbackReq.Nickname = &req.Nickname.Value
+ cbReq.Nickname = &req.Nickname.Value
}
if req.FaceURL != nil {
- callbackReq.FaceURL = &req.FaceURL.Value
+ cbReq.FaceURL = &req.FaceURL.Value
}
if req.RoleLevel != nil {
- callbackReq.RoleLevel = &req.RoleLevel.Value
+ cbReq.RoleLevel = &req.RoleLevel.Value
}
if req.Ex != nil {
- callbackReq.Ex = &req.Ex.Value
+ cbReq.Ex = &req.Ex.Value
}
- resp := &callbackstruct.CallbackAfterSetGroupMemberInfoResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterSetGroupMemberInfo); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterSetGroupMemberInfoResp{}, after)
}
-func CallbackQuitGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.QuitGroupReq) (err error) {
- if !globalConfig.Callback.CallbackQuitGroup.Enable {
- return nil
- }
+func (s *groupServer) webhookAfterQuitGroup(ctx context.Context, after *config.AfterConfig, req *group.QuitGroupReq) {
cbReq := &callbackstruct.CallbackQuitGroupReq{
- CallbackCommand: callbackstruct.CallbackQuitGroupCommand,
+ CallbackCommand: callbackstruct.CallbackAfterQuitGroupCommand,
GroupID: req.GroupID,
UserID: req.UserID,
}
- resp := &callbackstruct.CallbackQuitGroupResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackQuitGroup); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackQuitGroupResp{}, after)
}
-func CallbackKillGroupMember(ctx context.Context, globalConfig *config.GlobalConfig, req *pbgroup.KickGroupMemberReq) (err error) {
- if !globalConfig.Callback.CallbackKillGroupMember.Enable {
- return nil
- }
+func (s *groupServer) webhookAfterKickGroupMember(ctx context.Context, after *config.AfterConfig, req *group.KickGroupMemberReq) {
cbReq := &callbackstruct.CallbackKillGroupMemberReq{
- CallbackCommand: callbackstruct.CallbackKillGroupCommand,
+ CallbackCommand: callbackstruct.CallbackAfterKickGroupCommand,
GroupID: req.GroupID,
KickedUserIDs: req.KickedUserIDs,
}
- resp := &callbackstruct.CallbackKillGroupMemberResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackQuitGroup); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackKillGroupMemberResp{}, after)
}
-func CallbackDismissGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *callbackstruct.CallbackDisMissGroupReq) (err error) {
- if !globalConfig.Callback.CallbackDismissGroup.Enable {
- return nil
- }
- req.CallbackCommand = callbackstruct.CallbackDisMissGroupCommand
- resp := &callbackstruct.CallbackDisMissGroupResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackQuitGroup); err != nil {
- return err
- }
- return nil
+func (s *groupServer) webhookAfterDismissGroup(ctx context.Context, after *config.AfterConfig, req *callbackstruct.CallbackDisMissGroupReq) {
+ req.CallbackCommand = callbackstruct.CallbackAfterDisMissGroupCommand
+ s.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &callbackstruct.CallbackDisMissGroupResp{}, after)
}
-func CallbackApplyJoinGroupBefore(ctx context.Context, globalConfig *config.GlobalConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) {
- if !globalConfig.Callback.CallbackBeforeJoinGroup.Enable {
+func (s *groupServer) webhookBeforeApplyJoinGroup(ctx context.Context, before *config.BeforeConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand
+ resp := &callbackstruct.CallbackJoinGroupResp{}
+ if err := s.webhookClient.SyncPost(ctx, req.GetCallbackCommand(), req, resp, before); err != nil {
+ return err
+ }
return nil
- }
-
- req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand
-
- resp := &callbackstruct.CallbackJoinGroupResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeJoinGroup); err != nil {
- return err
- }
-
- return nil
+ })
}
-func CallbackAfterTransferGroupOwner(ctx context.Context, globalConfig *config.GlobalConfig, req *pbgroup.TransferGroupOwnerReq) (err error) {
- if !globalConfig.Callback.CallbackAfterTransferGroupOwner.Enable {
- return nil
- }
-
+func (s *groupServer) webhookAfterTransferGroupOwner(ctx context.Context, after *config.AfterConfig, req *group.TransferGroupOwnerReq) {
cbReq := &callbackstruct.CallbackTransferGroupOwnerReq{
- CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwner,
+ CallbackCommand: callbackstruct.CallbackAfterTransferGroupOwnerCommand,
GroupID: req.GroupID,
OldOwnerUserID: req.OldOwnerUserID,
NewOwnerUserID: req.NewOwnerUserID,
}
-
- resp := &callbackstruct.CallbackTransferGroupOwnerResp{}
- if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterTransferGroupOwner); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackTransferGroupOwnerResp{}, after)
}
-func CallbackBeforeInviteUserToGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.InviteUserToGroupReq) (err error) {
- if !globalConfig.Callback.CallbackBeforeInviteUserToGroup.Enable {
- return nil
- }
- callbackReq := &callbackstruct.CallbackBeforeInviteUserToGroupReq{
- CallbackCommand: callbackstruct.CallbackBeforeInviteJoinGroupCommand,
- OperationID: mcontext.GetOperationID(ctx),
- GroupID: req.GroupID,
- Reason: req.Reason,
- InvitedUserIDs: req.InvitedUserIDs,
- }
+func (s *groupServer) webhookBeforeInviteUserToGroup(ctx context.Context, before *config.BeforeConfig, req *group.InviteUserToGroupReq) (err error) {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &callbackstruct.CallbackBeforeInviteUserToGroupReq{
+ CallbackCommand: callbackstruct.CallbackBeforeInviteJoinGroupCommand,
+ OperationID: mcontext.GetOperationID(ctx),
+ GroupID: req.GroupID,
+ Reason: req.Reason,
+ InvitedUserIDs: req.InvitedUserIDs,
+ }
- resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{}
- err = http.CallBackPostReturn(
- ctx,
- globalConfig.Callback.CallbackUrl,
- callbackReq,
- resp,
- globalConfig.Callback.CallbackBeforeInviteUserToGroup,
- )
+ resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
- if err != nil {
- return err
- }
-
- if len(resp.RefusedMembersAccount) > 0 {
- // Handle the scenario where certain members are refused
- // You might want to update the req.Members list or handle it as per your business logic
- }
- return nil
+ if len(resp.RefusedMembersAccount) > 0 {
+ // Handle the scenario where certain members are refused
+ // You might want to update the req.Members list or handle it as per your business logic
+ }
+ return nil
+ })
}
-func CallbackAfterJoinGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.JoinGroupReq) error {
- if !globalConfig.Callback.CallbackAfterJoinGroup.Enable {
- return nil
- }
- callbackReq := &callbackstruct.CallbackAfterJoinGroupReq{
+func (s *groupServer) webhookAfterJoinGroup(ctx context.Context, after *config.AfterConfig, req *group.JoinGroupReq) {
+ cbReq := &callbackstruct.CallbackAfterJoinGroupReq{
CallbackCommand: callbackstruct.CallbackAfterJoinGroupCommand,
OperationID: mcontext.GetOperationID(ctx),
GroupID: req.GroupID,
@@ -340,68 +261,60 @@ func CallbackAfterJoinGroup(ctx context.Context, globalConfig *config.GlobalConf
JoinSource: req.JoinSource,
InviterUserID: req.InviterUserID,
}
- resp := &callbackstruct.CallbackAfterJoinGroupResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterJoinGroup); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterJoinGroupResp{}, after)
}
-func CallbackBeforeSetGroupInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupInfoReq) error {
- if !globalConfig.Callback.CallbackBeforeSetGroupInfo.Enable {
- return nil
- }
- callbackReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{
- CallbackCommand: callbackstruct.CallbackBeforeSetGroupInfoCommand,
- GroupID: req.GroupInfoForSet.GroupID,
- Notification: req.GroupInfoForSet.Notification,
- Introduction: req.GroupInfoForSet.Introduction,
- FaceURL: req.GroupInfoForSet.FaceURL,
- GroupName: req.GroupInfoForSet.GroupName,
- }
+func (s *groupServer) webhookBeforeSetGroupInfo(ctx context.Context, before *config.BeforeConfig, req *group.SetGroupInfoReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{
+ CallbackCommand: callbackstruct.CallbackBeforeSetGroupInfoCommand,
+ GroupID: req.GroupInfoForSet.GroupID,
+ Notification: req.GroupInfoForSet.Notification,
+ Introduction: req.GroupInfoForSet.Introduction,
+ FaceURL: req.GroupInfoForSet.FaceURL,
+ GroupName: req.GroupInfoForSet.GroupName,
+ }
+ if req.GroupInfoForSet.Ex != nil {
+ cbReq.Ex = req.GroupInfoForSet.Ex.Value
+ }
+ log.ZDebug(ctx, "debug CallbackBeforeSetGroupInfo", "ex", cbReq.Ex)
+ if req.GroupInfoForSet.NeedVerification != nil {
+ cbReq.NeedVerification = req.GroupInfoForSet.NeedVerification.Value
+ }
+ if req.GroupInfoForSet.LookMemberInfo != nil {
+ cbReq.LookMemberInfo = req.GroupInfoForSet.LookMemberInfo.Value
+ }
+ if req.GroupInfoForSet.ApplyMemberFriend != nil {
+ cbReq.ApplyMemberFriend = req.GroupInfoForSet.ApplyMemberFriend.Value
+ }
+ resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{}
- if req.GroupInfoForSet.Ex != nil {
- callbackReq.Ex = req.GroupInfoForSet.Ex.Value
- }
- log.ZDebug(ctx, "debug CallbackBeforeSetGroupInfo", callbackReq.Ex)
- if req.GroupInfoForSet.NeedVerification != nil {
- callbackReq.NeedVerification = req.GroupInfoForSet.NeedVerification.Value
- }
- if req.GroupInfoForSet.LookMemberInfo != nil {
- callbackReq.LookMemberInfo = req.GroupInfoForSet.LookMemberInfo.Value
- }
- if req.GroupInfoForSet.ApplyMemberFriend != nil {
- callbackReq.ApplyMemberFriend = req.GroupInfoForSet.ApplyMemberFriend.Value
- }
- resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{}
-
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackBeforeSetGroupInfo); err != nil {
- return err
- }
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
- if resp.Ex != nil {
- req.GroupInfoForSet.Ex = wrapperspb.String(*resp.Ex)
- }
- if resp.NeedVerification != nil {
- req.GroupInfoForSet.NeedVerification = wrapperspb.Int32(*resp.NeedVerification)
- }
- if resp.LookMemberInfo != nil {
- req.GroupInfoForSet.LookMemberInfo = wrapperspb.Int32(*resp.LookMemberInfo)
- }
- if resp.ApplyMemberFriend != nil {
- req.GroupInfoForSet.ApplyMemberFriend = wrapperspb.Int32(*resp.ApplyMemberFriend)
- }
- utils.NotNilReplace(&req.GroupInfoForSet.GroupID, &resp.GroupID)
- utils.NotNilReplace(&req.GroupInfoForSet.GroupName, &resp.GroupName)
- utils.NotNilReplace(&req.GroupInfoForSet.FaceURL, &resp.FaceURL)
- utils.NotNilReplace(&req.GroupInfoForSet.Introduction, &resp.Introduction)
- return nil
-}
-func CallbackAfterSetGroupInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupInfoReq) error {
- if !globalConfig.Callback.CallbackAfterSetGroupInfo.Enable {
+ if resp.Ex != nil {
+ req.GroupInfoForSet.Ex = wrapperspb.String(*resp.Ex)
+ }
+ if resp.NeedVerification != nil {
+ req.GroupInfoForSet.NeedVerification = wrapperspb.Int32(*resp.NeedVerification)
+ }
+ if resp.LookMemberInfo != nil {
+ req.GroupInfoForSet.LookMemberInfo = wrapperspb.Int32(*resp.LookMemberInfo)
+ }
+ if resp.ApplyMemberFriend != nil {
+ req.GroupInfoForSet.ApplyMemberFriend = wrapperspb.Int32(*resp.ApplyMemberFriend)
+ }
+ datautil.NotNilReplace(&req.GroupInfoForSet.GroupID, &resp.GroupID)
+ datautil.NotNilReplace(&req.GroupInfoForSet.GroupName, &resp.GroupName)
+ datautil.NotNilReplace(&req.GroupInfoForSet.FaceURL, &resp.FaceURL)
+ datautil.NotNilReplace(&req.GroupInfoForSet.Introduction, &resp.Introduction)
return nil
- }
- callbackReq := &callbackstruct.CallbackAfterSetGroupInfoReq{
+ })
+}
+
+func (s *groupServer) webhookAfterSetGroupInfo(ctx context.Context, after *config.AfterConfig, req *group.SetGroupInfoReq) {
+ cbReq := &callbackstruct.CallbackAfterSetGroupInfoReq{
CallbackCommand: callbackstruct.CallbackAfterSetGroupInfoCommand,
GroupID: req.GroupInfoForSet.GroupID,
Notification: req.GroupInfoForSet.Notification,
@@ -410,20 +323,16 @@ func CallbackAfterSetGroupInfo(ctx context.Context, globalConfig *config.GlobalC
GroupName: req.GroupInfoForSet.GroupName,
}
if req.GroupInfoForSet.Ex != nil {
- callbackReq.Ex = &req.GroupInfoForSet.Ex.Value
+ cbReq.Ex = &req.GroupInfoForSet.Ex.Value
}
if req.GroupInfoForSet.NeedVerification != nil {
- callbackReq.NeedVerification = &req.GroupInfoForSet.NeedVerification.Value
+ cbReq.NeedVerification = &req.GroupInfoForSet.NeedVerification.Value
}
if req.GroupInfoForSet.LookMemberInfo != nil {
- callbackReq.LookMemberInfo = &req.GroupInfoForSet.LookMemberInfo.Value
+ cbReq.LookMemberInfo = &req.GroupInfoForSet.LookMemberInfo.Value
}
if req.GroupInfoForSet.ApplyMemberFriend != nil {
- callbackReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value
- }
- resp := &callbackstruct.CallbackAfterSetGroupInfoResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterSetGroupInfo); err != nil {
- return err
+ cbReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value
}
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &callbackstruct.CallbackAfterSetGroupInfoResp{}, after)
}
diff --git a/internal/rpc/group/convert.go b/internal/rpc/group/convert.go
index ab4d3a2a1b..86978ce3ae 100644
--- a/internal/rpc/group/convert.go
+++ b/internal/rpc/group/convert.go
@@ -15,8 +15,8 @@
package group
import (
- "github.com/OpenIMSDK/protocol/sdkws"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/sdkws"
)
func (s *groupServer) groupDB2PB(group *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo {
@@ -41,10 +41,7 @@ func (s *groupServer) groupDB2PB(group *relation.GroupModel, ownerUserID string,
}
}
-func (s *groupServer) groupMemberDB2PB(
- member *relation.GroupMemberModel,
- appMangerLevel int32,
-) *sdkws.GroupMemberFullInfo {
+func (s *groupServer) groupMemberDB2PB(member *relation.GroupMemberModel, appMangerLevel int32) *sdkws.GroupMemberFullInfo {
return &sdkws.GroupMemberFullInfo{
GroupID: member.GroupID,
UserID: member.UserID,
diff --git a/internal/rpc/group/db_map.go b/internal/rpc/group/db_map.go
index e4c18bb17b..b4b503b950 100644
--- a/internal/rpc/group/db_map.go
+++ b/internal/rpc/group/db_map.go
@@ -18,9 +18,9 @@ import (
"context"
"time"
- pbgroup "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/mcontext"
+ pbgroup "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/mcontext"
)
func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[string]any {
diff --git a/internal/rpc/group/fill.go b/internal/rpc/group/fill.go
index ac539de198..c504db8d6e 100644
--- a/internal/rpc/group/fill.go
+++ b/internal/rpc/group/fill.go
@@ -21,5 +21,5 @@ import (
)
func (s *groupServer) PopulateGroupMember(ctx context.Context, members ...*relationtb.GroupMemberModel) error {
- return s.Notification.PopulateGroupMember(ctx, members...)
+ return s.notification.PopulateGroupMember(ctx, members...)
}
diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go
index 92d9e27d6a..13bd7f9beb 100644
--- a/internal/rpc/group/group.go
+++ b/internal/rpc/group/group.go
@@ -17,96 +17,108 @@ package group
import (
"context"
"fmt"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
"math/big"
"math/rand"
"strconv"
"strings"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- pbconversation "github.com/OpenIMSDK/protocol/conversation"
- pbgroup "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/protocol/wrapperspb"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/mw/specialerror"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/grouphash"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
+ "github.com/openimsdk/protocol/constant"
+ pbconversation "github.com/openimsdk/protocol/conversation"
+ pbgroup "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/protocol/wrapperspb"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/mw/specialerror"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/encrypt"
"google.golang.org/grpc"
)
-func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- mongo, err := unrelation.NewMongo(config)
+type groupServer struct {
+ db controller.GroupDatabase
+ user rpcclient.UserRpcClient
+ notification *GroupNotificationSender
+ conversationRpcClient rpcclient.ConversationRpcClient
+ msgRpcClient rpcclient.MessageRpcClient
+ config *Config
+ webhookClient *webhook.Client
+}
+
+type Config struct {
+ RpcConfig config.Group
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ WebhooksConfig config.Webhooks
+ LocalCacheConfig config.LocalCache
+}
+
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
- rdb, err := cache.NewRedis(config)
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
- groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase(config.Mongo.Database))
+ groupDB, err := mgo.NewGroupMongo(mgocli.GetDB())
if err != nil {
return err
}
- groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase(config.Mongo.Database))
+ groupMemberDB, err := mgo.NewGroupMember(mgocli.GetDB())
if err != nil {
return err
}
- groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase(config.Mongo.Database))
+ groupRequestDB, err := mgo.NewGroupRequestMgo(mgocli.GetDB())
if err != nil {
return err
}
- userRpcClient := rpcclient.NewUserRpcClient(client, config)
- msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
- conversationRpcClient := rpcclient.NewConversationRpcClient(client, config)
+ userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
+ msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
+ conversationRpcClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
var gs groupServer
- database := controller.NewGroupDatabase(rdb, groupDB, groupMemberDB, groupRequestDB, tx.NewMongo(mongo.GetClient()), grouphash.NewGroupHashFromGroupServer(&gs))
+ database := controller.NewGroupDatabase(rdb, &config.LocalCacheConfig, groupDB, groupMemberDB, groupRequestDB, mgocli.GetTx(), grouphash.NewGroupHashFromGroupServer(&gs))
gs.db = database
- gs.User = userRpcClient
- gs.Notification = notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, config, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) {
+ gs.user = userRpcClient
+ gs.notification = NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, config, 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
+ return datautil.Slice(users, func(e *sdkws.UserInfo) notification.CommonUser { return e }), nil
})
+ cache.InitLocalCache(&config.LocalCacheConfig)
gs.conversationRpcClient = conversationRpcClient
gs.msgRpcClient = msgRpcClient
gs.config = config
+ gs.webhookClient = webhook.NewWebhookClient(config.WebhooksConfig.URL)
pbgroup.RegisterGroupServer(server, &gs)
return nil
}
-type groupServer struct {
- db controller.GroupDatabase
- User rpcclient.UserRpcClient
- Notification *notification.GroupNotificationSender
- conversationRpcClient rpcclient.ConversationRpcClient
- msgRpcClient rpcclient.MessageRpcClient
- config *config.GlobalConfig
-}
-
-func (s *groupServer) GetJoinedGroupIDs(ctx context.Context, req *pbgroup.GetJoinedGroupIDsReq) (*pbgroup.GetJoinedGroupIDsResp, error) {
- //TODO implement me
- panic("implement me")
-}
-
func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) {
members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID)
if err != nil {
@@ -120,9 +132,7 @@ func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgro
groupIDs = append(groupIDs, member.GroupID)
}
for _, groupID := range groupIDs {
- if err = s.Notification.GroupMemberInfoSetNotification(ctx, groupID, req.UserID); err != nil {
- return nil, err
- }
+ s.notification.GroupMemberInfoSetNotification(ctx, groupID, req.UserID)
}
if err = s.db.DeleteGroupMemberHash(ctx, groupIDs); err != nil {
return nil, err
@@ -132,13 +142,13 @@ func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgro
}
func (s *groupServer) CheckGroupAdmin(ctx context.Context, groupID string) error {
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
groupMember, err := s.db.TakeGroupMember(ctx, groupID, mcontext.GetOpUserID(ctx))
if err != nil {
return err
}
if !(groupMember.RoleLevel == constant.GroupOwner || groupMember.RoleLevel == constant.GroupAdmin) {
- return errs.ErrNoPermission.Wrap("no group owner or admin")
+ return errs.ErrNoPermission.WrapMsg("no group owner or admin")
}
}
return nil
@@ -148,11 +158,11 @@ func (s *groupServer) GetPublicUserInfoMap(ctx context.Context, userIDs []string
if len(userIDs) == 0 {
return map[string]*sdkws.PublicUserInfo{}, nil
}
- users, err := s.User.GetPublicUserInfos(ctx, userIDs, complete)
+ users, err := s.user.GetPublicUserInfos(ctx, userIDs, complete)
if err != nil {
return nil, err
}
- return utils.SliceToMapAny(users, func(e *sdkws.PublicUserInfo) (string, *sdkws.PublicUserInfo) {
+ return datautil.SliceToMapAny(users, func(e *sdkws.PublicUserInfo) (string, *sdkws.PublicUserInfo) {
return e.UserID, e
}), nil
}
@@ -165,7 +175,7 @@ func (s *groupServer) GenGroupID(ctx context.Context, groupID *string) error {
if *groupID != "" {
_, err := s.db.TakeGroup(ctx, *groupID)
if err == nil {
- return errs.ErrGroupIDExisted.Wrap("group id existed " + *groupID)
+ return servererrs.ErrGroupIDExisted.WrapMsg("group id existed " + *groupID)
} else if s.IsNotFound(err) {
return nil
} else {
@@ -173,7 +183,7 @@ func (s *groupServer) GenGroupID(ctx context.Context, groupID *string) error {
}
}
for i := 0; i < 10; i++ {
- id := utils.Md5(strings.Join([]string{mcontext.GetOperationID(ctx), strconv.FormatInt(time.Now().UnixNano(), 10), strconv.Itoa(rand.Int())}, ",;,"))
+ id := encrypt.Md5(strings.Join([]string{mcontext.GetOperationID(ctx), strconv.FormatInt(time.Now().UnixNano(), 10), strconv.Itoa(rand.Int())}, ",;,"))
bi := big.NewInt(0)
bi.SetString(id[0:8], 16)
id = bi.String()
@@ -187,43 +197,49 @@ func (s *groupServer) GenGroupID(ctx context.Context, groupID *string) error {
return err
}
}
- return errs.ErrData.Wrap("group id gen error")
+ return servererrs.ErrData.WrapMsg("group id gen error")
}
func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupReq) (*pbgroup.CreateGroupResp, error) {
if req.GroupInfo.GroupType != constant.WorkingGroup {
- return nil, errs.ErrArgs.Wrap(fmt.Sprintf("group type only supports %d", constant.WorkingGroup))
+ return nil, errs.ErrArgs.WrapMsg(fmt.Sprintf("group type only supports %d", constant.WorkingGroup))
}
if req.OwnerUserID == "" {
- return nil, errs.ErrArgs.Wrap("no group owner")
+ return nil, errs.ErrArgs.WrapMsg("no group owner")
}
- if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config.Share.IMAdminUserID); err != nil {
+
return nil, err
}
userIDs := append(append(req.MemberUserIDs, req.AdminUserIDs...), req.OwnerUserID)
opUserID := mcontext.GetOpUserID(ctx)
- if !utils.Contain(opUserID, userIDs...) {
+ if !datautil.Contain(opUserID, userIDs...) {
userIDs = append(userIDs, opUserID)
}
- if utils.Duplicate(userIDs) {
- return nil, errs.ErrArgs.Wrap("group member repeated")
+
+ if datautil.Duplicate(userIDs) {
+ return nil, errs.ErrArgs.WrapMsg("group member repeated")
}
- userMap, err := s.User.GetUsersInfoMap(ctx, userIDs)
+
+ userMap, err := s.user.GetUsersInfoMap(ctx, userIDs)
if err != nil {
return nil, err
}
+
if len(userMap) != len(userIDs) {
- return nil, errs.ErrUserIDNotFound.Wrap("user not found")
+ return nil, servererrs.ErrUserIDNotFound.WrapMsg("user not found")
}
- // Callback Before create Group
- if err := CallbackBeforeCreateGroup(ctx, s.config, req); err != nil {
+
+ if err := s.webhookBeforeCreateGroup(ctx, &s.config.WebhooksConfig.BeforeCreateGroup, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
+
var groupMembers []*relationtb.GroupMemberModel
group := convert.Pb2DBGroupInfo(req.GroupInfo)
if err := s.GenGroupID(ctx, &group.GroupID); err != nil {
return nil, err
}
+
joinGroup := func(userID string, roleLevel int32) error {
groupMember := &relationtb.GroupMemberModel{
GroupID: group.GroupID,
@@ -235,7 +251,8 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
JoinTime: time.Now(),
MuteEndTime: time.UnixMilli(0),
}
- if err := CallbackBeforeMemberJoinGroup(ctx, s.config, groupMember, group.Ex); err != nil {
+
+ if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, groupMember, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
return err
}
groupMembers = append(groupMembers, groupMember)
@@ -258,6 +275,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
return nil, err
}
resp := &pbgroup.CreateGroupResp{GroupInfo: &sdkws.GroupInfo{}}
+
resp.GroupInfo = convert.Db2PbGroupInfo(group, req.OwnerUserID, uint32(len(userIDs)))
resp.GroupInfo.MemberCount = uint32(len(userIDs))
tips := &sdkws.GroupCreatedTips{
@@ -276,11 +294,10 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
if req.GroupInfo.GroupType == constant.SuperGroup {
go func() {
for _, userID := range userIDs {
- s.Notification.SuperGroupNotification(ctx, userID, userID)
+ s.notification.SuperGroupNotification(ctx, userID, userID)
}
}()
} else {
- // s.Notification.GroupCreatedNotification(ctx, group, groupMembers, userMap)
tips := &sdkws.GroupCreatedTips{
Group: resp.GroupInfo,
OperationTime: group.CreateTime.UnixMilli(),
@@ -294,8 +311,9 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
break
}
}
- s.Notification.GroupCreatedNotification(ctx, tips)
+ s.notification.GroupCreatedNotification(ctx, tips)
}
+
reqCallBackAfter := &pbgroup.CreateGroupReq{
MemberUserIDs: userIDs,
GroupInfo: resp.GroupInfo,
@@ -303,27 +321,25 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR
AdminUserIDs: req.AdminUserIDs,
}
- if err := CallbackAfterCreateGroup(ctx, s.config, reqCallBackAfter); err != nil {
- return nil, err
- }
+ s.webhookAfterCreateGroup(ctx, &s.config.WebhooksConfig.AfterCreateGroup, reqCallBackAfter)
return resp, nil
}
func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbgroup.GetJoinedGroupListReq) (*pbgroup.GetJoinedGroupListResp, error) {
- resp := &pbgroup.GetJoinedGroupListResp{}
- if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
total, members, err := s.db.PageGetJoinGroup(ctx, req.FromUserID, req.Pagination)
if err != nil {
return nil, err
}
+ var resp pbgroup.GetJoinedGroupListResp
resp.Total = uint32(total)
if len(members) == 0 {
- return resp, nil
+ return &resp, nil
}
- groupIDs := utils.Slice(members, func(e *relationtb.GroupMemberModel) string {
+ groupIDs := datautil.Slice(members, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
groups, err := s.db.FindGroup(ctx, groupIDs)
@@ -341,10 +357,10 @@ func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbgroup.GetJo
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
+ ownerMap := datautil.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
- resp.Groups = utils.Slice(utils.Order(groupIDs, groups, func(group *relationtb.GroupModel) string {
+ resp.Groups = datautil.Slice(datautil.Order(groupIDs, groups, func(group *relationtb.GroupModel) string {
return group.GroupID
}), func(group *relationtb.GroupModel) *sdkws.GroupInfo {
var userID string
@@ -353,17 +369,15 @@ func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbgroup.GetJo
}
return convert.Db2PbGroupInfo(group, userID, groupMemberNum[group.GroupID])
})
- return resp, nil
+ return &resp, nil
}
func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.InviteUserToGroupReq) (*pbgroup.InviteUserToGroupResp, error) {
- resp := &pbgroup.InviteUserToGroupResp{}
-
if len(req.InvitedUserIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("user empty")
+ return nil, errs.ErrArgs.WrapMsg("user empty")
}
- if utils.Duplicate(req.InvitedUserIDs) {
- return nil, errs.ErrArgs.Wrap("userID duplicate")
+ if datautil.Duplicate(req.InvitedUserIDs) {
+ return nil, errs.ErrArgs.WrapMsg("userID duplicate")
}
group, err := s.db.TakeGroup(ctx, req.GroupID)
if err != nil {
@@ -371,18 +385,21 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
}
if group.Status == constant.GroupStatusDismissed {
- return nil, errs.ErrDismissedAlready.Wrap()
+ return nil, servererrs.ErrDismissedAlready.WrapMsg("group dismissed checking group status found it dismissed")
}
- userMap, err := s.User.GetUsersInfoMap(ctx, req.InvitedUserIDs)
+
+ userMap, err := s.user.GetUsersInfoMap(ctx, req.InvitedUserIDs)
if err != nil {
return nil, err
}
+
if len(userMap) != len(req.InvitedUserIDs) {
- return nil, errs.ErrRecordNotFound.Wrap("user not found")
+ return nil, errs.ErrRecordNotFound.WrapMsg("user not found")
}
+
var groupMember *relationtb.GroupMemberModel
var opUserID string
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
opUserID = mcontext.GetOpUserID(ctx)
var err error
groupMember, err = s.db.TakeGroupMember(ctx, req.GroupID, opUserID)
@@ -394,11 +411,12 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
}
}
- if err := CallbackBeforeInviteUserToGroup(ctx, s.config, req); err != nil {
+ if err := s.webhookBeforeInviteUserToGroup(ctx, &s.config.WebhooksConfig.BeforeInviteUserToGroup, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
+
if group.NeedVerification == constant.AllNeedVerification {
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
if !(groupMember.RoleLevel == constant.GroupOwner || groupMember.RoleLevel == constant.GroupAdmin) {
var requests []*relationtb.GroupRequestModel
for _, userID := range req.InvitedUserIDs {
@@ -415,14 +433,14 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
return nil, err
}
for _, request := range requests {
- s.Notification.JoinGroupApplicationNotification(ctx, &pbgroup.JoinGroupReq{
+ s.notification.JoinGroupApplicationNotification(ctx, &pbgroup.JoinGroupReq{
GroupID: request.GroupID,
ReqMessage: request.ReqMsg,
JoinSource: request.JoinSource,
InviterUserID: request.InviterUserID,
})
}
- return resp, nil
+ return &pbgroup.InviteUserToGroupResp{}, nil
}
}
}
@@ -438,10 +456,12 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
JoinTime: time.Now(),
MuteEndTime: time.UnixMilli(0),
}
- if err := CallbackBeforeMemberJoinGroup(ctx, s.config, member, group.Ex); err != nil {
+
+ if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, groupMember, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
groupMembers = append(groupMembers, member)
+
}
if err := s.db.CreateGroup(ctx, nil, groupMembers); err != nil {
return nil, err
@@ -449,8 +469,8 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite
if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, req.InvitedUserIDs); err != nil {
return nil, err
}
- s.Notification.MemberInvitedNotification(ctx, req.GroupID, req.Reason, req.InvitedUserIDs)
- return resp, nil
+ s.notification.MemberInvitedNotification(ctx, req.GroupID, req.Reason, req.InvitedUserIDs)
+ return &pbgroup.InviteUserToGroupResp{}, nil
}
func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbgroup.GetGroupAllMemberReq) (*pbgroup.GetGroupAllMemberResp, error) {
@@ -461,15 +481,14 @@ func (s *groupServer) GetGroupAllMember(ctx context.Context, req *pbgroup.GetGro
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- resp := &pbgroup.GetGroupAllMemberResp{}
- resp.Members = utils.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
+ var resp pbgroup.GetGroupAllMemberResp
+ resp.Members = datautil.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
return convert.Db2PbGroupMember(e)
})
- return resp, nil
+ return &resp, nil
}
func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGroupMemberListReq) (*pbgroup.GetGroupMemberListResp, error) {
- resp := &pbgroup.GetGroupMemberListResp{}
var (
total int64
members []*relationtb.GroupMemberModel
@@ -501,31 +520,32 @@ func (s *groupServer) GetGroupMemberList(ctx context.Context, req *pbgroup.GetGr
}
}
- GMembers := utils.Paginate(groupMembers, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
- resp.Members = utils.Batch(convert.Db2PbGroupMember, GMembers)
- resp.Total = uint32(total)
- return resp, nil
- }
- resp.Total = uint32(total)
- resp.Members = utils.Batch(convert.Db2PbGroupMember, members)
- return resp, nil
+ members := datautil.Paginate(groupMembers, int(req.Pagination.GetPageNumber()), int(req.Pagination.GetShowNumber()))
+ return &pbgroup.GetGroupMemberListResp{
+ Total: uint32(total),
+ Members: datautil.Batch(convert.Db2PbGroupMember, members),
+ }, nil
+ }
+ return &pbgroup.GetGroupMemberListResp{
+ Total: uint32(total),
+ Members: datautil.Batch(convert.Db2PbGroupMember, members),
+ }, nil
}
func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGroupMemberReq) (*pbgroup.KickGroupMemberResp, error) {
- resp := &pbgroup.KickGroupMemberResp{}
group, err := s.db.TakeGroup(ctx, req.GroupID)
if err != nil {
return nil, err
}
if len(req.KickedUserIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("KickedUserIDs empty")
+ return nil, errs.ErrArgs.WrapMsg("KickedUserIDs empty")
}
- if utils.IsDuplicateStringSlice(req.KickedUserIDs) {
- return nil, errs.ErrArgs.Wrap("KickedUserIDs duplicate")
+ if datautil.Duplicate(req.KickedUserIDs) {
+ return nil, errs.ErrArgs.WrapMsg("KickedUserIDs duplicate")
}
opUserID := mcontext.GetOpUserID(ctx)
- if utils.IsContain(opUserID, req.KickedUserIDs) {
- return nil, errs.ErrArgs.Wrap("opUserID in KickedUserIDs")
+ if datautil.Contain(opUserID, req.KickedUserIDs...) {
+ return nil, errs.ErrArgs.WrapMsg("opUserID in KickedUserIDs")
}
members, err := s.db.FindGroupMembers(ctx, req.GroupID, append(req.KickedUserIDs, opUserID))
if err != nil {
@@ -538,27 +558,27 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou
for i, member := range members {
memberMap[member.UserID] = members[i]
}
- isAppManagerUid := authverify.IsAppManagerUid(ctx, s.config)
+ isAppManagerUid := authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID)
opMember := memberMap[opUserID]
for _, userID := range req.KickedUserIDs {
member, ok := memberMap[userID]
if !ok {
- return nil, errs.ErrUserIDNotFound.Wrap(userID)
+ return nil, servererrs.ErrUserIDNotFound.WrapMsg(userID)
}
if !isAppManagerUid {
if opMember == nil {
- return nil, errs.ErrNoPermission.Wrap("opUserID no in group")
+ return nil, errs.ErrNoPermission.WrapMsg("opUserID no in group")
}
switch opMember.RoleLevel {
case constant.GroupOwner:
case constant.GroupAdmin:
if member.RoleLevel == constant.GroupOwner || member.RoleLevel == constant.GroupAdmin {
- return nil, errs.ErrNoPermission.Wrap("group admins cannot remove the group owner and other admins")
+ return nil, errs.ErrNoPermission.WrapMsg("group admins cannot remove the group owner and other admins")
}
case constant.GroupOrdinaryUsers:
- return nil, errs.ErrNoPermission.Wrap("opUserID no permission")
+ return nil, errs.ErrNoPermission.WrapMsg("opUserID no permission")
default:
- return nil, errs.ErrNoPermission.Wrap("opUserID roleLevel unknown")
+ return nil, errs.ErrNoPermission.WrapMsg("opUserID roleLevel unknown")
}
}
}
@@ -605,24 +625,21 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou
for _, userID := range req.KickedUserIDs {
tips.KickedUserList = append(tips.KickedUserList, convert.Db2PbGroupMember(memberMap[userID]))
}
- s.Notification.MemberKickedNotification(ctx, tips)
+ s.notification.MemberKickedNotification(ctx, tips)
if err := s.deleteMemberAndSetConversationSeq(ctx, req.GroupID, req.KickedUserIDs); err != nil {
return nil, err
}
+ s.webhookAfterKickGroupMember(ctx, &s.config.WebhooksConfig.AfterKickGroupMember, req)
- if err := CallbackKillGroupMember(ctx, s.config, req); err != nil {
- return nil, err
- }
- return resp, nil
+ return &pbgroup.KickGroupMemberResp{}, nil
}
func (s *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbgroup.GetGroupMembersInfoReq) (*pbgroup.GetGroupMembersInfoResp, error) {
- resp := &pbgroup.GetGroupMembersInfoResp{}
if len(req.UserIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("userIDs empty")
+ return nil, errs.ErrArgs.WrapMsg("userIDs empty")
}
if req.GroupID == "" {
- return nil, errs.ErrArgs.Wrap("groupID empty")
+ return nil, errs.ErrArgs.WrapMsg("groupID empty")
}
members, err := s.db.FindGroupMembers(ctx, req.GroupID, req.UserIDs)
if err != nil {
@@ -631,10 +648,11 @@ func (s *groupServer) GetGroupMembersInfo(ctx context.Context, req *pbgroup.GetG
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- resp.Members = utils.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
- return convert.Db2PbGroupMember(e)
- })
- return resp, nil
+ return &pbgroup.GetGroupMembersInfoResp{
+ Members: datautil.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
+ return convert.Db2PbGroupMember(e)
+ }),
+ }, nil
}
// GetGroupApplicationList handles functions that get a list of group requests.
@@ -660,20 +678,20 @@ func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbgroup.
for _, gr := range groupRequests {
userIDs = append(userIDs, gr.UserID)
}
- userIDs = utils.Distinct(userIDs)
- userMap, err := s.User.GetPublicUserInfoMap(ctx, userIDs, true)
+ userIDs = datautil.Distinct(userIDs)
+ userMap, err := s.user.GetPublicUserInfoMap(ctx, userIDs, true)
if err != nil {
return nil, err
}
- groups, err := s.db.FindGroup(ctx, utils.Distinct(groupIDs))
+ groups, err := s.db.FindGroup(ctx, datautil.Distinct(groupIDs))
if err != nil {
return nil, err
}
- groupMap := utils.SliceToMap(groups, func(e *relationtb.GroupModel) string {
+ groupMap := datautil.SliceToMap(groups, func(e *relationtb.GroupModel) string {
return e.GroupID
})
- if ids := utils.Single(utils.Keys(groupMap), groupIDs); len(ids) > 0 {
- return nil, errs.ErrGroupIDNotFound.Wrap(strings.Join(ids, ","))
+ if ids := datautil.Single(datautil.Keys(groupMap), groupIDs); len(ids) > 0 {
+ return nil, servererrs.ErrGroupIDNotFound.WrapMsg(strings.Join(ids, ","))
}
groupMemberNumMap, err := s.db.MapGroupMemberNum(ctx, groupIDs)
if err != nil {
@@ -686,10 +704,10 @@ func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbgroup.
if err := s.PopulateGroupMember(ctx, owners...); err != nil {
return nil, err
}
- ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
+ ownerMap := datautil.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
- resp.GroupRequests = utils.Slice(groupRequests, func(e *relationtb.GroupRequestModel) *sdkws.GroupRequest {
+ resp.GroupRequests = datautil.Slice(groupRequests, func(e *relationtb.GroupRequestModel) *sdkws.GroupRequest {
var ownerUserID string
if owner, ok := ownerMap[e.GroupID]; ok {
ownerUserID = owner.UserID
@@ -700,9 +718,8 @@ func (s *groupServer) GetGroupApplicationList(ctx context.Context, req *pbgroup.
}
func (s *groupServer) GetGroupsInfo(ctx context.Context, req *pbgroup.GetGroupsInfoReq) (*pbgroup.GetGroupsInfoResp, error) {
- resp := &pbgroup.GetGroupsInfoResp{}
if len(req.GroupIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("groupID is empty")
+ return nil, errs.ErrArgs.WrapMsg("groupID is empty")
}
groups, err := s.db.FindGroup(ctx, req.GroupIDs)
if err != nil {
@@ -719,31 +736,31 @@ func (s *groupServer) GetGroupsInfo(ctx context.Context, req *pbgroup.GetGroupsI
if err := s.PopulateGroupMember(ctx, owners...); err != nil {
return nil, err
}
- ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
+ ownerMap := datautil.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
- resp.GroupInfos = utils.Slice(groups, func(e *relationtb.GroupModel) *sdkws.GroupInfo {
- var ownerUserID string
- if owner, ok := ownerMap[e.GroupID]; ok {
- ownerUserID = owner.UserID
- }
- return convert.Db2PbGroupInfo(e, ownerUserID, groupMemberNumMap[e.GroupID])
- })
- return resp, nil
+ return &pbgroup.GetGroupsInfoResp{
+ GroupInfos: datautil.Slice(groups, func(e *relationtb.GroupModel) *sdkws.GroupInfo {
+ var ownerUserID string
+ if owner, ok := ownerMap[e.GroupID]; ok {
+ ownerUserID = owner.UserID
+ }
+ return convert.Db2PbGroupInfo(e, ownerUserID, groupMemberNumMap[e.GroupID])
+ }),
+ }, nil
}
func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) (*pbgroup.GroupApplicationResponseResp, error) {
- defer log.ZInfo(ctx, utils.GetFuncName()+" Return")
- if !utils.Contain(req.HandleResult, constant.GroupResponseAgree, constant.GroupResponseRefuse) {
- return nil, errs.ErrArgs.Wrap("HandleResult unknown")
+ if !datautil.Contain(req.HandleResult, constant.GroupResponseAgree, constant.GroupResponseRefuse) {
+ return nil, errs.ErrArgs.WrapMsg("HandleResult unknown")
}
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
groupMember, err := s.db.TakeGroupMember(ctx, req.GroupID, mcontext.GetOpUserID(ctx))
if err != nil {
return nil, err
}
if !(groupMember.RoleLevel == constant.GroupOwner || groupMember.RoleLevel == constant.GroupAdmin) {
- return nil, errs.ErrNoPermission.Wrap("no group owner or admin")
+ return nil, errs.ErrNoPermission.WrapMsg("no group owner or admin")
}
}
group, err := s.db.TakeGroup(ctx, req.GroupID)
@@ -755,7 +772,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup
return nil, err
}
if groupRequest.HandleResult != 0 {
- return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed")
+ return nil, servererrs.ErrGroupRequestHandled.WrapMsg("group request already processed")
}
var inGroup bool
if _, err := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil {
@@ -763,7 +780,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup
} else if !s.IsNotFound(err) {
return nil, err
}
- if _, err := s.User.GetPublicUserInfo(ctx, req.FromUserID); err != nil {
+ if _, err := s.user.GetPublicUserInfo(ctx, req.FromUserID); err != nil {
return nil, err
}
var member *relationtb.GroupMemberModel
@@ -781,7 +798,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup
OperatorUserID: mcontext.GetOpUserID(ctx),
Ex: groupRequest.Ex,
}
- if err = CallbackBeforeMemberJoinGroup(ctx, s.config, member, group.Ex); err != nil {
+ if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, member, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
}
@@ -794,22 +811,21 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup
if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.FromUserID}); err != nil {
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)
+ s.notification.MemberEnterNotification(ctx, req.GroupID, req.FromUserID)
}
case constant.GroupResponseRefuse:
- s.Notification.GroupApplicationRejectedNotification(ctx, req)
+ s.notification.GroupApplicationRejectedNotification(ctx, req)
}
return &pbgroup.GroupApplicationResponseResp{}, nil
}
-func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) (resp *pbgroup.JoinGroupResp, err error) {
- defer log.ZInfo(ctx, "JoinGroup.Return")
- user, err := s.User.GetUserInfo(ctx, req.InviterUserID)
+func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) (*pbgroup.JoinGroupResp, error) {
+ user, err := s.user.GetUserInfo(ctx, req.InviterUserID)
if err != nil {
return nil, err
}
@@ -818,7 +834,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
return nil, err
}
if group.Status == constant.GroupStatusDismissed {
- return nil, errs.ErrDismissedAlready.Wrap()
+ return nil, servererrs.ErrDismissedAlready.Wrap()
}
reqCall := &callbackstruct.CallbackJoinGroupReq{
@@ -829,17 +845,17 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
Ex: req.Ex,
}
- if err = CallbackApplyJoinGroupBefore(ctx, s.config, reqCall); err != nil {
+ if err := s.webhookBeforeApplyJoinGroup(ctx, &s.config.WebhooksConfig.BeforeApplyJoinGroup, reqCall); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
+
_, err = s.db.TakeGroupMember(ctx, req.GroupID, req.InviterUserID)
if err == nil {
- return nil, errs.ErrArgs.Wrap("already in group")
- } else if !s.IsNotFound(err) && utils.Unwrap(err) != errs.ErrRecordNotFound {
+ return nil, errs.ErrArgs.Wrap()
+ } else if !s.IsNotFound(err) && errs.Unwrap(err) != errs.ErrRecordNotFound {
return nil, err
}
- log.ZInfo(ctx, "JoinGroup.groupInfo", "group", group, "eq", group.NeedVerification == constant.Directly)
- resp = &pbgroup.JoinGroupResp{}
+ log.ZDebug(ctx, "JoinGroup.groupInfo", "group", group, "eq", group.NeedVerification == constant.Directly)
if group.NeedVerification == constant.Directly {
groupMember := &relationtb.GroupMemberModel{
GroupID: group.GroupID,
@@ -850,9 +866,11 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
JoinTime: time.Now(),
MuteEndTime: time.UnixMilli(0),
}
- if err := CallbackBeforeMemberJoinGroup(ctx, s.config, groupMember, group.Ex); err != nil {
+
+ if err := s.webhookBeforeMemberJoinGroup(ctx, &s.config.WebhooksConfig.BeforeMemberJoinGroup, groupMember, group.Ex); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
+
if err := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); err != nil {
return nil, err
}
@@ -860,11 +878,10 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); err != nil {
return nil, err
}
- s.Notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID)
- if err = CallbackAfterJoinGroup(ctx, s.config, req); err != nil {
- return nil, err
- }
- return resp, nil
+ s.notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID)
+ s.webhookAfterJoinGroup(ctx, &s.config.WebhooksConfig.AfterJoinGroup, req)
+
+ return &pbgroup.JoinGroupResp{}, nil
}
groupRequest := relationtb.GroupRequestModel{
UserID: req.InviterUserID,
@@ -878,16 +895,15 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq)
if err = s.db.CreateGroupRequest(ctx, []*relationtb.GroupRequestModel{&groupRequest}); err != nil {
return nil, err
}
- s.Notification.JoinGroupApplicationNotification(ctx, req)
- return resp, nil
+ s.notification.JoinGroupApplicationNotification(ctx, req)
+ return &pbgroup.JoinGroupResp{}, nil
}
func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq) (*pbgroup.QuitGroupResp, error) {
- resp := &pbgroup.QuitGroupResp{}
if req.UserID == "" {
req.UserID = mcontext.GetOpUserID(ctx)
} else {
- if err := authverify.CheckAccessV3(ctx, req.UserID, s.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
}
@@ -896,7 +912,7 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq)
return nil, err
}
if member.RoleLevel == constant.GroupOwner {
- return nil, errs.ErrNoPermission.Wrap("group owner can't quit")
+ return nil, errs.ErrNoPermission.WrapMsg("group owner can't quit")
}
if err := s.PopulateGroupMember(ctx, member); err != nil {
return nil, err
@@ -905,20 +921,17 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq)
if err != nil {
return nil, err
}
- _ = s.Notification.MemberQuitNotification(ctx, s.groupMemberDB2PB(member, 0))
+ s.notification.MemberQuitNotification(ctx, s.groupMemberDB2PB(member, 0))
if err := s.deleteMemberAndSetConversationSeq(ctx, req.GroupID, []string{req.UserID}); err != nil {
return nil, err
}
+ s.webhookAfterQuitGroup(ctx, &s.config.WebhooksConfig.AfterQuitGroup, req)
- // callback
- if err := CallbackQuitGroup(ctx, s.config, req); err != nil {
- return nil, err
- }
- return resp, nil
+ return &pbgroup.QuitGroupResp{}, nil
}
func (s *groupServer) deleteMemberAndSetConversationSeq(ctx context.Context, groupID string, userIDs []string) error {
- conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
+ conevrsationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID)
maxSeq, err := s.msgRpcClient.GetConversationMaxSeq(ctx, conevrsationID)
if err != nil {
return err
@@ -928,30 +941,31 @@ func (s *groupServer) deleteMemberAndSetConversationSeq(ctx context.Context, gro
func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInfoReq) (*pbgroup.SetGroupInfoResp, error) {
var opMember *relationtb.GroupMemberModel
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
var err error
opMember, err = s.db.TakeGroupMember(ctx, req.GroupInfoForSet.GroupID, mcontext.GetOpUserID(ctx))
if err != nil {
return nil, err
}
if !(opMember.RoleLevel == constant.GroupOwner || opMember.RoleLevel == constant.GroupAdmin) {
- return nil, errs.ErrNoPermission.Wrap("no group owner or admin")
+ return nil, errs.ErrNoPermission.WrapMsg("no group owner or admin")
}
if err := s.PopulateGroupMember(ctx, opMember); err != nil {
return nil, err
}
}
- if err := CallbackBeforeSetGroupInfo(ctx, s.config, req); err != nil {
+
+ if err := s.webhookBeforeSetGroupInfo(ctx, &s.config.WebhooksConfig.BeforeSetGroupInfo, req); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
+
group, err := s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID)
if err != nil {
return nil, err
}
if group.Status == constant.GroupStatusDismissed {
- return nil, errs.Wrap(errs.ErrDismissedAlready)
+ return nil, servererrs.ErrDismissedAlready.Wrap()
}
- resp := &pbgroup.SetGroupInfoResp{}
count, err := s.db.FindGroupMemberNum(ctx, group.GroupID)
if err != nil {
@@ -966,7 +980,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
}
update := UpdateGroupInfoMap(ctx, req.GroupInfoForSet)
if len(update) == 0 {
- return resp, nil
+ return &pbgroup.SetGroupInfoResp{}, nil
}
if err := s.db.UpdateGroup(ctx, group.GroupID, update); err != nil {
return nil, err
@@ -988,8 +1002,8 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
num--
func() {
conversation := &pbconversation.ConversationReq{
- ConversationID: msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, req.GroupInfoForSet.GroupID),
- ConversationType: constant.SuperGroupChatType,
+ ConversationID: msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupInfoForSet.GroupID),
+ ConversationType: constant.ReadGroupChatType,
GroupID: req.GroupInfoForSet.GroupID,
}
resp, err := s.GetGroupMemberUserIDs(ctx, &pbgroup.GetGroupMemberUserIDsReq{GroupID: req.GroupInfoForSet.GroupID})
@@ -1002,32 +1016,31 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf
log.ZWarn(ctx, "SetConversations", err, resp.UserIDs, conversation)
}
}()
- _ = s.Notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser})
+ s.notification.GroupInfoSetAnnouncementNotification(ctx, &sdkws.GroupInfoSetAnnouncementTips{Group: tips.Group, OpUser: tips.OpUser})
}
if req.GroupInfoForSet.GroupName != "" {
num--
- _ = s.Notification.GroupInfoSetNameNotification(ctx, &sdkws.GroupInfoSetNameTips{Group: tips.Group, OpUser: tips.OpUser})
+ s.notification.GroupInfoSetNameNotification(ctx, &sdkws.GroupInfoSetNameTips{Group: tips.Group, OpUser: tips.OpUser})
}
if num > 0 {
- _ = s.Notification.GroupInfoSetNotification(ctx, tips)
- }
- if err := CallbackAfterSetGroupInfo(ctx, s.config, req); err != nil {
- return nil, err
+ s.notification.GroupInfoSetNotification(ctx, tips)
}
- return resp, nil
+
+ s.webhookAfterSetGroupInfo(ctx, &s.config.WebhooksConfig.AfterSetGroupInfo, req)
+
+ return &pbgroup.SetGroupInfoResp{}, nil
}
func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (*pbgroup.TransferGroupOwnerResp, error) {
- resp := &pbgroup.TransferGroupOwnerResp{}
group, err := s.db.TakeGroup(ctx, req.GroupID)
if err != nil {
return nil, err
}
if group.Status == constant.GroupStatusDismissed {
- return nil, errs.ErrDismissedAlready.Wrap("")
+ return nil, servererrs.ErrDismissedAlready.Wrap()
}
if req.OldOwnerUserID == req.NewOwnerUserID {
- return nil, errs.ErrArgs.Wrap("OldOwnerUserID == NewOwnerUserID")
+ return nil, errs.ErrArgs.WrapMsg("OldOwnerUserID == NewOwnerUserID")
}
members, err := s.db.FindGroupMembers(ctx, req.GroupID, []string{req.OldOwnerUserID, req.NewOwnerUserID})
if err != nil {
@@ -1036,41 +1049,39 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- memberMap := utils.SliceToMap(members, func(e *relationtb.GroupMemberModel) string { return e.UserID })
- if ids := utils.Single([]string{req.OldOwnerUserID, req.NewOwnerUserID}, utils.Keys(memberMap)); len(ids) > 0 {
- return nil, errs.ErrArgs.Wrap("user not in group " + strings.Join(ids, ","))
+ memberMap := datautil.SliceToMap(members, func(e *relationtb.GroupMemberModel) string { return e.UserID })
+ if ids := datautil.Single([]string{req.OldOwnerUserID, req.NewOwnerUserID}, datautil.Keys(memberMap)); len(ids) > 0 {
+ return nil, errs.ErrArgs.WrapMsg("user not in group " + strings.Join(ids, ","))
}
oldOwner := memberMap[req.OldOwnerUserID]
if oldOwner == nil {
- return nil, errs.ErrArgs.Wrap("OldOwnerUserID not in group " + req.NewOwnerUserID)
+ return nil, errs.ErrArgs.WrapMsg("OldOwnerUserID not in group " + req.NewOwnerUserID)
}
newOwner := memberMap[req.NewOwnerUserID]
if newOwner == nil {
- return nil, errs.ErrArgs.Wrap("NewOwnerUser not in group " + req.NewOwnerUserID)
+ return nil, errs.ErrArgs.WrapMsg("NewOwnerUser not in group " + req.NewOwnerUserID)
}
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
if !(mcontext.GetOpUserID(ctx) == oldOwner.UserID && oldOwner.RoleLevel == constant.GroupOwner) {
- return nil, errs.ErrNoPermission.Wrap("no permission transfer group owner")
+ return nil, errs.ErrNoPermission.WrapMsg("no permission transfer group owner")
}
}
if err := s.db.TransferGroupOwner(ctx, req.GroupID, req.OldOwnerUserID, req.NewOwnerUserID, newOwner.RoleLevel); err != nil {
return nil, err
}
- if err := CallbackAfterTransferGroupOwner(ctx, s.config, req); err != nil {
- return nil, err
- }
- s.Notification.GroupOwnerTransferredNotification(ctx, req)
- return resp, nil
+ s.webhookAfterTransferGroupOwner(ctx, &s.config.WebhooksConfig.AfterTransferGroupOwner, req)
+
+ s.notification.GroupOwnerTransferredNotification(ctx, req)
+ return &pbgroup.TransferGroupOwnerResp{}, nil
}
func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq) (*pbgroup.GetGroupsResp, error) {
- resp := &pbgroup.GetGroupsResp{}
var (
group []*relationtb.GroupModel
err error
)
-
+ var resp pbgroup.GetGroupsResp
if req.GroupID != "" {
group, err = s.db.FindGroup(ctx, []string{req.GroupID})
resp.Total = uint32(len(group))
@@ -1084,7 +1095,7 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq)
return nil, err
}
- groupIDs := utils.Slice(group, func(e *relationtb.GroupModel) string {
+ groupIDs := datautil.Slice(group, func(e *relationtb.GroupModel) string {
return e.GroupID
})
@@ -1093,14 +1104,14 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq)
return nil, err
}
- ownerMemberMap := utils.SliceToMap(ownerMembers, func(e *relationtb.GroupMemberModel) string {
+ ownerMemberMap := datautil.SliceToMap(ownerMembers, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
groupMemberNumMap, err := s.db.MapGroupMemberNum(ctx, groupIDs)
if err != nil {
return nil, err
}
- resp.Groups = utils.Slice(group, func(group *relationtb.GroupModel) *pbgroup.CMSGroup {
+ resp.Groups = datautil.Slice(group, func(group *relationtb.GroupModel) *pbgroup.CMSGroup {
var (
userID string
username string
@@ -1111,28 +1122,27 @@ func (s *groupServer) GetGroups(ctx context.Context, req *pbgroup.GetGroupsReq)
}
return convert.Db2PbCMSGroup(group, userID, username, groupMemberNumMap[group.GroupID])
})
- return resp, nil
+ return &resp, nil
}
func (s *groupServer) GetGroupMembersCMS(ctx context.Context, req *pbgroup.GetGroupMembersCMSReq) (*pbgroup.GetGroupMembersCMSResp, error) {
- resp := &pbgroup.GetGroupMembersCMSResp{}
total, members, err := s.db.SearchGroupMember(ctx, req.UserName, req.GroupID, req.Pagination)
if err != nil {
return nil, err
}
+ var resp pbgroup.GetGroupMembersCMSResp
resp.Total = uint32(total)
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- resp.Members = utils.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
+ resp.Members = datautil.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
return convert.Db2PbGroupMember(e)
})
- return resp, nil
+ return &resp, nil
}
func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbgroup.GetUserReqApplicationListReq) (*pbgroup.GetUserReqApplicationListResp, error) {
- resp := &pbgroup.GetUserReqApplicationListResp{}
- user, err := s.User.GetPublicUserInfo(ctx, req.UserID)
+ user, err := s.user.GetPublicUserInfo(ctx, req.UserID)
if err != nil {
return nil, err
}
@@ -1140,18 +1150,17 @@ func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbgrou
if err != nil {
return nil, err
}
- resp.Total = uint32(total)
if len(requests) == 0 {
- return resp, nil
+ return &pbgroup.GetUserReqApplicationListResp{Total: uint32(total)}, nil
}
- groupIDs := utils.Distinct(utils.Slice(requests, func(e *relationtb.GroupRequestModel) string {
+ groupIDs := datautil.Distinct(datautil.Slice(requests, func(e *relationtb.GroupRequestModel) string {
return e.GroupID
}))
groups, err := s.db.FindGroup(ctx, groupIDs)
if err != nil {
return nil, err
}
- groupMap := utils.SliceToMap(groups, func(e *relationtb.GroupModel) string {
+ groupMap := datautil.SliceToMap(groups, func(e *relationtb.GroupModel) string {
return e.GroupID
})
owners, err := s.db.FindGroupsOwner(ctx, groupIDs)
@@ -1161,33 +1170,33 @@ func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbgrou
if err := s.PopulateGroupMember(ctx, owners...); err != nil {
return nil, err
}
- ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
+ ownerMap := datautil.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
groupMemberNum, err := s.db.MapGroupMemberNum(ctx, groupIDs)
if err != nil {
return nil, err
}
- resp.GroupRequests = utils.Slice(requests, func(e *relationtb.GroupRequestModel) *sdkws.GroupRequest {
- var ownerUserID string
- if owner, ok := ownerMap[e.GroupID]; ok {
- ownerUserID = owner.UserID
- }
- return convert.Db2PbGroupRequest(e, user, convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerUserID, groupMemberNum[e.GroupID]))
- })
- return resp, nil
+ return &pbgroup.GetUserReqApplicationListResp{
+ Total: uint32(total),
+ GroupRequests: datautil.Slice(requests, func(e *relationtb.GroupRequestModel) *sdkws.GroupRequest {
+ var ownerUserID string
+ if owner, ok := ownerMap[e.GroupID]; ok {
+ ownerUserID = owner.UserID
+ }
+ return convert.Db2PbGroupRequest(e, user, convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerUserID, groupMemberNum[e.GroupID]))
+ }),
+ }, nil
}
func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGroupReq) (*pbgroup.DismissGroupResp, error) {
- defer log.ZInfo(ctx, "DismissGroup.return")
- resp := &pbgroup.DismissGroupResp{}
owner, err := s.db.TakeGroupOwner(ctx, req.GroupID)
if err != nil {
return nil, err
}
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
if owner.UserID != mcontext.GetOpUserID(ctx) {
- return nil, errs.ErrNoPermission.Wrap("not group owner")
+ return nil, errs.ErrNoPermission.WrapMsg("not group owner")
}
}
if err := s.PopulateGroupMember(ctx, owner); err != nil {
@@ -1198,7 +1207,7 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou
return nil, err
}
if !req.DeleteMember && group.Status == constant.GroupStatusDismissed {
- return nil, errs.ErrDismissedAlready.Wrap("group status is dismissed")
+ return nil, servererrs.ErrDismissedAlready.WrapMsg("group status is dismissed")
}
if err := s.db.DismissGroup(ctx, req.GroupID, req.DeleteMember); err != nil {
return nil, err
@@ -1215,27 +1224,25 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou
if mcontext.GetOpUserID(ctx) == owner.UserID {
tips.OpUser = s.groupMemberDB2PB(owner, 0)
}
- s.Notification.GroupDismissedNotification(ctx, tips)
+ s.notification.GroupDismissedNotification(ctx, tips)
}
membersID, err := s.db.FindGroupMemberUserID(ctx, group.GroupID)
if err != nil {
return nil, err
}
- reqCall := &callbackstruct.CallbackDisMissGroupReq{
+ cbReq := &callbackstruct.CallbackDisMissGroupReq{
GroupID: req.GroupID,
OwnerID: owner.UserID,
MembersID: membersID,
GroupType: string(group.GroupType),
}
- if err := CallbackDismissGroup(ctx, s.config, reqCall); err != nil {
- return nil, err
- }
- return resp, nil
+ s.webhookAfterDismissGroup(ctx, &s.config.WebhooksConfig.AfterDismissGroup, cbReq)
+
+ return &pbgroup.DismissGroupResp{}, nil
}
func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbgroup.MuteGroupMemberReq) (*pbgroup.MuteGroupMemberResp, error) {
- resp := &pbgroup.MuteGroupMemberResp{}
member, err := s.db.TakeGroupMember(ctx, req.GroupID, req.UserID)
if err != nil {
return nil, err
@@ -1243,21 +1250,21 @@ func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbgroup.MuteGrou
if err := s.PopulateGroupMember(ctx, member); err != nil {
return nil, err
}
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
opMember, err := s.db.TakeGroupMember(ctx, req.GroupID, mcontext.GetOpUserID(ctx))
if err != nil {
return nil, err
}
switch member.RoleLevel {
case constant.GroupOwner:
- return nil, errs.ErrNoPermission.Wrap("set group owner mute")
+ return nil, errs.ErrNoPermission.WrapMsg("set group owner mute")
case constant.GroupAdmin:
if opMember.RoleLevel != constant.GroupOwner {
- return nil, errs.ErrNoPermission.Wrap("set group admin mute")
+ return nil, errs.ErrNoPermission.WrapMsg("set group admin mute")
}
case constant.GroupOrdinaryUsers:
if !(opMember.RoleLevel == constant.GroupAdmin || opMember.RoleLevel == constant.GroupOwner) {
- return nil, errs.ErrNoPermission.Wrap("set group ordinary users mute")
+ return nil, errs.ErrNoPermission.WrapMsg("set group ordinary users mute")
}
}
}
@@ -1265,8 +1272,8 @@ func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbgroup.MuteGrou
if err := s.db.UpdateGroupMember(ctx, member.GroupID, member.UserID, data); err != nil {
return nil, err
}
- s.Notification.GroupMemberMutedNotification(ctx, req.GroupID, req.UserID, req.MutedSeconds)
- return resp, nil
+ s.notification.GroupMemberMutedNotification(ctx, req.GroupID, req.UserID, req.MutedSeconds)
+ return &pbgroup.MuteGroupMemberResp{}, nil
}
func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbgroup.CancelMuteGroupMemberReq) (*pbgroup.CancelMuteGroupMemberResp, error) {
@@ -1277,21 +1284,21 @@ func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbgroup.Ca
if err := s.PopulateGroupMember(ctx, member); err != nil {
return nil, err
}
- if !authverify.IsAppManagerUid(ctx, s.config) {
+ if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) {
opMember, err := s.db.TakeGroupMember(ctx, req.GroupID, mcontext.GetOpUserID(ctx))
if err != nil {
return nil, err
}
switch member.RoleLevel {
case constant.GroupOwner:
- return nil, errs.ErrNoPermission.Wrap("set group owner mute")
+ return nil, errs.ErrNoPermission.WrapMsg("set group owner mute")
case constant.GroupAdmin:
if opMember.RoleLevel != constant.GroupOwner {
- return nil, errs.ErrNoPermission.Wrap("set group admin mute")
+ return nil, errs.ErrNoPermission.WrapMsg("set group admin mute")
}
case constant.GroupOrdinaryUsers:
if !(opMember.RoleLevel == constant.GroupAdmin || opMember.RoleLevel == constant.GroupOwner) {
- return nil, errs.ErrNoPermission.Wrap("set group ordinary users mute")
+ return nil, errs.ErrNoPermission.WrapMsg("set group ordinary users mute")
}
}
}
@@ -1299,44 +1306,41 @@ func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbgroup.Ca
if err := s.db.UpdateGroupMember(ctx, member.GroupID, member.UserID, data); err != nil {
return nil, err
}
- s.Notification.GroupMemberCancelMutedNotification(ctx, req.GroupID, req.UserID)
+ s.notification.GroupMemberCancelMutedNotification(ctx, req.GroupID, req.UserID)
return &pbgroup.CancelMuteGroupMemberResp{}, nil
}
func (s *groupServer) MuteGroup(ctx context.Context, req *pbgroup.MuteGroupReq) (*pbgroup.MuteGroupResp, error) {
- resp := &pbgroup.MuteGroupResp{}
if err := s.CheckGroupAdmin(ctx, req.GroupID); err != nil {
return nil, err
}
if err := s.db.UpdateGroup(ctx, req.GroupID, UpdateGroupStatusMap(constant.GroupStatusMuted)); err != nil {
return nil, err
}
- s.Notification.GroupMutedNotification(ctx, req.GroupID)
- return resp, nil
+ s.notification.GroupMutedNotification(ctx, req.GroupID)
+ return &pbgroup.MuteGroupResp{}, nil
}
func (s *groupServer) CancelMuteGroup(ctx context.Context, req *pbgroup.CancelMuteGroupReq) (*pbgroup.CancelMuteGroupResp, error) {
- resp := &pbgroup.CancelMuteGroupResp{}
if err := s.CheckGroupAdmin(ctx, req.GroupID); err != nil {
return nil, err
}
if err := s.db.UpdateGroup(ctx, req.GroupID, UpdateGroupStatusMap(constant.GroupOk)); err != nil {
return nil, err
}
- s.Notification.GroupCancelMutedNotification(ctx, req.GroupID)
- return resp, nil
+ s.notification.GroupCancelMutedNotification(ctx, req.GroupID)
+ return &pbgroup.CancelMuteGroupResp{}, nil
}
func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGroupMemberInfoReq) (*pbgroup.SetGroupMemberInfoResp, error) {
- resp := &pbgroup.SetGroupMemberInfoResp{}
if len(req.Members) == 0 {
- return nil, errs.ErrArgs.Wrap("members empty")
+ return nil, errs.ErrArgs.WrapMsg("members empty")
}
opUserID := mcontext.GetOpUserID(ctx)
if opUserID == "" {
- return nil, errs.ErrNoPermission.Wrap("no op user id")
+ return nil, errs.ErrNoPermission.WrapMsg("no op user id")
}
- isAppManagerUid := authverify.IsAppManagerUid(ctx, s.config)
+ isAppManagerUid := authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID)
for i := range req.Members {
req.Members[i].FaceURL = nil
}
@@ -1345,10 +1349,10 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
if member.RoleLevel != nil {
switch member.RoleLevel.Value {
case constant.GroupOwner:
- return nil, errs.ErrNoPermission.Wrap("cannot set ungroup owner")
+ return nil, errs.ErrNoPermission.WrapMsg("cannot set ungroup owner")
case constant.GroupAdmin, constant.GroupOrdinaryUsers:
default:
- return nil, errs.ErrArgs.Wrap("invalid role level")
+ return nil, errs.ErrArgs.WrapMsg("invalid role level")
}
}
groupMembers[member.GroupID] = append(groupMembers[member.GroupID], req.Members[i])
@@ -1358,7 +1362,7 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
userIDs := make([]string, 0, len(members)+1)
for _, member := range members {
if _, ok := temp[member.UserID]; ok {
- return nil, errs.ErrArgs.Wrap(fmt.Sprintf("repeat group %s user %s", member.GroupID, member.UserID))
+ return nil, errs.ErrArgs.WrapMsg(fmt.Sprintf("repeat group %s user %s", member.GroupID, member.UserID))
}
temp[member.UserID] = struct{}{}
userIDs = append(userIDs, member.UserID)
@@ -1386,22 +1390,22 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
case constant.GroupAdmin:
for _, member := range dbMembers {
if member.RoleLevel == constant.GroupOwner {
- return nil, errs.ErrNoPermission.Wrap("admin can not change group owner")
+ return nil, errs.ErrNoPermission.WrapMsg("admin can not change group owner")
}
if member.RoleLevel == constant.GroupAdmin && member.UserID != opUserID {
- return nil, errs.ErrNoPermission.Wrap("admin can not change other group admin")
+ return nil, errs.ErrNoPermission.WrapMsg("admin can not change other group admin")
}
}
case constant.GroupOrdinaryUsers:
for _, member := range dbMembers {
if !(member.RoleLevel == constant.GroupOrdinaryUsers && member.UserID == opUserID) {
- return nil, errs.ErrNoPermission.Wrap("ordinary users can not change other role level")
+ return nil, errs.ErrNoPermission.WrapMsg("ordinary users can not change other role level")
}
}
default:
for _, member := range dbMembers {
if member.RoleLevel >= roleLevel {
- return nil, errs.ErrNoPermission.Wrap("can not change higher role level")
+ return nil, errs.ErrNoPermission.WrapMsg("can not change higher role level")
}
}
}
@@ -1409,21 +1413,24 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
}
case 1:
if opUserIndex >= 0 {
- return nil, errs.ErrArgs.Wrap("user not in group")
+ return nil, errs.ErrArgs.WrapMsg("user not in group")
}
if !isAppManagerUid {
- return nil, errs.ErrNoPermission.Wrap("user not in group")
+ return nil, errs.ErrNoPermission.WrapMsg("user not in group")
}
default:
- return nil, errs.ErrArgs.Wrap("user not in group")
+ return nil, errs.ErrArgs.WrapMsg("user not in group")
}
}
+
for i := 0; i < len(req.Members); i++ {
- if err := CallbackBeforeSetGroupMemberInfo(ctx, s.config, req.Members[i]); err != nil {
+
+ if err := s.webhookBeforeSetGroupMemberInfo(ctx, &s.config.WebhooksConfig.BeforeSetGroupMemberInfo, req.Members[i]); err != nil && err != servererrs.ErrCallbackContinue {
return nil, err
}
+
}
- if err := s.db.UpdateGroupMembers(ctx, utils.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember {
+ if err := s.db.UpdateGroupMembers(ctx, datautil.Slice(req.Members, func(e *pbgroup.SetGroupMemberInfo) *relationtb.BatchUpdateGroupMember {
return &relationtb.BatchUpdateGroupMember{
GroupID: e.GroupID,
UserID: e.UserID,
@@ -1436,59 +1443,56 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr
if member.RoleLevel != nil {
switch member.RoleLevel.Value {
case constant.GroupAdmin:
- s.Notification.GroupMemberSetToAdminNotification(ctx, member.GroupID, member.UserID)
+ s.notification.GroupMemberSetToAdminNotification(ctx, member.GroupID, member.UserID)
case constant.GroupOrdinaryUsers:
- s.Notification.GroupMemberSetToOrdinaryUserNotification(ctx, member.GroupID, member.UserID)
+ s.notification.GroupMemberSetToOrdinaryUserNotification(ctx, member.GroupID, member.UserID)
}
}
if member.Nickname != nil || member.FaceURL != nil || member.Ex != nil {
- s.Notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID)
+ s.notification.GroupMemberInfoSetNotification(ctx, member.GroupID, member.UserID)
}
}
for i := 0; i < len(req.Members); i++ {
- if err := CallbackAfterSetGroupMemberInfo(ctx, s.config, req.Members[i]); err != nil {
- return nil, err
- }
+ s.webhookAfterSetGroupMemberInfo(ctx, &s.config.WebhooksConfig.AfterSetGroupMemberInfo, req.Members[i])
}
- return resp, nil
+ return &pbgroup.SetGroupMemberInfoResp{}, nil
}
func (s *groupServer) GetGroupAbstractInfo(ctx context.Context, req *pbgroup.GetGroupAbstractInfoReq) (*pbgroup.GetGroupAbstractInfoResp, error) {
- resp := &pbgroup.GetGroupAbstractInfoResp{}
if len(req.GroupIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("groupIDs empty")
+ return nil, errs.ErrArgs.WrapMsg("groupIDs empty")
}
- if utils.Duplicate(req.GroupIDs) {
- return nil, errs.ErrArgs.Wrap("groupIDs duplicate")
+ if datautil.Duplicate(req.GroupIDs) {
+ return nil, errs.ErrArgs.WrapMsg("groupIDs duplicate")
}
groups, err := s.db.FindGroup(ctx, req.GroupIDs)
if err != nil {
return nil, err
}
- if ids := utils.Single(req.GroupIDs, utils.Slice(groups, func(group *relationtb.GroupModel) string {
+ if ids := datautil.Single(req.GroupIDs, datautil.Slice(groups, func(group *relationtb.GroupModel) string {
return group.GroupID
})); len(ids) > 0 {
- return nil, errs.ErrGroupIDNotFound.Wrap("not found group " + strings.Join(ids, ","))
+ return nil, servererrs.ErrGroupIDNotFound.WrapMsg("not found group " + strings.Join(ids, ","))
}
groupUserMap, err := s.db.MapGroupMemberUserID(ctx, req.GroupIDs)
if err != nil {
return nil, err
}
- if ids := utils.Single(req.GroupIDs, utils.Keys(groupUserMap)); len(ids) > 0 {
- return nil, errs.ErrGroupIDNotFound.Wrap(fmt.Sprintf("group %s not found member", strings.Join(ids, ",")))
+ if ids := datautil.Single(req.GroupIDs, datautil.Keys(groupUserMap)); len(ids) > 0 {
+ return nil, servererrs.ErrGroupIDNotFound.WrapMsg(fmt.Sprintf("group %s not found member", strings.Join(ids, ",")))
}
- resp.GroupAbstractInfos = utils.Slice(groups, func(group *relationtb.GroupModel) *pbgroup.GroupAbstractInfo {
- users := groupUserMap[group.GroupID]
- return convert.Db2PbGroupAbstractInfo(group.GroupID, users.MemberNum, users.Hash)
- })
- return resp, nil
+ return &pbgroup.GetGroupAbstractInfoResp{
+ GroupAbstractInfos: datautil.Slice(groups, func(group *relationtb.GroupModel) *pbgroup.GroupAbstractInfo {
+ users := groupUserMap[group.GroupID]
+ return convert.Db2PbGroupAbstractInfo(group.GroupID, users.MemberNum, users.Hash)
+ }),
+ }, nil
}
func (s *groupServer) GetUserInGroupMembers(ctx context.Context, req *pbgroup.GetUserInGroupMembersReq) (*pbgroup.GetUserInGroupMembersResp, error) {
- resp := &pbgroup.GetUserInGroupMembersResp{}
if len(req.GroupIDs) == 0 {
- return nil, errs.ErrArgs.Wrap("groupIDs empty")
+ return nil, errs.ErrArgs.WrapMsg("groupIDs empty")
}
members, err := s.db.FindGroupMemberUser(ctx, req.GroupIDs, req.UserID)
if err != nil {
@@ -1497,25 +1501,26 @@ func (s *groupServer) GetUserInGroupMembers(ctx context.Context, req *pbgroup.Ge
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- resp.Members = utils.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
- return convert.Db2PbGroupMember(e)
- })
- return resp, nil
+ return &pbgroup.GetUserInGroupMembersResp{
+ Members: datautil.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
+ return convert.Db2PbGroupMember(e)
+ }),
+ }, nil
}
-func (s *groupServer) GetGroupMemberUserIDs(ctx context.Context, req *pbgroup.GetGroupMemberUserIDsReq) (resp *pbgroup.GetGroupMemberUserIDsResp, err error) {
- resp = &pbgroup.GetGroupMemberUserIDsResp{}
- resp.UserIDs, err = s.db.FindGroupMemberUserID(ctx, req.GroupID)
+func (s *groupServer) GetGroupMemberUserIDs(ctx context.Context, req *pbgroup.GetGroupMemberUserIDsReq) (*pbgroup.GetGroupMemberUserIDsResp, error) {
+ userIDs, err := s.db.FindGroupMemberUserID(ctx, req.GroupID)
if err != nil {
return nil, err
}
- return resp, nil
+ return &pbgroup.GetGroupMemberUserIDsResp{
+ UserIDs: userIDs,
+ }, nil
}
func (s *groupServer) GetGroupMemberRoleLevel(ctx context.Context, req *pbgroup.GetGroupMemberRoleLevelReq) (*pbgroup.GetGroupMemberRoleLevelResp, error) {
- resp := &pbgroup.GetGroupMemberRoleLevelResp{}
if len(req.RoleLevels) == 0 {
- return nil, errs.ErrArgs.Wrap("RoleLevels empty")
+ return nil, errs.ErrArgs.WrapMsg("RoleLevels empty")
}
members, err := s.db.FindGroupMemberRoleLevels(ctx, req.GroupID, req.RoleLevels)
if err != nil {
@@ -1524,33 +1529,33 @@ func (s *groupServer) GetGroupMemberRoleLevel(ctx context.Context, req *pbgroup.
if err := s.PopulateGroupMember(ctx, members...); err != nil {
return nil, err
}
- resp.Members = utils.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
- return convert.Db2PbGroupMember(e)
- })
- return resp, nil
+ return &pbgroup.GetGroupMemberRoleLevelResp{
+ Members: datautil.Slice(members, func(e *relationtb.GroupMemberModel) *sdkws.GroupMemberFullInfo {
+ return convert.Db2PbGroupMember(e)
+ }),
+ }, nil
}
func (s *groupServer) GetGroupUsersReqApplicationList(ctx context.Context, req *pbgroup.GetGroupUsersReqApplicationListReq) (*pbgroup.GetGroupUsersReqApplicationListResp, error) {
- resp := &pbgroup.GetGroupUsersReqApplicationListResp{}
requests, err := s.db.FindGroupRequests(ctx, req.GroupID, req.UserIDs)
if err != nil {
return nil, err
}
if len(requests) == 0 {
- return resp, nil
+ return &pbgroup.GetGroupUsersReqApplicationListResp{}, nil
}
- groupIDs := utils.Distinct(utils.Slice(requests, func(e *relationtb.GroupRequestModel) string {
+ groupIDs := datautil.Distinct(datautil.Slice(requests, func(e *relationtb.GroupRequestModel) string {
return e.GroupID
}))
groups, err := s.db.FindGroup(ctx, groupIDs)
if err != nil {
return nil, err
}
- groupMap := utils.SliceToMap(groups, func(e *relationtb.GroupModel) string {
+ groupMap := datautil.SliceToMap(groups, func(e *relationtb.GroupModel) string {
return e.GroupID
})
- if ids := utils.Single(groupIDs, utils.Keys(groupMap)); len(ids) > 0 {
- return nil, errs.ErrGroupIDNotFound.Wrap(strings.Join(ids, ","))
+ if ids := datautil.Single(groupIDs, datautil.Keys(groupMap)); len(ids) > 0 {
+ return nil, servererrs.ErrGroupIDNotFound.WrapMsg(strings.Join(ids, ","))
}
owners, err := s.db.FindGroupsOwner(ctx, groupIDs)
if err != nil {
@@ -1559,20 +1564,21 @@ func (s *groupServer) GetGroupUsersReqApplicationList(ctx context.Context, req *
if err := s.PopulateGroupMember(ctx, owners...); err != nil {
return nil, err
}
- ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
+ ownerMap := datautil.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string {
return e.GroupID
})
groupMemberNum, err := s.db.MapGroupMemberNum(ctx, groupIDs)
if err != nil {
return nil, err
}
- resp.GroupRequests = utils.Slice(requests, func(e *relationtb.GroupRequestModel) *sdkws.GroupRequest {
- var ownerUserID string
- if owner, ok := ownerMap[e.GroupID]; ok {
- ownerUserID = owner.UserID
- }
- return convert.Db2PbGroupRequest(e, nil, convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerUserID, groupMemberNum[e.GroupID]))
- })
- resp.Total = int64(len(resp.GroupRequests))
- return resp, nil
+ return &pbgroup.GetGroupUsersReqApplicationListResp{
+ Total: int64(len(requests)),
+ GroupRequests: datautil.Slice(requests, func(e *relationtb.GroupRequestModel) *sdkws.GroupRequest {
+ var ownerUserID string
+ if owner, ok := ownerMap[e.GroupID]; ok {
+ ownerUserID = owner.UserID
+ }
+ return convert.Db2PbGroupRequest(e, nil, convert.Db2PbGroupInfo(groupMap[e.GroupID], ownerUserID, groupMemberNum[e.GroupID]))
+ }),
+ }, nil
}
diff --git a/pkg/rpcclient/notification/group.go b/internal/rpc/group/notification.go
similarity index 57%
rename from pkg/rpcclient/notification/group.go
rename to internal/rpc/group/notification.go
index c72aa839b2..6d7cebcbc6 100644
--- a/pkg/rpcclient/notification/group.go
+++ b/internal/rpc/group/notification.go
@@ -12,35 +12,31 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notification
+package group
import (
"context"
"fmt"
+ "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
- "github.com/OpenIMSDK/protocol/constant"
- pbgroup "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ pbgroup "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/stringutil"
)
-func NewGroupNotificationSender(
- db controller.GroupDatabase,
- msgRpcClient *rpcclient.MessageRpcClient,
- userRpcClient *rpcclient.UserRpcClient,
- config *config.GlobalConfig,
- fn func(ctx context.Context, userIDs []string) ([]CommonUser, error),
-) *GroupNotificationSender {
+func NewGroupNotificationSender(db controller.GroupDatabase, msgRpcClient *rpcclient.MessageRpcClient, userRpcClient *rpcclient.UserRpcClient, config *Config, fn func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)) *GroupNotificationSender {
return &GroupNotificationSender{
- NotificationSender: rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient), rpcclient.WithUserRpcClient(userRpcClient)),
+ NotificationSender: rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithRpcClient(msgRpcClient), rpcclient.WithUserRpcClient(userRpcClient)),
getUsersInfo: fn,
db: db,
config: config,
@@ -49,9 +45,9 @@ func NewGroupNotificationSender(
type GroupNotificationSender struct {
*rpcclient.NotificationSender
- getUsersInfo func(ctx context.Context, userIDs []string) ([]CommonUser, error)
+ getUsersInfo func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)
db controller.GroupDatabase
- config *config.GlobalConfig
+ config *Config
}
func (g *GroupNotificationSender) PopulateGroupMember(ctx context.Context, members ...*relation.GroupMemberModel) error {
@@ -65,11 +61,11 @@ func (g *GroupNotificationSender) PopulateGroupMember(ctx context.Context, membe
}
}
if len(emptyUserIDs) > 0 {
- users, err := g.getUsersInfo(ctx, utils.Keys(emptyUserIDs))
+ users, err := g.getUsersInfo(ctx, datautil.Keys(emptyUserIDs))
if err != nil {
return err
}
- userMap := make(map[string]CommonUser)
+ userMap := make(map[string]notification.CommonUser)
for i, user := range users {
userMap[user.GetUserID()] = users[i]
}
@@ -95,7 +91,7 @@ func (g *GroupNotificationSender) getUser(ctx context.Context, userID string) (*
return nil, err
}
if len(users) == 0 {
- return nil, errs.ErrUserIDNotFound.Wrap(fmt.Sprintf("user %s not found", userID))
+ return nil, servererrs.ErrUserIDNotFound.WrapMsg(fmt.Sprintf("user %s not found", userID))
}
return &sdkws.PublicUserInfo{
UserID: users[0].GetUserID(),
@@ -177,7 +173,7 @@ func (g *GroupNotificationSender) getGroupMember(ctx context.Context, groupID st
return nil, err
}
if len(members) == 0 {
- return nil, errs.ErrInternalServer.Wrap(fmt.Sprintf("group %s member %s not found", groupID, userID))
+ return nil, errs.ErrInternalServer.WrapMsg(fmt.Sprintf("group %s member %s not found", groupID, userID))
}
return members[0], nil
}
@@ -191,7 +187,7 @@ func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Contex
return nil, err
}
fn := func(e *relation.GroupMemberModel) string { return e.UserID }
- return utils.Slice(members, fn), nil
+ return datautil.Slice(members, fn), nil
}
//nolint:unused
@@ -248,11 +244,11 @@ func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberM
func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) (err error) {
if opUser == nil {
- return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is nil")
+ return errs.ErrInternalServer.WrapMsg("**sdkws.GroupMemberFullInfo is nil")
}
userID := mcontext.GetOpUserID(ctx)
if groupID != "" {
- if authverify.IsManagerUserID(userID, g.config) {
+ if authverify.IsManagerUserID(userID, g.config.Share.IMAdminUserID) {
*opUser = &sdkws.GroupMemberFullInfo{
GroupID: groupID,
UserID: userID,
@@ -291,121 +287,122 @@ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws
return nil
}
-func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupCreatedNotification(ctx context.Context, tips *sdkws.GroupCreatedTips) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupCreatedNotification, tips)
}
-func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupInfoSetNotification(ctx context.Context, tips *sdkws.GroupInfoSetTips) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips, rpcclient.WithRpcGetUserName())
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNotification, tips, rpcclient.WithRpcGetUserName())
}
-func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupInfoSetNameNotification(ctx context.Context, tips *sdkws.GroupInfoSetNameTips) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetNameNotification, tips)
}
-func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupInfoSetAnnouncementNotification(ctx context.Context, tips *sdkws.GroupInfoSetAnnouncementTips) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips, rpcclient.WithRpcGetUserName())
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupInfoSetAnnouncementNotification, tips, rpcclient.WithRpcGetUserName())
}
-func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbgroup.JoinGroupReq) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) JoinGroupApplicationNotification(ctx context.Context, req *pbgroup.JoinGroupReq) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, req.GroupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
- user, err := g.getUser(ctx, req.InviterUserID)
+ var user *sdkws.PublicUserInfo
+ user, err = g.getUser(ctx, req.InviterUserID)
if err != nil {
- return err
+ return
}
userIDs, err := g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
userIDs = append(userIDs, req.InviterUserID, mcontext.GetOpUserID(ctx))
tips := &sdkws.JoinGroupApplicationTips{Group: group, Applicant: user, ReqMsg: req.ReqMessage}
- for _, userID := range utils.Distinct(userIDs) {
- err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.JoinGroupApplicationNotification, tips)
- if err != nil {
- log.ZError(ctx, "JoinGroupApplicationNotification failed", err, "group", req.GroupID, "userID", userID)
- }
+ for _, userID := range datautil.Distinct(userIDs) {
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.JoinGroupApplicationNotification, tips)
}
- return nil
}
-func (g *GroupNotificationSender) MemberQuitNotification(ctx context.Context, member *sdkws.GroupMemberFullInfo) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) MemberQuitNotification(ctx context.Context, member *sdkws.GroupMemberFullInfo) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, member.GroupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, member.GroupID)
if err != nil {
- return err
+ return
}
tips := &sdkws.MemberQuitTips{Group: group, QuitUser: member}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), member.GroupID, constant.MemberQuitNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), member.GroupID, constant.MemberQuitNotification, tips)
}
-func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, req.GroupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
- userIDs, err := g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
+ var userIDs []string
+ userIDs, err = g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg}
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ return
}
for _, userID := range append(userIDs, req.FromUserID) {
if userID == req.FromUserID {
@@ -413,32 +410,30 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte
} else {
tips.ReceiverAs = 1
}
- err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips)
- if err != nil {
- log.ZError(ctx, "failed", err)
- }
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationAcceptedNotification, tips)
}
- return nil
}
-func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx context.Context, req *pbgroup.GroupApplicationResponseReq) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, req.GroupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
- userIDs, err := g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
+ var userIDs []string
+ userIDs, err = g.getGroupOwnerAndAdminUserID(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupApplicationRejectedTips{Group: group, HandleMsg: req.HandledMsg}
if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ return
}
for _, userID := range append(userIDs, req.FromUserID) {
if userID == req.FromUserID {
@@ -446,265 +441,281 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte
} else {
tips.ReceiverAs = 1
}
- err = g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips)
- if err != nil {
- log.ZError(ctx, "failed", err)
- }
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), userID, constant.GroupApplicationRejectedNotification, tips)
}
- return nil
}
-func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupOwnerTransferredNotification(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, req.GroupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, req.GroupID)
if err != nil {
- return err
+ return
}
opUserID := mcontext.GetOpUserID(ctx)
- member, err := g.getGroupMemberMap(ctx, req.GroupID, []string{opUserID, req.NewOwnerUserID})
+ var member map[string]*sdkws.GroupMemberFullInfo
+ member, err = g.getGroupMemberMap(ctx, req.GroupID, []string{opUserID, req.NewOwnerUserID})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupOwnerTransferredTips{Group: group, OpUser: member[opUserID], NewGroupOwner: member[req.NewOwnerUserID]}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupOwnerTransferredNotification, tips)
}
-func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) MemberKickedNotification(ctx context.Context, tips *sdkws.MemberKickedTips) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.MemberKickedNotification, tips)
}
-func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, groupID, reason string, invitedUserIDList []string) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) MemberInvitedNotification(ctx context.Context, groupID, reason string, invitedUserIDList []string) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
+
+ var users []*sdkws.GroupMemberFullInfo
+ users, err = g.getGroupMembers(ctx, groupID, invitedUserIDList)
if err != nil {
- return err
- }
- users, err := g.getGroupMembers(ctx, groupID, invitedUserIDList)
- if err != nil {
- return err
+ return
}
tips := &sdkws.MemberInvitedTips{Group: group, InvitedUserList: users}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
- }
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips)
+ err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberInvitedNotification, tips)
}
-func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, groupID string, entrantUserID string) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) MemberEnterNotification(ctx context.Context, groupID string, entrantUserID string) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- user, err := g.getGroupMember(ctx, groupID, entrantUserID)
+ var user *sdkws.GroupMemberFullInfo
+ user, err = g.getGroupMember(ctx, groupID, entrantUserID)
if err != nil {
- return err
+ return
}
tips := &sdkws.MemberEnterTips{Group: group, EntrantUser: user}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberEnterNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.MemberEnterNotification, tips)
}
-func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupDismissedNotification(ctx context.Context, tips *sdkws.GroupDismissedTips) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), tips.Group.GroupID, constant.GroupDismissedNotification, tips)
}
-func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupMemberMutedNotification(ctx context.Context, groupID, groupMemberUserID string, mutedSeconds uint32) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- user, err := g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
+ var user map[string]*sdkws.GroupMemberFullInfo
+ user, err = g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupMemberMutedTips{
Group: group, MutedSeconds: mutedSeconds,
OpUser: user[mcontext.GetOpUserID(ctx)], MutedUser: user[groupMemberUserID],
}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberMutedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberMutedNotification, tips)
}
-func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupMemberCancelMutedNotification(ctx context.Context, groupID, groupMemberUserID string) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- user, err := g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
+ var user map[string]*sdkws.GroupMemberFullInfo
+ user, err = g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupMemberCancelMutedTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], MutedUser: user[groupMemberUserID]}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberCancelMutedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberCancelMutedNotification, tips)
}
-func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupMutedNotification(ctx context.Context, groupID string) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- users, err := g.getGroupMembers(ctx, groupID, []string{mcontext.GetOpUserID(ctx)})
+ var users []*sdkws.GroupMemberFullInfo
+ users, err = g.getGroupMembers(ctx, groupID, []string{mcontext.GetOpUserID(ctx)})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupMutedTips{Group: group}
if len(users) > 0 {
tips.OpUser = users[0]
}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMutedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMutedNotification, tips)
}
-func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Context, groupID string) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Context, groupID string) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- users, err := g.getGroupMembers(ctx, groupID, []string{mcontext.GetOpUserID(ctx)})
+ var users []*sdkws.GroupMemberFullInfo
+ users, err = g.getGroupMembers(ctx, groupID, []string{mcontext.GetOpUserID(ctx)})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupCancelMutedTips{Group: group}
if len(users) > 0 {
tips.OpUser = users[0]
}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupCancelMutedNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupCancelMutedNotification, tips)
}
-func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
- group, err := g.getGroupInfo(ctx, groupID)
+func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) {
+ var err error
+ defer func() {
+ if err != nil {
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
+ }
+ }()
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- user, err := g.getGroupMemberMap(ctx, groupID, []string{groupMemberUserID})
+ var user map[string]*sdkws.GroupMemberFullInfo
+ user, err = g.getGroupMemberMap(ctx, groupID, []string{groupMemberUserID})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberInfoSetNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberInfoSetNotification, tips)
}
-func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
- group, err := g.getGroupInfo(ctx, groupID)
+func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) {
+ var err error
+ defer func() {
+ if err != nil {
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
+ }
+ }()
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
user, err := g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToAdminNotification, tips)
}
-func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) {
- defer log.ZDebug(ctx, "return")
+func (g *GroupNotificationSender) GroupMemberSetToOrdinaryUserNotification(ctx context.Context, groupID, groupMemberUserID string) {
+ var err error
defer func() {
if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
+ log.ZError(ctx, stringutil.GetFuncName(1)+" failed", err)
}
}()
- group, err := g.getGroupInfo(ctx, groupID)
+ var group *sdkws.GroupInfo
+ group, err = g.getGroupInfo(ctx, groupID)
if err != nil {
- return err
+ return
}
- user, err := g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
+ var user map[string]*sdkws.GroupMemberFullInfo
+ user, err = g.getGroupMemberMap(ctx, groupID, []string{mcontext.GetOpUserID(ctx), groupMemberUserID})
if err != nil {
- return err
+ return
}
tips := &sdkws.GroupMemberInfoSetTips{Group: group, OpUser: user[mcontext.GetOpUserID(ctx)], ChangedUser: user[groupMemberUserID]}
- if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
- return err
+ if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil {
+ return
}
- return g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips)
+ g.Notification(ctx, mcontext.GetOpUserID(ctx), group.GroupID, constant.GroupMemberSetToOrdinaryUserNotification, tips)
}
-func (g *GroupNotificationSender) SuperGroupNotification(ctx context.Context, sendID, recvID string) (err error) {
- defer log.ZDebug(ctx, "return")
- defer func() {
- if err != nil {
- log.ZError(ctx, utils.GetFuncName(1)+" failed", err)
- }
- }()
- err = g.Notification(ctx, sendID, recvID, constant.SuperGroupUpdateNotification, nil)
- return err
+func (g *GroupNotificationSender) SuperGroupNotification(ctx context.Context, sendID, recvID string) {
+ g.Notification(ctx, sendID, recvID, constant.SuperGroupUpdateNotification, nil)
}
diff --git a/internal/rpc/group/statistics.go b/internal/rpc/group/statistics.go
index d909e95035..6adb1261af 100644
--- a/internal/rpc/group/statistics.go
+++ b/internal/rpc/group/statistics.go
@@ -18,13 +18,13 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/tools/errs"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/tools/errs"
)
func (s *groupServer) GroupCreateCount(ctx context.Context, req *group.GroupCreateCountReq) (*group.GroupCreateCountResp, error) {
if req.Start > req.End {
- return nil, errs.ErrArgs.Wrap("start > end")
+ return nil, errs.ErrArgs.WrapMsg("start > end: %d > %d", req.Start, req.End)
}
total, err := s.db.CountTotal(ctx, nil)
if err != nil {
diff --git a/internal/rpc/group/super_group.go b/internal/rpc/group/super_group.go
deleted file mode 100644
index f893a79c23..0000000000
--- a/internal/rpc/group/super_group.go
+++ /dev/null
@@ -1,30 +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 group
-
-import (
- "context"
- "errors"
-
- pbgroup "github.com/OpenIMSDK/protocol/group"
-)
-
-func (s *groupServer) GetJoinedSuperGroupList(context.Context, *pbgroup.GetJoinedSuperGroupListReq) (*pbgroup.GetJoinedSuperGroupListResp, error) {
- return nil, errors.New("deprecated")
-}
-
-func (s *groupServer) GetSuperGroupsInfo(context.Context, *pbgroup.GetSuperGroupsInfoReq) (resp *pbgroup.GetSuperGroupsInfoResp, err error) {
- return nil, errors.New("deprecated")
-}
diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go
index ef7c72368d..bfba4824fe 100644
--- a/internal/rpc/msg/as_read.go
+++ b/internal/rpc/msg/as_read.go
@@ -17,19 +17,20 @@ package msg
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- utils2 "github.com/OpenIMSDK/tools/utils"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
"github.com/redis/go-redis/v9"
)
-func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) {
+func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (*msg.GetConversationsHasReadAndMaxSeqResp, error) {
var conversationIDs []string
if len(req.ConversationIDs) == 0 {
+ var err error
conversationIDs, err = m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
if err != nil {
return nil, err
@@ -37,14 +38,17 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
} else {
conversationIDs = req.ConversationIDs
}
+
hasReadSeqs, err := m.MsgDatabase.GetHasReadSeqs(ctx, req.UserID, conversationIDs)
if err != nil {
return nil, err
}
+
conversations, err := m.ConversationLocalCache.GetConversations(ctx, req.UserID, conversationIDs)
if err != nil {
return nil, err
}
+
conversationMaxSeqMap := make(map[string]int64)
for _, conversation := range conversations {
if conversation.MaxSeq != 0 {
@@ -55,95 +59,77 @@ func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *m
if err != nil {
return nil, err
}
- resp = &msg.GetConversationsHasReadAndMaxSeqResp{Seqs: make(map[string]*msg.Seqs)}
- for conversarionID, maxSeq := range maxSeqs {
- resp.Seqs[conversarionID] = &msg.Seqs{
- HasReadSeq: hasReadSeqs[conversarionID],
+ resp := &msg.GetConversationsHasReadAndMaxSeqResp{Seqs: make(map[string]*msg.Seqs)}
+ for conversationID, maxSeq := range maxSeqs {
+ resp.Seqs[conversationID] = &msg.Seqs{
+ HasReadSeq: hasReadSeqs[conversationID],
MaxSeq: maxSeq,
}
- if v, ok := conversationMaxSeqMap[conversarionID]; ok {
- resp.Seqs[conversarionID].MaxSeq = v
+ if v, ok := conversationMaxSeqMap[conversationID]; ok {
+ resp.Seqs[conversationID].MaxSeq = v
}
}
return resp, nil
}
-func (m *msgServer) SetConversationHasReadSeq(
- ctx context.Context,
- req *msg.SetConversationHasReadSeqReq,
-) (resp *msg.SetConversationHasReadSeqResp, err error) {
+func (m *msgServer) SetConversationHasReadSeq(ctx context.Context, req *msg.SetConversationHasReadSeqReq) (*msg.SetConversationHasReadSeqResp, error) {
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
if err != nil {
- return
+ return nil, err
}
if req.HasReadSeq > maxSeq {
- return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq")
+ return nil, errs.ErrArgs.WrapMsg("hasReadSeq must not be bigger than maxSeq")
}
if err := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); err != nil {
return nil, err
}
- if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID,
- req.UserID, nil, req.HasReadSeq); err != nil {
- return
- }
+ m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, req.UserID, nil, req.HasReadSeq)
return &msg.SetConversationHasReadSeqResp{}, nil
}
-func (m *msgServer) MarkMsgsAsRead(
- ctx context.Context,
- req *msg.MarkMsgsAsReadReq,
-) (resp *msg.MarkMsgsAsReadResp, err error) {
+func (m *msgServer) MarkMsgsAsRead(ctx context.Context, req *msg.MarkMsgsAsReadReq) (*msg.MarkMsgsAsReadResp, error) {
if len(req.Seqs) < 1 {
- return nil, errs.ErrArgs.Wrap("seqs must not be empty")
+ return nil, errs.ErrArgs.WrapMsg("seqs must not be empty")
}
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
if err != nil {
- return
+ return nil, err
}
hasReadSeq := req.Seqs[len(req.Seqs)-1]
if hasReadSeq > maxSeq {
- return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq")
+ return nil, errs.ErrArgs.WrapMsg("hasReadSeq must not be bigger than maxSeq")
}
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID)
if err != nil {
- return
+ return nil, err
}
- if err = m.MsgDatabase.MarkSingleChatMsgsAsRead(ctx, req.UserID, req.ConversationID, req.Seqs); err != nil {
- return
+ if err := m.MsgDatabase.MarkSingleChatMsgsAsRead(ctx, req.UserID, req.ConversationID, req.Seqs); err != nil {
+ return nil, err
}
-
currentHasReadSeq, err := m.MsgDatabase.GetHasReadSeq(ctx, req.UserID, req.ConversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
- return
+ return nil, err
}
if hasReadSeq > currentHasReadSeq {
err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, hasReadSeq)
if err != nil {
- return
+ return nil, err
}
}
- req_callback := &cbapi.CallbackSingleMsgReadReq{
+ reqCallback := &cbapi.CallbackSingleMsgReadReq{
ConversationID: conversation.ConversationID,
UserID: req.UserID,
Seqs: req.Seqs,
ContentType: conversation.ConversationType,
}
- if err = CallbackSingleMsgRead(ctx, m.config, req_callback); err != nil {
- return nil, err
- }
-
- if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
- m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq); err != nil {
- return
- }
+ m.webhookAfterSingleMsgRead(ctx, &m.config.WebhooksConfig.AfterSingleMsgRead, reqCallback)
+ m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
+ m.conversationAndGetRecvID(conversation, req.UserID), req.Seqs, hasReadSeq)
return &msg.MarkMsgsAsReadResp{}, nil
}
-func (m *msgServer) MarkConversationAsRead(
- ctx context.Context,
- req *msg.MarkConversationAsReadReq,
-) (resp *msg.MarkConversationAsReadResp, err error) {
+func (m *msgServer) MarkConversationAsRead(ctx context.Context, req *msg.MarkConversationAsReadReq) (*msg.MarkConversationAsReadResp, error) {
conversation, err := m.ConversationLocalCache.GetConversation(ctx, req.UserID, req.ConversationID)
if err != nil {
return nil, err
@@ -154,15 +140,14 @@ func (m *msgServer) MarkConversationAsRead(
}
var seqs []int64
- log.ZDebug(ctx, "MarkConversationAsRead", "hasReadSeq", hasReadSeq,
- "req.HasReadSeq", req.HasReadSeq)
+ log.ZDebug(ctx, "MarkConversationAsRead", "hasReadSeq", hasReadSeq, "req.HasReadSeq", req.HasReadSeq)
if conversation.ConversationType == constant.SingleChatType {
for i := hasReadSeq + 1; i <= req.HasReadSeq; i++ {
seqs = append(seqs, i)
}
- //avoid client missed call MarkConversationMessageAsRead by order
+ // avoid client missed call MarkConversationMessageAsRead by order
for _, val := range req.Seqs {
- if !utils2.Contain(val, seqs...) {
+ if !datautil.Contain(val, seqs...) {
seqs = append(seqs, val)
}
}
@@ -179,12 +164,9 @@ func (m *msgServer) MarkConversationAsRead(
}
hasReadSeq = req.HasReadSeq
}
- if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
- m.conversationAndGetRecvID(conversation, req.UserID), seqs, hasReadSeq); err != nil {
- return nil, err
- }
-
- } else if conversation.ConversationType == constant.SuperGroupChatType ||
+ m.sendMarkAsReadNotification(ctx, req.ConversationID, conversation.ConversationType, req.UserID,
+ m.conversationAndGetRecvID(conversation, req.UserID), seqs, hasReadSeq)
+ } else if conversation.ConversationType == constant.ReadGroupChatType ||
conversation.ConversationType == constant.NotificationChatType {
if req.HasReadSeq > hasReadSeq {
err = m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq)
@@ -193,11 +175,8 @@ func (m *msgServer) MarkConversationAsRead(
}
hasReadSeq = req.HasReadSeq
}
- if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID,
- req.UserID, seqs, hasReadSeq); err != nil {
- return nil, err
- }
-
+ m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID,
+ req.UserID, seqs, hasReadSeq)
}
reqCall := &cbapi.CallbackGroupMsgReadReq{
@@ -206,30 +185,18 @@ func (m *msgServer) MarkConversationAsRead(
UnreadMsgNum: req.HasReadSeq,
ContentType: int64(conversation.ConversationType),
}
- if err := CallbackGroupMsgRead(ctx, m.config, reqCall); err != nil {
- return nil, err
- }
+ m.webhookAfterGroupMsgRead(ctx, &m.config.WebhooksConfig.AfterGroupMsgRead, reqCall)
return &msg.MarkConversationAsReadResp{}, nil
}
-func (m *msgServer) sendMarkAsReadNotification(
- ctx context.Context,
- conversationID string,
- sessionType int32,
- sendID, recvID string,
- seqs []int64,
- hasReadSeq int64,
-) error {
+func (m *msgServer) sendMarkAsReadNotification(ctx context.Context, conversationID string, sessionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) {
tips := &sdkws.MarkAsReadTips{
MarkAsReadUserID: sendID,
ConversationID: conversationID,
Seqs: seqs,
HasReadSeq: hasReadSeq,
}
- err := m.notificationSender.NotificationWithSesstionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips)
- if err != nil {
- log.ZWarn(ctx, "send has read Receipt err", err)
- }
- return nil
+ m.notificationSender.NotificationWithSessionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips)
+
}
diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go
index 927bbe0c22..10404675eb 100644
--- a/internal/rpc/msg/callback.go
+++ b/internal/rpc/msg/callback.go
@@ -16,16 +16,15 @@ package msg
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
- "github.com/OpenIMSDK/protocol/constant"
- pbchat "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ "github.com/openimsdk/protocol/constant"
+ pbchat "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
"google.golang.org/protobuf/proto"
)
@@ -62,138 +61,118 @@ func GetContent(msg *sdkws.MsgData) string {
}
}
-func callbackBeforeSendSingleMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
- if !globalConfig.Callback.CallbackBeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing {
+func (m *msgServer) webhookBeforeSendSingleMsg(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ if msg.MsgData.ContentType == constant.Typing {
+ return nil
+ }
+ cbReq := &cbapi.CallbackBeforeSendSingleMsgReq{
+ CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendSingleMsgCommand),
+ RecvID: msg.MsgData.RecvID,
+ }
+ resp := &cbapi.CallbackBeforeSendSingleMsgResp{}
+ if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+
return nil
- }
- req := &cbapi.CallbackBeforeSendSingleMsgReq{
- CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendSingleMsgCommand),
- RecvID: msg.MsgData.RecvID,
- }
- resp := &cbapi.CallbackBeforeSendSingleMsgResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeSendSingleMsg); err != nil {
- return err
- }
- return nil
+ })
}
-func callbackAfterSendSingleMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
- if !globalConfig.Callback.CallbackAfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing {
- return nil
+func (m *msgServer) webhookAfterSendSingleMsg(ctx context.Context, after *config.AfterConfig, msg *pbchat.SendMsgReq) {
+ if msg.MsgData.ContentType == constant.Typing {
+ return
}
- req := &cbapi.CallbackAfterSendSingleMsgReq{
+ cbReq := &cbapi.CallbackAfterSendSingleMsgReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendSingleMsgCommand),
RecvID: msg.MsgData.RecvID,
}
- resp := &cbapi.CallbackAfterSendSingleMsgResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackAfterSendSingleMsg); err != nil {
- return err
- }
- return nil
+ m.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendSingleMsgResp{}, after)
}
-func callbackBeforeSendGroupMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
- if !globalConfig.Callback.CallbackBeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing {
+func (m *msgServer) webhookBeforeSendGroupMsg(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ if msg.MsgData.ContentType == constant.Typing {
+ return nil
+ }
+ cbReq := &cbapi.CallbackBeforeSendGroupMsgReq{
+ CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendGroupMsgCommand),
+ GroupID: msg.MsgData.GroupID,
+ }
+ resp := &cbapi.CallbackBeforeSendGroupMsgResp{}
+ if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
return nil
- }
- req := &cbapi.CallbackBeforeSendGroupMsgReq{
- CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeSendGroupMsgCommand),
- GroupID: msg.MsgData.GroupID,
- }
- resp := &cbapi.CallbackBeforeSendGroupMsgResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeSendGroupMsg); err != nil {
- return err
- }
- return nil
+ })
}
-func callbackAfterSendGroupMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
- if !globalConfig.Callback.CallbackAfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing {
- return nil
+func (m *msgServer) webhookAfterSendGroupMsg(ctx context.Context, after *config.AfterConfig, msg *pbchat.SendMsgReq) {
+ if msg.MsgData.ContentType == constant.Typing {
+ return
}
- req := &cbapi.CallbackAfterSendGroupMsgReq{
+ cbReq := &cbapi.CallbackAfterSendGroupMsgReq{
CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackAfterSendGroupMsgCommand),
GroupID: msg.MsgData.GroupID,
}
- resp := &cbapi.CallbackAfterSendGroupMsgResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackAfterSendGroupMsg); err != nil {
- return err
- }
- return nil
+ m.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterSendGroupMsgResp{}, after)
}
-func callbackMsgModify(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error {
- if !globalConfig.Callback.CallbackMsgModify.Enable || msg.MsgData.ContentType != constant.Text {
+func (m *msgServer) webhookBeforeMsgModify(ctx context.Context, before *config.BeforeConfig, msg *pbchat.SendMsgReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ if msg.MsgData.ContentType != constant.Text {
+ return nil
+ }
+ cbReq := &cbapi.CallbackMsgModifyCommandReq{
+ CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackBeforeMsgModifyCommand),
+ }
+ resp := &cbapi.CallbackMsgModifyCommandResp{}
+ if err := m.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+
+ if resp.Content != nil {
+ msg.MsgData.Content = []byte(*resp.Content)
+ }
+ datautil.NotNilReplace(msg.MsgData.OfflinePushInfo, resp.OfflinePushInfo)
+ datautil.NotNilReplace(&msg.MsgData.RecvID, resp.RecvID)
+ datautil.NotNilReplace(&msg.MsgData.GroupID, resp.GroupID)
+ datautil.NotNilReplace(&msg.MsgData.ClientMsgID, resp.ClientMsgID)
+ datautil.NotNilReplace(&msg.MsgData.ServerMsgID, resp.ServerMsgID)
+ datautil.NotNilReplace(&msg.MsgData.SenderPlatformID, resp.SenderPlatformID)
+ datautil.NotNilReplace(&msg.MsgData.SenderNickname, resp.SenderNickname)
+ datautil.NotNilReplace(&msg.MsgData.SenderFaceURL, resp.SenderFaceURL)
+ datautil.NotNilReplace(&msg.MsgData.SessionType, resp.SessionType)
+ datautil.NotNilReplace(&msg.MsgData.MsgFrom, resp.MsgFrom)
+ datautil.NotNilReplace(&msg.MsgData.ContentType, resp.ContentType)
+ datautil.NotNilReplace(&msg.MsgData.Status, resp.Status)
+ datautil.NotNilReplace(&msg.MsgData.Options, resp.Options)
+ datautil.NotNilReplace(&msg.MsgData.AtUserIDList, resp.AtUserIDList)
+ datautil.NotNilReplace(&msg.MsgData.AttachedInfo, resp.AttachedInfo)
+ datautil.NotNilReplace(&msg.MsgData.Ex, resp.Ex)
return nil
- }
- req := &cbapi.CallbackMsgModifyCommandReq{
- CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackMsgModifyCommand),
- }
- resp := &cbapi.CallbackMsgModifyCommandResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil {
- return err
- }
- if resp.Content != nil {
- msg.MsgData.Content = []byte(*resp.Content)
- }
- utils.NotNilReplace(msg.MsgData.OfflinePushInfo, resp.OfflinePushInfo)
- utils.NotNilReplace(&msg.MsgData.RecvID, resp.RecvID)
- utils.NotNilReplace(&msg.MsgData.GroupID, resp.GroupID)
- utils.NotNilReplace(&msg.MsgData.ClientMsgID, resp.ClientMsgID)
- utils.NotNilReplace(&msg.MsgData.ServerMsgID, resp.ServerMsgID)
- utils.NotNilReplace(&msg.MsgData.SenderPlatformID, resp.SenderPlatformID)
- utils.NotNilReplace(&msg.MsgData.SenderNickname, resp.SenderNickname)
- utils.NotNilReplace(&msg.MsgData.SenderFaceURL, resp.SenderFaceURL)
- utils.NotNilReplace(&msg.MsgData.SessionType, resp.SessionType)
- utils.NotNilReplace(&msg.MsgData.MsgFrom, resp.MsgFrom)
- utils.NotNilReplace(&msg.MsgData.ContentType, resp.ContentType)
- utils.NotNilReplace(&msg.MsgData.Status, resp.Status)
- utils.NotNilReplace(&msg.MsgData.Options, resp.Options)
- utils.NotNilReplace(&msg.MsgData.AtUserIDList, resp.AtUserIDList)
- utils.NotNilReplace(&msg.MsgData.AttachedInfo, resp.AttachedInfo)
- utils.NotNilReplace(&msg.MsgData.Ex, resp.Ex)
- log.ZDebug(ctx, "callbackMsgModify", "msg", msg.MsgData)
- return nil
+ })
}
-func CallbackGroupMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackGroupMsgReadReq) error {
- if !globalConfig.Callback.CallbackGroupMsgRead.Enable {
- return nil
- }
- req.CallbackCommand = cbapi.CallbackGroupMsgReadCommand
- resp := &cbapi.CallbackGroupMsgReadResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackGroupMsgRead); err != nil {
- return err
- }
- return nil
+func (m *msgServer) webhookAfterGroupMsgRead(ctx context.Context, after *config.AfterConfig, req *cbapi.CallbackGroupMsgReadReq) {
+ req.CallbackCommand = cbapi.CallbackAfterGroupMsgReadCommand
+ m.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackGroupMsgReadResp{}, after)
}
-func CallbackSingleMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackSingleMsgReadReq) error {
- if !globalConfig.Callback.CallbackSingleMsgRead.Enable {
- return nil
- }
- req.CallbackCommand = cbapi.CallbackSingleMsgRead
+func (m *msgServer) webhookAfterSingleMsgRead(ctx context.Context, after *config.AfterConfig, req *cbapi.CallbackSingleMsgReadReq) {
- resp := &cbapi.CallbackSingleMsgReadResp{}
+ req.CallbackCommand = cbapi.CallbackAfterSingleMsgReadCommand
+
+ m.webhookClient.AsyncPost(ctx, req.GetCallbackCommand(), req, &cbapi.CallbackSingleMsgReadResp{}, after)
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackSingleMsgRead); err != nil {
- return err
- }
- return nil
}
-func CallbackAfterRevokeMsg(ctx context.Context, globalConfig *config.GlobalConfig, req *pbchat.RevokeMsgReq) error {
- if !globalConfig.Callback.CallbackAfterRevokeMsg.Enable {
- return nil
- }
+
+func (m *msgServer) webhookAfterRevokeMsg(ctx context.Context, after *config.AfterConfig, req *pbchat.RevokeMsgReq) {
callbackReq := &cbapi.CallbackAfterRevokeMsgReq{
CallbackCommand: cbapi.CallbackAfterRevokeMsgCommand,
ConversationID: req.ConversationID,
Seq: req.Seq,
UserID: req.UserID,
}
- resp := &cbapi.CallbackAfterRevokeMsgResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterRevokeMsg); err != nil {
- return err
- }
- return nil
+ m.webhookClient.AsyncPost(ctx, callbackReq.GetCallbackCommand(), callbackReq, &cbapi.CallbackAfterRevokeMsgResp{}, after)
}
diff --git a/internal/rpc/msg/clear.go b/internal/rpc/msg/clear.go
new file mode 100644
index 0000000000..774eae32cd
--- /dev/null
+++ b/internal/rpc/msg/clear.go
@@ -0,0 +1,77 @@
+package msg
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/pkg/authverify"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/wrapperspb"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "strings"
+ "time"
+)
+
+func (m *msgServer) ClearMsg(ctx context.Context, req *msg.ClearMsgReq) (_ *msg.ClearMsgResp, err error) {
+ if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
+ return nil, err
+ }
+ if req.Timestamp > time.Now().UnixMilli() {
+ return nil, errs.ErrArgs.WrapMsg("request millisecond timestamp error")
+ }
+ var (
+ docNum int
+ msgNum int
+ start = time.Now()
+ )
+ clearMsg := func(ctx context.Context) (bool, error) {
+ conversationSeqs := make(map[string]struct{})
+ defer func() {
+ req := &conversation.UpdateConversationReq{
+ MsgDestructTime: wrapperspb.Int64(time.Now().UnixMilli()),
+ }
+ for conversationID := range conversationSeqs {
+ req.ConversationID = conversationID
+ if err := m.Conversation.UpdateConversations(ctx, req); err != nil {
+ log.ZError(ctx, "update conversation max seq failed", err, "conversationID", conversationID, "msgDestructTime", req.MsgDestructTime)
+ }
+ }
+ }()
+ msgs, err := m.MsgDatabase.GetBeforeMsg(ctx, req.Timestamp, 100)
+ if err != nil {
+ return false, err
+ }
+ if len(msgs) == 0 {
+ return false, nil
+ }
+ for _, msg := range msgs {
+ index, err := m.MsgDatabase.DeleteDocMsgBefore(ctx, req.Timestamp, msg)
+ if err != nil {
+ return false, err
+ }
+ if len(index) == 0 {
+ return false, errs.ErrInternalServer.WrapMsg("delete doc msg failed")
+ }
+ docNum++
+ msgNum += len(index)
+ conversationID := msg.DocID[:strings.LastIndex(msg.DocID, ":")]
+ if _, ok := conversationSeqs[conversationID]; !ok {
+ conversationSeqs[conversationID] = struct{}{}
+ }
+ }
+ return true, nil
+ }
+ for {
+ keep, err := clearMsg(ctx)
+ if err != nil {
+ log.ZError(ctx, "clear msg failed", err, "docNum", docNum, "msgNum", msgNum, "cost", time.Since(start))
+ return nil, err
+ }
+ if !keep {
+ log.ZInfo(ctx, "clear msg success", "docNum", docNum, "msgNum", msgNum, "cost", time.Since(start))
+ break
+ }
+ log.ZInfo(ctx, "clearing message", "docNum", docNum, "msgNum", msgNum, "cost", time.Since(start))
+ }
+ return &msg.ClearMsgResp{}, nil
+}
diff --git a/internal/rpc/msg/delete.go b/internal/rpc/msg/delete.go
index 14e24d23e1..e19bba867e 100644
--- a/internal/rpc/msg/delete.go
+++ b/internal/rpc/msg/delete.go
@@ -17,13 +17,13 @@ package msg
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/timeutil"
)
func (m *msgServer) getMinSeqs(maxSeqs map[string]int64) map[string]int64 {
@@ -41,11 +41,8 @@ func (m *msgServer) validateDeleteSyncOpt(opt *msg.DeleteSyncOpt) (isSyncSelf, i
return opt.IsSyncSelf, opt.IsSyncOther
}
-func (m *msgServer) ClearConversationsMsg(
- ctx context.Context,
- req *msg.ClearConversationsMsgReq,
-) (*msg.ClearConversationsMsgResp, error) {
- if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
+func (m *msgServer) ClearConversationsMsg(ctx context.Context, req *msg.ClearConversationsMsgReq) (*msg.ClearConversationsMsgResp, error) {
+ if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
if err := m.clearConversation(ctx, req.ConversationIDs, req.UserID, req.DeleteSyncOpt); err != nil {
@@ -54,18 +51,14 @@ func (m *msgServer) ClearConversationsMsg(
return &msg.ClearConversationsMsgResp{}, nil
}
-func (m *msgServer) UserClearAllMsg(
- ctx context.Context,
- req *msg.UserClearAllMsgReq,
-) (*msg.UserClearAllMsgResp, error) {
- if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
+func (m *msgServer) UserClearAllMsg(ctx context.Context, req *msg.UserClearAllMsgReq) (*msg.UserClearAllMsgResp, error) {
+ if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
if err != nil {
return nil, err
}
- log.ZDebug(ctx, "GetMaxSeq", "conversationIDs", conversationIDs)
if err := m.clearConversation(ctx, conversationIDs, req.UserID, req.DeleteSyncOpt); err != nil {
return nil, err
}
@@ -73,7 +66,7 @@ func (m *msgServer) UserClearAllMsg(
}
func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*msg.DeleteMsgsResp, error) {
- if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
isSyncSelf, isSyncOther := m.validateDeleteSyncOpt(req.DeleteSyncOpt)
@@ -86,7 +79,7 @@ func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*ms
return nil, err
}
tips := &sdkws.DeleteMsgsTips{UserID: req.UserID, ConversationID: req.ConversationID, Seqs: req.Seqs}
- m.notificationSender.NotificationWithSesstionType(
+ m.notificationSender.NotificationWithSessionType(
ctx,
req.UserID,
m.conversationAndGetRecvID(conversations[0], req.UserID),
@@ -100,16 +93,13 @@ func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*ms
}
if isSyncSelf {
tips := &sdkws.DeleteMsgsTips{UserID: req.UserID, ConversationID: req.ConversationID, Seqs: req.Seqs}
- m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, req.UserID, constant.DeleteMsgsNotification, constant.SingleChatType, tips)
+ m.notificationSender.NotificationWithSessionType(ctx, req.UserID, req.UserID, constant.DeleteMsgsNotification, constant.SingleChatType, tips)
}
}
return &msg.DeleteMsgsResp{}, nil
}
-func (m *msgServer) DeleteMsgPhysicalBySeq(
- ctx context.Context,
- req *msg.DeleteMsgPhysicalBySeqReq,
-) (*msg.DeleteMsgPhysicalBySeqResp, error) {
+func (m *msgServer) DeleteMsgPhysicalBySeq(ctx context.Context, req *msg.DeleteMsgPhysicalBySeqReq) (*msg.DeleteMsgPhysicalBySeqResp, error) {
err := m.MsgDatabase.DeleteMsgsPhysicalBySeqs(ctx, req.ConversationID, req.Seqs)
if err != nil {
return nil, err
@@ -117,37 +107,20 @@ func (m *msgServer) DeleteMsgPhysicalBySeq(
return &msg.DeleteMsgPhysicalBySeqResp{}, nil
}
-func (m *msgServer) DeleteMsgPhysical(
- ctx context.Context,
- req *msg.DeleteMsgPhysicalReq,
-) (*msg.DeleteMsgPhysicalResp, error) {
- if err := authverify.CheckAdmin(ctx, m.config); err != nil {
+func (m *msgServer) DeleteMsgPhysical(ctx context.Context, req *msg.DeleteMsgPhysicalReq) (*msg.DeleteMsgPhysicalResp, error) {
+ if err := authverify.CheckAdmin(ctx, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
- remainTime := utils.GetCurrentTimestampBySecond() - req.Timestamp
+ remainTime := timeutil.GetCurrentTimestampBySecond() - req.Timestamp
for _, conversationID := range req.ConversationIDs {
if err := m.MsgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, remainTime); err != nil {
- log.ZWarn(
- ctx,
- "DeleteConversationMsgsAndSetMinSeq error",
- err,
- "conversationID",
- conversationID,
- "err",
- err,
- )
+ log.ZWarn(ctx, "DeleteConversationMsgsAndSetMinSeq error", err, "conversationID", conversationID, "err", err)
}
}
return &msg.DeleteMsgPhysicalResp{}, nil
}
-func (m *msgServer) clearConversation(
- ctx context.Context,
- conversationIDs []string,
- userID string,
- deleteSyncOpt *msg.DeleteSyncOpt,
-) error {
- defer log.ZDebug(ctx, "clearConversation return line")
+func (m *msgServer) clearConversation(ctx context.Context, conversationIDs []string, userID string, deleteSyncOpt *msg.DeleteSyncOpt) error {
conversations, err := m.Conversation.GetConversationsByConversationID(ctx, conversationIDs)
if err != nil {
return err
@@ -171,14 +144,7 @@ func (m *msgServer) clearConversation(
// notification 2 self
if isSyncSelf {
tips := &sdkws.ClearConversationTips{UserID: userID, ConversationIDs: existConversationIDs}
- m.notificationSender.NotificationWithSesstionType(
- ctx,
- userID,
- userID,
- constant.ClearConversationNotification,
- constant.SingleChatType,
- tips,
- )
+ m.notificationSender.NotificationWithSessionType(ctx, userID, userID, constant.ClearConversationNotification, constant.SingleChatType, tips)
}
} else {
if err := m.MsgDatabase.SetMinSeqs(ctx, m.getMinSeqs(maxSeqs)); err != nil {
@@ -186,7 +152,7 @@ func (m *msgServer) clearConversation(
}
for _, conversation := range existConversations {
tips := &sdkws.ClearConversationTips{UserID: userID, ConversationIDs: []string{conversation.ConversationID}}
- m.notificationSender.NotificationWithSesstionType(ctx, userID, m.conversationAndGetRecvID(conversation, userID), constant.ClearConversationNotification, conversation.ConversationType, tips)
+ m.notificationSender.NotificationWithSessionType(ctx, userID, m.conversationAndGetRecvID(conversation, userID), constant.ClearConversationNotification, conversation.ConversationType, tips)
}
}
if err := m.MsgDatabase.UserSetHasReadSeqs(ctx, userID, maxSeqs); err != nil {
diff --git a/internal/rpc/msg/message_interceptor.go b/internal/rpc/msg/message_interceptor.go
deleted file mode 100644
index 97eac613d9..0000000000
--- a/internal/rpc/msg/message_interceptor.go
+++ /dev/null
@@ -1,43 +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 msg
-
-import (
- "context"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-type MessageInterceptorFunc func(ctx context.Context, globalConfig *config.GlobalConfig, req *msg.SendMsgReq) (*sdkws.MsgData, error)
-
-func MessageHasReadEnabled(_ context.Context, globalConfig *config.GlobalConfig, req *msg.SendMsgReq) (*sdkws.MsgData, error) {
- switch {
- case req.MsgData.ContentType == constant.HasReadReceipt && req.MsgData.SessionType == constant.SingleChatType:
- if !globalConfig.SingleMessageHasReadReceiptEnable {
- return nil, errs.ErrMessageHasReadDisable.Wrap()
- }
- return req.MsgData, nil
- case req.MsgData.ContentType == constant.HasReadReceipt && req.MsgData.SessionType == constant.SuperGroupChatType:
- if !globalConfig.GroupMessageHasReadReceiptEnable {
- return nil, errs.ErrMessageHasReadDisable.Wrap()
- }
- return req.MsgData, nil
- }
- return req.MsgData, nil
-}
diff --git a/internal/rpc/msg/msg_status.go b/internal/rpc/msg/msg_status.go
index 80c5833a50..b524d6236a 100644
--- a/internal/rpc/msg/msg_status.go
+++ b/internal/rpc/msg/msg_status.go
@@ -17,15 +17,12 @@ package msg
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- pbmsg "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/tools/mcontext"
+ "github.com/openimsdk/protocol/constant"
+ pbmsg "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/tools/mcontext"
)
-func (m *msgServer) SetSendMsgStatus(
- ctx context.Context,
- req *pbmsg.SetSendMsgStatusReq,
-) (*pbmsg.SetSendMsgStatusResp, error) {
+func (m *msgServer) SetSendMsgStatus(ctx context.Context, req *pbmsg.SetSendMsgStatusReq) (*pbmsg.SetSendMsgStatusResp, error) {
resp := &pbmsg.SetSendMsgStatusResp{}
if err := m.MsgDatabase.SetSendMsgStatus(ctx, mcontext.GetOperationID(ctx), req.Status); err != nil {
return nil, err
@@ -33,10 +30,7 @@ func (m *msgServer) SetSendMsgStatus(
return resp, nil
}
-func (m *msgServer) GetSendMsgStatus(
- ctx context.Context,
- req *pbmsg.GetSendMsgStatusReq,
-) (*pbmsg.GetSendMsgStatusResp, error) {
+func (m *msgServer) GetSendMsgStatus(ctx context.Context, req *pbmsg.GetSendMsgStatusReq) (*pbmsg.GetSendMsgStatusResp, error) {
resp := &pbmsg.GetSendMsgStatusResp{}
status, err := m.MsgDatabase.GetSendMsgStatus(ctx, mcontext.GetOperationID(ctx))
if IsNotFound(err) {
diff --git a/pkg/rpcclient/notification/msg.go b/internal/rpc/msg/notification.go
similarity index 61%
rename from pkg/rpcclient/notification/msg.go
rename to internal/rpc/msg/notification.go
index beaef1d969..3b13676bf6 100644
--- a/pkg/rpcclient/notification/msg.go
+++ b/internal/rpc/msg/notification.go
@@ -12,40 +12,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notification
+package msg
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
)
type MsgNotificationSender struct {
*rpcclient.NotificationSender
}
-func NewMsgNotificationSender(config *config.GlobalConfig, opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender {
- return &MsgNotificationSender{rpcclient.NewNotificationSender(config, opts...)}
+func NewMsgNotificationSender(config *Config, opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender {
+ return &MsgNotificationSender{rpcclient.NewNotificationSender(&config.NotificationConfig, opts...)}
}
-func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) error {
+func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) {
tips := sdkws.DeleteMsgsTips{
UserID: userID,
ConversationID: conversationID,
Seqs: seqs,
}
- return m.Notification(ctx, userID, userID, constant.DeleteMsgsNotification, &tips)
+ m.Notification(ctx, userID, userID, constant.DeleteMsgsNotification, &tips)
}
-func (m *MsgNotificationSender) MarkAsReadNotification(ctx context.Context, conversationID string, sesstionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) error {
+func (m *MsgNotificationSender) MarkAsReadNotification(ctx context.Context, conversationID string, sessionType int32, sendID, recvID string, seqs []int64, hasReadSeq int64) {
tips := &sdkws.MarkAsReadTips{
MarkAsReadUserID: sendID,
ConversationID: conversationID,
Seqs: seqs,
HasReadSeq: hasReadSeq,
}
- return m.NotificationWithSesstionType(ctx, sendID, recvID, constant.HasReadReceipt, sesstionType, tips)
+ m.NotificationWithSessionType(ctx, sendID, recvID, constant.HasReadReceipt, sessionType, tips)
}
diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go
index 99690b0cc7..7dbc307a19 100644
--- a/internal/rpc/msg/revoke.go
+++ b/internal/rpc/msg/revoke.go
@@ -19,29 +19,29 @@ import (
"encoding/json"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
)
func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.RevokeMsgResp, error) {
- defer log.ZDebug(ctx, "RevokeMsg return line")
if req.UserID == "" {
- return nil, errs.ErrArgs.Wrap("user_id is empty")
+ return nil, errs.ErrArgs.WrapMsg("user_id is empty")
}
if req.ConversationID == "" {
- return nil, errs.ErrArgs.Wrap("conversation_id is empty")
+ return nil, errs.ErrArgs.WrapMsg("conversation_id is empty")
}
if req.Seq < 0 {
- return nil, errs.ErrArgs.Wrap("seq is invalid")
+ return nil, errs.ErrArgs.WrapMsg("seq is invalid")
}
- if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
user, err := m.UserLocalCache.GetUserInfo(ctx, req.UserID)
@@ -53,24 +53,24 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
return nil, err
}
if len(msgs) == 0 || msgs[0] == nil {
- return nil, errs.ErrRecordNotFound.Wrap("msg not found")
+ return nil, errs.ErrRecordNotFound.WrapMsg("msg not found")
}
if msgs[0].ContentType == constant.MsgRevokeNotification {
- return nil, errs.ErrMsgAlreadyRevoke.Wrap("msg already revoke")
+ return nil, servererrs.ErrMsgAlreadyRevoke.WrapMsg("msg already revoke")
}
data, _ := json.Marshal(msgs[0])
- log.ZInfo(ctx, "GetMsgBySeqs", "conversationID", req.ConversationID, "seq", req.Seq, "msg", string(data))
+ log.ZDebug(ctx, "GetMsgBySeqs", "conversationID", req.ConversationID, "seq", req.Seq, "msg", string(data))
var role int32
- if !authverify.IsAppManagerUid(ctx, m.config) {
+ if !authverify.IsAppManagerUid(ctx, m.config.Share.IMAdminUserID) {
switch msgs[0].SessionType {
case constant.SingleChatType:
- if err := authverify.CheckAccessV3(ctx, msgs[0].SendID, m.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, msgs[0].SendID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
role = user.AppMangerLevel
- case constant.SuperGroupChatType:
- members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, utils.Distinct([]string{req.UserID, msgs[0].SendID}))
+ case constant.ReadGroupChatType:
+ members, err := m.GroupLocalCache.GetGroupMemberInfoMap(ctx, msgs[0].GroupID, datautil.Distinct([]string{req.UserID, msgs[0].SendID}))
if err != nil {
return nil, err
}
@@ -79,21 +79,21 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
case constant.GroupOwner:
case constant.GroupAdmin:
if members[msgs[0].SendID].RoleLevel != constant.GroupOrdinaryUsers {
- return nil, errs.ErrNoPermission.Wrap("no permission")
+ return nil, errs.ErrNoPermission.WrapMsg("no permission")
}
default:
- return nil, errs.ErrNoPermission.Wrap("no permission")
+ return nil, errs.ErrNoPermission.WrapMsg("no permission")
}
}
if member := members[req.UserID]; member != nil {
role = member.RoleLevel
}
default:
- return nil, errs.ErrInternalServer.Wrap("msg sessionType not supported")
+ return nil, errs.ErrInternalServer.WrapMsg("msg sessionType not supported")
}
}
now := time.Now().UnixMilli()
- err = m.MsgDatabase.RevokeMsg(ctx, req.ConversationID, req.Seq, &unrelationtb.RevokeModel{
+ err = m.MsgDatabase.RevokeMsg(ctx, req.ConversationID, req.Seq, &relation.RevokeModel{
Role: role,
UserID: req.UserID,
Nickname: user.Nickname,
@@ -104,11 +104,9 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
}
revokerUserID := mcontext.GetOpUserID(ctx)
var flag bool
- if len(m.config.Manager.UserID) > 0 {
- flag = utils.Contain(revokerUserID, m.config.Manager.UserID...)
- }
- if len(m.config.Manager.UserID) == 0 && len(m.config.IMAdmin.UserID) > 0 {
- flag = utils.Contain(revokerUserID, m.config.IMAdmin.UserID...)
+
+ if len(m.config.Share.IMAdminUserID) > 0 {
+ flag = datautil.Contain(revokerUserID, m.config.Share.IMAdminUserID...)
}
tips := sdkws.RevokeMsgTips{
RevokerUserID: revokerUserID,
@@ -120,16 +118,12 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg.
IsAdminRevoke: flag,
}
var recvID string
- if msgs[0].SessionType == constant.SuperGroupChatType {
+ if msgs[0].SessionType == constant.ReadGroupChatType {
recvID = msgs[0].GroupID
} else {
recvID = msgs[0].RecvID
}
- if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil {
- return nil, err
- }
- if err = CallbackAfterRevokeMsg(ctx, m.config, req); err != nil {
- return nil, err
- }
+ m.notificationSender.NotificationWithSessionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips)
+ m.webhookAfterRevokeMsg(ctx, &m.config.WebhooksConfig.AfterRevokeMsg, req)
return &msg.RevokeMsgResp{}, nil
}
diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go
index ea59c40cff..4a2d21019b 100644
--- a/internal/rpc/msg/send.go
+++ b/internal/rpc/msg/send.go
@@ -17,67 +17,59 @@ package msg
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- pbconversation "github.com/OpenIMSDK/protocol/conversation"
- pbmsg "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/protocol/wrapperspb"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
+ "github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
+ "github.com/openimsdk/protocol/constant"
+ pbconversation "github.com/openimsdk/protocol/conversation"
+ pbmsg "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/protocol/wrapperspb"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/stringutil"
)
-func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, error error) {
- resp = &pbmsg.SendMsgResp{}
+func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (*pbmsg.SendMsgResp, error) {
if req.MsgData != nil {
- flag := isMessageHasReadEnabled(req.MsgData, m.config)
- if !flag {
- return nil, errs.ErrMessageHasReadDisable.Wrap()
- }
m.encapsulateMsgData(req.MsgData)
switch req.MsgData.SessionType {
case constant.SingleChatType:
return m.sendMsgSingleChat(ctx, req)
case constant.NotificationChatType:
return m.sendMsgNotification(ctx, req)
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return m.sendMsgSuperGroupChat(ctx, req)
default:
- return nil, errs.ErrArgs.Wrap("unknown sessionType")
+ return nil, errs.ErrArgs.WrapMsg("unknown sessionType")
}
- } else {
- return nil, errs.ErrArgs.Wrap("msgData is nil")
}
+ return nil, errs.ErrArgs.WrapMsg("msgData is nil")
}
-func (m *msgServer) sendMsgSuperGroupChat(
- ctx context.Context,
- req *pbmsg.SendMsgReq,
-) (resp *pbmsg.SendMsgResp, err error) {
+func (m *msgServer) sendMsgSuperGroupChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
if err = m.messageVerification(ctx, req); err != nil {
prommetrics.GroupChatMsgProcessFailedCounter.Inc()
return nil, err
}
- if err = callbackBeforeSendGroupMsg(ctx, m.config, req); err != nil {
+
+ if err = m.webhookBeforeSendGroupMsg(ctx, &m.config.WebhooksConfig.BeforeSendGroupMsg, req); err != nil {
return nil, err
}
-
- if err := callbackMsgModify(ctx, m.config, req); err != nil {
+ if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
return nil, err
}
- err = m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData)
+ err = m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData)
if err != nil {
return nil, err
}
if req.MsgData.ContentType == constant.AtText {
go m.setConversationAtInfo(ctx, req.MsgData)
}
- if err = callbackAfterSendGroupMsg(ctx, m.config, req); err != nil {
- log.ZWarn(ctx, "CallbackAfterSendGroupMsg", err)
- }
+
+ m.webhookAfterSendGroupMsg(ctx, &m.config.WebhooksConfig.AfterSendGroupMsg, req)
prommetrics.GroupChatMsgProcessSuccessCounter.Inc()
resp = &pbmsg.SendMsgResp{}
resp.SendTime = req.MsgData.SendTime
@@ -95,43 +87,39 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa
ConversationType: msg.SessionType,
GroupID: msg.GroupID,
}
- tagAll := utils.IsContain(constant.AtAllString, msg.AtUserIDList)
+ tagAll := datautil.Contain(constant.AtAllString, msg.AtUserIDList...)
if tagAll {
memberUserIDList, err := m.GroupLocalCache.GetGroupMemberIDs(ctx, msg.GroupID)
if err != nil {
log.ZWarn(ctx, "GetGroupMemberIDs", err)
return
}
- atUserID = utils.DifferenceString([]string{constant.AtAllString}, msg.AtUserIDList)
+ atUserID = stringutil.DifferenceString([]string{constant.AtAllString}, msg.AtUserIDList)
if len(atUserID) == 0 { // just @everyone
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAll}
- } else { //@Everyone and @other people
+ } else { // @Everyone and @other people
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAllAtMe}
err = m.Conversation.SetConversations(ctx, atUserID, conversation)
if err != nil {
log.ZWarn(ctx, "SetConversations", err, "userID", atUserID, "conversation", conversation)
}
- memberUserIDList = utils.DifferenceString(atUserID, memberUserIDList)
+ memberUserIDList = stringutil.DifferenceString(atUserID, memberUserIDList)
}
conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAll}
err = m.Conversation.SetConversations(ctx, memberUserIDList, conversation)
if err != nil {
log.ZWarn(ctx, "SetConversations", err, "userID", memberUserIDList, "conversation", conversation)
}
- } else {
- conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtMe}
- err := m.Conversation.SetConversations(ctx, msg.AtUserIDList, conversation)
- if err != nil {
- log.ZWarn(ctx, "SetConversations", err, msg.AtUserIDList, conversation)
- }
+ }
+ conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtMe}
+ err := m.Conversation.SetConversations(ctx, msg.AtUserIDList, conversation)
+ if err != nil {
+ log.ZWarn(ctx, "SetConversations", err, msg.AtUserIDList, conversation)
}
}
-func (m *msgServer) sendMsgNotification(
- ctx context.Context,
- req *pbmsg.SendMsgReq,
-) (resp *pbmsg.SendMsgResp, err error) {
- if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
+func (m *msgServer) sendMsgNotification(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
+ if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
return nil, err
}
resp = &pbmsg.SendMsgResp{
@@ -143,7 +131,6 @@ func (m *msgServer) sendMsgNotification(
}
func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) {
- log.ZDebug(ctx, "sendMsgSingleChat return")
if err := m.messageVerification(ctx, req); err != nil {
return nil, err
}
@@ -153,7 +140,7 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
isSend, err = m.modifyMessageByUserMessageReceiveOpt(
ctx,
req.MsgData.RecvID,
- utils.GenConversationIDForSingle(req.MsgData.SendID, req.MsgData.RecvID),
+ conversationutil.GenConversationIDForSingle(req.MsgData.SendID, req.MsgData.RecvID),
constant.SingleChatType,
req,
)
@@ -165,31 +152,23 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, nil
} else {
- if err = callbackBeforeSendSingleMsg(ctx, m.config, req); err != nil {
+ if err = m.webhookBeforeSendSingleMsg(ctx, &m.config.WebhooksConfig.BeforeSendSingleMsg, req); err != nil {
return nil, err
}
-
- if err := callbackMsgModify(ctx, m.config, req); err != nil {
+ if err := m.webhookBeforeMsgModify(ctx, &m.config.WebhooksConfig.BeforeMsgModify, req); err != nil {
return nil, err
}
- if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
+
+ if err := m.MsgDatabase.MsgToMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil {
prommetrics.SingleChatMsgProcessFailedCounter.Inc()
return nil, err
}
- err = callbackAfterSendSingleMsg(ctx, m.config, req)
- if err != nil {
- log.ZWarn(ctx, "CallbackAfterSendSingleMsg", err, "req", req)
- }
- resp = &pbmsg.SendMsgResp{
+ m.webhookAfterSendSingleMsg(ctx, &m.config.WebhooksConfig.AfterSendSingleMsg, req)
+ prommetrics.SingleChatMsgProcessSuccessCounter.Inc()
+ return &pbmsg.SendMsgResp{
ServerMsgID: req.MsgData.ServerMsgID,
ClientMsgID: req.MsgData.ClientMsgID,
SendTime: req.MsgData.SendTime,
- }
- prommetrics.SingleChatMsgProcessSuccessCounter.Inc()
- return resp, nil
+ }, nil
}
}
-
-func (m *msgServer) BatchSendMsg(ctx context.Context, in *pbmsg.BatchSendMessageReq) (*pbmsg.BatchSendMessageResp, error) {
- return nil, nil
-}
diff --git a/internal/rpc/msg/seq.go b/internal/rpc/msg/seq.go
index dfc2ad0b1b..27465c2105 100644
--- a/internal/rpc/msg/seq.go
+++ b/internal/rpc/msg/seq.go
@@ -17,13 +17,10 @@ package msg
import (
"context"
- pbmsg "github.com/OpenIMSDK/protocol/msg"
+ pbmsg "github.com/openimsdk/protocol/msg"
)
-func (m *msgServer) GetConversationMaxSeq(
- ctx context.Context,
- req *pbmsg.GetConversationMaxSeqReq,
-) (resp *pbmsg.GetConversationMaxSeqResp, err error) {
+func (m *msgServer) GetConversationMaxSeq(ctx context.Context, req *pbmsg.GetConversationMaxSeqReq) (*pbmsg.GetConversationMaxSeqResp, error) {
maxSeq, err := m.MsgDatabase.GetMaxSeq(ctx, req.ConversationID)
if err != nil {
return nil, err
diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go
index f2d735a70c..3f4df8d4b7 100644
--- a/internal/rpc/msg/server.go
+++ b/internal/rpc/msg/server.go
@@ -15,69 +15,84 @@
package msg
import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/tools/discoveryregistry"
+ "context"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/redisutil"
+
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
"github.com/openimsdk/open-im-server/v3/pkg/rpccache"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/tools/discovery"
"google.golang.org/grpc"
)
+type MessageInterceptorFunc func(ctx context.Context, globalConfig *Config, req *msg.SendMsgReq) (*sdkws.MsgData, error)
type (
+ // MessageInterceptorChain defines a chain of message interceptor functions.
MessageInterceptorChain []MessageInterceptorFunc
- msgServer struct {
- RegisterCenter discoveryregistry.SvcDiscoveryRegistry
- MsgDatabase controller.CommonMsgDatabase
- Conversation *rpcclient.ConversationRpcClient
- UserLocalCache *rpccache.UserLocalCache
- FriendLocalCache *rpccache.FriendLocalCache
- GroupLocalCache *rpccache.GroupLocalCache
- ConversationLocalCache *rpccache.ConversationLocalCache
- Handlers MessageInterceptorChain
- notificationSender *rpcclient.NotificationSender
- config *config.GlobalConfig
+
+ // MsgServer encapsulates dependencies required for message handling.
+ msgServer struct {
+ RegisterCenter discovery.SvcDiscoveryRegistry // Service discovery registry for service registration.
+ MsgDatabase controller.CommonMsgDatabase // Interface for message database operations.
+ Conversation *rpcclient.ConversationRpcClient // RPC client for conversation service.
+ UserLocalCache *rpccache.UserLocalCache // Local cache for user data.
+ FriendLocalCache *rpccache.FriendLocalCache // Local cache for friend data.
+ GroupLocalCache *rpccache.GroupLocalCache // Local cache for group data.
+ ConversationLocalCache *rpccache.ConversationLocalCache // Local cache for conversation data.
+ Handlers MessageInterceptorChain // Chain of handlers for processing messages.
+ notificationSender *rpcclient.NotificationSender // RPC client for sending notifications.
+ config *Config // Global configuration settings.
+ webhookClient *webhook.Client
+ }
+
+ Config struct {
+ RpcConfig config.Msg
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ KafkaConfig config.Kafka
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ WebhooksConfig config.Webhooks
+ LocalCacheConfig config.LocalCache
}
)
func (m *msgServer) addInterceptorHandler(interceptorFunc ...MessageInterceptorFunc) {
m.Handlers = append(m.Handlers, interceptorFunc...)
-}
-//func (m *msgServer) execInterceptorHandler(ctx context.Context, config *config.GlobalConfig, req *msg.SendMsgReq) error {
-// for _, handler := range m.Handlers {
-// msgData, err := handler(ctx, config, req)
-// if err != nil {
-// return err
-// }
-// req.MsgData = msgData
-// }
-// return nil
-//}
+}
-func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- rdb, err := cache.NewRedis(config)
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
- mongo, err := unrelation.NewMongo(config)
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
- if err := mongo.CreateMsgIndex(); err != nil {
+ msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB())
+ if err != nil {
return err
}
- cacheModel := cache.NewMsgCacheModel(rdb, config)
- msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase(config.Mongo.Database))
- conversationClient := rpcclient.NewConversationRpcClient(client, config)
- userRpcClient := rpcclient.NewUserRpcClient(client, config)
- groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
- friendRpcClient := rpcclient.NewFriendRpcClient(client, config)
- msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, cacheModel, config)
+ //todo MsgCacheTimeout
+ msgModel := cache.NewMsgCache(rdb, config.RedisConfig.EnablePipeline)
+ seqModel := cache.NewSeqCache(rdb)
+ conversationClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation)
+ userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID)
+ groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
+ friendRpcClient := rpcclient.NewFriendRpcClient(client, config.Share.RpcRegisterName.Friend)
+ msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, seqModel, &config.KafkaConfig)
if err != nil {
return err
}
@@ -85,28 +100,29 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg
Conversation: &conversationClient,
MsgDatabase: msgDatabase,
RegisterCenter: client,
- UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, rdb),
- GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, rdb),
- ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, rdb),
- FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, rdb),
+ UserLocalCache: rpccache.NewUserLocalCache(userRpcClient, &config.LocalCacheConfig, rdb),
+ GroupLocalCache: rpccache.NewGroupLocalCache(groupRpcClient, &config.LocalCacheConfig, rdb),
+ ConversationLocalCache: rpccache.NewConversationLocalCache(conversationClient, &config.LocalCacheConfig, rdb),
+ FriendLocalCache: rpccache.NewFriendLocalCache(friendRpcClient, &config.LocalCacheConfig, rdb),
config: config,
+ webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
}
- s.notificationSender = rpcclient.NewNotificationSender(config, rpcclient.WithLocalSendMsg(s.SendMsg))
- s.addInterceptorHandler(MessageHasReadEnabled)
+
+ s.notificationSender = rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithLocalSendMsg(s.SendMsg))
msg.RegisterMsgServer(server, s)
return nil
}
-func (m *msgServer) conversationAndGetRecvID(conversation *conversation.Conversation, userID string) (recvID string) {
+func (m *msgServer) conversationAndGetRecvID(conversation *conversation.Conversation, userID string) string {
if conversation.ConversationType == constant.SingleChatType ||
conversation.ConversationType == constant.NotificationChatType {
if userID == conversation.OwnerUserID {
- recvID = conversation.UserID
+ return conversation.UserID
} else {
- recvID = conversation.OwnerUserID
+ return conversation.OwnerUserID
}
- } else if conversation.ConversationType == constant.SuperGroupChatType {
- recvID = conversation.GroupID
+ } else if conversation.ConversationType == constant.ReadGroupChatType {
+ return conversation.GroupID
}
- return
+ return ""
}
diff --git a/internal/rpc/msg/statistics.go b/internal/rpc/msg/statistics.go
index e62954dea4..15a0aaa577 100644
--- a/internal/rpc/msg/statistics.go
+++ b/internal/rpc/msg/statistics.go
@@ -18,28 +18,20 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/utils/datautil"
)
func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq) (*msg.GetActiveUserResp, error) {
- msgCount, userCount, users, dateCount, err := m.MsgDatabase.RangeUserSendCount(
- ctx,
- time.UnixMilli(req.Start),
- time.UnixMilli(req.End),
- req.Group,
- req.Ase,
- req.Pagination.PageNumber,
- req.Pagination.ShowNumber,
- )
+ msgCount, userCount, users, dateCount, err := m.MsgDatabase.RangeUserSendCount(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End), req.Group, req.Ase, req.Pagination.PageNumber, req.Pagination.ShowNumber)
if err != nil {
return nil, err
}
var pbUsers []*msg.ActiveUser
if len(users) > 0 {
- userIDs := utils.Slice(users, func(e *unrelation.UserCount) string { return e.UserID })
+ userIDs := datautil.Slice(users, func(e *relation.UserCount) string { return e.UserID })
userMap, err := m.UserLocalCache.GetUsersInfoMap(ctx, userIDs)
if err != nil {
return nil, err
@@ -68,20 +60,13 @@ func (m *msgServer) GetActiveUser(ctx context.Context, req *msg.GetActiveUserReq
}
func (m *msgServer) GetActiveGroup(ctx context.Context, req *msg.GetActiveGroupReq) (*msg.GetActiveGroupResp, error) {
- msgCount, groupCount, groups, dateCount, err := m.MsgDatabase.RangeGroupSendCount(
- ctx,
- time.UnixMilli(req.Start),
- time.UnixMilli(req.End),
- req.Ase,
- req.Pagination.PageNumber,
- req.Pagination.ShowNumber,
- )
+ msgCount, groupCount, groups, dateCount, err := m.MsgDatabase.RangeGroupSendCount(ctx, time.UnixMilli(req.Start), time.UnixMilli(req.End), req.Ase, req.Pagination.PageNumber, req.Pagination.ShowNumber)
if err != nil {
return nil, err
}
var pbgroups []*msg.ActiveGroup
if len(groups) > 0 {
- groupIDs := utils.Slice(groups, func(e *unrelation.GroupCount) string { return e.GroupID })
+ groupIDs := datautil.Slice(groups, func(e *relation.GroupCount) string { return e.GroupID })
resp, err := m.GroupLocalCache.GetGroupInfos(ctx, groupIDs)
if err != nil {
return nil, err
diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go
index 0a8b3f89e7..afb79506ee 100644
--- a/internal/rpc/msg/sync_msg.go
+++ b/internal/rpc/msg/sync_msg.go
@@ -16,20 +16,19 @@ package msg
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/timeutil"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/log"
)
-func (m *msgServer) PullMessageBySeqs(
- ctx context.Context,
- req *sdkws.PullMessageBySeqsReq,
-) (*sdkws.PullMessageBySeqsResp, error) {
+func (m *msgServer) PullMessageBySeqs(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) {
resp := &sdkws.PullMessageBySeqsResp{}
resp.Msgs = make(map[string]*sdkws.PullMsgs)
resp.NotificationMsgs = make(map[string]*sdkws.PullMsgs)
@@ -88,7 +87,7 @@ func (m *msgServer) PullMessageBySeqs(
}
func (m *msgServer) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sdkws.GetMaxSeqResp, error) {
- if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil {
+ if err := authverify.CheckAccessV3(ctx, req.UserID, m.config.Share.IMAdminUserID); err != nil {
return nil, err
}
conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID)
@@ -96,9 +95,9 @@ func (m *msgServer) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sd
return nil, err
}
for _, conversationID := range conversationIDs {
- conversationIDs = append(conversationIDs, utils.GetNotificationConversationIDByConversationID(conversationID))
+ conversationIDs = append(conversationIDs, conversationutil.GetNotificationConversationIDByConversationID(conversationID))
}
- conversationIDs = append(conversationIDs, utils.GetSelfNotificationConversationID(req.UserID))
+ conversationIDs = append(conversationIDs, conversationutil.GetSelfNotificationConversationID(req.UserID))
log.ZDebug(ctx, "GetMaxSeq", "conversationIDs", conversationIDs)
maxSeqs, err := m.MsgDatabase.GetMaxSeqs(ctx, conversationIDs)
if err != nil {
@@ -133,7 +132,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
switch chatLog.SessionType {
case constant.SingleChatType, constant.NotificationChatType:
recvIDs = append(recvIDs, chatLog.RecvID)
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
groupIDs = append(groupIDs, chatLog.GroupID)
}
}
@@ -175,7 +174,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
// Construct response with updated information
for _, chatLog := range chatLogs {
pbchatLog := &msg.ChatLog{}
- utils.CopyStructFields(pbchatLog, chatLog)
+ datautil.CopyStructFields(pbchatLog, chatLog)
pbchatLog.SendTime = chatLog.SendTime
pbchatLog.CreateTime = chatLog.CreateTime
if chatLog.SenderNickname == "" {
@@ -184,7 +183,7 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
switch chatLog.SessionType {
case constant.SingleChatType, constant.NotificationChatType:
pbchatLog.RecvNickname = recvMap[chatLog.RecvID]
- case constant.GroupChatType, constant.SuperGroupChatType:
+ case constant.WriteGroupChatType, constant.ReadGroupChatType:
groupInfo := groupMap[chatLog.GroupID]
pbchatLog.SenderFaceURL = groupInfo.FaceURL
pbchatLog.GroupMemberCount = groupInfo.MemberCount // Reflects actual member count
@@ -200,5 +199,5 @@ func (m *msgServer) SearchMessage(ctx context.Context, req *msg.SearchMessageReq
}
func (m *msgServer) GetServerTime(ctx context.Context, _ *msg.GetServerTimeReq) (*msg.GetServerTimeResp, error) {
- return &msg.GetServerTimeResp{ServerTime: utils.GetCurrentTimestampByMill()}, nil
+ return &msg.GetServerTimeResp{ServerTime: timeutil.GetCurrentTimestampByMill()}, nil
}
diff --git a/internal/rpc/msg/utils.go b/internal/rpc/msg/utils.go
index 48665562ad..69b4d0bf6d 100644
--- a/internal/rpc/msg/utils.go
+++ b/internal/rpc/msg/utils.go
@@ -15,34 +15,13 @@
package msg
import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/errs"
"github.com/redis/go-redis/v9"
"go.mongodb.org/mongo-driver/mongo"
)
-func isMessageHasReadEnabled(msgData *sdkws.MsgData, config *config.GlobalConfig) bool {
- switch {
- case msgData.ContentType == constant.HasReadReceipt && msgData.SessionType == constant.SingleChatType:
- if config.SingleMessageHasReadReceiptEnable {
- return true
- } else {
- return false
- }
- case msgData.ContentType == constant.HasReadReceipt && msgData.SessionType == constant.SuperGroupChatType:
- if config.GroupMessageHasReadReceiptEnable {
- return true
- } else {
- return false
- }
- }
- return true
-}
-
func IsNotFound(err error) bool {
- switch utils.Unwrap(err) {
+ switch errs.Unwrap(err) {
case redis.Nil, mongo.ErrNoDocuments:
return true
default:
diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go
index 5bfd7013ea..33879bfe7b 100644
--- a/internal/rpc/msg/verify.go
+++ b/internal/rpc/msg/verify.go
@@ -16,16 +16,18 @@ package msg
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/encrypt"
+ "github.com/openimsdk/tools/utils/timeutil"
"math/rand"
"strconv"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
)
var ExcludeContentType = []int{constant.HasReadReceipt}
@@ -50,10 +52,7 @@ type MessageRevoked struct {
func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgReq) error {
switch data.MsgData.SessionType {
case constant.SingleChatType:
- if len(m.config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, m.config.Manager.UserID) {
- return nil
- }
- if utils.IsContain(data.MsgData.SendID, m.config.IMAdmin.UserID) {
+ if datautil.Contain(data.MsgData.SendID, m.config.Share.IMAdminUserID...) {
return nil
}
if data.MsgData.ContentType <= constant.NotificationEnd &&
@@ -65,35 +64,33 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
return err
}
if black {
- return errs.ErrBlockedByPeer.Wrap()
+ return servererrs.ErrBlockedByPeer.Wrap()
}
- if m.config.MessageVerify.FriendVerify != nil && *m.config.MessageVerify.FriendVerify {
+ if m.config.RpcConfig.FriendVerify {
friend, err := m.FriendLocalCache.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID)
if err != nil {
return err
}
if !friend {
- return errs.ErrNotPeersFriend.Wrap()
+ return servererrs.ErrNotPeersFriend.Wrap()
}
return nil
}
return nil
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
groupInfo, err := m.GroupLocalCache.GetGroupInfo(ctx, data.MsgData.GroupID)
if err != nil {
return err
}
if groupInfo.Status == constant.GroupStatusDismissed &&
data.MsgData.ContentType != constant.GroupDismissedNotification {
- return errs.ErrDismissedAlready.Wrap()
+ return servererrs.ErrDismissedAlready.Wrap()
}
if groupInfo.GroupType == constant.SuperGroup {
return nil
}
- if len(m.config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, m.config.Manager.UserID) {
- return nil
- }
- if utils.IsContain(data.MsgData.SendID, m.config.IMAdmin.UserID) {
+
+ if datautil.Contain(data.MsgData.SendID, m.config.Share.IMAdminUserID...) {
return nil
}
if data.MsgData.ContentType <= constant.NotificationEnd &&
@@ -105,13 +102,13 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
return err
}
if _, ok := memberIDs[data.MsgData.SendID]; !ok {
- return errs.ErrNotInGroupYet.Wrap()
+ return servererrs.ErrNotInGroupYet.Wrap()
}
groupMemberInfo, err := m.GroupLocalCache.GetGroupMember(ctx, data.MsgData.GroupID, data.MsgData.SendID)
if err != nil {
if errs.ErrRecordNotFound.Is(err) {
- return errs.ErrNotInGroupYet.Wrap(err.Error())
+ return servererrs.ErrNotInGroupYet.WrapMsg(err.Error())
}
return err
}
@@ -119,10 +116,10 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
return nil
} else {
if groupMemberInfo.MuteEndTime >= time.Now().UnixMilli() {
- return errs.ErrMutedInGroup.Wrap()
+ return servererrs.ErrMutedInGroup.Wrap()
}
if groupInfo.Status == constant.GroupStatusMuted && groupMemberInfo.RoleLevel != constant.GroupAdmin {
- return errs.ErrMutedGroup.Wrap()
+ return servererrs.ErrMutedGroup.Wrap()
}
}
return nil
@@ -134,7 +131,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe
func (m *msgServer) encapsulateMsgData(msg *sdkws.MsgData) {
msg.ServerMsgID = GetMsgID(msg.SendID)
if msg.SendTime == 0 {
- msg.SendTime = utils.GetCurrentTimestampByMill()
+ msg.SendTime = timeutil.GetCurrentTimestampByMill()
}
switch msg.ContentType {
case constant.Text:
@@ -159,36 +156,30 @@ func (m *msgServer) encapsulateMsgData(msg *sdkws.MsgData) {
fallthrough
case constant.Quote:
case constant.Revoke:
- utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
case constant.HasReadReceipt:
- utils.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
case constant.Typing:
- utils.SetSwitchFromOptions(msg.Options, constant.IsHistory, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsPersistent, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsSenderSync, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
- utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsHistory, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsPersistent, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsSenderSync, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsSenderConversationUpdate, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false)
+ datautil.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false)
}
}
func GetMsgID(sendID string) string {
- t := time.Now().Format("2006-01-02 15:04:05")
- return utils.Md5(t + "-" + sendID + "-" + strconv.Itoa(rand.Int()))
+ t := timeutil.GetCurrentTimeFormatted()
+ return encrypt.Md5(t + "-" + sendID + "-" + strconv.Itoa(rand.Int()))
}
-func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
- ctx context.Context,
- userID, conversationID string,
- sessionType int,
- pb *msg.SendMsgReq,
-) (bool, error) {
- defer log.ZDebug(ctx, "modifyMessageByUserMessageReceiveOpt return")
+func (m *msgServer) modifyMessageByUserMessageReceiveOpt(ctx context.Context, userID, conversationID string, sessionType int, pb *msg.SendMsgReq) (bool, error) {
opt, err := m.UserLocalCache.GetUserGlobalMsgRecvOpt(ctx, userID)
if err != nil {
return false, err
@@ -201,10 +192,9 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
if pb.MsgData.Options == nil {
pb.MsgData.Options = make(map[string]bool, 10)
}
- utils.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
+ datautil.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
return true, nil
}
- // conversationID := utils.GetConversationIDBySessionType(conversationID, sessionType)
singleOpt, err := m.ConversationLocalCache.GetSingleConversationRecvMsgOpt(ctx, userID, conversationID)
if errs.ErrRecordNotFound.Is(err) {
return true, nil
@@ -215,7 +205,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
case constant.ReceiveMessage:
return true, nil
case constant.NotReceiveMessage:
- if utils.IsContainInt(int(pb.MsgData.ContentType), ExcludeContentType) {
+ if datautil.Contain(int(pb.MsgData.ContentType), ExcludeContentType...) {
return true, nil
}
return false, nil
@@ -223,7 +213,7 @@ func (m *msgServer) modifyMessageByUserMessageReceiveOpt(
if pb.MsgData.Options == nil {
pb.MsgData.Options = make(map[string]bool, 10)
}
- utils.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
+ datautil.SetSwitchFromOptions(pb.MsgData.Options, constant.IsOfflinePush, false)
return true, nil
}
return true, nil
diff --git a/internal/rpc/third/log.go b/internal/rpc/third/log.go
index 97428254c7..7712851ed9 100644
--- a/internal/rpc/third/log.go
+++ b/internal/rpc/third/log.go
@@ -17,16 +17,16 @@ package third
import (
"context"
"crypto/rand"
- "fmt"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/third"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/utils"
- utils2 "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/stringutil"
)
func genLogID() string {
@@ -45,7 +45,7 @@ func genLogID() string {
}
func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq) (*third.UploadLogsResp, error) {
- var DBlogs []*relationtb.LogModel
+ var dbLogs []*relationtb.LogModel
userID := ctx.Value(constant.OpUserID).(string)
platform := constant.PlatformID2Name[int(req.Platform)]
for _, fileURL := range req.FileURLs {
@@ -70,11 +70,11 @@ func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq)
}
}
if log.LogID == "" {
- return nil, errs.ErrData.Wrap("LogModel id gen error")
+ return nil, servererrs.ErrData.WrapMsg("LogModel id gen error")
}
- DBlogs = append(DBlogs, &log)
+ dbLogs = append(dbLogs, &log)
}
- err := t.thirdDatabase.UploadLogs(ctx, DBlogs)
+ err := t.thirdDatabase.UploadLogs(ctx, dbLogs)
if err != nil {
return nil, err
}
@@ -82,7 +82,7 @@ func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq)
}
func (t *thirdServer) DeleteLogs(ctx context.Context, req *third.DeleteLogsReq) (*third.DeleteLogsResp, error) {
- if err := authverify.CheckAdmin(ctx, t.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
return nil, err
}
userID := ""
@@ -94,8 +94,8 @@ func (t *thirdServer) DeleteLogs(ctx context.Context, req *third.DeleteLogsReq)
for _, log := range logs {
logIDs = append(logIDs, log.LogID)
}
- if ids := utils2.Single(req.LogIDs, logIDs); len(ids) > 0 {
- return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("logIDs not found%#v", ids))
+ if ids := datautil.Single(req.LogIDs, logIDs); len(ids) > 0 {
+ return nil, errs.ErrRecordNotFound.WrapMsg("logIDs not found", "logIDs", ids)
}
err = t.thirdDatabase.DeleteLogs(ctx, req.LogIDs, userID)
if err != nil {
@@ -110,7 +110,7 @@ func dbToPbLogInfos(logs []*relationtb.LogModel) []*third.LogInfo {
return &third.LogInfo{
Filename: log.FileName,
UserID: log.UserID,
- Platform: utils.StringToInt32(log.Platform),
+ Platform: stringutil.StringToInt32(log.Platform),
Url: log.Url,
CreateTime: log.CreateTime.UnixMilli(),
LogID: log.LogID,
@@ -119,11 +119,11 @@ func dbToPbLogInfos(logs []*relationtb.LogModel) []*third.LogInfo {
Ex: log.Ex,
}
}
- return utils.Slice(logs, db2pbForLogInfo)
+ return datautil.Slice(logs, db2pbForLogInfo)
}
func (t *thirdServer) SearchLogs(ctx context.Context, req *third.SearchLogsReq) (*third.SearchLogsResp, error) {
- if err := authverify.CheckAdmin(ctx, t.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
return nil, err
}
var (
@@ -131,7 +131,7 @@ func (t *thirdServer) SearchLogs(ctx context.Context, req *third.SearchLogsReq)
userIDs []string
)
if req.StartTime > req.EndTime {
- return nil, errs.ErrArgs.Wrap("startTime>endTime")
+ return nil, errs.ErrArgs.WrapMsg("startTime>endTime")
}
if req.StartTime == 0 && req.EndTime == 0 {
t := time.Date(2019, time.January, 1, 0, 0, 0, 0, time.UTC)
diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go
index f79b73a993..21d982268e 100644
--- a/internal/rpc/third/s3.go
+++ b/internal/rpc/third/s3.go
@@ -23,15 +23,16 @@ import (
"strconv"
"time"
- "github.com/OpenIMSDK/protocol/third"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
"github.com/google/uuid"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/s3"
+ "github.com/openimsdk/tools/s3/cont"
+ "github.com/openimsdk/tools/utils/datautil"
)
func (t *thirdServer) PartLimit(ctx context.Context, req *third.PartLimitReq) (*third.PartLimitResp, error) {
@@ -52,7 +53,6 @@ func (t *thirdServer) PartSize(ctx context.Context, req *third.PartSizeReq) (*th
}
func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.InitiateMultipartUploadReq) (*third.InitiateMultipartUploadResp, error) {
- defer log.ZDebug(ctx, "return")
if err := t.checkUploadName(ctx, req.Name); err != nil {
return nil, err
}
@@ -74,7 +74,7 @@ func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.In
return nil, err
}
return &third.InitiateMultipartUploadResp{
- Url: t.apiAddress(obj.Name),
+ Url: t.apiAddress(req.UrlPrefix, obj.Name),
}, nil
}
return nil, err
@@ -107,8 +107,7 @@ func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.In
}
func (t *thirdServer) AuthSign(ctx context.Context, req *third.AuthSignReq) (*third.AuthSignResp, error) {
- defer log.ZDebug(ctx, "return")
- partNumbers := utils.Slice(req.PartNumbers, func(partNumber int32) int { return int(partNumber) })
+ partNumbers := datautil.Slice(req.PartNumbers, func(partNumber int32) int { return int(partNumber) })
result, err := t.s3dataBase.AuthSign(ctx, req.UploadID, partNumbers)
if err != nil {
return nil, err
@@ -131,7 +130,6 @@ func (t *thirdServer) AuthSign(ctx context.Context, req *third.AuthSignReq) (*th
}
func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.CompleteMultipartUploadReq) (*third.CompleteMultipartUploadResp, error) {
- defer log.ZDebug(ctx, "return")
if err := t.checkUploadName(ctx, req.Name); err != nil {
return nil, err
}
@@ -153,7 +151,7 @@ func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.Co
return nil, err
}
return &third.CompleteMultipartUploadResp{
- Url: t.apiAddress(obj.Name),
+ Url: t.apiAddress(req.UrlPrefix, obj.Name),
}, nil
}
@@ -169,7 +167,7 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
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")
+ return nil, errs.ErrArgs.WrapMsg("invalid query type")
}
}
expireTime, rawURL, err := t.s3dataBase.AccessURL(ctx, req.Name, t.defaultExpire, opt)
@@ -184,10 +182,10 @@ func (t *thirdServer) AccessURL(ctx context.Context, req *third.AccessURLReq) (*
func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateFormDataReq) (*third.InitiateFormDataResp, error) {
if req.Name == "" {
- return nil, errs.ErrArgs.Wrap("name is empty")
+ return nil, errs.ErrArgs.WrapMsg("name is empty")
}
if req.Size <= 0 {
- return nil, errs.ErrArgs.Wrap("size must be greater than 0")
+ return nil, errs.ErrArgs.WrapMsg("size must be greater than 0")
}
if err := t.checkUploadName(ctx, req.Name); err != nil {
return nil, err
@@ -209,7 +207,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
}
uid, err := uuid.NewRandom()
if err != nil {
- return nil, errs.Wrap(err, "uuid NewRandom failed")
+ return nil, errs.WrapMsg(err, "uuid NewRandom failed")
}
if key == "" {
date := time.Now().Format("20060102")
@@ -224,7 +222,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
}
mateData, err := json.Marshal(&mate)
if err != nil {
- return nil, errs.Wrap(err, "marshal failed")
+ return nil, errs.WrapMsg(err, "marshal failed")
}
resp, err := t.s3dataBase.FormData(ctx, key, req.Size, req.ContentType, duration)
if err != nil {
@@ -237,7 +235,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
Header: toPbMapArray(resp.Header),
FormData: resp.FormData,
Expires: resp.Expires.UnixMilli(),
- SuccessCodes: utils.Slice(resp.SuccessCodes, func(code int) int32 {
+ SuccessCodes: datautil.Slice(resp.SuccessCodes, func(code int) int32 {
return int32(code)
}),
}, nil
@@ -245,15 +243,15 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF
func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteFormDataReq) (*third.CompleteFormDataResp, error) {
if req.Id == "" {
- return nil, errs.ErrArgs.Wrap("id is empty")
+ return nil, errs.ErrArgs.WrapMsg("id is empty")
}
data, err := base64.RawStdEncoding.DecodeString(req.Id)
if err != nil {
- return nil, errs.ErrArgs.Wrap("invalid id " + err.Error())
+ return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
}
var mate FormDataMate
if err := json.Unmarshal(data, &mate); err != nil {
- return nil, errs.ErrArgs.Wrap("invalid id " + err.Error())
+ return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
}
if err := t.checkUploadName(ctx, mate.Name); err != nil {
return nil, err
@@ -263,7 +261,7 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF
return nil, err
}
if info.Size > 0 && info.Size != mate.Size {
- return nil, errs.ErrData.Wrap("file size mismatch")
+ return nil, servererrs.ErrData.WrapMsg("file size mismatch")
}
obj := &relation.ObjectModel{
Name: mate.Name,
@@ -278,11 +276,11 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF
if err := t.s3dataBase.SetObject(ctx, obj); err != nil {
return nil, err
}
- return &third.CompleteFormDataResp{Url: t.apiAddress(mate.Name)}, nil
+ return &third.CompleteFormDataResp{Url: t.apiAddress(req.UrlPrefix, mate.Name)}, nil
}
-func (t *thirdServer) apiAddress(name string) string {
- return t.apiURL + name
+func (t *thirdServer) apiAddress(prefix, name string) string {
+ return prefix + name
}
type FormDataMate struct {
diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go
index 928fb0f42c..9bf8cafa9e 100644
--- a/internal/rpc/third/third.go
+++ b/internal/rpc/third/third.go
@@ -17,73 +17,79 @@ package third
import (
"context"
"fmt"
- "net/url"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"time"
- "github.com/OpenIMSDK/protocol/third"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/s3"
+ "github.com/openimsdk/tools/s3/cos"
+ "github.com/openimsdk/tools/s3/minio"
+ "github.com/openimsdk/tools/s3/oss"
"google.golang.org/grpc"
)
-func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- mongo, err := unrelation.NewMongo(config)
+type thirdServer struct {
+ thirdDatabase controller.ThirdDatabase
+ s3dataBase controller.S3Database
+ userRpcClient rpcclient.UserRpcClient
+ defaultExpire time.Duration
+ config *Config
+}
+type Config struct {
+ RpcConfig config.Third
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ MinioConfig config.Minio
+ LocalCacheConfig config.LocalCache
+}
+
+func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error {
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
- logdb, err := mgo.NewLogMongo(mongo.GetDatabase(config.Mongo.Database))
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
- s3db, err := mgo.NewS3Mongo(mongo.GetDatabase(config.Mongo.Database))
+ logdb, err := mgo.NewLogMongo(mgocli.GetDB())
if err != nil {
return err
}
- apiURL := config.Object.ApiURL
- if apiURL == "" {
- return errs.Wrap(fmt.Errorf("api is empty"))
- }
- if _, err := url.Parse(config.Object.ApiURL); err != nil {
- return err
- }
- if apiURL[len(apiURL)-1] != '/' {
- apiURL += "/"
- }
- apiURL += "object/"
- rdb, err := cache.NewRedis(config)
+ s3db, err := mgo.NewS3Mongo(mgocli.GetDB())
if err != nil {
return err
}
// Select the oss method according to the profile policy
- enable := config.Object.Enable
+ enable := config.RpcConfig.Object.Enable
var o s3.Interface
switch enable {
case "minio":
- o, err = minio.NewMinio(cache.NewMinioCache(rdb), minio.Config(config.Object.Minio))
+ o, err = minio.NewMinio(ctx, cache.NewMinioCache(rdb), *config.MinioConfig.Build())
case "cos":
- o, err = cos.NewCos(cos.Config(config.Object.Cos))
+ o, err = cos.NewCos(*config.RpcConfig.Object.Cos.Build())
case "oss":
- o, err = oss.NewOSS(oss.Config(config.Object.Oss))
+ o, err = oss.NewOSS(*config.RpcConfig.Object.Oss.Build())
default:
err = fmt.Errorf("invalid object enable: %s", enable)
}
if err != nil {
return err
}
+ cache.InitLocalCache(&config.LocalCacheConfig)
third.RegisterThirdServer(server, &thirdServer{
- apiURL: apiURL,
- thirdDatabase: controller.NewThirdDatabase(cache.NewMsgCacheModel(rdb, config), logdb),
- userRpcClient: rpcclient.NewUserRpcClient(client, config),
+ thirdDatabase: controller.NewThirdDatabase(cache.NewThirdCache(rdb), logdb),
+ userRpcClient: rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID),
s3dataBase: controller.NewS3Database(rdb, o, s3db),
defaultExpire: time.Hour * 24 * 7,
config: config,
@@ -91,15 +97,6 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg
return nil
}
-type thirdServer struct {
- apiURL string
- thirdDatabase controller.ThirdDatabase
- s3dataBase controller.S3Database
- userRpcClient rpcclient.UserRpcClient
- defaultExpire time.Duration
- config *config.GlobalConfig
-}
-
func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) {
err = t.thirdDatabase.FcmUpdateToken(ctx, req.Account, int(req.PlatformID), req.FcmToken, req.ExpireTime)
if err != nil {
diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go
index d8491d3549..ac4be39689 100644
--- a/internal/rpc/third/tool.go
+++ b/internal/rpc/third/tool.go
@@ -16,15 +16,14 @@ package third
import (
"context"
- "errors"
"fmt"
"strings"
"unicode/utf8"
- "github.com/OpenIMSDK/protocol/third"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mcontext"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/mcontext"
)
func toPbMapArray(m map[string][]string) []*third.KeyValues {
@@ -43,21 +42,21 @@ func toPbMapArray(m map[string][]string) []*third.KeyValues {
func (t *thirdServer) checkUploadName(ctx context.Context, name string) error {
if name == "" {
- return errs.ErrArgs.Wrap("name is empty")
+ return errs.ErrArgs.WrapMsg("name is empty")
}
if name[0] == '/' {
- return errs.ErrArgs.Wrap("name cannot start with `/`")
+ return errs.ErrArgs.WrapMsg("name cannot start with `/`")
}
if err := checkValidObjectName(name); err != nil {
- return errs.ErrArgs.Wrap(err.Error())
+ return errs.ErrArgs.WrapMsg(err.Error())
}
opUserID := mcontext.GetOpUserID(ctx)
if opUserID == "" {
- return errs.ErrNoPermission.Wrap("opUserID is empty")
+ return errs.ErrNoPermission.WrapMsg("opUserID is empty")
}
- if !authverify.IsManagerUserID(opUserID, t.config) {
+ if !authverify.IsManagerUserID(opUserID, t.config.Share.IMAdminUserID) {
if !strings.HasPrefix(name, opUserID+"/") {
- return errs.ErrNoPermission.Wrap(fmt.Sprintf("name must start with `%s/`", opUserID))
+ return errs.ErrNoPermission.WrapMsg(fmt.Sprintf("name must start with `%s/`", opUserID))
}
}
return nil
@@ -65,21 +64,21 @@ func (t *thirdServer) checkUploadName(ctx context.Context, name string) error {
func checkValidObjectNamePrefix(objectName string) error {
if len(objectName) > 1024 {
- return errors.New("object name cannot be longer than 1024 characters")
+ return errs.New("object name cannot be longer than 1024 characters")
}
if !utf8.ValidString(objectName) {
- return errors.New("object name with non UTF-8 strings are not supported")
+ return errs.New("object name with non UTF-8 strings are not supported")
}
return nil
}
func checkValidObjectName(objectName string) error {
if strings.TrimSpace(objectName) == "" {
- return errors.New("object name cannot be empty")
+ return errs.New("object name cannot be empty")
}
return checkValidObjectNamePrefix(objectName)
}
func (t *thirdServer) IsManagerUserID(opUserID string) bool {
- return authverify.IsManagerUserID(opUserID, t.config)
+ return authverify.IsManagerUserID(opUserID, t.config.Share.IMAdminUserID)
}
diff --git a/internal/rpc/user/callback.go b/internal/rpc/user/callback.go
index 34f211973d..1bdf399d22 100644
--- a/internal/rpc/user/callback.go
+++ b/internal/rpc/user/callback.go
@@ -16,118 +16,101 @@ package user
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ "github.com/openimsdk/tools/utils/datautil"
- pbuser "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/utils"
cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+ pbuser "github.com/openimsdk/protocol/user"
)
-func CallbackBeforeUpdateUserInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoReq) error {
- if !globalConfig.Callback.CallbackBeforeUpdateUserInfo.Enable {
+func (s *userServer) webhookBeforeUpdateUserInfo(ctx context.Context, before *config.BeforeConfig, req *pbuser.UpdateUserInfoReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{
+ CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoCommand,
+ UserID: req.UserInfo.UserID,
+ FaceURL: &req.UserInfo.FaceURL,
+ Nickname: &req.UserInfo.Nickname,
+ }
+ resp := &cbapi.CallbackBeforeUpdateUserInfoResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+
+ datautil.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL)
+ datautil.NotNilReplace(&req.UserInfo.Ex, resp.Ex)
+ datautil.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
return nil
- }
- cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{
- CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoCommand,
- UserID: req.UserInfo.UserID,
- FaceURL: &req.UserInfo.FaceURL,
- Nickname: &req.UserInfo.Nickname,
- }
- resp := &cbapi.CallbackBeforeUpdateUserInfoResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil {
- return err
- }
- utils.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL)
- utils.NotNilReplace(&req.UserInfo.Ex, resp.Ex)
- utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname)
- return nil
+ })
}
-func CallbackAfterUpdateUserInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoReq) error {
- if !globalConfig.Callback.CallbackAfterUpdateUserInfo.Enable {
- return nil
- }
+
+func (s *userServer) webhookAfterUpdateUserInfo(ctx context.Context, after *config.AfterConfig, req *pbuser.UpdateUserInfoReq) {
cbReq := &cbapi.CallbackAfterUpdateUserInfoReq{
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoCommand,
UserID: req.UserInfo.UserID,
FaceURL: req.UserInfo.FaceURL,
Nickname: req.UserInfo.Nickname,
}
- resp := &cbapi.CallbackAfterUpdateUserInfoResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUpdateUserInfoResp{}, after)
}
-func CallbackBeforeUpdateUserInfoEx(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoExReq) error {
- if !globalConfig.Callback.CallbackBeforeUpdateUserInfoEx.Enable {
+
+func (s *userServer) webhookBeforeUpdateUserInfoEx(ctx context.Context, before *config.BeforeConfig, req *pbuser.UpdateUserInfoExReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{
+ CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand,
+ UserID: req.UserInfo.UserID,
+ FaceURL: req.UserInfo.FaceURL,
+ Nickname: req.UserInfo.Nickname,
+ }
+ resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{}
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
+
+ datautil.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL)
+ datautil.NotNilReplace(req.UserInfo.Ex, resp.Ex)
+ datautil.NotNilReplace(req.UserInfo.Nickname, resp.Nickname)
return nil
- }
- cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{
- CallbackCommand: cbapi.CallbackBeforeUpdateUserInfoExCommand,
- UserID: req.UserInfo.UserID,
- FaceURL: req.UserInfo.FaceURL,
- Nickname: req.UserInfo.Nickname,
- }
- resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfoEx); err != nil {
- return err
- }
- utils.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL)
- utils.NotNilReplace(req.UserInfo.Ex, resp.Ex)
- utils.NotNilReplace(req.UserInfo.Nickname, resp.Nickname)
- return nil
+ })
}
-func CallbackAfterUpdateUserInfoEx(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoExReq) error {
- if !globalConfig.Callback.CallbackAfterUpdateUserInfoEx.Enable {
- return nil
- }
+
+func (s *userServer) webhookAfterUpdateUserInfoEx(ctx context.Context, after *config.AfterConfig, req *pbuser.UpdateUserInfoExReq) {
cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{
CallbackCommand: cbapi.CallbackAfterUpdateUserInfoExCommand,
UserID: req.UserInfo.UserID,
FaceURL: req.UserInfo.FaceURL,
Nickname: req.UserInfo.Nickname,
}
- resp := &cbapi.CallbackAfterUpdateUserInfoExResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfoEx); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUpdateUserInfoExResp{}, after)
}
-func CallbackBeforeUserRegister(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UserRegisterReq) error {
- if !globalConfig.Callback.CallbackBeforeUserRegister.Enable {
- return nil
- }
- cbReq := &cbapi.CallbackBeforeUserRegisterReq{
- CallbackCommand: cbapi.CallbackBeforeUserRegisterCommand,
- Secret: req.Secret,
- Users: req.Users,
- }
+func (s *userServer) webhookBeforeUserRegister(ctx context.Context, before *config.BeforeConfig, req *pbuser.UserRegisterReq) error {
+ return webhook.WithCondition(ctx, before, func(ctx context.Context) error {
+ cbReq := &cbapi.CallbackBeforeUserRegisterReq{
+ CallbackCommand: cbapi.CallbackBeforeUserRegisterCommand,
+ Secret: req.Secret,
+ Users: req.Users,
+ }
- resp := &cbapi.CallbackBeforeUserRegisterResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil {
- return err
- }
- if len(resp.Users) != 0 {
- req.Users = resp.Users
- }
- return nil
-}
+ resp := &cbapi.CallbackBeforeUserRegisterResp{}
+
+ if err := s.webhookClient.SyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, resp, before); err != nil {
+ return err
+ }
-func CallbackAfterUserRegister(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UserRegisterReq) error {
- if !globalConfig.Callback.CallbackAfterUserRegister.Enable {
+ if len(resp.Users) != 0 {
+ req.Users = resp.Users
+ }
return nil
- }
+ })
+}
+
+func (s *userServer) webhookAfterUserRegister(ctx context.Context, after *config.AfterConfig, req *pbuser.UserRegisterReq) {
cbReq := &cbapi.CallbackAfterUserRegisterReq{
CallbackCommand: cbapi.CallbackAfterUserRegisterCommand,
Secret: req.Secret,
Users: req.Users,
}
- resp := &cbapi.CallbackAfterUserRegisterResp{}
- if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterUpdateUserInfo); err != nil {
- return err
- }
- return nil
+ s.webhookClient.AsyncPost(ctx, cbReq.GetCallbackCommand(), cbReq, &cbapi.CallbackAfterUserRegisterResp{}, after)
}
diff --git a/pkg/rpcclient/notification/user.go b/internal/rpc/user/notification.go
similarity index 72%
rename from pkg/rpcclient/notification/user.go
rename to internal/rpc/user/notification.go
index 610967fd55..348cd56281 100644
--- a/pkg/rpcclient/notification/user.go
+++ b/internal/rpc/user/notification.go
@@ -12,22 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package notification
+package user
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
)
type UserNotificationSender struct {
*rpcclient.NotificationSender
- getUsersInfo func(ctx context.Context, userIDs []string) ([]CommonUser, error)
+ getUsersInfo func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error)
// db controller
db controller.UserDatabase
}
@@ -44,7 +44,7 @@ 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) {
+ f := func(ctx context.Context, userIDs []string) (result []notification.CommonUser, err error) {
users, err := fn(ctx, userIDs)
if err != nil {
return nil, err
@@ -58,13 +58,9 @@ func WithUserFunc(
}
}
-func NewUserNotificationSender(
- config *config.GlobalConfig,
- msgRpcClient *rpcclient.MessageRpcClient,
- opts ...userNotificationSenderOptions,
-) *UserNotificationSender {
+func NewUserNotificationSender(config *Config, msgRpcClient *rpcclient.MessageRpcClient, opts ...userNotificationSenderOptions) *UserNotificationSender {
f := &UserNotificationSender{
- NotificationSender: rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient)),
+ NotificationSender: rpcclient.NewNotificationSender(&config.NotificationConfig, rpcclient.WithRpcClient(msgRpcClient)),
}
for _, opt := range opts {
opt(f)
@@ -101,24 +97,24 @@ func NewUserNotificationSender(
func (u *UserNotificationSender) UserStatusChangeNotification(
ctx context.Context,
tips *sdkws.UserStatusChangeTips,
-) error {
- return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserStatusChangeNotification, tips)
+) {
+ u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserStatusChangeNotification, tips)
}
func (u *UserNotificationSender) UserCommandUpdateNotification(
ctx context.Context,
tips *sdkws.UserCommandUpdateTips,
-) error {
- return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandUpdateNotification, tips)
+) {
+ u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandUpdateNotification, tips)
}
func (u *UserNotificationSender) UserCommandAddNotification(
ctx context.Context,
tips *sdkws.UserCommandAddTips,
-) error {
- return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandAddNotification, tips)
+) {
+ u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandAddNotification, tips)
}
func (u *UserNotificationSender) UserCommandDeleteNotification(
ctx context.Context,
tips *sdkws.UserCommandDeleteTips,
-) error {
- return u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandDeleteNotification, tips)
+) {
+ u.Notification(ctx, tips.FromUserID, tips.ToUserID, constant.UserCommandDeleteNotification, tips)
}
diff --git a/internal/rpc/user/statistics.go b/internal/rpc/user/statistics.go
index 42068f2ce6..709c0146b5 100644
--- a/internal/rpc/user/statistics.go
+++ b/internal/rpc/user/statistics.go
@@ -18,27 +18,24 @@ import (
"context"
"time"
- pbuser "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/errs"
+ pbuser "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/errs"
)
-func (s *userServer) UserRegisterCount(
- ctx context.Context,
- req *pbuser.UserRegisterCountReq,
-) (*pbuser.UserRegisterCountResp, error) {
+func (s *userServer) UserRegisterCount(ctx context.Context, req *pbuser.UserRegisterCountReq) (*pbuser.UserRegisterCountResp, error) {
if req.Start > req.End {
- return nil, errs.ErrArgs.Wrap("start > end")
+ return nil, errs.ErrArgs.WrapMsg("start > end")
}
- total, err := s.CountTotal(ctx, nil)
+ total, err := s.db.CountTotal(ctx, nil)
if err != nil {
return nil, err
}
start := time.UnixMilli(req.Start)
- before, err := s.CountTotal(ctx, &start)
+ before, err := s.db.CountTotal(ctx, &start)
if err != nil {
return nil, err
}
- count, err := s.CountRangeEverydayTotal(ctx, start, time.UnixMilli(req.End))
+ count, err := s.db.CountRangeEverydayTotal(ctx, start, time.UnixMilli(req.End))
if err != nil {
return nil, err
}
diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go
index 3d13cd7b6f..c453ac9f85 100644
--- a/internal/rpc/user/user.go
+++ b/internal/rpc/user/user.go
@@ -16,91 +16,100 @@ package user
import (
"context"
- "fmt"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/friend"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
+ "github.com/openimsdk/tools/db/redisutil"
"math/rand"
"strings"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- pbuser "github.com/OpenIMSDK/protocol/user"
- registry "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/pagination"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ pbuser "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
+ registry "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
"google.golang.org/grpc"
)
type userServer struct {
- controller.UserDatabase
- friendNotificationSender *notification.FriendNotificationSender
- userNotificationSender *notification.UserNotificationSender
+ db controller.UserDatabase
+ friendNotificationSender *friend.FriendNotificationSender
+ userNotificationSender *UserNotificationSender
friendRpcClient *rpcclient.FriendRpcClient
groupRpcClient *rpcclient.GroupRpcClient
RegisterCenter registry.SvcDiscoveryRegistry
- config *config.GlobalConfig
+ config *Config
+ webhookClient *webhook.Client
}
-func (s *userServer) GetGroupOnlineUser(ctx context.Context, req *pbuser.GetGroupOnlineUserReq) (*pbuser.GetGroupOnlineUserResp, error) {
- //TODO implement me
- panic("implement me")
+type Config struct {
+ RpcConfig config.User
+ RedisConfig config.Redis
+ MongodbConfig config.Mongo
+ KafkaConfig config.Kafka
+ ZookeeperConfig config.ZooKeeper
+ NotificationConfig config.Notification
+ Share config.Share
+ WebhooksConfig config.Webhooks
+ LocalCacheConfig config.LocalCache
}
-func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
- rdb, err := cache.NewRedis(config)
+func Start(ctx context.Context, config *Config, client registry.SvcDiscoveryRegistry, server *grpc.Server) error {
+ mgocli, err := mongoutil.NewMongoDB(ctx, config.MongodbConfig.Build())
if err != nil {
return err
}
- mongo, err := unrelation.NewMongo(config)
+ rdb, err := redisutil.NewRedisClient(ctx, config.RedisConfig.Build())
if err != nil {
return err
}
users := make([]*tablerelation.UserModel, 0)
- if len(config.IMAdmin.UserID) != len(config.IMAdmin.Nickname) {
- return errs.Wrap(fmt.Errorf("the count of ImAdmin.UserID is not equal to the count of ImAdmin.Nickname"))
- }
- for k, v := range config.IMAdmin.UserID {
- users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin})
+
+ for _, v := range config.Share.IMAdminUserID {
+ users = append(users, &tablerelation.UserModel{UserID: v, Nickname: v, AppMangerLevel: constant.AppNotificationAdmin})
}
- userDB, err := mgo.NewUserMongo(mongo.GetDatabase(config.Mongo.Database))
+ userDB, err := mgo.NewUserMongo(mgocli.GetDB())
if err != nil {
return err
}
- cache := cache.NewUserCacheRedis(rdb, userDB, cache.GetDefaultOpt())
- userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase(config.Mongo.Database))
- database := controller.NewUserDatabase(userDB, cache, tx.NewMongo(mongo.GetClient()), userMongoDB)
- friendRpcClient := rpcclient.NewFriendRpcClient(client, config)
- groupRpcClient := rpcclient.NewGroupRpcClient(client, config)
- msgRpcClient := rpcclient.NewMessageRpcClient(client, config)
+ userCache := cache.NewUserCacheRedis(rdb, &config.LocalCacheConfig, userDB, cache.GetDefaultOpt())
+ userMongoDB := mgo.NewUserMongoDriver(mgocli.GetDB())
+ database := controller.NewUserDatabase(userDB, userCache, mgocli.GetTx(), userMongoDB)
+ friendRpcClient := rpcclient.NewFriendRpcClient(client, config.Share.RpcRegisterName.Friend)
+ groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group)
+ msgRpcClient := rpcclient.NewMessageRpcClient(client, config.Share.RpcRegisterName.Msg)
+ cache.InitLocalCache(&config.LocalCacheConfig)
u := &userServer{
- UserDatabase: database,
+ db: database,
RegisterCenter: client,
friendRpcClient: &friendRpcClient,
groupRpcClient: &groupRpcClient,
- friendNotificationSender: notification.NewFriendNotificationSender(config, &msgRpcClient, notification.WithDBFunc(database.FindWithError)),
- userNotificationSender: notification.NewUserNotificationSender(config, &msgRpcClient, notification.WithUserFunc(database.FindWithError)),
+ friendNotificationSender: friend.NewFriendNotificationSender(&config.NotificationConfig, &msgRpcClient, friend.WithDBFunc(database.FindWithError)),
+ userNotificationSender: NewUserNotificationSender(config, &msgRpcClient, WithUserFunc(database.FindWithError)),
config: config,
+ webhookClient: webhook.NewWebhookClient(config.WebhooksConfig.URL),
}
pbuser.RegisterUserServer(server, u)
- return u.UserDatabase.InitOnce(context.Background(), users)
+ return u.db.InitOnce(context.Background(), users)
}
func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesignateUsersReq) (resp *pbuser.GetDesignateUsersResp, err error) {
resp = &pbuser.GetDesignateUsersResp{}
- users, err := s.FindWithError(ctx, req.UserIDs)
+ users, err := s.db.FindWithError(ctx, req.UserIDs)
if err != nil {
return nil, err
}
@@ -108,20 +117,26 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig
return resp, nil
}
+// deprecated:
+
+//UpdateUserInfo
+
func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) {
resp = &pbuser.UpdateUserInfoResp{}
- err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config)
+ err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
- if err := CallbackBeforeUpdateUserInfo(ctx, s.config, req); err != nil {
+
+ if err := s.webhookBeforeUpdateUserInfo(ctx, &s.config.WebhooksConfig.BeforeUpdateUserInfo, req); err != nil {
return nil, err
}
+
data := convert.UserPb2DBMap(req.UserInfo)
- if err := s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
+ if err := s.db.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
return nil, err
}
- _ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
+ s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
if err != nil {
return nil, err
@@ -134,9 +149,7 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
for _, friendID := range friends {
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
}
- if err = CallbackAfterUpdateUserInfo(ctx, s.config, req); err != nil {
- return nil, err
- }
+ s.webhookAfterUpdateUserInfo(ctx, &s.config.WebhooksConfig.AfterUpdateUserInfo, req)
if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
return nil, err
}
@@ -144,19 +157,18 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI
}
func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (resp *pbuser.UpdateUserInfoExResp, err error) {
resp = &pbuser.UpdateUserInfoExResp{}
- err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config)
+ err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
-
- if err = CallbackBeforeUpdateUserInfoEx(ctx, s.config, req); err != nil {
+ if err = s.webhookBeforeUpdateUserInfoEx(ctx, &s.config.WebhooksConfig.BeforeUpdateUserInfoEx, req); err != nil {
return nil, err
}
data := convert.UserPb2DBMapEx(req.UserInfo)
- if err = s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
+ if err = s.db.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil {
return nil, err
}
- _ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
+ s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID)
friends, err := s.friendRpcClient.GetFriendIDs(ctx, req.UserInfo.UserID)
if err != nil {
return nil, err
@@ -169,9 +181,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse
for _, friendID := range friends {
s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID)
}
- if err := CallbackAfterUpdateUserInfoEx(ctx, s.config, req); err != nil {
- return nil, err
- }
+ s.webhookAfterUpdateUserInfoEx(ctx, &s.config.WebhooksConfig.AfterUpdateUserInfoEx, req)
if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil {
return nil, err
}
@@ -179,12 +189,12 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse
}
func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.SetGlobalRecvMessageOptReq) (resp *pbuser.SetGlobalRecvMessageOptResp, err error) {
resp = &pbuser.SetGlobalRecvMessageOptResp{}
- if _, err := s.FindWithError(ctx, []string{req.UserID}); err != nil {
+ if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
return nil, err
}
m := make(map[string]any, 1)
m["global_recv_msg_opt"] = req.GlobalRecvMsgOpt
- if err := s.UpdateByMap(ctx, req.UserID, m); err != nil {
+ if err := s.db.UpdateByMap(ctx, req.UserID, m); err != nil {
return nil, err
}
s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserID)
@@ -193,14 +203,14 @@ func (s *userServer) SetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Se
func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckReq) (resp *pbuser.AccountCheckResp, err error) {
resp = &pbuser.AccountCheckResp{}
- if utils.Duplicate(req.CheckUserIDs) {
- return nil, errs.ErrArgs.Wrap("userID repeated")
+ if datautil.Duplicate(req.CheckUserIDs) {
+ return nil, errs.ErrArgs.WrapMsg("userID repeated")
}
- err = authverify.CheckAdmin(ctx, s.config)
+ err = authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
- users, err := s.Find(ctx, req.CheckUserIDs)
+ users, err := s.db.Find(ctx, req.CheckUserIDs)
if err != nil {
return nil, err
}
@@ -222,13 +232,13 @@ func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckR
func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPaginationUsersReq) (resp *pbuser.GetPaginationUsersResp, err error) {
if req.UserID == "" && req.NickName == "" {
- total, users, err := s.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
+ total, users, err := s.db.PageFindUser(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.Pagination)
if err != nil {
return nil, err
}
return &pbuser.GetPaginationUsersResp{Total: int32(total), Users: convert.UsersDB2Pb(users)}, err
} else {
- total, users, err := s.PageFindUserWithKeyword(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.UserID, req.NickName, req.Pagination)
+ total, users, err := s.db.PageFindUserWithKeyword(ctx, constant.IMOrdinaryUser, constant.AppOrdinaryUsers, req.UserID, req.NickName, req.Pagination)
if err != nil {
return nil, err
}
@@ -241,33 +251,33 @@ func (s *userServer) GetPaginationUsers(ctx context.Context, req *pbuser.GetPagi
func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterReq) (resp *pbuser.UserRegisterResp, err error) {
resp = &pbuser.UserRegisterResp{}
if len(req.Users) == 0 {
- return nil, errs.ErrArgs.Wrap("users is empty")
+ return nil, errs.ErrArgs.WrapMsg("users is empty")
}
- if req.Secret != s.config.Secret {
- log.ZDebug(ctx, "UserRegister", s.config.Secret, req.Secret)
- return nil, errs.ErrNoPermission.Wrap("secret invalid")
+ if req.Secret != s.config.Share.Secret {
+ log.ZDebug(ctx, "UserRegister", s.config.Share.Secret, req.Secret)
+ return nil, errs.ErrNoPermission.WrapMsg("secret invalid")
}
- if utils.DuplicateAny(req.Users, func(e *sdkws.UserInfo) string { return e.UserID }) {
- return nil, errs.ErrArgs.Wrap("userID repeated")
+ if datautil.DuplicateAny(req.Users, func(e *sdkws.UserInfo) string { return e.UserID }) {
+ return nil, errs.ErrArgs.WrapMsg("userID repeated")
}
userIDs := make([]string, 0)
for _, user := range req.Users {
if user.UserID == "" {
- return nil, errs.ErrArgs.Wrap("userID is empty")
+ return nil, errs.ErrArgs.WrapMsg("userID is empty")
}
if strings.Contains(user.UserID, ":") {
- return nil, errs.ErrArgs.Wrap("userID contains ':' is invalid userID")
+ return nil, errs.ErrArgs.WrapMsg("userID contains ':' is invalid userID")
}
userIDs = append(userIDs, user.UserID)
}
- exist, err := s.IsExist(ctx, userIDs)
+ exist, err := s.db.IsExist(ctx, userIDs)
if err != nil {
return nil, err
}
if exist {
- return nil, errs.ErrRegisteredAlready.Wrap("userID registered already")
+ return nil, servererrs.ErrRegisteredAlready.WrapMsg("userID registered already")
}
- if err := CallbackBeforeUserRegister(ctx, s.config, req); err != nil {
+ if err := s.webhookBeforeUserRegister(ctx, &s.config.WebhooksConfig.BeforeUserRegister, req); err != nil {
return nil, err
}
now := time.Now()
@@ -283,18 +293,16 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR
GlobalRecvMsgOpt: user.GlobalRecvMsgOpt,
})
}
- if err := s.Create(ctx, users); err != nil {
+ if err := s.db.Create(ctx, users); err != nil {
return nil, err
}
- if err := CallbackAfterUserRegister(ctx, s.config, req); err != nil {
- return nil, err
- }
+ s.webhookAfterUserRegister(ctx, &s.config.WebhooksConfig.AfterUserRegister, req)
return resp, nil
}
func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.GetGlobalRecvMessageOptReq) (resp *pbuser.GetGlobalRecvMessageOptResp, err error) {
- user, err := s.FindWithError(ctx, []string{req.UserID})
+ user, err := s.db.FindWithError(ctx, []string{req.UserID})
if err != nil {
return nil, err
}
@@ -303,7 +311,7 @@ func (s *userServer) GetGlobalRecvMessageOpt(ctx context.Context, req *pbuser.Ge
// GetAllUserID Get user account by page.
func (s *userServer) GetAllUserID(ctx context.Context, req *pbuser.GetAllUserIDReq) (resp *pbuser.GetAllUserIDResp, err error) {
- total, userIDs, err := s.UserDatabase.GetAllUserID(ctx, req.Pagination)
+ total, userIDs, err := s.db.GetAllUserID(ctx, req.Pagination)
if err != nil {
return nil, err
}
@@ -313,18 +321,18 @@ func (s *userServer) GetAllUserID(ctx context.Context, req *pbuser.GetAllUserIDR
// SubscribeOrCancelUsersStatus Subscribe online or cancel online users.
func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbuser.SubscribeOrCancelUsersStatusReq) (resp *pbuser.SubscribeOrCancelUsersStatusResp, err error) {
if req.Genre == constant.SubscriberUser {
- err = s.UserDatabase.SubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
+ err = s.db.SubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
if err != nil {
return nil, err
}
var status []*pbuser.OnlineStatus
- status, err = s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
+ status, err = s.db.GetUserStatus(ctx, req.UserIDs)
if err != nil {
return nil, err
}
return &pbuser.SubscribeOrCancelUsersStatusResp{StatusList: status}, nil
} else if req.Genre == constant.Unsubscribe {
- err = s.UserDatabase.UnsubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
+ err = s.db.UnsubscribeUsersStatus(ctx, req.UserID, req.UserIDs)
if err != nil {
return nil, err
}
@@ -335,7 +343,7 @@ func (s *userServer) SubscribeOrCancelUsersStatus(ctx context.Context, req *pbus
// GetUserStatus Get the online status of the user.
func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatusReq) (resp *pbuser.GetUserStatusResp,
err error) {
- onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, req.UserIDs)
+ onlineStatusList, err := s.db.GetUserStatus(ctx, req.UserIDs)
if err != nil {
return nil, err
}
@@ -345,11 +353,11 @@ func (s *userServer) GetUserStatus(ctx context.Context, req *pbuser.GetUserStatu
// SetUserStatus Synchronize user's online status.
func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatusReq) (resp *pbuser.SetUserStatusResp,
err error) {
- err = s.UserDatabase.SetUserStatus(ctx, req.UserID, req.Status, req.PlatformID)
+ err = s.db.SetUserStatus(ctx, req.UserID, req.Status, req.PlatformID)
if err != nil {
return nil, err
}
- list, err := s.UserDatabase.GetSubscribedList(ctx, req.UserID)
+ list, err := s.db.GetSubscribedList(ctx, req.UserID)
if err != nil {
return nil, err
}
@@ -369,11 +377,11 @@ func (s *userServer) SetUserStatus(ctx context.Context, req *pbuser.SetUserStatu
// 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)
+ userList, err := s.db.GetAllSubscribeList(ctx, req.UserID)
if err != nil {
return nil, err
}
- onlineStatusList, err := s.UserDatabase.GetUserStatus(ctx, userList)
+ onlineStatusList, err := s.db.GetUserStatus(ctx, userList)
if err != nil {
return nil, err
}
@@ -382,7 +390,7 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context,
// ProcessUserCommandAdd user general function add.
func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) {
- err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
+ err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
@@ -395,8 +403,8 @@ func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.Proc
if req.Ex != nil {
value = req.Ex.Value
}
- // Assuming you have a method in s.UserDatabase to add a user command
- err = s.UserDatabase.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, value, ex)
+ // Assuming you have a method in s.db to add a user command
+ err = s.db.AddUserCommand(ctx, req.UserID, req.Type, req.Uuid, value, ex)
if err != nil {
return nil, err
}
@@ -404,21 +412,18 @@ func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.Proc
FromUserID: req.UserID,
ToUserID: req.UserID,
}
- err = s.userNotificationSender.UserCommandAddNotification(ctx, tips)
- if err != nil {
- return nil, err
- }
+ s.userNotificationSender.UserCommandAddNotification(ctx, tips)
return &pbuser.ProcessUserCommandAddResp{}, nil
}
// ProcessUserCommandDelete user general function delete.
func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) {
- err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
+ err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
- err = s.UserDatabase.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
+ err = s.db.DeleteUserCommand(ctx, req.UserID, req.Type, req.Uuid)
if err != nil {
return nil, err
}
@@ -426,17 +431,13 @@ func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.P
FromUserID: req.UserID,
ToUserID: req.UserID,
}
- err = s.userNotificationSender.UserCommandDeleteNotification(ctx, tips)
- if err != nil {
- return nil, err
- }
-
+ s.userNotificationSender.UserCommandDeleteNotification(ctx, tips)
return &pbuser.ProcessUserCommandDeleteResp{}, nil
}
// ProcessUserCommandUpdate user general function update.
func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) {
- err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
+ err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
@@ -450,8 +451,8 @@ func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.P
val["ex"] = req.Ex.Value
}
- // Assuming you have a method in s.UserDatabase to update a user command
- err = s.UserDatabase.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, val)
+ // Assuming you have a method in s.db to update a user command
+ err = s.db.UpdateUserCommand(ctx, req.UserID, req.Type, req.Uuid, val)
if err != nil {
return nil, err
}
@@ -459,21 +460,18 @@ func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.P
FromUserID: req.UserID,
ToUserID: req.UserID,
}
- err = s.userNotificationSender.UserCommandUpdateNotification(ctx, tips)
- if err != nil {
- return nil, err
- }
+ s.userNotificationSender.UserCommandUpdateNotification(ctx, tips)
return &pbuser.ProcessUserCommandUpdateResp{}, nil
}
func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) {
- err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
+ err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
// Fetch user commands from the database
- commands, err := s.UserDatabase.GetUserCommands(ctx, req.UserID, req.Type)
+ commands, err := s.db.GetUserCommands(ctx, req.UserID, req.Type)
if err != nil {
return nil, err
}
@@ -497,12 +495,12 @@ func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.Proc
}
func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) {
- err := authverify.CheckAccessV3(ctx, req.UserID, s.config)
+ err := authverify.CheckAccessV3(ctx, req.UserID, s.config.Share.IMAdminUserID)
if err != nil {
return nil, err
}
// Fetch user commands from the database
- commands, err := s.UserDatabase.GetAllUserCommands(ctx, req.UserID)
+ commands, err := s.db.GetAllUserCommands(ctx, req.UserID)
if err != nil {
return nil, err
}
@@ -526,14 +524,14 @@ func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.P
}
func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) {
- if err := authverify.CheckIMAdmin(ctx, s.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
if req.UserID == "" {
for i := 0; i < 20; i++ {
userId := s.genUserID()
- _, err := s.UserDatabase.FindWithError(ctx, []string{userId})
+ _, err := s.db.FindWithError(ctx, []string{userId})
if err == nil {
continue
}
@@ -541,12 +539,12 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
break
}
if req.UserID == "" {
- return nil, errs.ErrInternalServer.Wrap("gen user id failed")
+ return nil, errs.ErrInternalServer.WrapMsg("gen user id failed")
}
} else {
- _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID})
+ _, err := s.db.FindWithError(ctx, []string{req.UserID})
if err == nil {
- return nil, errs.ErrArgs.Wrap("userID is used")
+ return nil, errs.ErrArgs.WrapMsg("userID is used")
}
}
@@ -557,7 +555,7 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
CreateTime: time.Now(),
AppMangerLevel: constant.AppNotificationAdmin,
}
- if err := s.UserDatabase.Create(ctx, []*tablerelation.UserModel{user}); err != nil {
+ if err := s.db.Create(ctx, []*tablerelation.UserModel{user}); err != nil {
return nil, err
}
@@ -569,11 +567,11 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add
}
func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) {
- if err := authverify.CheckIMAdmin(ctx, s.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
- if _, err := s.UserDatabase.FindWithError(ctx, []string{req.UserID}); err != nil {
+ if _, err := s.db.FindWithError(ctx, []string{req.UserID}); err != nil {
return nil, errs.ErrArgs.Wrap()
}
@@ -587,7 +585,7 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu
user["face_url"] = req.FaceURL
}
- if err := s.UserDatabase.UpdateByMap(ctx, req.UserID, user); err != nil {
+ if err := s.db.UpdateByMap(ctx, req.UserID, user); err != nil {
return nil, err
}
@@ -596,7 +594,7 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu
func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) {
// Check if user is an admin
- if err := authverify.CheckIMAdmin(ctx, s.config); err != nil {
+ if err := authverify.CheckAdmin(ctx, s.config.Share.IMAdminUserID); err != nil {
return nil, err
}
@@ -606,7 +604,7 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
// If a keyword is provided in the request
if req.Keyword != "" {
// Find users by keyword
- users, err = s.UserDatabase.Find(ctx, []string{req.Keyword})
+ users, err = s.db.Find(ctx, []string{req.Keyword})
if err != nil {
return nil, err
}
@@ -618,7 +616,7 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
}
// Find users by nickname if no users found by keyword
- users, err = s.UserDatabase.FindByNickname(ctx, req.Keyword)
+ users, err = s.db.FindByNickname(ctx, req.Keyword)
if err != nil {
return nil, err
}
@@ -627,7 +625,7 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
}
// If no keyword, find users with notification settings
- users, err = s.UserDatabase.FindNotification(ctx, constant.AppNotificationAdmin)
+ users, err = s.db.FindNotification(ctx, constant.AppNotificationAdmin)
if err != nil {
return nil, err
}
@@ -638,17 +636,17 @@ func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.
func (s *userServer) GetNotificationAccount(ctx context.Context, req *pbuser.GetNotificationAccountReq) (*pbuser.GetNotificationAccountResp, error) {
if req.UserID == "" {
- return nil, errs.ErrArgs.Wrap("userID is empty")
+ return nil, errs.ErrArgs.WrapMsg("userID is empty")
}
- user, err := s.UserDatabase.GetUserByID(ctx, req.UserID)
+ user, err := s.db.GetUserByID(ctx, req.UserID)
if err != nil {
- return nil, errs.ErrUserIDNotFound.Wrap()
+ return nil, servererrs.ErrUserIDNotFound.Wrap()
}
if user.AppMangerLevel == constant.AppAdmin || user.AppMangerLevel == constant.AppNotificationAdmin {
return &pbuser.GetNotificationAccountResp{}, nil
}
- return nil, errs.ErrNoPermission.Wrap("notification messages cannot be sent for this ID")
+ return nil, errs.ErrNoPermission.WrapMsg("notification messages cannot be sent for this ID")
}
func (s *userServer) genUserID() string {
@@ -670,7 +668,7 @@ func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pag
accounts := make([]*pbuser.NotificationAccountInfo, 0)
var total int64
for _, v := range users {
- if v.AppMangerLevel == constant.AppNotificationAdmin && !utils.IsContain(v.UserID, s.config.IMAdmin.UserID) {
+ if v.AppMangerLevel == constant.AppNotificationAdmin && !datautil.Contain(v.UserID, s.config.Share.IMAdminUserID...) {
temp := &pbuser.NotificationAccountInfo{
UserID: v.UserID,
FaceURL: v.FaceURL,
@@ -681,7 +679,7 @@ func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pag
}
}
- notificationAccounts := utils.Paginate(accounts, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
+ notificationAccounts := datautil.Paginate(accounts, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
return &pbuser.SearchNotificationAccountResp{Total: total, NotificationAccounts: notificationAccounts}
}
diff --git a/internal/tools/conversation.go b/internal/tools/conversation.go
deleted file mode 100644
index b555a33614..0000000000
--- a/internal/tools/conversation.go
+++ /dev/null
@@ -1,150 +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 tools
-
-import (
- "context"
- "math/rand"
- "time"
-
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
-)
-
-//func (c *MsgTool) ConversationsDestructMsgs() {
-// log.ZInfo(context.Background(), "start msg destruct cron task")
-// ctx := mcontext.NewCtx(utils.GetSelfFuncName())
-// conversations, err := c.conversationDatabase.GetConversationIDsNeedDestruct(ctx)
-// if err != nil {
-// log.ZError(ctx, "get conversation id need destruct failed", err)
-// return
-// }
-// log.ZDebug(context.Background(), "nums conversations need destruct", "nums", len(conversations))
-// for _, conversation := range conversations {
-// ctx = mcontext.NewCtx(utils.GetSelfFuncName() + "-" + utils.OperationIDGenerator() + "-" + conversation.ConversationID + "-" + conversation.OwnerUserID)
-// log.ZDebug(
-// ctx,
-// "UserMsgsDestruct",
-// "conversationID",
-// conversation.ConversationID,
-// "ownerUserID",
-// conversation.OwnerUserID,
-// "msgDestructTime",
-// conversation.MsgDestructTime,
-// "lastMsgDestructTime",
-// conversation.LatestMsgDestructTime,
-// )
-// now := time.Now()
-// seqs, err := c.msgDatabase.UserMsgsDestruct(ctx, conversation.OwnerUserID, conversation.ConversationID, conversation.MsgDestructTime, conversation.LatestMsgDestructTime)
-// if err != nil {
-// log.ZError(ctx, "user msg destruct failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
-// continue
-// }
-// if len(seqs) > 0 {
-// if err := c.conversationDatabase.UpdateUsersConversationField(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]interface{}{"latest_msg_destruct_time": now}); err
-// != nil {
-// log.ZError(ctx, "updateUsersConversationField failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
-// continue
-// }
-// if err := c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs); err != nil {
-// log.ZError(ctx, "userDeleteMsgsNotification failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
-// }
-// }
-// }
-//}
-
-func (c *MsgTool) ConversationsDestructMsgs() {
- log.ZInfo(context.Background(), "start msg destruct cron task")
- ctx := mcontext.NewCtx(utils.GetSelfFuncName())
- num, err := c.conversationDatabase.GetAllConversationIDsNumber(ctx)
- if err != nil {
- log.ZError(ctx, "GetAllConversationIDsNumber failed", err)
- return
- }
- const batchNum = 50
- log.ZDebug(ctx, "GetAllConversationIDsNumber", "num", num)
- if num == 0 {
- return
- }
- count := int(num/batchNum + num/batchNum/2)
- if count < 1 {
- count = 1
- }
- maxPage := 1 + num/batchNum
- if num%batchNum != 0 {
- maxPage++
- }
- for i := 0; i < count; i++ {
- pageNumber := rand.Int63() % maxPage
- pagination := &sdkws.RequestPagination{
- PageNumber: int32(pageNumber),
- ShowNumber: batchNum,
- }
- conversationIDs, err := c.conversationDatabase.PageConversationIDs(ctx, pagination)
- if err != nil {
- log.ZError(ctx, "PageConversationIDs failed", err, "pageNumber", pageNumber)
- continue
- }
- log.ZError(ctx, "PageConversationIDs failed", err, "pageNumber", pageNumber, "conversationIDsNum", len(conversationIDs), "conversationIDs", conversationIDs)
- if len(conversationIDs) == 0 {
- continue
- }
- conversations, err := c.conversationDatabase.GetConversationsByConversationID(ctx, conversationIDs)
- if err != nil {
- log.ZError(ctx, "GetConversationsByConversationID failed", err, "conversationIDs", conversationIDs)
- continue
- }
- temp := make([]*relation.ConversationModel, 0, len(conversations))
- for i, conversation := range conversations {
- if conversation.IsMsgDestruct && conversation.MsgDestructTime != 0 && (time.Now().Unix() > (conversation.MsgDestructTime+conversation.LatestMsgDestructTime.Unix()+8*60*60)) ||
- conversation.LatestMsgDestructTime.IsZero() {
- temp = append(temp, conversations[i])
- }
- }
- for _, conversation := range temp {
- ctx = mcontext.NewCtx(utils.GetSelfFuncName() + "-" + utils.OperationIDGenerator() + "-" + conversation.ConversationID + "-" + conversation.OwnerUserID)
- log.ZDebug(
- ctx,
- "UserMsgsDestruct",
- "conversationID",
- conversation.ConversationID,
- "ownerUserID",
- conversation.OwnerUserID,
- "msgDestructTime",
- conversation.MsgDestructTime,
- "lastMsgDestructTime",
- conversation.LatestMsgDestructTime,
- )
- now := time.Now()
- seqs, err := c.msgDatabase.UserMsgsDestruct(ctx, conversation.OwnerUserID, conversation.ConversationID, conversation.MsgDestructTime, conversation.LatestMsgDestructTime)
- if err != nil {
- log.ZError(ctx, "user msg destruct failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
- continue
- }
- if len(seqs) > 0 {
- if err := c.conversationDatabase.UpdateUsersConversationField(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]any{"latest_msg_destruct_time": now}); err != nil {
- log.ZError(ctx, "updateUsersConversationField failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
- continue
- }
- if err := c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs); err != nil {
- log.ZError(ctx, "userDeleteMsgsNotification failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID)
- }
- }
- }
- }
-}
diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go
index 7535e9b96d..7161f55fc1 100644
--- a/internal/tools/cron_task.go
+++ b/internal/tools/cron_task.go
@@ -17,92 +17,72 @@ package tools
import (
"context"
"fmt"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/mw"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
"os"
"os/signal"
"syscall"
"time"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/redis/go-redis/v9"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
"github.com/robfig/cron/v3"
)
-func StartTask(config *config.GlobalConfig) error {
- fmt.Println("cron task start, config", config.ChatRecordsClearTime)
-
- msgTool, err := InitMsgTool(config)
- if err != nil {
- return err
- }
-
- msgTool.convertTools()
+type CronTaskConfig struct {
+ CronTask config.CronTask
+ ZookeeperConfig config.ZooKeeper
+ Share config.Share
+}
- rdb, err := cache.NewRedis(config)
- if err != nil {
- return err
+func Start(ctx context.Context, config *CronTaskConfig) error {
+ log.CInfo(ctx, "CRON-TASK server is initializing", "chatRecordsClearTime", config.CronTask.ChatRecordsClearTime, "msgDestructTime", config.CronTask.RetainChatRecords)
+ if config.CronTask.RetainChatRecords < 1 {
+ return errs.New("msg destruct time must be greater than 1").Wrap()
}
-
- // register cron tasks
- var crontab = cron.New()
- fmt.Printf("Start chatRecordsClearTime cron task, cron config: %s\n", config.ChatRecordsClearTime)
- _, err = crontab.AddFunc(config.ChatRecordsClearTime, cronWrapFunc(config, rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq))
+ client, err := kdisc.NewDiscoveryRegister(&config.ZookeeperConfig, &config.Share)
if err != nil {
- return errs.Wrap(err)
+ return errs.WrapMsg(err, "failed to register discovery service")
}
-
- fmt.Printf("Start msgDestruct cron task, cron config: %s\n", config.MsgDestructTime)
- _, err = crontab.AddFunc(config.MsgDestructTime, cronWrapFunc(config, rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs))
+ client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()))
+ ctx, exitBy := context.WithCancelCause(context.Background())
+ ctx = mcontext.SetOpUserID(ctx, config.Share.IMAdminUserID[0])
+ conn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg)
if err != nil {
- return errs.Wrap(err, "cron_conversations_destruct_msgs")
- }
-
- // start crontab
- crontab.Start()
-
- sigs := make(chan os.Signal, 1)
- signal.Notify(sigs, syscall.SIGTERM)
- <-sigs
-
- // stop crontab, Wait for the running task to exit.
- ctx := crontab.Stop()
-
- select {
- case <-ctx.Done():
- // graceful exit
-
- case <-time.After(15 * time.Second):
- // forced exit on timeout
- }
-
- return nil
-}
-
-// netlock redis lock.
-func netlock(rdb redis.UniversalClient, key string, ttl time.Duration) bool {
- value := "used"
- ok, err := rdb.SetNX(context.Background(), key, value, ttl).Result() // nolint
- if err != nil {
- // when err is about redis server, return true.
- return false
+ return err
}
-
- return ok
-}
-
-func cronWrapFunc(config *config.GlobalConfig, rdb redis.UniversalClient, key string, fn func()) func() {
- enableCronLocker := config.EnableCronLocker
- return func() {
- // if don't enable cron-locker, call fn directly.
- if !enableCronLocker {
- fn()
- return
+ cli := msg.NewMsgClient(conn)
+ go func() {
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGTERM)
+ select {
+ case <-ctx.Done():
+ case s := <-sigs:
+ exitBy(fmt.Errorf("exit signal %s", s))
}
-
- // when acquire redis lock, call fn().
- if netlock(rdb, key, 5*time.Second) {
- fn()
+ }()
+ crontab := cron.New()
+ clearFunc := func() {
+ now := time.Now()
+ deltime := now.Add(-time.Hour * 24 * time.Duration(config.CronTask.RetainChatRecords))
+ ctx := mcontext.SetOperationID(ctx, fmt.Sprintf("cron_%d_%d", os.Getpid(), deltime.UnixMilli()))
+ log.ZInfo(ctx, "clear chat records", "deltime", deltime, "timestamp", deltime.UnixMilli())
+ if _, err := cli.ClearMsg(ctx, &msg.ClearMsgReq{Timestamp: deltime.UnixMilli()}); err != nil {
+ log.ZError(ctx, "cron clear chat records failed", err, "deltime", deltime, "cont", time.Since(now))
+ return
}
+ log.ZInfo(ctx, "cron clear chat records success", "deltime", deltime, "cont", time.Since(now))
+ }
+ if _, err := crontab.AddFunc(config.CronTask.ChatRecordsClearTime, clearFunc); err != nil {
+ return errs.Wrap(err)
}
+ log.ZInfo(ctx, "start cron task", "chatRecordsClearTime", config.CronTask.ChatRecordsClearTime)
+ crontab.Start()
+ <-ctx.Done()
+ return context.Cause(ctx)
}
diff --git a/internal/tools/cron_task_test.go b/internal/tools/cron_task_test.go
deleted file mode 100644
index 17346b1c51..0000000000
--- a/internal/tools/cron_task_test.go
+++ /dev/null
@@ -1,124 +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 tools
-
-import (
- "flag"
- "fmt"
- "math/rand"
- "os"
- "sync"
- "testing"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "gopkg.in/yaml.v3"
-
- "github.com/redis/go-redis/v9"
- "github.com/robfig/cron/v3"
- "github.com/stretchr/testify/assert"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-func TestDisLock(t *testing.T) {
- rdb := redis.NewClient(&redis.Options{})
- defer rdb.Close()
-
- assert.Equal(t, true, netlock(rdb, "cron-1", 1*time.Second))
-
- // if exists, get false
- assert.Equal(t, false, netlock(rdb, "cron-1", 1*time.Second))
-
- time.Sleep(2 * time.Second)
-
- // wait for key on timeout, get true
- assert.Equal(t, true, netlock(rdb, "cron-1", 2*time.Second))
-
- // set different key
- assert.Equal(t, true, netlock(rdb, "cron-2", 2*time.Second))
-}
-
-func TestCronWrapFunc(t *testing.T) {
- rdb := redis.NewClient(&redis.Options{})
- defer rdb.Close()
-
- once := sync.Once{}
- done := make(chan struct{}, 1)
- cb := func() {
- once.Do(func() {
- close(done)
- })
- }
-
- start := time.Now()
- key := fmt.Sprintf("cron-%v", rand.Int31())
- crontab := cron.New(cron.WithSeconds())
- crontab.AddFunc("*/1 * * * * *", cronWrapFunc(config.NewGlobalConfig(), rdb, key, cb))
- crontab.Start()
- <-done
-
- dur := time.Since(start)
- assert.LessOrEqual(t, dur.Seconds(), float64(2*time.Second))
- crontab.Stop()
-}
-
-func TestCronWrapFuncWithNetlock(t *testing.T) {
- conf, err := initCfg()
- if err != nil {
- panic(err)
- }
- conf.EnableCronLocker = true
- rdb := redis.NewClient(&redis.Options{})
- defer rdb.Close()
-
- done := make(chan string, 10)
-
- crontab := cron.New(cron.WithSeconds())
-
- key := fmt.Sprintf("cron-%v", rand.Int31())
- crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() {
- done <- "host1"
- }))
- crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() {
- done <- "host2"
- }))
- crontab.Start()
-
- time.Sleep(12 * time.Second)
- // the ttl of netlock is 5s, so expected value is 2.
- assert.Equal(t, len(done), 2)
-
- crontab.Stop()
-}
-
-func initCfg() (*config.GlobalConfig, error) {
- const (
- defaultCfgPath = "../../../../../config/config.yaml"
- )
-
- cfgPath := flag.String("c", defaultCfgPath, "Path to the configuration file")
- data, err := os.ReadFile(*cfgPath)
- if err != nil {
- return nil, errs.Wrap(err, "ReadFile unmarshal failed")
- }
-
- conf := config.NewGlobalConfig()
- err = yaml.Unmarshal(data, &conf)
- if err != nil {
- return nil, errs.Wrap(err, "InitConfig unmarshal failed")
- }
- return conf, nil
-}
diff --git a/internal/tools/msg.go b/internal/tools/msg.go
deleted file mode 100644
index 67c3895cb2..0000000000
--- a/internal/tools/msg.go
+++ /dev/null
@@ -1,232 +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 tools
-
-import (
- "context"
- "fmt"
- "math"
- "math/rand"
-
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/mw"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
- kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
- "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification"
- "github.com/redis/go-redis/v9"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
-)
-
-type MsgTool struct {
- msgDatabase controller.CommonMsgDatabase
- conversationDatabase controller.ConversationDatabase
- userDatabase controller.UserDatabase
- groupDatabase controller.GroupDatabase
- msgNotificationSender *notification.MsgNotificationSender
- Config *config.GlobalConfig
-}
-
-func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controller.UserDatabase,
- groupDatabase controller.GroupDatabase, conversationDatabase controller.ConversationDatabase,
- msgNotificationSender *notification.MsgNotificationSender, config *config.GlobalConfig,
-) *MsgTool {
- return &MsgTool{
- msgDatabase: msgDatabase,
- userDatabase: userDatabase,
- groupDatabase: groupDatabase,
- conversationDatabase: conversationDatabase,
- msgNotificationSender: msgNotificationSender,
- Config: config,
- }
-}
-
-func InitMsgTool(config *config.GlobalConfig) (*MsgTool, error) {
- rdb, err := cache.NewRedis(config)
- if err != nil {
- return nil, err
- }
- mongo, err := unrelation.NewMongo(config)
- if err != nil {
- return nil, err
- }
- discov, err := kdisc.NewDiscoveryRegister(config)
- if err != nil {
- return nil, err
- }
- discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
- userDB, err := mgo.NewUserMongo(mongo.GetDatabase(config.Mongo.Database))
- if err != nil {
- return nil, err
- }
- msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase(config.Mongo.Database), config)
- if err != nil {
- return nil, err
- }
- userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase(config.Mongo.Database))
- ctxTx := tx.NewMongo(mongo.GetClient())
- userDatabase := controller.NewUserDatabase(
- userDB,
- cache.NewUserCacheRedis(rdb, userDB, cache.GetDefaultOpt()),
- ctxTx,
- userMongoDB,
- )
- groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase(config.Mongo.Database))
- if err != nil {
- return nil, err
- }
- groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase(config.Mongo.Database))
- if err != nil {
- return nil, err
- }
- groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase(config.Mongo.Database))
- if err != nil {
- return nil, err
- }
- conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase(config.Mongo.Database))
- if err != nil {
- return nil, err
- }
- groupDatabase := controller.NewGroupDatabase(rdb, groupDB, groupMemberDB, groupRequestDB, ctxTx, nil)
- conversationDatabase := controller.NewConversationDatabase(
- conversationDB,
- cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB),
- ctxTx,
- )
- msgRpcClient := rpcclient.NewMessageRpcClient(discov, config)
- msgNotificationSender := notification.NewMsgNotificationSender(config, rpcclient.WithRpcClient(&msgRpcClient))
- msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase, msgNotificationSender, config)
- return msgTool, nil
-}
-
-//func (c *MsgTool) AllConversationClearMsgAndFixSeq() {
-// ctx := mcontext.NewCtx(utils.GetSelfFuncName())
-// log.ZInfo(ctx, "============================ start del cron task ============================")
-// conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
-// if err != nil {
-// log.ZError(ctx, "GetAllConversationIDs failed", err)
-// return
-// }
-// for _, conversationID := range conversationIDs {
-// conversationIDs = append(conversationIDs, utils.GetNotificationConversationIDByConversationID(conversationID))
-// }
-// c.ClearConversationsMsg(ctx, conversationIDs)
-// log.ZInfo(ctx, "============================ start del cron finished ============================")
-//}
-
-func (c *MsgTool) AllConversationClearMsgAndFixSeq() {
- ctx := mcontext.NewCtx(utils.GetSelfFuncName())
- log.ZInfo(ctx, "============================ start del cron task ============================")
- num, err := c.conversationDatabase.GetAllConversationIDsNumber(ctx)
- if err != nil {
- log.ZError(ctx, "GetAllConversationIDsNumber failed", err)
- return
- }
- const batchNum = 50
- log.ZDebug(ctx, "GetAllConversationIDsNumber", "num", num)
- if num == 0 {
- return
- }
- count := int(num/batchNum + num/batchNum/2)
- if count < 1 {
- count = 1
- }
- maxPage := 1 + num/batchNum
- if num%batchNum != 0 {
- maxPage++
- }
- for i := 0; i < count; i++ {
- pageNumber := rand.Int63() % maxPage
- pagination := &sdkws.RequestPagination{
- PageNumber: int32(pageNumber),
- ShowNumber: batchNum,
- }
- conversationIDs, err := c.conversationDatabase.PageConversationIDs(ctx, pagination)
- if err != nil {
- log.ZError(ctx, "PageConversationIDs failed", err, "pageNumber", pageNumber)
- continue
- }
- log.ZDebug(ctx, "PageConversationIDs failed", "pageNumber", pageNumber, "conversationIDsNum", len(conversationIDs), "conversationIDs", conversationIDs)
- if len(conversationIDs) == 0 {
- continue
- }
- c.ClearConversationsMsg(ctx, conversationIDs)
- }
- log.ZInfo(ctx, "============================ start del cron finished ============================")
-}
-
-func (c *MsgTool) ClearConversationsMsg(ctx context.Context, conversationIDs []string) {
- for _, conversationID := range conversationIDs {
- if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(c.Config.RetainChatRecords*24*60*60)); err != nil {
- log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID", conversationID, "DBRetainChatRecords", c.Config.RetainChatRecords)
- }
- if err := c.checkMaxSeq(ctx, conversationID); err != nil {
- log.ZError(ctx, "fixSeq failed", err, "conversationID", conversationID)
- }
- }
-}
-
-func (c *MsgTool) checkMaxSeqWithMongo(ctx context.Context, conversationID string, maxSeqCache int64) error {
- minSeqMongo, maxSeqMongo, err := c.msgDatabase.GetMongoMaxAndMinSeq(ctx, conversationID)
- if err != nil {
- return err
- }
- if math.Abs(float64(maxSeqMongo-maxSeqCache)) > 10 {
- err = fmt.Errorf("cache max seq and mongo max seq is diff > 10, maxSeqMongo:%d,minSeqMongo:%d,maxSeqCache:%d,conversationID:%s", maxSeqMongo, minSeqMongo, maxSeqCache, conversationID)
- return errs.Wrap(err)
- }
- return nil
-}
-
-func (c *MsgTool) checkMaxSeq(ctx context.Context, conversationID string) error {
- maxSeq, err := c.msgDatabase.GetMaxSeq(ctx, conversationID)
- if err != nil {
- if errs.Unwrap(err) == redis.Nil {
- return nil
- }
- return err
- }
- if err := c.checkMaxSeqWithMongo(ctx, conversationID, maxSeq); err != nil {
- return err
- }
- return nil
-}
-
-func (c *MsgTool) FixAllSeq(ctx context.Context) error {
- conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx)
- if err != nil {
- return err
- }
- for _, conversationID := range conversationIDs {
- conversationIDs = append(conversationIDs, utils.GetNotificationConversationIDByConversationID(conversationID))
- }
- for _, conversationID := range conversationIDs {
- if err := c.checkMaxSeq(ctx, conversationID); err != nil {
- log.ZWarn(ctx, "fixSeq failed", err, "conversationID", conversationID)
- }
- }
- fmt.Println("fix all seq finished")
- return nil
-}
diff --git a/internal/tools/msg_doc_convert.go b/internal/tools/msg_doc_convert.go
deleted file mode 100644
index eea1b69e8a..0000000000
--- a/internal/tools/msg_doc_convert.go
+++ /dev/null
@@ -1,46 +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 tools
-
-import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/openimsdk/open-im-server/v3/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, nil)
- 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)
-}
diff --git a/magefile.go b/magefile.go
new file mode 100644
index 0000000000..98ffa48f3f
--- /dev/null
+++ b/magefile.go
@@ -0,0 +1,43 @@
+//go:build mage
+// +build mage
+
+package main
+
+import (
+ "github.com/openimsdk/gomake/mageutil"
+ "os"
+ "strings"
+)
+
+var Default = Build
+
+func Build() {
+ platforms := os.Getenv("PLATFORMS")
+ if platforms == "" {
+ platforms = mageutil.DetectPlatform()
+ }
+
+ for _, platform := range strings.Split(platforms, " ") {
+ mageutil.CompileForPlatform(platform)
+ }
+
+ mageutil.PrintGreen("All binaries under cmd and tools were successfully compiled.")
+}
+
+func Start() {
+ mageutil.InitForSSC()
+ err := setMaxOpenFiles()
+ if err != nil {
+ mageutil.PrintRed("setMaxOpenFiles failed " + err.Error())
+ os.Exit(1)
+ }
+ mageutil.StartToolsAndServices()
+}
+
+func Stop() {
+ mageutil.StopAndCheckBinaries()
+}
+
+func Check() {
+ mageutil.CheckAndReportBinariesStatus()
+}
diff --git a/magefile_unix.go b/magefile_unix.go
new file mode 100644
index 0000000000..ff6b6de4e4
--- /dev/null
+++ b/magefile_unix.go
@@ -0,0 +1,20 @@
+//go:build mage && !windows
+// +build mage,!windows
+
+package main
+
+import (
+ "github.com/openimsdk/gomake/mageutil"
+ "syscall"
+)
+
+func setMaxOpenFiles() error {
+ var rLimit syscall.Rlimit
+ err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+ if err != nil {
+ return err
+ }
+ rLimit.Max = uint64(mageutil.MaxFileDescriptors)
+ rLimit.Cur = uint64(mageutil.MaxFileDescriptors)
+ return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
+}
diff --git a/magefile_windows.go b/magefile_windows.go
new file mode 100644
index 0000000000..7441bfd9ce
--- /dev/null
+++ b/magefile_windows.go
@@ -0,0 +1,8 @@
+//go:build mage
+// +build mage
+
+package main
+
+func setMaxOpenFiles() error {
+ return nil
+}
diff --git a/pkg/apistruct/doc.go b/pkg/apistruct/doc.go
index 2f14045844..6be41c9e53 100644
--- a/pkg/apistruct/doc.go
+++ b/pkg/apistruct/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/apistruct/manage.go b/pkg/apistruct/manage.go
index f9f542835a..e79b477222 100644
--- a/pkg/apistruct/manage.go
+++ b/pkg/apistruct/manage.go
@@ -15,7 +15,7 @@
package apistruct
import (
- sdkws "github.com/OpenIMSDK/protocol/sdkws"
+ sdkws "github.com/openimsdk/protocol/sdkws"
)
// SendMsg defines the structure for sending messages with various metadata.
diff --git a/pkg/apistruct/msg.go b/pkg/apistruct/msg.go
index d1ce427fc5..f4a9f884c4 100644
--- a/pkg/apistruct/msg.go
+++ b/pkg/apistruct/msg.go
@@ -29,6 +29,7 @@ type PictureElem struct {
BigPicture PictureBaseInfo `mapstructure:"bigPicture" validate:"required"`
SnapshotPicture PictureBaseInfo `mapstructure:"snapshotPicture" validate:"required"`
}
+
type SoundElem struct {
UUID string `mapstructure:"uuid"`
SoundPath string `mapstructure:"soundPath"`
@@ -36,6 +37,7 @@ type SoundElem struct {
DataSize int64 `mapstructure:"dataSize"`
Duration int64 `mapstructure:"duration" validate:"required,min=1"`
}
+
type VideoElem struct {
VideoPath string `mapstructure:"videoPath"`
VideoUUID string `mapstructure:"videoUUID"`
@@ -50,6 +52,7 @@ type VideoElem struct {
SnapshotWidth int32 `mapstructure:"snapshotWidth" validate:"required"`
SnapshotHeight int32 `mapstructure:"snapshotHeight" validate:"required"`
}
+
type FileElem struct {
FilePath string `mapstructure:"filePath"`
UUID string `mapstructure:"uuid"`
diff --git a/pkg/authverify/doc.go b/pkg/authverify/doc.go
index f9173a7084..14d63a4108 100644
--- a/pkg/authverify/doc.go
+++ b/pkg/authverify/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go
index 8127e08dfd..f1b377bad7 100644
--- a/pkg/authverify/token.go
+++ b/pkg/authverify/token.go
@@ -18,12 +18,10 @@ import (
"context"
"fmt"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/tokenverify"
- "github.com/OpenIMSDK/tools/utils"
"github.com/golang-jwt/jwt/v4"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
)
func Secret(secret string) jwt.Keyfunc {
@@ -32,63 +30,28 @@ func Secret(secret string) jwt.Keyfunc {
}
}
-func CheckAccessV3(ctx context.Context, ownerUserID string, config *config.GlobalConfig) (err error) {
+func CheckAccessV3(ctx context.Context, ownerUserID string, imAdminUserID []string) (err error) {
opUserID := mcontext.GetOpUserID(ctx)
- if len(config.Manager.UserID) > 0 && utils.IsContain(opUserID, config.Manager.UserID) {
- return nil
- }
- if utils.IsContain(opUserID, config.IMAdmin.UserID) {
+ if datautil.Contain(opUserID, imAdminUserID...) {
return nil
}
if opUserID == ownerUserID {
return nil
}
- return errs.ErrNoPermission.Wrap("ownerUserID", ownerUserID)
+ return servererrs.ErrNoPermission.WrapMsg("ownerUserID", ownerUserID)
}
-func IsAppManagerUid(ctx context.Context, config *config.GlobalConfig) bool {
- return (len(config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Manager.UserID)) ||
- utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID)
+func IsAppManagerUid(ctx context.Context, imAdminUserID []string) bool {
+ return datautil.Contain(mcontext.GetOpUserID(ctx), imAdminUserID...)
}
-func CheckAdmin(ctx context.Context, config *config.GlobalConfig) error {
- if len(config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Manager.UserID) {
+func CheckAdmin(ctx context.Context, imAdminUserID []string) error {
+ if datautil.Contain(mcontext.GetOpUserID(ctx), imAdminUserID...) {
return nil
}
- if utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID) {
- return nil
- }
- return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx)))
+ return servererrs.ErrNoPermission.WrapMsg(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx)))
}
-func CheckIMAdmin(ctx context.Context, config *config.GlobalConfig) error {
- if utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID) {
- return nil
- }
- if len(config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Manager.UserID) {
- return nil
- }
- return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not CheckIMAdmin userID", mcontext.GetOpUserID(ctx)))
-}
-
-func ParseRedisInterfaceToken(redisToken any, secret string) (*tokenverify.Claims, error) {
- return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret(secret))
-}
-
-func IsManagerUserID(opUserID string, config *config.GlobalConfig) bool {
- return (len(config.Manager.UserID) > 0 && utils.IsContain(opUserID, config.Manager.UserID)) || utils.IsContain(opUserID, config.IMAdmin.UserID)
-}
-
-func WsVerifyToken(token, userID, secret string, platformID int) error {
- claim, err := tokenverify.GetClaimFromToken(token, Secret(secret))
- if err != nil {
- return err
- }
- if claim.UserID != userID {
- return errs.ErrTokenInvalid.Wrap(fmt.Sprintf("token uid %s != userID %s", claim.UserID, userID))
- }
- if claim.PlatformID != platformID {
- return errs.ErrTokenInvalid.Wrap(fmt.Sprintf("token platform %d != %d", claim.PlatformID, platformID))
- }
- return nil
+func IsManagerUserID(opUserID string, imAdminUserID []string) bool {
+ return datautil.Contain(opUserID, imAdminUserID...)
}
diff --git a/pkg/callbackstruct/common.go b/pkg/callbackstruct/common.go
index c58b9e415e..d6714f5f20 100644
--- a/pkg/callbackstruct/common.go
+++ b/pkg/callbackstruct/common.go
@@ -14,7 +14,10 @@
package callbackstruct
-import "github.com/OpenIMSDK/tools/errs"
+import (
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/tools/errs"
+)
const (
Next = 1
@@ -61,7 +64,7 @@ type CommonCallbackResp struct {
}
func (c CommonCallbackResp) Parse() error {
- if c.ActionCode != errs.NoError || c.NextCode == Next {
+ if c.ActionCode == servererrs.NoError && c.NextCode == Next {
return errs.NewCodeError(int(c.ErrCode), c.ErrMsg).WithDetail(c.ErrDlt)
}
return nil
diff --git a/pkg/callbackstruct/constant.go b/pkg/callbackstruct/constant.go
index f3bcf1383d..66e1598cda 100644
--- a/pkg/callbackstruct/constant.go
+++ b/pkg/callbackstruct/constant.go
@@ -14,47 +14,44 @@
package callbackstruct
-const CallbackBeforeInviteJoinGroupCommand = "callbackBeforeInviteJoinGroupCommand"
-const CallbackAfterJoinGroupCommand = "callbackAfterJoinGroupCommand"
-const CallbackAfterSetGroupInfoCommand = "callbackAfterSetGroupInfoCommand"
-const CallbackBeforeSetGroupInfoCommand = "callbackBeforeSetGroupInfoCommand"
-
-const CallbackAfterRevokeMsgCommand = "callbackBeforeAfterMsgCommand"
-const CallbackBeforeAddBlackCommand = "callbackBeforeAddBlackCommand"
-const CallbackAfterAddFriendCommand = "callbackAfterAddFriendCommand"
-const CallbackBeforeAddFriendAgreeCommand = "callbackBeforeAddFriendAgreeCommand"
-
-const CallbackAfterDeleteFriendCommand = "callbackAfterDeleteFriendCommand"
-const CallbackBeforeImportFriendsCommand = "callbackBeforeImportFriendsCommand"
-const CallbackAfterImportFriendsCommand = "callbackAfterImportFriendsCommand"
-const CallbackAfterRemoveBlackCommand = "callbackAfterRemoveBlackCommand"
-
const (
- CallbackQuitGroupCommand = "callbackQuitGroupCommand"
- CallbackKillGroupCommand = "callbackKillGroupCommand"
- CallbackDisMissGroupCommand = "callbackDisMissGroupCommand"
+ CallbackBeforeInviteJoinGroupCommand = "callbackBeforeInviteJoinGroupCommand"
+ CallbackAfterJoinGroupCommand = "callbackAfterJoinGroupCommand"
+ CallbackAfterSetGroupInfoCommand = "callbackAfterSetGroupInfoCommand"
+ CallbackBeforeSetGroupInfoCommand = "callbackBeforeSetGroupInfoCommand"
+ CallbackAfterRevokeMsgCommand = "callbackBeforeAfterMsgCommand"
+ CallbackBeforeAddBlackCommand = "callbackBeforeAddBlackCommand"
+ CallbackAfterAddFriendCommand = "callbackAfterAddFriendCommand"
+ CallbackBeforeAddFriendAgreeCommand = "callbackBeforeAddFriendAgreeCommand"
+ CallbackAfterDeleteFriendCommand = "callbackAfterDeleteFriendCommand"
+ CallbackBeforeImportFriendsCommand = "callbackBeforeImportFriendsCommand"
+ CallbackAfterImportFriendsCommand = "callbackAfterImportFriendsCommand"
+ CallbackAfterRemoveBlackCommand = "callbackAfterRemoveBlackCommand"
+ CallbackAfterQuitGroupCommand = "callbackAfterQuitGroupCommand"
+ CallbackAfterKickGroupCommand = "callbackAfterKickGroupCommand"
+ CallbackAfterDisMissGroupCommand = "callbackAfterDisMissGroupCommand"
CallbackBeforeJoinGroupCommand = "callbackBeforeJoinGroupCommand"
- CallbackGroupMsgReadCommand = "callbackGroupMsgReadCommand"
- CallbackMsgModifyCommand = "callbackMsgModifyCommand"
+ CallbackAfterGroupMsgReadCommand = "callbackAfterGroupMsgReadCommand"
+ CallbackBeforeMsgModifyCommand = "callbackBeforeMsgModifyCommand"
CallbackAfterUpdateUserInfoCommand = "callbackAfterUpdateUserInfoCommand"
CallbackAfterUpdateUserInfoExCommand = "callbackAfterUpdateUserInfoExCommand"
CallbackBeforeUpdateUserInfoExCommand = "callbackBeforeUpdateUserInfoExCommand"
CallbackBeforeUserRegisterCommand = "callbackBeforeUserRegisterCommand"
CallbackAfterUserRegisterCommand = "callbackAfterUserRegisterCommand"
- CallbackAfterTransferGroupOwner = "callbackAfterTransferGroupOwner"
- CallbackBeforeSetFriendRemark = "callbackBeforeSetFriendRemark"
- CallbackAfterSetFriendRemark = "callbackAfterSetFriendRemark"
- CallbackSingleMsgRead = "callbackSingleMsgRead"
+ CallbackAfterTransferGroupOwnerCommand = "callbackAfterTransferGroupOwnerCommand"
+ CallbackBeforeSetFriendRemarkCommand = "callbackBeforeSetFriendRemarkCommand"
+ CallbackAfterSetFriendRemarkCommand = "callbackAfterSetFriendRemarkCommand"
+ CallbackAfterSingleMsgReadCommand = "callbackAfterSingleMsgReadCommand"
CallbackBeforeSendSingleMsgCommand = "callbackBeforeSendSingleMsgCommand"
CallbackAfterSendSingleMsgCommand = "callbackAfterSendSingleMsgCommand"
CallbackBeforeSendGroupMsgCommand = "callbackBeforeSendGroupMsgCommand"
CallbackAfterSendGroupMsgCommand = "callbackAfterSendGroupMsgCommand"
- CallbackUserOnlineCommand = "callbackUserOnlineCommand"
- CallbackUserOfflineCommand = "callbackUserOfflineCommand"
- CallbackUserKickOffCommand = "callbackUserKickOffCommand"
- CallbackOfflinePushCommand = "callbackOfflinePushCommand"
- CallbackOnlinePushCommand = "callbackOnlinePushCommand"
- CallbackSuperGroupOnlinePushCommand = "callbackSuperGroupOnlinePushCommand"
+ CallbackAfterUserOnlineCommand = "callbackAfterUserOnlineCommand"
+ CallbackAfterUserOfflineCommand = "callbackAfterUserOfflineCommand"
+ CallbackAfterUserKickOffCommand = "callbackAfterUserKickOffCommand"
+ CallbackBeforeOfflinePushCommand = "callbackBeforeOfflinePushCommand"
+ CallbackBeforeOnlinePushCommand = "callbackBeforeOnlinePushCommand"
+ CallbackBeforeGroupOnlinePushCommand = "callbackBeforeGroupOnlinePushCommand"
CallbackBeforeAddFriendCommand = "callbackBeforeAddFriendCommand"
CallbackBeforeUpdateUserInfoCommand = "callbackBeforeUpdateUserInfoCommand"
CallbackBeforeCreateGroupCommand = "callbackBeforeCreateGroupCommand"
diff --git a/pkg/callbackstruct/doc.go b/pkg/callbackstruct/doc.go
index c3445c60e7..965186c6c2 100644
--- a/pkg/callbackstruct/doc.go
+++ b/pkg/callbackstruct/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/callbackstruct/group.go b/pkg/callbackstruct/group.go
index 467061a4aa..e78d45ab4c 100644
--- a/pkg/callbackstruct/group.go
+++ b/pkg/callbackstruct/group.go
@@ -15,8 +15,8 @@
package callbackstruct
import (
- common "github.com/OpenIMSDK/protocol/sdkws"
"github.com/openimsdk/open-im-server/v3/pkg/apistruct"
+ common "github.com/openimsdk/protocol/sdkws"
)
type CallbackCommand string
diff --git a/pkg/callbackstruct/message.go b/pkg/callbackstruct/message.go
index 2864e28b1e..902fa61105 100644
--- a/pkg/callbackstruct/message.go
+++ b/pkg/callbackstruct/message.go
@@ -15,7 +15,7 @@
package callbackstruct
import (
- sdkws "github.com/OpenIMSDK/protocol/sdkws"
+ sdkws "github.com/openimsdk/protocol/sdkws"
)
type CallbackBeforeSendSingleMsgReq struct {
diff --git a/pkg/callbackstruct/push.go b/pkg/callbackstruct/push.go
index 0566c35e2d..0b0f29b51b 100644
--- a/pkg/callbackstruct/push.go
+++ b/pkg/callbackstruct/push.go
@@ -14,7 +14,7 @@
package callbackstruct
-import common "github.com/OpenIMSDK/protocol/sdkws"
+import common "github.com/openimsdk/protocol/sdkws"
type CallbackBeforePushReq struct {
UserStatusBatchCallbackReq
diff --git a/pkg/callbackstruct/revoke.go b/pkg/callbackstruct/revoke.go
index 1f5e0b0c11..b36985ed35 100644
--- a/pkg/callbackstruct/revoke.go
+++ b/pkg/callbackstruct/revoke.go
@@ -20,6 +20,7 @@ type CallbackAfterRevokeMsgReq struct {
Seq int64 `json:"seq"`
UserID string `json:"userID"`
}
+
type CallbackAfterRevokeMsgResp struct {
CommonCallbackResp
}
diff --git a/pkg/callbackstruct/user.go b/pkg/callbackstruct/user.go
index 98536882d8..504c7ffb7e 100644
--- a/pkg/callbackstruct/user.go
+++ b/pkg/callbackstruct/user.go
@@ -15,8 +15,8 @@
package callbackstruct
import (
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/protocol/wrapperspb"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/protocol/wrapperspb"
)
type CallbackBeforeUpdateUserInfoReq struct {
@@ -26,6 +26,7 @@ type CallbackBeforeUpdateUserInfoReq struct {
FaceURL *string `json:"faceURL"`
Ex *string `json:"ex"`
}
+
type CallbackBeforeUpdateUserInfoResp struct {
CommonCallbackResp
Nickname *string `json:"nickName"`
diff --git a/pkg/common/cachekey/doc.go b/pkg/common/cachekey/doc.go
new file mode 100644
index 0000000000..4975537ec6
--- /dev/null
+++ b/pkg/common/cachekey/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cachekey // import "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
diff --git a/pkg/common/cachekey/token.go b/pkg/common/cachekey/token.go
new file mode 100644
index 0000000000..94468dc315
--- /dev/null
+++ b/pkg/common/cachekey/token.go
@@ -0,0 +1,11 @@
+package cachekey
+
+import "github.com/openimsdk/protocol/constant"
+
+const (
+ UidPidToken = "UID_PID_TOKEN_STATUS:"
+)
+
+func GetTokenKey(userID string, platformID int) string {
+ return UidPidToken + userID + ":" + constant.PlatformIDToName(platformID)
+}
diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go
index bee16fdad4..022fb10978 100644
--- a/pkg/common/cmd/api.go
+++ b/pkg/common/cmd/api.go
@@ -15,43 +15,40 @@
package cmd
import (
- "github.com/OpenIMSDK/protocol/constant"
+ "context"
"github.com/openimsdk/open-im-server/v3/internal/api"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra"
)
type ApiCmd struct {
*RootCmd
- initFunc func(config *config.GlobalConfig, port int, promPort int) error
+ ctx context.Context
+ configMap map[string]any
+ apiConfig *api.Config
}
func NewApiCmd() *ApiCmd {
- ret := &ApiCmd{RootCmd: NewRootCmd("api"), initFunc: api.Start}
- ret.SetRootCmdPt(ret)
- ret.addPreRun()
- ret.addRunE()
- return ret
-}
-
-func (a *ApiCmd) addPreRun() {
- a.Command.PreRun = func(cmd *cobra.Command, args []string) {
- a.port = a.getPortFlag(cmd)
- a.prometheusPort = a.getPrometheusPortFlag(cmd)
+ var apiConfig api.Config
+ ret := &ApiCmd{apiConfig: &apiConfig}
+ ret.configMap = map[string]any{
+ OpenIMAPICfgFileName: &apiConfig.RpcConfig,
+ ZookeeperConfigFileName: &apiConfig.ZookeeperConfig,
+ ShareFileName: &apiConfig.Share,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
}
+ return ret
}
-func (a *ApiCmd) addRunE() {
- a.Command.RunE = func(cmd *cobra.Command, args []string) error {
- return a.initFunc(a.config, a.port, a.prometheusPort)
- }
+func (a *ApiCmd) Exec() error {
+ return a.Execute()
}
-func (a *ApiCmd) GetPortFromConfig(portType string) int {
- if portType == constant.FlagPort {
- return a.config.Api.OpenImApiPort[0]
- } else if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.ApiPrometheusPort[0]
- }
- return 0
+func (a *ApiCmd) runE() error {
+ return api.Start(a.ctx, a.Index(), a.apiConfig)
}
diff --git a/pkg/common/cmd/auth.go b/pkg/common/cmd/auth.go
new file mode 100644
index 0000000000..5ed02ffd00
--- /dev/null
+++ b/pkg/common/cmd/auth.go
@@ -0,0 +1,59 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/auth"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type AuthRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ authConfig *auth.Config
+}
+
+func NewAuthRpcCmd() *AuthRpcCmd {
+ var authConfig auth.Config
+ ret := &AuthRpcCmd{authConfig: &authConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCAuthCfgFileName: &authConfig.RpcConfig,
+ RedisConfigFileName: &authConfig.RedisConfig,
+ ZookeeperConfigFileName: &authConfig.ZookeeperConfig,
+ ShareFileName: &authConfig.Share,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+
+ return ret
+}
+
+func (a *AuthRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *AuthRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.authConfig.ZookeeperConfig, &a.authConfig.RpcConfig.Prometheus, a.authConfig.RpcConfig.RPC.ListenIP,
+ a.authConfig.RpcConfig.RPC.RegisterIP, a.authConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.authConfig.Share.RpcRegisterName.Auth, &a.authConfig.Share, a.authConfig, auth.Start)
+}
diff --git a/pkg/common/cmd/constant.go b/pkg/common/cmd/constant.go
index c332ce3a62..55eb4a069f 100644
--- a/pkg/common/cmd/constant.go
+++ b/pkg/common/cmd/constant.go
@@ -14,13 +14,83 @@
package cmd
+import (
+ "strings"
+)
+
+var (
+ FileName string
+ NotificationFileName string
+ ShareFileName string
+ WebhooksConfigFileName string
+ LocalCacheConfigFileName string
+ KafkaConfigFileName string
+ RedisConfigFileName string
+ ZookeeperConfigFileName string
+ MongodbConfigFileName string
+ MinioConfigFileName string
+ LogConfigFileName string
+ OpenIMAPICfgFileName string
+ OpenIMCronTaskCfgFileName string
+ OpenIMMsgGatewayCfgFileName string
+ OpenIMMsgTransferCfgFileName string
+ OpenIMPushCfgFileName string
+ OpenIMRPCAuthCfgFileName string
+ OpenIMRPCConversationCfgFileName string
+ OpenIMRPCFriendCfgFileName string
+ OpenIMRPCGroupCfgFileName string
+ OpenIMRPCMsgCfgFileName string
+ OpenIMRPCThirdCfgFileName string
+ OpenIMRPCUserCfgFileName string
+)
+
+var ConfigEnvPrefixMap map[string]string
+
+func init() {
+ FileName = "config.yaml"
+ NotificationFileName = "notification.yml"
+ ShareFileName = "share.yml"
+ WebhooksConfigFileName = "webhooks.yml"
+ LocalCacheConfigFileName = "local-cache.yml"
+ KafkaConfigFileName = "kafka.yml"
+ RedisConfigFileName = "redis.yml"
+ ZookeeperConfigFileName = "zookeeper.yml"
+ MongodbConfigFileName = "mongodb.yml"
+ MinioConfigFileName = "minio.yml"
+ LogConfigFileName = "log.yml"
+ OpenIMAPICfgFileName = "openim-api.yml"
+ OpenIMCronTaskCfgFileName = "openim-crontask.yml"
+ OpenIMMsgGatewayCfgFileName = "openim-msggateway.yml"
+ OpenIMMsgTransferCfgFileName = "openim-msgtransfer.yml"
+ OpenIMPushCfgFileName = "openim-push.yml"
+ OpenIMRPCAuthCfgFileName = "openim-rpc-auth.yml"
+ OpenIMRPCConversationCfgFileName = "openim-rpc-conversation.yml"
+ OpenIMRPCFriendCfgFileName = "openim-rpc-friend.yml"
+ OpenIMRPCGroupCfgFileName = "openim-rpc-group.yml"
+ OpenIMRPCMsgCfgFileName = "openim-rpc-msg.yml"
+ OpenIMRPCThirdCfgFileName = "openim-rpc-third.yml"
+ OpenIMRPCUserCfgFileName = "openim-rpc-user.yml"
+
+ ConfigEnvPrefixMap = make(map[string]string)
+ fileNames := []string{
+ FileName, NotificationFileName, ShareFileName, WebhooksConfigFileName,
+ KafkaConfigFileName, RedisConfigFileName, ZookeeperConfigFileName,
+ MongodbConfigFileName, MinioConfigFileName, LogConfigFileName,
+ OpenIMAPICfgFileName, OpenIMCronTaskCfgFileName, OpenIMMsgGatewayCfgFileName,
+ OpenIMMsgTransferCfgFileName, OpenIMPushCfgFileName, OpenIMRPCAuthCfgFileName,
+ OpenIMRPCConversationCfgFileName, OpenIMRPCFriendCfgFileName, OpenIMRPCGroupCfgFileName,
+ OpenIMRPCMsgCfgFileName, OpenIMRPCThirdCfgFileName, OpenIMRPCUserCfgFileName,
+ }
+
+ for _, fileName := range fileNames {
+ envKey := strings.TrimSuffix(strings.TrimSuffix(fileName, ".yml"), ".yaml")
+ envKey = "IMENV_" + envKey
+ envKey = strings.ToUpper(strings.ReplaceAll(envKey, "-", "_"))
+ ConfigEnvPrefixMap[fileName] = envKey
+ }
+}
+
const (
- RpcPushServer = "push"
- RpcAuthServer = "auth"
- RpcConversationServer = "conversation"
- RpcFriendServer = "friend"
- RpcGroupServer = "group"
- RpcMsgServer = "msg"
- RpcThirdServer = "third"
- RpcUserServer = "user"
+ FlagConf = "config_folder_path"
+ FlagTransferIndex = "index"
)
diff --git a/pkg/common/cmd/conversation.go b/pkg/common/cmd/conversation.go
new file mode 100644
index 0000000000..0a617c7296
--- /dev/null
+++ b/pkg/common/cmd/conversation.go
@@ -0,0 +1,61 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type ConversationRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ conversationConfig *conversation.Config
+}
+
+func NewConversationRpcCmd() *ConversationRpcCmd {
+ var conversationConfig conversation.Config
+ ret := &ConversationRpcCmd{conversationConfig: &conversationConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCConversationCfgFileName: &conversationConfig.RpcConfig,
+ RedisConfigFileName: &conversationConfig.RedisConfig,
+ ZookeeperConfigFileName: &conversationConfig.ZookeeperConfig,
+ MongodbConfigFileName: &conversationConfig.MongodbConfig,
+ ShareFileName: &conversationConfig.Share,
+ NotificationFileName: &conversationConfig.NotificationConfig,
+ LocalCacheConfigFileName: &conversationConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *ConversationRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *ConversationRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.conversationConfig.ZookeeperConfig, &a.conversationConfig.RpcConfig.Prometheus, a.conversationConfig.RpcConfig.RPC.ListenIP,
+ a.conversationConfig.RpcConfig.RPC.RegisterIP, a.conversationConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.conversationConfig.Share.RpcRegisterName.Conversation, &a.conversationConfig.Share, a.conversationConfig, conversation.Start)
+}
diff --git a/pkg/common/cmd/cron_task.go b/pkg/common/cmd/cron_task.go
index d8c9dd2a8b..be26f5af36 100644
--- a/pkg/common/cmd/cron_task.go
+++ b/pkg/common/cmd/cron_task.go
@@ -15,34 +15,40 @@
package cmd
import (
+ "context"
"github.com/openimsdk/open-im-server/v3/internal/tools"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra"
)
type CronTaskCmd struct {
*RootCmd
- initFunc func(config *config.GlobalConfig) error
+ ctx context.Context
+ configMap map[string]any
+ cronTaskConfig *tools.CronTaskConfig
}
func NewCronTaskCmd() *CronTaskCmd {
- ret := &CronTaskCmd{RootCmd: NewRootCmd("cronTask", WithCronTaskLogName()),
- initFunc: tools.StartTask}
- ret.addRunE()
- ret.SetRootCmdPt(ret)
- return ret
-}
-
-func (c *CronTaskCmd) addRunE() {
- c.Command.RunE = func(cmd *cobra.Command, args []string) error {
- return c.initFunc(c.config)
+ var cronTaskConfig tools.CronTaskConfig
+ ret := &CronTaskCmd{cronTaskConfig: &cronTaskConfig}
+ ret.configMap = map[string]any{
+ OpenIMCronTaskCfgFileName: &cronTaskConfig.CronTask,
+ ZookeeperConfigFileName: &cronTaskConfig.ZookeeperConfig,
+ ShareFileName: &cronTaskConfig.Share,
}
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
}
-func (c *CronTaskCmd) Exec() error {
- return c.Execute()
+func (a *CronTaskCmd) Exec() error {
+ return a.Execute()
}
-func (c *CronTaskCmd) GetPortFromConfig(portType string) int {
- return 0
+func (a *CronTaskCmd) runE() error {
+ return tools.Start(a.ctx, a.cronTaskConfig)
}
diff --git a/pkg/common/cmd/doc.go b/pkg/common/cmd/doc.go
index 991a85eb9a..96d80b64ac 100644
--- a/pkg/common/cmd/doc.go
+++ b/pkg/common/cmd/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/common/cmd/friend.go b/pkg/common/cmd/friend.go
new file mode 100644
index 0000000000..b8d46f77ef
--- /dev/null
+++ b/pkg/common/cmd/friend.go
@@ -0,0 +1,62 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/friend"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type FriendRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ friendConfig *friend.Config
+}
+
+func NewFriendRpcCmd() *FriendRpcCmd {
+ var friendConfig friend.Config
+ ret := &FriendRpcCmd{friendConfig: &friendConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCFriendCfgFileName: &friendConfig.RpcConfig,
+ RedisConfigFileName: &friendConfig.RedisConfig,
+ ZookeeperConfigFileName: &friendConfig.ZookeeperConfig,
+ MongodbConfigFileName: &friendConfig.MongodbConfig,
+ ShareFileName: &friendConfig.Share,
+ NotificationFileName: &friendConfig.NotificationConfig,
+ WebhooksConfigFileName: &friendConfig.WebhooksConfig,
+ LocalCacheConfigFileName: &friendConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *FriendRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *FriendRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.friendConfig.ZookeeperConfig, &a.friendConfig.RpcConfig.Prometheus, a.friendConfig.RpcConfig.RPC.ListenIP,
+ a.friendConfig.RpcConfig.RPC.RegisterIP, a.friendConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.friendConfig.Share.RpcRegisterName.Friend, &a.friendConfig.Share, a.friendConfig, friend.Start)
+}
diff --git a/pkg/common/cmd/group.go b/pkg/common/cmd/group.go
new file mode 100644
index 0000000000..8bf9778249
--- /dev/null
+++ b/pkg/common/cmd/group.go
@@ -0,0 +1,62 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/group"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type GroupRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ groupConfig *group.Config
+}
+
+func NewGroupRpcCmd() *GroupRpcCmd {
+ var groupConfig group.Config
+ ret := &GroupRpcCmd{groupConfig: &groupConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCGroupCfgFileName: &groupConfig.RpcConfig,
+ RedisConfigFileName: &groupConfig.RedisConfig,
+ ZookeeperConfigFileName: &groupConfig.ZookeeperConfig,
+ MongodbConfigFileName: &groupConfig.MongodbConfig,
+ ShareFileName: &groupConfig.Share,
+ NotificationFileName: &groupConfig.NotificationConfig,
+ WebhooksConfigFileName: &groupConfig.WebhooksConfig,
+ LocalCacheConfigFileName: &groupConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *GroupRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *GroupRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.groupConfig.ZookeeperConfig, &a.groupConfig.RpcConfig.Prometheus, a.groupConfig.RpcConfig.RPC.ListenIP,
+ a.groupConfig.RpcConfig.RPC.RegisterIP, a.groupConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.groupConfig.Share.RpcRegisterName.Group, &a.groupConfig.Share, a.groupConfig, group.Start)
+}
diff --git a/pkg/common/cmd/msg.go b/pkg/common/cmd/msg.go
new file mode 100644
index 0000000000..a3b521b4b7
--- /dev/null
+++ b/pkg/common/cmd/msg.go
@@ -0,0 +1,63 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/msg"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type MsgRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ msgConfig *msg.Config
+}
+
+func NewMsgRpcCmd() *MsgRpcCmd {
+ var msgConfig msg.Config
+ ret := &MsgRpcCmd{msgConfig: &msgConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCMsgCfgFileName: &msgConfig.RpcConfig,
+ RedisConfigFileName: &msgConfig.RedisConfig,
+ ZookeeperConfigFileName: &msgConfig.ZookeeperConfig,
+ MongodbConfigFileName: &msgConfig.MongodbConfig,
+ KafkaConfigFileName: &msgConfig.KafkaConfig,
+ ShareFileName: &msgConfig.Share,
+ NotificationFileName: &msgConfig.NotificationConfig,
+ WebhooksConfigFileName: &msgConfig.WebhooksConfig,
+ LocalCacheConfigFileName: &msgConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *MsgRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *MsgRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.msgConfig.ZookeeperConfig, &a.msgConfig.RpcConfig.Prometheus, a.msgConfig.RpcConfig.RPC.ListenIP,
+ a.msgConfig.RpcConfig.RPC.RegisterIP, a.msgConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.msgConfig.Share.RpcRegisterName.Msg, &a.msgConfig.Share, a.msgConfig, msg.Start)
+}
diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go
index 37aedd933d..897fd70087 100644
--- a/pkg/common/cmd/msg_gateway.go
+++ b/pkg/common/cmd/msg_gateway.go
@@ -15,61 +15,43 @@
package cmd
import (
- "log"
+ "context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/OpenIMSDK/protocol/constant"
"github.com/openimsdk/open-im-server/v3/internal/msggateway"
+
+ "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra"
)
type MsgGatewayCmd struct {
*RootCmd
+ ctx context.Context
+ configMap map[string]any
+ msgGatewayConfig *msggateway.Config
}
func NewMsgGatewayCmd() *MsgGatewayCmd {
- ret := &MsgGatewayCmd{NewRootCmd("msgGateway")}
- ret.addRunE()
- ret.SetRootCmdPt(ret)
- return ret
-}
-
-func (m *MsgGatewayCmd) AddWsPortFlag() {
- m.Command.Flags().IntP(constant.FlagWsPort, "w", 0, "ws server listen port")
-}
-
-func (m *MsgGatewayCmd) getWsPortFlag(cmd *cobra.Command) int {
- port, err := cmd.Flags().GetInt(constant.FlagWsPort)
- if err != nil {
- log.Println("Error getting ws port flag:", err)
- }
- if port == 0 {
- port = m.PortFromConfig(constant.FlagWsPort)
+ var msgGatewayConfig msggateway.Config
+ ret := &MsgGatewayCmd{msgGatewayConfig: &msgGatewayConfig}
+ ret.configMap = map[string]any{
+ OpenIMMsgGatewayCfgFileName: &msgGatewayConfig.MsgGateway,
+ ZookeeperConfigFileName: &msgGatewayConfig.ZookeeperConfig,
+ ShareFileName: &msgGatewayConfig.Share,
+ WebhooksConfigFileName: &msgGatewayConfig.WebhooksConfig,
}
- return port
-}
-
-func (m *MsgGatewayCmd) addRunE() {
- m.Command.RunE = func(cmd *cobra.Command, args []string) error {
- return msggateway.RunWsAndServer(m.config, m.getPortFlag(cmd), m.getWsPortFlag(cmd), m.getPrometheusPortFlag(cmd))
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
}
+ return ret
}
func (m *MsgGatewayCmd) Exec() error {
return m.Execute()
}
-func (m *MsgGatewayCmd) GetPortFromConfig(portType string) int {
- switch portType {
- case constant.FlagWsPort:
- return m.config.LongConnSvr.OpenImWsPort[0]
-
- case constant.FlagPort:
- return m.config.LongConnSvr.OpenImMessageGatewayPort[0]
-
- case constant.FlagPrometheusPort:
- return m.config.Prometheus.MessageGatewayPrometheusPort[0]
-
- default:
- return 0
- }
+func (m *MsgGatewayCmd) runE() error {
+ return msggateway.Start(m.ctx, m.Index(), m.msgGatewayConfig)
}
diff --git a/pkg/common/cmd/msg_gateway_test.go b/pkg/common/cmd/msg_gateway_test.go
index c0ea2b057c..d820627b50 100644
--- a/pkg/common/cmd/msg_gateway_test.go
+++ b/pkg/common/cmd/msg_gateway_test.go
@@ -15,11 +15,12 @@
package cmd
import (
- "testing"
-
- "github.com/OpenIMSDK/protocol/constant"
+ "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/tools/apiresp"
+ "github.com/openimsdk/tools/utils/jsonutil"
"github.com/stretchr/testify/mock"
- "gotest.tools/assert"
+ "math"
+ "testing"
)
// MockRootCmd is a mock type for the RootCmd type
@@ -32,20 +33,29 @@ func (m *MockRootCmd) Execute() error {
return args.Error(0)
}
-func TestMsgGatewayCmd_GetPortFromConfig(t *testing.T) {
- msgGatewayCmd := &MsgGatewayCmd{RootCmd: &RootCmd{}}
- tests := []struct {
- portType string
- want int
- }{
- {constant.FlagWsPort, 8080}, // Replace 8080 with the expected port from the config
- {constant.FlagPort, 8081}, // Replace 8081 with the expected port from the config
- {"invalid", 0},
+func TestName(t *testing.T) {
+ resp := &apiresp.ApiResponse{
+ ErrCode: 1234,
+ ErrMsg: "test",
+ ErrDlt: "4567",
+ Data: &auth.UserTokenResp{
+ Token: "1234567",
+ ExpireTimeSeconds: math.MaxInt64,
+ },
+ }
+ data, err := resp.MarshalJSON()
+ if err != nil {
+ panic(err)
}
- for _, tt := range tests {
- t.Run(tt.portType, func(t *testing.T) {
- got := msgGatewayCmd.GetPortFromConfig(tt.portType)
- assert.Equal(t, tt.want, got)
- })
+ t.Log(string(data))
+
+ var rReso apiresp.ApiResponse
+ rReso.Data = &auth.UserTokenResp{}
+
+ if err := jsonutil.JsonUnmarshal(data, &rReso); err != nil {
+ panic(err)
}
+
+ t.Logf("%+v\n", rReso)
+
}
diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go
index d98154f3a3..86f42dc56f 100644
--- a/pkg/common/cmd/msg_transfer.go
+++ b/pkg/common/cmd/msg_transfer.go
@@ -15,53 +15,44 @@
package cmd
import (
- "fmt"
-
- "github.com/OpenIMSDK/protocol/constant"
+ "context"
"github.com/openimsdk/open-im-server/v3/internal/msgtransfer"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/system/program"
"github.com/spf13/cobra"
)
type MsgTransferCmd struct {
*RootCmd
+ ctx context.Context
+ configMap map[string]any
+ msgTransferConfig *msgtransfer.Config
}
func NewMsgTransferCmd() *MsgTransferCmd {
- ret := &MsgTransferCmd{NewRootCmd("msgTransfer")}
- ret.addRunE()
- ret.SetRootCmdPt(ret)
- return ret
-}
-
-func (m *MsgTransferCmd) addRunE() {
- m.Command.RunE = func(cmd *cobra.Command, args []string) error {
- return msgtransfer.StartTransfer(m.config, m.getPrometheusPortFlag(cmd))
+ var msgTransferConfig msgtransfer.Config
+ ret := &MsgTransferCmd{msgTransferConfig: &msgTransferConfig}
+ ret.configMap = map[string]any{
+ OpenIMMsgTransferCfgFileName: &msgTransferConfig.MsgTransfer,
+ RedisConfigFileName: &msgTransferConfig.RedisConfig,
+ MongodbConfigFileName: &msgTransferConfig.MongodbConfig,
+ KafkaConfigFileName: &msgTransferConfig.KafkaConfig,
+ ZookeeperConfigFileName: &msgTransferConfig.ZookeeperConfig,
+ ShareFileName: &msgTransferConfig.Share,
+ WebhooksConfigFileName: &msgTransferConfig.WebhooksConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
}
+ return ret
}
func (m *MsgTransferCmd) Exec() error {
return m.Execute()
}
-func (m *MsgTransferCmd) GetPortFromConfig(portType string) int {
- if portType == constant.FlagPort {
- return 0
- } else if portType == constant.FlagPrometheusPort {
- n := m.getTransferProgressFlagValue()
- return m.config.Prometheus.MessageTransferPrometheusPort[n]
- }
- return 0
-}
-
-func (m *MsgTransferCmd) AddTransferProgressFlag() {
- m.Command.Flags().IntP(constant.FlagTransferProgressIndex, "n", 0, "transfer progress index")
-}
-
-func (m *MsgTransferCmd) getTransferProgressFlagValue() int {
- nIndex, err := m.Command.Flags().GetInt(constant.FlagTransferProgressIndex)
- if err != nil {
- fmt.Println("get transfer cmd error,make sure it is k8s env or not")
- return 0
- }
- return nIndex
+func (m *MsgTransferCmd) runE() error {
+ return msgtransfer.Start(m.ctx, m.Index(), m.msgTransferConfig)
}
diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go
index df15acd87f..a0a9b04101 100644
--- a/pkg/common/cmd/msg_utils.go
+++ b/pkg/common/cmd/msg_utils.go
@@ -15,19 +15,24 @@
package cmd
import (
- "github.com/openimsdk/open-im-server/v3/internal/tools"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
"github.com/spf13/cobra"
)
type MsgUtilsCmd struct {
cobra.Command
- MsgTool *tools.MsgTool
}
func (m *MsgUtilsCmd) AddUserIDFlag() {
m.Command.PersistentFlags().StringP("userID", "u", "", "openIM userID")
}
+func (m *MsgUtilsCmd) AddIndexFlag() {
+ m.Command.PersistentFlags().IntP(FlagTransferIndex, "i", 0, "process startup sequence number")
+}
+
+func (m *MsgUtilsCmd) AddConfigDirFlag() {
+ m.Command.PersistentFlags().StringP(FlagConf, "c", "", "path of config directory")
+
+}
func (m *MsgUtilsCmd) getUserIDFlag(cmdLines *cobra.Command) string {
userID, _ := cmdLines.Flags().GetString("userID")
@@ -44,7 +49,7 @@ func (m *MsgUtilsCmd) AddFixAllFlag() {
} */
func (m *MsgUtilsCmd) AddClearAllFlag() {
- m.Command.PersistentFlags().BoolP("clearAll", "c", false, "openIM clear all seqs")
+ m.Command.PersistentFlags().BoolP("clearAll", "", false, "openIM clear all seqs")
}
/* func (m *MsgUtilsCmd) getClearAllFlag(cmdLines *cobra.Command) bool {
@@ -136,27 +141,7 @@ func NewSeqCmd() *SeqCmd {
func (s *SeqCmd) GetSeqCmd() *cobra.Command {
s.Command.Run = func(cmdLines *cobra.Command, args []string) {
- _, err := tools.InitMsgTool(s.MsgTool.Config)
- if err != nil {
- util.ExitWithError(err)
- }
- userID := s.getUserIDFlag(cmdLines)
- superGroupID := s.getSuperGroupIDFlag(cmdLines)
- // beginSeq := s.getBeginSeqFlag(cmdLines)
- // limit := s.getLimitFlag(cmdLines)
- if userID != "" {
- // seq, err := msgTool.s(context.Background(), userID)
- if err != nil {
- panic(err)
- }
- // println(seq)
- } else if superGroupID != "" {
- // seq, err := msgTool.GetSuperGroupSeq(context.Background(), superGroupID)
- if err != nil {
- panic(err)
- }
- // println(seq)
- }
+
}
return &s.Command
}
diff --git a/pkg/common/cmd/push.go b/pkg/common/cmd/push.go
new file mode 100644
index 0000000000..0140ced237
--- /dev/null
+++ b/pkg/common/cmd/push.go
@@ -0,0 +1,63 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/push"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type PushRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ pushConfig *push.Config
+}
+
+func NewPushRpcCmd() *PushRpcCmd {
+ var pushConfig push.Config
+ ret := &PushRpcCmd{pushConfig: &pushConfig}
+ ret.configMap = map[string]any{
+ OpenIMPushCfgFileName: &pushConfig.RpcConfig,
+ RedisConfigFileName: &pushConfig.RedisConfig,
+ ZookeeperConfigFileName: &pushConfig.ZookeeperConfig,
+ MongodbConfigFileName: &pushConfig.MongodbConfig,
+ KafkaConfigFileName: &pushConfig.KafkaConfig,
+ ShareFileName: &pushConfig.Share,
+ NotificationFileName: &pushConfig.NotificationConfig,
+ WebhooksConfigFileName: &pushConfig.WebhooksConfig,
+ LocalCacheConfigFileName: &pushConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *PushRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *PushRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.pushConfig.ZookeeperConfig, &a.pushConfig.RpcConfig.Prometheus, a.pushConfig.RpcConfig.RPC.ListenIP,
+ a.pushConfig.RpcConfig.RPC.RegisterIP, a.pushConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.pushConfig.Share.RpcRegisterName.Push, &a.pushConfig.Share, a.pushConfig, push.Start)
+}
diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go
index 478942a5bb..9002813676 100644
--- a/pkg/common/cmd/root.go
+++ b/pkg/common/cmd/root.go
@@ -16,34 +16,34 @@ package cmd
import (
"fmt"
+ "path/filepath"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
"github.com/spf13/cobra"
)
-type RootCmdPt interface {
- GetPortFromConfig(portType string) int
-}
-
type RootCmd struct {
Command cobra.Command
- Name string
+ processName string
port int
prometheusPort int
- cmdItf RootCmdPt
- config *config.GlobalConfig
+ log config.Log
+ index int
+}
+
+func (r *RootCmd) Index() int {
+ return r.index
}
-func (rc *RootCmd) Port() int {
- return rc.port
+func (r *RootCmd) Port() int {
+ return r.port
}
type CmdOpts struct {
loggerPrefixName string
+ configMap map[string]any
}
func WithCronTaskLogName() func(*CmdOpts) {
@@ -57,41 +57,63 @@ func WithLogName(logName string) func(*CmdOpts) {
opts.loggerPrefixName = logName
}
}
+func WithConfigMap(configMap map[string]any) func(*CmdOpts) {
+ return func(opts *CmdOpts) {
+ opts.configMap = configMap
+ }
+}
-func NewRootCmd(name string, opts ...func(*CmdOpts)) *RootCmd {
- rootCmd := &RootCmd{Name: name, config: config.NewGlobalConfig()}
+func NewRootCmd(processName string, opts ...func(*CmdOpts)) *RootCmd {
+ rootCmd := &RootCmd{processName: processName}
cmd := cobra.Command{
- Use: "Start openIM application",
- Short: fmt.Sprintf(`Start %s `, name),
- Long: fmt.Sprintf(`Start %s `, name),
+ Use: "Start openIM application",
+ Long: fmt.Sprintf(`Start %s `, processName),
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
return rootCmd.persistentPreRun(cmd, opts...)
},
+ SilenceUsage: true,
+ SilenceErrors: false,
}
+ cmd.Flags().StringP(FlagConf, "c", "", "path of config directory")
+ cmd.Flags().IntP(FlagTransferIndex, "i", 0, "process startup sequence number")
+
rootCmd.Command = cmd
- rootCmd.addConfFlag()
return rootCmd
}
-func (rc *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) error {
- if err := rc.initializeConfiguration(cmd); err != nil {
- return fmt.Errorf("failed to get configuration from command: %w", err)
+func (r *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) error {
+ cmdOpts := r.applyOptions(opts...)
+ if err := r.initializeConfiguration(cmd, cmdOpts); err != nil {
+ return err
}
- cmdOpts := rc.applyOptions(opts...)
-
- if err := rc.initializeLogger(cmdOpts); err != nil {
- return errs.Wrap(err, "failed to initialize logger")
+ if err := r.initializeLogger(cmdOpts); err != nil {
+ return errs.WrapMsg(err, "failed to initialize logger")
}
return nil
}
-func (rc *RootCmd) initializeConfiguration(cmd *cobra.Command) error {
- return rc.getConfFromCmdAndInit(cmd)
+func (r *RootCmd) initializeConfiguration(cmd *cobra.Command, opts *CmdOpts) error {
+ configDirectory, _, err := r.getFlag(cmd)
+ if err != nil {
+ return err
+ }
+ // Load common configuration file
+ //opts.configMap[ShareFileName] = StructEnvPrefix{EnvPrefix: shareEnvPrefix, ConfigStruct: &r.share}
+ for configFileName, configStruct := range opts.configMap {
+ err := config.LoadConfig(filepath.Join(configDirectory, configFileName),
+ ConfigEnvPrefixMap[configFileName], configStruct)
+ if err != nil {
+ return err
+ }
+ }
+ // Load common log configuration file
+ return config.LoadConfig(filepath.Join(configDirectory, LogConfigFileName),
+ ConfigEnvPrefixMap[LogConfigFileName], &r.log)
}
-func (rc *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts {
+func (r *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts {
cmdOpts := defaultCmdOpts()
for _, opt := range opts {
opt(cmdOpts)
@@ -100,92 +122,45 @@ func (rc *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts {
return cmdOpts
}
-func (rc *RootCmd) initializeLogger(cmdOpts *CmdOpts) error {
- logConfig := rc.config.Log
-
- return log.InitFromConfig(
+func (r *RootCmd) initializeLogger(cmdOpts *CmdOpts) error {
+ err := log.InitFromConfig(
cmdOpts.loggerPrefixName,
- rc.Name,
- logConfig.RemainLogLevel,
- logConfig.IsStdout,
- logConfig.IsJson,
- logConfig.StorageLocation,
- logConfig.RemainRotationCount,
- logConfig.RotationTime,
+ r.processName,
+ r.log.RemainLogLevel,
+ r.log.IsStdout,
+ r.log.IsJson,
+ r.log.StorageLocation,
+ r.log.RemainRotationCount,
+ r.log.RotationTime,
+ config.Version,
)
+ if err != nil {
+ return errs.Wrap(err)
+ }
+ return errs.Wrap(log.InitConsoleLogger(r.processName, r.log.RemainLogLevel, r.log.IsJson, config.Version))
+
}
func defaultCmdOpts() *CmdOpts {
return &CmdOpts{
- loggerPrefixName: "openim-all",
+ loggerPrefixName: "openim-service-log",
}
}
-func (r *RootCmd) SetRootCmdPt(cmdItf RootCmdPt) {
- r.cmdItf = cmdItf
-}
-
-func (r *RootCmd) addConfFlag() {
- r.Command.Flags().StringP(constant.FlagConf, "c", "", "path to config file folder")
-}
-
-func (r *RootCmd) AddPortFlag() {
- r.Command.Flags().IntP(constant.FlagPort, "p", 0, "server listen port")
-}
-
-func (r *RootCmd) getPortFlag(cmd *cobra.Command) int {
- port, err := cmd.Flags().GetInt(constant.FlagPort)
+func (r *RootCmd) getFlag(cmd *cobra.Command) (string, int, error) {
+ configDirectory, err := cmd.Flags().GetString(FlagConf)
if err != nil {
- // Wrapping the error with additional context
- return 0
+ return "", 0, errs.Wrap(err)
}
- if port == 0 {
- port = r.PortFromConfig(constant.FlagPort)
- }
- return port
-}
-
-// // GetPortFlag returns the port flag.
-func (r *RootCmd) GetPortFlag() int {
- return r.port
-}
-
-func (r *RootCmd) AddPrometheusPortFlag() {
- r.Command.Flags().IntP(constant.FlagPrometheusPort, "", 0, "server prometheus listen port")
-}
-
-func (r *RootCmd) getPrometheusPortFlag(cmd *cobra.Command) int {
- port, err := cmd.Flags().GetInt(constant.FlagPrometheusPort)
- if err != nil || port == 0 {
- port = r.PortFromConfig(constant.FlagPrometheusPort)
- if err != nil {
- return 0
- }
+ index, err := cmd.Flags().GetInt(FlagTransferIndex)
+ if err != nil {
+ return "", 0, errs.Wrap(err)
}
- return port
-}
-
-func (r *RootCmd) GetPrometheusPortFlag() int {
- return r.prometheusPort
-}
-
-func (r *RootCmd) getConfFromCmdAndInit(cmdLines *cobra.Command) error {
- configFolderPath, _ := cmdLines.Flags().GetString(constant.FlagConf)
- fmt.Println("The directory of the configuration file to start the process:", configFolderPath)
- return config2.InitConfig(r.config, configFolderPath)
+ r.index = index
+ return configDirectory, index, nil
}
func (r *RootCmd) Execute() error {
return r.Command.Execute()
}
-
-func (r *RootCmd) AddCommand(cmds ...*cobra.Command) {
- r.Command.AddCommand(cmds...)
-}
-
-func (r *RootCmd) PortFromConfig(portType string) int {
- // Retrieve the port and cache it
- port := r.cmdItf.GetPortFromConfig(portType)
- return port
-}
diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go
deleted file mode 100644
index 9c6dbddd6f..0000000000
--- a/pkg/common/cmd/rpc.go
+++ /dev/null
@@ -1,156 +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 cmd
-
-import (
- "errors"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
- "github.com/spf13/cobra"
- "google.golang.org/grpc"
-)
-
-type rpcInitFuc func(config *config2.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error
-
-type RpcCmd struct {
- *RootCmd
- RpcRegisterName string
- initFunc rpcInitFuc
-}
-
-func NewRpcCmd(name string, initFunc rpcInitFuc) *RpcCmd {
- ret := &RpcCmd{RootCmd: NewRootCmd(name), initFunc: initFunc}
- ret.addPreRun()
- ret.addRunE()
- ret.SetRootCmdPt(ret)
- return ret
-}
-
-func (a *RpcCmd) addPreRun() {
- a.Command.PreRun = func(cmd *cobra.Command, args []string) {
- a.port = a.getPortFlag(cmd)
- a.prometheusPort = a.getPrometheusPortFlag(cmd)
- }
-}
-
-func (a *RpcCmd) addRunE() {
- a.Command.RunE = func(cmd *cobra.Command, args []string) error {
- rpcRegisterName, err := a.GetRpcRegisterNameFromConfig()
- if err != nil {
- return err
- } else {
- return a.StartSvr(rpcRegisterName, a.initFunc)
- }
- }
-}
-
-func (a *RpcCmd) Exec() error {
- return a.Execute()
-}
-
-func (a *RpcCmd) StartSvr(name string, rpcFn func(config *config2.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error {
- if a.GetPortFlag() == 0 {
- return errs.Wrap(errors.New("port is required"))
- }
- return startrpc.Start(a.GetPortFlag(), name, a.GetPrometheusPortFlag(), a.config, rpcFn)
-}
-
-func (a *RpcCmd) GetPortFromConfig(portType string) int {
- switch a.Name {
- case RpcPushServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImPushPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.PushPrometheusPort[0]
- }
- case RpcAuthServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImAuthPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.AuthPrometheusPort[0]
- }
- case RpcConversationServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImConversationPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.ConversationPrometheusPort[0]
- }
- case RpcFriendServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImFriendPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.FriendPrometheusPort[0]
- }
- case RpcGroupServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImGroupPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.GroupPrometheusPort[0]
- }
- case RpcMsgServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImMessagePort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.MessagePrometheusPort[0]
- }
- case RpcThirdServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImThirdPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.ThirdPrometheusPort[0]
- }
- case RpcUserServer:
- if portType == constant.FlagPort {
- return a.config.RpcPort.OpenImUserPort[0]
- }
- if portType == constant.FlagPrometheusPort {
- return a.config.Prometheus.UserPrometheusPort[0]
- }
- }
- return 0
-}
-
-func (a *RpcCmd) GetRpcRegisterNameFromConfig() (string, error) {
- switch a.Name {
- case RpcPushServer:
- return a.config.RpcRegisterName.OpenImPushName, nil
- case RpcAuthServer:
- return a.config.RpcRegisterName.OpenImAuthName, nil
- case RpcConversationServer:
- return a.config.RpcRegisterName.OpenImConversationName, nil
- case RpcFriendServer:
- return a.config.RpcRegisterName.OpenImFriendName, nil
- case RpcGroupServer:
- return a.config.RpcRegisterName.OpenImGroupName, nil
- case RpcMsgServer:
- return a.config.RpcRegisterName.OpenImMsgName, nil
- case RpcThirdServer:
- return a.config.RpcRegisterName.OpenImThirdName, nil
- case RpcUserServer:
- return a.config.RpcRegisterName.OpenImUserName, nil
- }
- return "", errs.Wrap(errors.New("can not get rpc register name"), a.Name)
-}
diff --git a/pkg/common/cmd/third.go b/pkg/common/cmd/third.go
new file mode 100644
index 0000000000..0dfa7d5be5
--- /dev/null
+++ b/pkg/common/cmd/third.go
@@ -0,0 +1,62 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/third"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type ThirdRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ thirdConfig *third.Config
+}
+
+func NewThirdRpcCmd() *ThirdRpcCmd {
+ var thirdConfig third.Config
+ ret := &ThirdRpcCmd{thirdConfig: &thirdConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCThirdCfgFileName: &thirdConfig.RpcConfig,
+ RedisConfigFileName: &thirdConfig.RedisConfig,
+ ZookeeperConfigFileName: &thirdConfig.ZookeeperConfig,
+ MongodbConfigFileName: &thirdConfig.MongodbConfig,
+ ShareFileName: &thirdConfig.Share,
+ NotificationFileName: &thirdConfig.NotificationConfig,
+ MinioConfigFileName: &thirdConfig.MinioConfig,
+ LocalCacheConfigFileName: &thirdConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *ThirdRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *ThirdRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.thirdConfig.ZookeeperConfig, &a.thirdConfig.RpcConfig.Prometheus, a.thirdConfig.RpcConfig.RPC.ListenIP,
+ a.thirdConfig.RpcConfig.RPC.RegisterIP, a.thirdConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.thirdConfig.Share.RpcRegisterName.Third, &a.thirdConfig.Share, a.thirdConfig, third.Start)
+}
diff --git a/pkg/common/cmd/user.go b/pkg/common/cmd/user.go
new file mode 100644
index 0000000000..315b93256c
--- /dev/null
+++ b/pkg/common/cmd/user.go
@@ -0,0 +1,63 @@
+// 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 cmd
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/internal/rpc/user"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/spf13/cobra"
+)
+
+type UserRpcCmd struct {
+ *RootCmd
+ ctx context.Context
+ configMap map[string]any
+ userConfig *user.Config
+}
+
+func NewUserRpcCmd() *UserRpcCmd {
+ var userConfig user.Config
+ ret := &UserRpcCmd{userConfig: &userConfig}
+ ret.configMap = map[string]any{
+ OpenIMRPCUserCfgFileName: &userConfig.RpcConfig,
+ RedisConfigFileName: &userConfig.RedisConfig,
+ ZookeeperConfigFileName: &userConfig.ZookeeperConfig,
+ MongodbConfigFileName: &userConfig.MongodbConfig,
+ KafkaConfigFileName: &userConfig.KafkaConfig,
+ ShareFileName: &userConfig.Share,
+ NotificationFileName: &userConfig.NotificationConfig,
+ WebhooksConfigFileName: &userConfig.WebhooksConfig,
+ LocalCacheConfigFileName: &userConfig.LocalCacheConfig,
+ }
+ ret.RootCmd = NewRootCmd(program.GetProcessName(), WithConfigMap(ret.configMap))
+ ret.ctx = context.WithValue(context.Background(), "version", config.Version)
+ ret.Command.RunE = func(cmd *cobra.Command, args []string) error {
+ return ret.runE()
+ }
+ return ret
+}
+
+func (a *UserRpcCmd) Exec() error {
+ return a.Execute()
+}
+
+func (a *UserRpcCmd) runE() error {
+ return startrpc.Start(a.ctx, &a.userConfig.ZookeeperConfig, &a.userConfig.RpcConfig.Prometheus, a.userConfig.RpcConfig.RPC.ListenIP,
+ a.userConfig.RpcConfig.RPC.RegisterIP, a.userConfig.RpcConfig.RPC.Ports,
+ a.Index(), a.userConfig.Share.RpcRegisterName.User, &a.userConfig.Share, a.userConfig, user.Start)
+}
diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go
index 8bc8713551..caf31036cd 100644
--- a/pkg/common/config/config.go
+++ b/pkg/common/config/config.go
@@ -15,433 +15,514 @@
package config
import (
- "bytes"
+ "fmt"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/openimsdk/tools/mq/kafka"
+ "github.com/openimsdk/tools/s3/cos"
+ "github.com/openimsdk/tools/s3/minio"
+ "github.com/openimsdk/tools/s3/oss"
+ "net"
"time"
-
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "gopkg.in/yaml.v3"
)
-var Config GlobalConfig
+type CacheConfig struct {
+ Topic string `mapstructure:"topic"`
+ SlotNum int `mapstructure:"slotNum"`
+ SlotSize int `mapstructure:"slotSize"`
+ SuccessExpire int `mapstructure:"successExpire"`
+ FailedExpire int `mapstructure:"failedExpire"`
+}
-const ConfKey = "conf"
+type LocalCache struct {
+ User CacheConfig `mapstructure:"user"`
+ Group CacheConfig `mapstructure:"group"`
+ Friend CacheConfig `mapstructure:"friend"`
+ Conversation CacheConfig `mapstructure:"conversation"`
+}
-type CallBackConfig struct {
- Enable bool `yaml:"enable"`
- CallbackTimeOut int `yaml:"timeout"`
- CallbackFailedContinue *bool `yaml:"failedContinue"`
+type Log struct {
+ StorageLocation string `mapstructure:"storageLocation"`
+ RotationTime uint `mapstructure:"rotationTime"`
+ RemainRotationCount uint `mapstructure:"remainRotationCount"`
+ RemainLogLevel int `mapstructure:"remainLogLevel"`
+ IsStdout bool `mapstructure:"isStdout"`
+ IsJson bool `mapstructure:"isJson"`
+ WithStack bool `mapstructure:"withStack"`
}
-type NotificationConf struct {
- IsSendMsg bool `yaml:"isSendMsg"`
- ReliabilityLevel int `yaml:"reliabilityLevel"` // 1 online 2 persistent
- UnreadCount bool `yaml:"unreadCount"`
- OfflinePush POfflinePush `yaml:"offlinePush"`
+type Minio struct {
+ Bucket string `mapstructure:"bucket"`
+ AccessKeyID string `mapstructure:"accessKeyID"`
+ SecretAccessKey string `mapstructure:"secretAccessKey"`
+ SessionToken string `mapstructure:"sessionToken"`
+ InternalAddress string `mapstructure:"internalAddress"`
+ ExternalAddress string `mapstructure:"externalAddress"`
+ PublicRead bool `mapstructure:"publicRead"`
}
-type POfflinePush struct {
- Enable bool `yaml:"enable"`
- Title string `yaml:"title"`
- Desc string `yaml:"desc"`
- Ext string `yaml:"ext"`
+type Mongo struct {
+ URI string `mapstructure:"uri"`
+ Address []string `mapstructure:"address"`
+ Database string `mapstructure:"database"`
+ Username string `mapstructure:"username"`
+ Password string `mapstructure:"password"`
+ MaxPoolSize int `mapstructure:"maxPoolSize"`
+ MaxRetry int `mapstructure:"maxRetry"`
+}
+type Kafka struct {
+ Username string `mapstructure:"username"`
+ Password string `mapstructure:"password"`
+ ProducerAck string `mapstructure:"producerAck"`
+ CompressType string `mapstructure:"compressType"`
+ Address []string `mapstructure:"address"`
+ ToRedisTopic string `mapstructure:"toRedisTopic"`
+ ToMongoTopic string `mapstructure:"toMongoTopic"`
+ ToPushTopic string `mapstructure:"toPushTopic"`
+ ToRedisGroupID string `mapstructure:"toRedisGroupID"`
+ ToMongoGroupID string `mapstructure:"toMongoGroupID"`
+ ToPushGroupID string `mapstructure:"toPushGroupID"`
+ Tls TLSConfig `mapstructure:"tls"`
+}
+type TLSConfig struct {
+ EnableTLS bool `mapstructure:"enableTLS"`
+ CACrt string `mapstructure:"caCrt"`
+ ClientCrt string `mapstructure:"clientCrt"`
+ ClientKey string `mapstructure:"clientKey"`
+ ClientKeyPwd string `mapstructure:"clientKeyPwd"`
+ InsecureSkipVerify bool `mapstructure:"insecureSkipVerify"`
}
-type MYSQL struct {
- Address []string `yaml:"address"`
- Username string `yaml:"username"`
- Password string `yaml:"password"`
- Database string `yaml:"database"`
- MaxOpenConn int `yaml:"maxOpenConn"`
- MaxIdleConn int `yaml:"maxIdleConn"`
- MaxLifeTime int `yaml:"maxLifeTime"`
- LogLevel int `yaml:"logLevel"`
- SlowThreshold int `yaml:"slowThreshold"`
+type API struct {
+ Api struct {
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"api"`
+ Prometheus struct {
+ Enable bool `mapstructure:"enable"`
+ Ports []int `mapstructure:"ports"`
+ GrafanaURL string `mapstructure:"grafanaURL"`
+ } `mapstructure:"prometheus"`
}
-type GlobalConfig struct {
- Envs struct {
- Discovery string `yaml:"discovery"`
- }
- Zookeeper struct {
- Schema string `yaml:"schema"`
- ZkAddr []string `yaml:"address"`
- Username string `yaml:"username"`
- Password string `yaml:"password"`
- } `yaml:"zookeeper"`
-
- Mysql *MYSQL `yaml:"mysql"`
-
- Mongo struct {
- Uri string `yaml:"uri"`
- Address []string `yaml:"address"`
- Database string `yaml:"database"`
- Username string `yaml:"username"`
- Password string `yaml:"password"`
- MaxPoolSize int `yaml:"maxPoolSize"`
- } `yaml:"mongo"`
-
- Redis struct {
- ClusterMode bool `yaml:"clusterMode"`
- Address []string `yaml:"address"`
- Username string `yaml:"username"`
- Password string `yaml:"password"`
- EnablePipeline bool `yaml:"enablePipeline"`
- } `yaml:"redis"`
-
- Kafka struct {
- Username string `yaml:"username"`
- Password string `yaml:"password"`
- ProducerAck string `yaml:"producerAck"`
- CompressType string `yaml:"compressType"`
- Addr []string `yaml:"addr"`
- TLS *struct {
- CACrt string `yaml:"caCrt"`
- ClientCrt string `yaml:"clientCrt"`
- ClientKey string `yaml:"clientKey"`
- ClientKeyPwd string `yaml:"clientKeyPwd"`
- InsecureSkipVerify bool `yaml:"insecureSkipVerify"`
- } `yaml:"tls"`
- LatestMsgToRedis struct {
- Topic string `yaml:"topic"`
- } `yaml:"latestMsgToRedis"`
- MsgToMongo struct {
- Topic string `yaml:"topic"`
- } `yaml:"offlineMsgToMongo"`
- MsgToPush struct {
- Topic string `yaml:"topic"`
- } `yaml:"msgToPush"`
- ConsumerGroupID struct {
- MsgToRedis string `yaml:"msgToRedis"`
- MsgToMongo string `yaml:"msgToMongo"`
- MsgToMySql string `yaml:"msgToMySql"`
- MsgToPush string `yaml:"msgToPush"`
- } `yaml:"consumerGroupID"`
- } `yaml:"kafka"`
-
- Rpc struct {
- RegisterIP string `yaml:"registerIP"`
- ListenIP string `yaml:"listenIP"`
- } `yaml:"rpc"`
+type CronTask struct {
+ ChatRecordsClearTime string `mapstructure:"chatRecordsClearTime"`
+ RetainChatRecords int `mapstructure:"retainChatRecords"`
+}
- Api struct {
- OpenImApiPort []int `yaml:"openImApiPort"`
- ListenIP string `yaml:"listenIP"`
- } `yaml:"api"`
-
- Object struct {
- Enable string `yaml:"enable"`
- ApiURL string `yaml:"apiURL"`
- Minio struct {
- Bucket string `yaml:"bucket"`
- Endpoint string `yaml:"endpoint"`
- AccessKeyID string `yaml:"accessKeyID"`
- SecretAccessKey string `yaml:"secretAccessKey"`
- SessionToken string `yaml:"sessionToken"`
- SignEndpoint string `yaml:"signEndpoint"`
- PublicRead bool `yaml:"publicRead"`
- } `yaml:"minio"`
- Cos struct {
- BucketURL string `yaml:"bucketURL"`
- SecretID string `yaml:"secretID"`
- SecretKey string `yaml:"secretKey"`
- SessionToken string `yaml:"sessionToken"`
- PublicRead bool `yaml:"publicRead"`
- } `yaml:"cos"`
- Oss struct {
- Endpoint string `yaml:"endpoint"`
- Bucket string `yaml:"bucket"`
- BucketURL string `yaml:"bucketURL"`
- AccessKeyID string `yaml:"accessKeyID"`
- AccessKeySecret string `yaml:"accessKeySecret"`
- SessionToken string `yaml:"sessionToken"`
- PublicRead bool `yaml:"publicRead"`
- } `yaml:"oss"`
- Kodo struct {
- Endpoint string `yaml:"endpoint"`
- Bucket string `yaml:"bucket"`
- BucketURL string `yaml:"bucketURL"`
- AccessKeyID string `yaml:"accessKeyID"`
- AccessKeySecret string `yaml:"accessKeySecret"`
- SessionToken string `yaml:"sessionToken"`
- PublicRead bool `yaml:"publicRead"`
- } `yaml:"kodo"`
- Aws struct {
- Endpoint string `yaml:"endpoint"`
- Region string `yaml:"region"`
- Bucket string `yaml:"bucket"`
- AccessKeyID string `yaml:"accessKeyID"`
- AccessKeySecret string `yaml:"accessKeySecret"`
- PublicRead bool `yaml:"publicRead"`
- } `yaml:"aws"`
- } `yaml:"object"`
-
- RpcPort struct {
- OpenImUserPort []int `yaml:"openImUserPort"`
- OpenImFriendPort []int `yaml:"openImFriendPort"`
- OpenImMessagePort []int `yaml:"openImMessagePort"`
- OpenImMessageGatewayPort []int `yaml:"openImMessageGatewayPort"`
- OpenImGroupPort []int `yaml:"openImGroupPort"`
- OpenImAuthPort []int `yaml:"openImAuthPort"`
- OpenImPushPort []int `yaml:"openImPushPort"`
- OpenImConversationPort []int `yaml:"openImConversationPort"`
- OpenImRtcPort []int `yaml:"openImRtcPort"`
- OpenImThirdPort []int `yaml:"openImThirdPort"`
- } `yaml:"rpcPort"`
-
- RpcRegisterName struct {
- OpenImUserName string `yaml:"openImUserName"`
- OpenImFriendName string `yaml:"openImFriendName"`
- OpenImMsgName string `yaml:"openImMsgName"`
- OpenImPushName string `yaml:"openImPushName"`
- OpenImMessageGatewayName string `yaml:"openImMessageGatewayName"`
- OpenImGroupName string `yaml:"openImGroupName"`
- OpenImAuthName string `yaml:"openImAuthName"`
- OpenImConversationName string `yaml:"openImConversationName"`
- OpenImThirdName string `yaml:"openImThirdName"`
- } `yaml:"rpcRegisterName"`
-
- Log struct {
- StorageLocation string `yaml:"storageLocation"`
- RotationTime uint `yaml:"rotationTime"`
- RemainRotationCount uint `yaml:"remainRotationCount"`
- RemainLogLevel int `yaml:"remainLogLevel"`
- IsStdout bool `yaml:"isStdout"`
- IsJson bool `yaml:"isJson"`
- WithStack bool `yaml:"withStack"`
- } `yaml:"log"`
+type OfflinePushConfig struct {
+ Enable bool `mapstructure:"enable"`
+ Title string `mapstructure:"title"`
+ Desc string `mapstructure:"desc"`
+ Ext string `mapstructure:"ext"`
+}
+
+type NotificationConfig struct {
+ IsSendMsg bool `mapstructure:"isSendMsg"`
+ ReliabilityLevel int `mapstructure:"reliabilityLevel"`
+ UnreadCount bool `mapstructure:"unreadCount"`
+ OfflinePush OfflinePushConfig `mapstructure:"offlinePush"`
+}
+
+type Notification struct {
+ GroupCreated NotificationConfig `mapstructure:"groupCreated"`
+ GroupInfoSet NotificationConfig `mapstructure:"groupInfoSet"`
+ JoinGroupApplication NotificationConfig `mapstructure:"joinGroupApplication"`
+ MemberQuit NotificationConfig `mapstructure:"memberQuit"`
+ GroupApplicationAccepted NotificationConfig `mapstructure:"groupApplicationAccepted"`
+ GroupApplicationRejected NotificationConfig `mapstructure:"groupApplicationRejected"`
+ GroupOwnerTransferred NotificationConfig `mapstructure:"groupOwnerTransferred"`
+ MemberKicked NotificationConfig `mapstructure:"memberKicked"`
+ MemberInvited NotificationConfig `mapstructure:"memberInvited"`
+ MemberEnter NotificationConfig `mapstructure:"memberEnter"`
+ GroupDismissed NotificationConfig `mapstructure:"groupDismissed"`
+ GroupMuted NotificationConfig `mapstructure:"groupMuted"`
+ GroupCancelMuted NotificationConfig `mapstructure:"groupCancelMuted"`
+ GroupMemberMuted NotificationConfig `mapstructure:"groupMemberMuted"`
+ GroupMemberCancelMuted NotificationConfig `mapstructure:"groupMemberCancelMuted"`
+ GroupMemberInfoSet NotificationConfig `mapstructure:"groupMemberInfoSet"`
+ GroupMemberSetToAdmin NotificationConfig `yaml:"groupMemberSetToAdmin"`
+ GroupMemberSetToOrdinary NotificationConfig `yaml:"groupMemberSetToOrdinaryUser"`
+ GroupInfoSetAnnouncement NotificationConfig `mapstructure:"groupInfoSetAnnouncement"`
+ GroupInfoSetName NotificationConfig `mapstructure:"groupInfoSetName"`
+ FriendApplicationAdded NotificationConfig `mapstructure:"friendApplicationAdded"`
+ FriendApplicationApproved NotificationConfig `mapstructure:"friendApplicationApproved"`
+ FriendApplicationRejected NotificationConfig `mapstructure:"friendApplicationRejected"`
+ FriendAdded NotificationConfig `mapstructure:"friendAdded"`
+ FriendDeleted NotificationConfig `mapstructure:"friendDeleted"`
+ FriendRemarkSet NotificationConfig `mapstructure:"friendRemarkSet"`
+ BlackAdded NotificationConfig `mapstructure:"blackAdded"`
+ BlackDeleted NotificationConfig `mapstructure:"blackDeleted"`
+ FriendInfoUpdated NotificationConfig `mapstructure:"friendInfoUpdated"`
+ UserInfoUpdated NotificationConfig `mapstructure:"userInfoUpdated"`
+ UserStatusChanged NotificationConfig `mapstructure:"userStatusChanged"`
+ ConversationChanged NotificationConfig `mapstructure:"conversationChanged"`
+ ConversationSetPrivate NotificationConfig `mapstructure:"conversationSetPrivate"`
+}
+type Prometheus struct {
+ Enable bool `mapstructure:"enable"`
+ Ports []int `mapstructure:"ports"`
+}
+
+type MsgGateway struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
+ ListenIP string `mapstructure:"listenIP"`
LongConnSvr struct {
- OpenImMessageGatewayPort []int `yaml:"openImMessageGatewayPort"`
- OpenImWsPort []int `yaml:"openImWsPort"`
- WebsocketMaxConnNum int `yaml:"websocketMaxConnNum"`
- WebsocketMaxMsgLen int `yaml:"websocketMaxMsgLen"`
- WebsocketTimeout int `yaml:"websocketTimeout"`
- WebsocketWriteBufferSize int `yaml:"websocketWriteBufferSize"`
- } `yaml:"longConnSvr"`
-
- Push struct {
- MaxConcurrentWorkers int `yaml:"maxConcurrentWorkers"`
- Enable string `yaml:"enable"`
- GeTui struct {
- PushUrl string `yaml:"pushUrl"`
- AppKey string `yaml:"appKey"`
- Intent string `yaml:"intent"`
- MasterSecret string `yaml:"masterSecret"`
- ChannelID string `yaml:"channelID"`
- ChannelName string `yaml:"channelName"`
- } `yaml:"geTui"`
- Fcm struct {
- ServiceAccount string `yaml:"serviceAccount"`
- } `yaml:"fcm"`
- Jpns struct {
- AppKey string `yaml:"appKey"`
- MasterSecret string `yaml:"masterSecret"`
- PushUrl string `yaml:"pushUrl"`
- PushIntent string `yaml:"pushIntent"`
- } `yaml:"jpns"`
- }
- Manager struct {
- UserID []string `yaml:"userID"`
- Nickname []string `yaml:"nickname"`
- } `yaml:"manager"`
-
- IMAdmin struct {
- UserID []string `yaml:"userID"`
- Nickname []string `yaml:"nickname"`
- } `yaml:"im-admin"`
-
- MultiLoginPolicy int `yaml:"multiLoginPolicy"`
- ChatPersistenceMysql bool `yaml:"chatPersistenceMysql"`
- MsgCacheTimeout int `yaml:"msgCacheTimeout"`
- GroupMessageHasReadReceiptEnable bool `yaml:"groupMessageHasReadReceiptEnable"`
- SingleMessageHasReadReceiptEnable bool `yaml:"singleMessageHasReadReceiptEnable"`
- RetainChatRecords int `yaml:"retainChatRecords"`
- ChatRecordsClearTime string `yaml:"chatRecordsClearTime"`
- MsgDestructTime string `yaml:"msgDestructTime"`
- Secret string `yaml:"secret"`
- EnableCronLocker bool `yaml:"enableCronLocker"`
- TokenPolicy struct {
- Expire int64 `yaml:"expire"`
- } `yaml:"tokenPolicy"`
- MessageVerify struct {
- FriendVerify *bool `yaml:"friendVerify"`
- } `yaml:"messageVerify"`
-
- LocalCache localCache `yaml:"localCache"`
+ Ports []int `mapstructure:"ports"`
+ WebsocketMaxConnNum int `mapstructure:"websocketMaxConnNum"`
+ WebsocketMaxMsgLen int `mapstructure:"websocketMaxMsgLen"`
+ WebsocketTimeout int `mapstructure:"websocketTimeout"`
+ } `mapstructure:"longConnSvr"`
+ MultiLoginPolicy int `mapstructure:"multiLoginPolicy"`
+}
+type MsgTransfer struct {
+ Prometheus Prometheus `mapstructure:"prometheus"`
+}
+
+type Push struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
+ MaxConcurrentWorkers int `mapstructure:"maxConcurrentWorkers"`
+ Enable string `mapstructure:"enable"`
+ GeTui struct {
+ PushUrl string `mapstructure:"pushUrl"`
+ MasterSecret string `mapstructure:"masterSecret"`
+ AppKey string `mapstructure:"appKey"`
+ Intent string `mapstructure:"intent"`
+ ChannelID string `mapstructure:"channelID"`
+ ChannelName string `mapstructure:"channelName"`
+ } `mapstructure:"geTui"`
+ FCM struct {
+ ServiceAccount string `mapstructure:"serviceAccount"`
+ } `mapstructure:"fcm"`
+ JPNS struct {
+ AppKey string `mapstructure:"appKey"`
+ MasterSecret string `mapstructure:"masterSecret"`
+ PushURL string `mapstructure:"pushURL"`
+ PushIntent string `mapstructure:"pushIntent"`
+ } `mapstructure:"jpns"`
IOSPush struct {
- PushSound string `yaml:"pushSound"`
- BadgeCount bool `yaml:"badgeCount"`
- Production bool `yaml:"production"`
- } `yaml:"iosPush"`
- Callback struct {
- CallbackUrl string `yaml:"url"`
- CallbackBeforeSendSingleMsg CallBackConfig `yaml:"beforeSendSingleMsg"`
- CallbackAfterSendSingleMsg CallBackConfig `yaml:"afterSendSingleMsg"`
- CallbackBeforeSendGroupMsg CallBackConfig `yaml:"beforeSendGroupMsg"`
- CallbackAfterSendGroupMsg CallBackConfig `yaml:"afterSendGroupMsg"`
- CallbackMsgModify CallBackConfig `yaml:"msgModify"`
- CallbackSingleMsgRead CallBackConfig `yaml:"singleMsgRead"`
- CallbackGroupMsgRead CallBackConfig `yaml:"groupMsgRead"`
- CallbackUserOnline CallBackConfig `yaml:"userOnline"`
- CallbackUserOffline CallBackConfig `yaml:"userOffline"`
- CallbackUserKickOff CallBackConfig `yaml:"userKickOff"`
- CallbackOfflinePush CallBackConfig `yaml:"offlinePush"`
- CallbackOnlinePush CallBackConfig `yaml:"onlinePush"`
- CallbackBeforeSuperGroupOnlinePush CallBackConfig `yaml:"superGroupOnlinePush"`
- CallbackBeforeAddFriend CallBackConfig `yaml:"beforeAddFriend"`
- CallbackBeforeSetFriendRemark CallBackConfig `yaml:"callbackBeforeSetFriendRemark"`
- CallbackAfterSetFriendRemark CallBackConfig `yaml:"callbackAfterSetFriendRemark"`
- CallbackBeforeUpdateUserInfo CallBackConfig `yaml:"beforeUpdateUserInfo"`
- CallbackBeforeUpdateUserInfoEx CallBackConfig `yaml:"beforeUpdateUserInfoEx"`
- CallbackAfterUpdateUserInfoEx CallBackConfig `yaml:"afterUpdateUserInfoEx"`
- CallbackBeforeUserRegister CallBackConfig `yaml:"beforeUserRegister"`
- CallbackAfterUpdateUserInfo CallBackConfig `yaml:"updateUserInfo"`
- CallbackAfterUserRegister CallBackConfig `yaml:"afterUserRegister"`
- CallbackBeforeCreateGroup CallBackConfig `yaml:"beforeCreateGroup"`
- CallbackAfterCreateGroup CallBackConfig `yaml:"afterCreateGroup"`
- CallbackBeforeMemberJoinGroup CallBackConfig `yaml:"beforeMemberJoinGroup"`
- CallbackBeforeSetGroupMemberInfo CallBackConfig `yaml:"beforeSetGroupMemberInfo"`
- CallbackAfterSetGroupMemberInfo CallBackConfig `yaml:"afterSetGroupMemberInfo"`
- CallbackQuitGroup CallBackConfig `yaml:"quitGroup"`
- CallbackKillGroupMember CallBackConfig `yaml:"killGroupMember"`
- CallbackDismissGroup CallBackConfig `yaml:"dismissGroup"`
- CallbackBeforeJoinGroup CallBackConfig `yaml:"joinGroup"`
- CallbackAfterTransferGroupOwner CallBackConfig `yaml:"transferGroupOwner"`
- CallbackBeforeInviteUserToGroup CallBackConfig `yaml:"beforeInviteUserToGroup"`
- CallbackAfterJoinGroup CallBackConfig `yaml:"joinGroupAfter"`
- CallbackAfterSetGroupInfo CallBackConfig `yaml:"setGroupInfoAfter"`
- CallbackBeforeSetGroupInfo CallBackConfig `yaml:"setGroupInfoBefore"`
- CallbackAfterRevokeMsg CallBackConfig `yaml:"revokeMsgAfter"`
- CallbackBeforeAddBlack CallBackConfig `yaml:"addBlackBefore"`
- CallbackAfterAddFriend CallBackConfig `yaml:"addFriendAfter"`
- CallbackBeforeAddFriendAgree CallBackConfig `yaml:"addFriendAgreeBefore"`
-
- CallbackAfterDeleteFriend CallBackConfig `yaml:"deleteFriendAfter"`
- CallbackBeforeImportFriends CallBackConfig `yaml:"importFriendsBefore"`
- CallbackAfterImportFriends CallBackConfig `yaml:"importFriendsAfter"`
- CallbackAfterRemoveBlack CallBackConfig `yaml:"removeBlackAfter"`
- } `yaml:"callback"`
+ PushSound string `mapstructure:"pushSound"`
+ BadgeCount bool `mapstructure:"badgeCount"`
+ Production bool `mapstructure:"production"`
+ } `mapstructure:"iosPush"`
+}
- Prometheus struct {
- Enable bool `yaml:"enable"`
- GrafanaUrl string `yaml:"grafanaUrl"`
- ApiPrometheusPort []int `yaml:"apiPrometheusPort"`
- UserPrometheusPort []int `yaml:"userPrometheusPort"`
- FriendPrometheusPort []int `yaml:"friendPrometheusPort"`
- MessagePrometheusPort []int `yaml:"messagePrometheusPort"`
- MessageGatewayPrometheusPort []int `yaml:"messageGatewayPrometheusPort"`
- GroupPrometheusPort []int `yaml:"groupPrometheusPort"`
- AuthPrometheusPort []int `yaml:"authPrometheusPort"`
- PushPrometheusPort []int `yaml:"pushPrometheusPort"`
- ConversationPrometheusPort []int `yaml:"conversationPrometheusPort"`
- RtcPrometheusPort []int `yaml:"rtcPrometheusPort"`
- MessageTransferPrometheusPort []int `yaml:"messageTransferPrometheusPort"`
- ThirdPrometheusPort []int `yaml:"thirdPrometheusPort"`
- } `yaml:"prometheus"`
- Notification notification `yaml:"notification"`
-}
-
-func NewGlobalConfig() *GlobalConfig {
- return &GlobalConfig{}
-}
-
-type notification struct {
- GroupCreated NotificationConf `yaml:"groupCreated"`
- GroupInfoSet NotificationConf `yaml:"groupInfoSet"`
- JoinGroupApplication NotificationConf `yaml:"joinGroupApplication"`
- MemberQuit NotificationConf `yaml:"memberQuit"`
- GroupApplicationAccepted NotificationConf `yaml:"groupApplicationAccepted"`
- GroupApplicationRejected NotificationConf `yaml:"groupApplicationRejected"`
- GroupOwnerTransferred NotificationConf `yaml:"groupOwnerTransferred"`
- MemberKicked NotificationConf `yaml:"memberKicked"`
- MemberInvited NotificationConf `yaml:"memberInvited"`
- MemberEnter NotificationConf `yaml:"memberEnter"`
- GroupDismissed NotificationConf `yaml:"groupDismissed"`
- GroupMuted NotificationConf `yaml:"groupMuted"`
- GroupCancelMuted NotificationConf `yaml:"groupCancelMuted"`
- GroupMemberMuted NotificationConf `yaml:"groupMemberMuted"`
- GroupMemberCancelMuted NotificationConf `yaml:"groupMemberCancelMuted"`
- GroupMemberInfoSet NotificationConf `yaml:"groupMemberInfoSet"`
- GroupMemberSetToAdmin NotificationConf `yaml:"groupMemberSetToAdmin"`
- GroupMemberSetToOrdinary NotificationConf `yaml:"groupMemberSetToOrdinaryUser"`
- GroupInfoSetAnnouncement NotificationConf `yaml:"groupInfoSetAnnouncement"`
- GroupInfoSetName NotificationConf `yaml:"groupInfoSetName"`
- ////////////////////////user///////////////////////
- UserInfoUpdated NotificationConf `yaml:"userInfoUpdated"`
- UserStatusChanged NotificationConf `yaml:"userStatusChanged"`
- //////////////////////friend///////////////////////
- FriendApplicationAdded NotificationConf `yaml:"friendApplicationAdded"`
- FriendApplicationApproved NotificationConf `yaml:"friendApplicationApproved"`
- FriendApplicationRejected NotificationConf `yaml:"friendApplicationRejected"`
- FriendAdded NotificationConf `yaml:"friendAdded"`
- FriendDeleted NotificationConf `yaml:"friendDeleted"`
- FriendRemarkSet NotificationConf `yaml:"friendRemarkSet"`
- BlackAdded NotificationConf `yaml:"blackAdded"`
- BlackDeleted NotificationConf `yaml:"blackDeleted"`
- FriendInfoUpdated NotificationConf `yaml:"friendInfoUpdated"`
- //////////////////////conversation///////////////////////
- ConversationChanged NotificationConf `yaml:"conversationChanged"`
- ConversationSetPrivate NotificationConf `yaml:"conversationSetPrivate"`
+type Auth struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
+ TokenPolicy struct {
+ Expire int64 `mapstructure:"expire"`
+ } `mapstructure:"tokenPolicy"`
}
-type LocalCache struct {
- Topic string `yaml:"topic"`
- SlotNum int `yaml:"slotNum"`
- SlotSize int `yaml:"slotSize"`
- SuccessExpire int `yaml:"successExpire"` // second
- FailedExpire int `yaml:"failedExpire"` // second
+type Conversation struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
}
-func (l LocalCache) Failed() time.Duration {
- return time.Second * time.Duration(l.FailedExpire)
+type Friend struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
}
-func (l LocalCache) Success() time.Duration {
- return time.Second * time.Duration(l.SuccessExpire)
+type Group struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
}
-func (l LocalCache) Enable() bool {
- return l.Topic != "" && l.SlotNum > 0 && l.SlotSize > 0
+type Msg struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
+ FriendVerify bool `mapstructure:"friendVerify"`
+}
+
+type Third struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
+ Object struct {
+ Enable string `mapstructure:"enable"`
+ Cos Cos `mapstructure:"cos"`
+ Oss Oss `mapstructure:"oss"`
+ Kodo struct {
+ Endpoint string `mapstructure:"endpoint"`
+ Bucket string `mapstructure:"bucket"`
+ BucketURL string `mapstructure:"bucketURL"`
+ AccessKeyID string `mapstructure:"accessKeyID"`
+ AccessKeySecret string `mapstructure:"accessKeySecret"`
+ SessionToken string `mapstructure:"sessionToken"`
+ PublicRead bool `mapstructure:"publicRead"`
+ } `mapstructure:"kodo"`
+ Aws struct {
+ Endpoint string `mapstructure:"endpoint"`
+ Region string `mapstructure:"region"`
+ Bucket string `mapstructure:"bucket"`
+ AccessKeyID string `mapstructure:"accessKeyID"`
+ AccessKeySecret string `mapstructure:"accessKeySecret"`
+ PublicRead bool `mapstructure:"publicRead"`
+ } `mapstructure:"aws"`
+ } `mapstructure:"object"`
+}
+type Cos struct {
+ BucketURL string `mapstructure:"bucketURL"`
+ SecretID string `mapstructure:"secretID"`
+ SecretKey string `mapstructure:"secretKey"`
+ SessionToken string `mapstructure:"sessionToken"`
+ PublicRead bool `mapstructure:"publicRead"`
+}
+type Oss struct {
+ Endpoint string `mapstructure:"endpoint"`
+ Bucket string `mapstructure:"bucket"`
+ BucketURL string `mapstructure:"bucketURL"`
+ AccessKeyID string `mapstructure:"accessKeyID"`
+ AccessKeySecret string `mapstructure:"accessKeySecret"`
+ SessionToken string `mapstructure:"sessionToken"`
+ PublicRead bool `mapstructure:"publicRead"`
+}
+
+type User struct {
+ RPC struct {
+ RegisterIP string `mapstructure:"registerIP"`
+ ListenIP string `mapstructure:"listenIP"`
+ Ports []int `mapstructure:"ports"`
+ } `mapstructure:"rpc"`
+ Prometheus Prometheus `mapstructure:"prometheus"`
+}
+
+type Redis struct {
+ Address []string `mapstructure:"address"`
+ Username string `mapstructure:"username"`
+ Password string `mapstructure:"password"`
+ EnablePipeline bool `mapstructure:"enablePipeline"`
+ ClusterMode bool `mapstructure:"clusterMode"`
+ DB int `mapstructure:"db"`
+ MaxRetry int `mapstructure:"MaxRetry"`
}
-type localCache struct {
- User LocalCache `yaml:"user"`
- Group LocalCache `yaml:"group"`
- Friend LocalCache `yaml:"friend"`
- Conversation LocalCache `yaml:"conversation"`
+type BeforeConfig struct {
+ Enable bool `mapstructure:"enable"`
+ Timeout int `mapstructure:"timeout"`
+ FailedContinue bool `mapstructure:"failedContinue"`
}
-func (c *GlobalConfig) GetServiceNames() []string {
+type AfterConfig struct {
+ Enable bool `mapstructure:"enable"`
+ Timeout int `mapstructure:"timeout"`
+}
+
+type Share struct {
+ Secret string `mapstructure:"secret"`
+ Env string `mapstructure:"env"`
+ RpcRegisterName RpcRegisterName `mapstructure:"rpcRegisterName"`
+ IMAdminUserID []string `mapstructure:"imAdminUserID"`
+}
+type RpcRegisterName struct {
+ User string `mapstructure:"user"`
+ Friend string `mapstructure:"friend"`
+ Msg string `mapstructure:"msg"`
+ Push string `mapstructure:"push"`
+ MessageGateway string `mapstructure:"messageGateway"`
+ Group string `mapstructure:"group"`
+ Auth string `mapstructure:"auth"`
+ Conversation string `mapstructure:"conversation"`
+ Third string `mapstructure:"third"`
+}
+
+func (r *RpcRegisterName) GetServiceNames() []string {
return []string{
- c.RpcRegisterName.OpenImUserName,
- c.RpcRegisterName.OpenImFriendName,
- c.RpcRegisterName.OpenImMsgName,
- c.RpcRegisterName.OpenImPushName,
- c.RpcRegisterName.OpenImMessageGatewayName,
- c.RpcRegisterName.OpenImGroupName,
- c.RpcRegisterName.OpenImAuthName,
- c.RpcRegisterName.OpenImConversationName,
- c.RpcRegisterName.OpenImThirdName,
+ r.User,
+ r.Friend,
+ r.Msg,
+ r.Push,
+ r.MessageGateway,
+ r.Group,
+ r.Auth,
+ r.Conversation,
+ r.Third,
}
}
-func (c *GlobalConfig) RegisterConf2Registry(registry discoveryregistry.SvcDiscoveryRegistry) error {
- data, err := yaml.Marshal(c)
- if err != nil {
- return err
+// FullConfig stores all configurations for before and after events
+type Webhooks struct {
+ URL string `mapstructure:"url"`
+ BeforeSendSingleMsg BeforeConfig `mapstructure:"beforeSendSingleMsg"`
+ BeforeUpdateUserInfoEx BeforeConfig `mapstructure:"beforeUpdateUserInfoEx"`
+ AfterUpdateUserInfoEx AfterConfig `mapstructure:"afterUpdateUserInfoEx"`
+ AfterSendSingleMsg AfterConfig `mapstructure:"afterSendSingleMsg"`
+ BeforeSendGroupMsg BeforeConfig `mapstructure:"beforeSendGroupMsg"`
+ BeforeMsgModify BeforeConfig `mapstructure:"beforeMsgModify"`
+ AfterSendGroupMsg AfterConfig `mapstructure:"afterSendGroupMsg"`
+ AfterUserOnline AfterConfig `mapstructure:"afterUserOnline"`
+ AfterUserOffline AfterConfig `mapstructure:"afterUserOffline"`
+ AfterUserKickOff AfterConfig `mapstructure:"afterUserKickOff"`
+ BeforeOfflinePush BeforeConfig `mapstructure:"beforeOfflinePush"`
+ BeforeOnlinePush BeforeConfig `mapstructure:"beforeOnlinePush"`
+ BeforeGroupOnlinePush BeforeConfig `mapstructure:"beforeGroupOnlinePush"`
+ BeforeAddFriend BeforeConfig `mapstructure:"beforeAddFriend"`
+ BeforeUpdateUserInfo BeforeConfig `mapstructure:"beforeUpdateUserInfo"`
+ AfterUpdateUserInfo AfterConfig `mapstructure:"afterUpdateUserInfo"`
+ BeforeCreateGroup BeforeConfig `mapstructure:"beforeCreateGroup"`
+ AfterCreateGroup AfterConfig `mapstructure:"afterCreateGroup"`
+ BeforeMemberJoinGroup BeforeConfig `mapstructure:"beforeMemberJoinGroup"`
+ BeforeSetGroupMemberInfo BeforeConfig `mapstructure:"beforeSetGroupMemberInfo"`
+ AfterSetGroupMemberInfo AfterConfig `mapstructure:"afterSetGroupMemberInfo"`
+ AfterQuitGroup AfterConfig `mapstructure:"afterQuitGroup"`
+ AfterKickGroupMember AfterConfig `mapstructure:"afterKickGroupMember"`
+ AfterDismissGroup AfterConfig `mapstructure:"afterDismissGroup"`
+ BeforeApplyJoinGroup BeforeConfig `mapstructure:"beforeApplyJoinGroup"`
+ AfterGroupMsgRead AfterConfig `mapstructure:"afterGroupMsgRead"`
+ AfterSingleMsgRead AfterConfig `mapstructure:"afterSingleMsgRead"`
+ BeforeUserRegister BeforeConfig `mapstructure:"beforeUserRegister"`
+ AfterUserRegister AfterConfig `mapstructure:"afterUserRegister"`
+ AfterTransferGroupOwner AfterConfig `mapstructure:"afterTransferGroupOwner"`
+ BeforeSetFriendRemark BeforeConfig `mapstructure:"beforeSetFriendRemark"`
+ AfterSetFriendRemark AfterConfig `mapstructure:"afterSetFriendRemark"`
+ AfterGroupMsgRevoke AfterConfig `mapstructure:"afterGroupMsgRevoke"`
+ AfterJoinGroup AfterConfig `mapstructure:"afterJoinGroup"`
+ BeforeInviteUserToGroup BeforeConfig `mapstructure:"beforeInviteUserToGroup"`
+ AfterSetGroupInfo AfterConfig `mapstructure:"afterSetGroupInfo"`
+ BeforeSetGroupInfo BeforeConfig `mapstructure:"beforeSetGroupInfo"`
+ AfterRevokeMsg AfterConfig `mapstructure:"afterRevokeMsg"`
+ BeforeAddBlack BeforeConfig `mapstructure:"beforeAddBlack"`
+ AfterAddFriend AfterConfig `mapstructure:"afterAddFriend"`
+ BeforeAddFriendAgree BeforeConfig `mapstructure:"beforeAddFriendAgree"`
+ AfterDeleteFriend AfterConfig `mapstructure:"afterDeleteFriend"`
+ BeforeImportFriends BeforeConfig `mapstructure:"beforeImportFriends"`
+ AfterImportFriends AfterConfig `mapstructure:"afterImportFriends"`
+ AfterRemoveBlack AfterConfig `mapstructure:"afterRemoveBlack"`
+}
+
+type ZooKeeper struct {
+ Schema string `mapstructure:"schema"`
+ Address []string `mapstructure:"address"`
+ Username string `mapstructure:"username"`
+ Password string `mapstructure:"password"`
+}
+
+func (m *Mongo) Build() *mongoutil.Config {
+ return &mongoutil.Config{
+ Uri: m.URI,
+ Address: m.Address,
+ Database: m.Database,
+ Username: m.Username,
+ Password: m.Password,
+ MaxPoolSize: m.MaxPoolSize,
+ MaxRetry: m.MaxRetry,
}
- return registry.RegisterConf2Registry(ConfKey, data)
}
-func (c *GlobalConfig) GetConfFromRegistry(registry discoveryregistry.SvcDiscoveryRegistry) ([]byte, error) {
- return registry.GetConfFromRegistry(ConfKey)
+func (r *Redis) Build() *redisutil.Config {
+ return &redisutil.Config{
+ ClusterMode: r.ClusterMode,
+ Address: r.Address,
+ Username: r.Username,
+ Password: r.Password,
+ DB: r.DB,
+ MaxRetry: r.MaxRetry,
+ }
}
-func (c *GlobalConfig) EncodeConfig() []byte {
- buf := bytes.NewBuffer(nil)
- if err := yaml.NewEncoder(buf).Encode(c); err != nil {
- panic(err)
+func (k *Kafka) Build() *kafka.Config {
+ return &kafka.Config{
+ Username: k.Username,
+ Password: k.Password,
+ ProducerAck: k.ProducerAck,
+ CompressType: k.CompressType,
+ Addr: k.Address,
+ TLS: kafka.TLSConfig{
+ EnableTLS: k.Tls.EnableTLS,
+ CACrt: k.Tls.CACrt,
+ ClientCrt: k.Tls.ClientCrt,
+ ClientKey: k.Tls.ClientKey,
+ ClientKeyPwd: k.Tls.ClientKeyPwd,
+ InsecureSkipVerify: k.Tls.InsecureSkipVerify,
+ },
+ }
+}
+func (m *Minio) Build() *minio.Config {
+ conf := minio.Config{
+ Bucket: m.Bucket,
+ AccessKeyID: m.AccessKeyID,
+ SecretAccessKey: m.SecretAccessKey,
+ SessionToken: m.SessionToken,
+ PublicRead: m.PublicRead,
}
- return buf.Bytes()
+ if _, _, err := net.SplitHostPort(m.InternalAddress); err == nil {
+ conf.Endpoint = fmt.Sprintf("http://%s", m.InternalAddress)
+ } else {
+ conf.Endpoint = m.InternalAddress
+ }
+ if _, _, err := net.SplitHostPort(m.ExternalAddress); err == nil {
+ conf.SignEndpoint = fmt.Sprintf("http://%s", m.ExternalAddress)
+ } else {
+ conf.SignEndpoint = m.ExternalAddress
+ }
+ return &conf
+}
+func (c *Cos) Build() *cos.Config {
+ return &cos.Config{
+ BucketURL: c.BucketURL,
+ SecretID: c.SecretID,
+ SecretKey: c.SecretKey,
+ SessionToken: c.SessionToken,
+ PublicRead: c.PublicRead,
+ }
+}
+
+func (o *Oss) Build() *oss.Config {
+ return &oss.Config{
+ Endpoint: o.Endpoint,
+ Bucket: o.Bucket,
+ BucketURL: o.BucketURL,
+ AccessKeyID: o.AccessKeyID,
+ AccessKeySecret: o.AccessKeySecret,
+ SessionToken: o.SessionToken,
+ PublicRead: o.PublicRead,
+ }
+}
+
+func (l *CacheConfig) Failed() time.Duration {
+ return time.Second * time.Duration(l.FailedExpire)
+}
+
+func (l *CacheConfig) Success() time.Duration {
+ return time.Second * time.Duration(l.SuccessExpire)
+}
+
+func (l *CacheConfig) Enable() bool {
+ return l.Topic != "" && l.SlotNum > 0 && l.SlotSize > 0
}
diff --git a/pkg/common/config/constant.go b/pkg/common/config/constant.go
new file mode 100644
index 0000000000..f425a624ce
--- /dev/null
+++ b/pkg/common/config/constant.go
@@ -0,0 +1,37 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+const ConfKey = "conf"
+
+const (
+ // DefaultDirPerm is used for creating general directories, allowing the owner to read, write, and execute,
+ // while the group and others can only read and execute.
+ DefaultDirPerm = 0755
+
+ // PrivateFilePerm is used for sensitive files, allowing only the owner to read and write.
+ PrivateFilePerm = 0600
+
+ // ExecFilePerm is used for executable files, allowing the owner to read, write, and execute,
+ // while the group and others can only read.
+ ExecFilePerm = 0754
+
+ // SharedDirPerm is used for shared directories, allowing the owner and group to read, write, and execute,
+ // with no permissions for others.
+ SharedDirPerm = 0770
+
+ // ReadOnlyDirPerm is used for read-only directories, allowing the owner, group, and others to only read.
+ ReadOnlyDirPerm = 0555
+)
diff --git a/pkg/common/config/doc.go b/pkg/common/config/doc.go
index 4b76cb9fa6..189a5b5834 100644
--- a/pkg/common/config/doc.go
+++ b/pkg/common/config/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/common/config/load_config.go b/pkg/common/config/load_config.go
new file mode 100644
index 0000000000..9272896b45
--- /dev/null
+++ b/pkg/common/config/load_config.go
@@ -0,0 +1,27 @@
+package config
+
+import (
+ "github.com/mitchellh/mapstructure"
+ "github.com/openimsdk/tools/errs"
+ "github.com/spf13/viper"
+ "strings"
+)
+
+func LoadConfig(path string, envPrefix string, config any) error {
+ v := viper.New()
+ v.SetConfigFile(path)
+ v.SetEnvPrefix(envPrefix)
+ v.AutomaticEnv()
+ v.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
+
+ if err := v.ReadInConfig(); err != nil {
+ return errs.WrapMsg(err, "failed to read config file", "path", path, "envPrefix", envPrefix)
+ }
+
+ if err := v.Unmarshal(config, func(config *mapstructure.DecoderConfig) {
+ config.TagName = "mapstructure"
+ }); err != nil {
+ return errs.WrapMsg(err, "failed to unmarshal config", "path", path, "envPrefix", envPrefix)
+ }
+ return nil
+}
diff --git a/pkg/common/config/load_config_test.go b/pkg/common/config/load_config_test.go
new file mode 100644
index 0000000000..256214565b
--- /dev/null
+++ b/pkg/common/config/load_config_test.go
@@ -0,0 +1,38 @@
+package config
+
+import (
+ "github.com/stretchr/testify/assert"
+ "testing"
+)
+
+func TestLoadLogConfig(t *testing.T) {
+ var log Log
+ err := LoadConfig("../../../config/log.yml", "IMENV_LOG", &log)
+ assert.Nil(t, err)
+ assert.Equal(t, "../../../../logs/", log.StorageLocation)
+}
+
+func TestLoadMinioConfig(t *testing.T) {
+ var storageConfig Minio
+ err := LoadConfig("../../../config/minio.yml", "IMENV_MINIO", &storageConfig)
+ assert.Nil(t, err)
+ assert.Equal(t, "openim", storageConfig.Bucket)
+}
+
+func TestLoadWebhooksConfig(t *testing.T) {
+ var webhooks Webhooks
+ err := LoadConfig("../../../config/webhooks.yml", "IMENV_WEBHOOKS", &webhooks)
+ assert.Nil(t, err)
+ assert.Equal(t, 5, webhooks.BeforeAddBlack.Timeout)
+
+}
+
+func TestLoadOpenIMRpcUserConfig(t *testing.T) {
+ var user User
+ err := LoadConfig("../../../config/openim-rpc-user.yml", "IMENV_OPENIM_RPC_USER", &user)
+ assert.Nil(t, err)
+ //export IMENV_OPENIM_RPC_USER_RPC_LISTENIP="0.0.0.0"
+ assert.Equal(t, "0.0.0.0", user.RPC.ListenIP)
+ //export IMENV_OPENIM_RPC_USER_RPC_PORTS="10110,10111,10112"
+ assert.Equal(t, []int{10110, 10111, 10112}, user.RPC.Ports)
+}
diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go
index bfbf6daf7b..28e9f5db60 100644
--- a/pkg/common/config/parse.go
+++ b/pkg/common/config/parse.go
@@ -16,15 +16,14 @@ package config
import (
_ "embed"
- "fmt"
"os"
"path/filepath"
- "github.com/OpenIMSDK/protocol/constant"
- "gopkg.in/yaml.v3"
-
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
- "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/field"
+ "gopkg.in/yaml.v3"
)
//go:embed version
@@ -37,34 +36,33 @@ const (
)
// return absolude path join ../config/, this is k8s container config path.
-func GetDefaultConfigPath() string {
+func GetDefaultConfigPath() (string, error) {
executablePath, err := os.Executable()
if err != nil {
- fmt.Println("GetDefaultConfigPath error:", err.Error())
- return ""
+ return "", errs.WrapMsg(err, "failed to get executable path")
}
- configPath, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../config/"))
+ configPath, err := field.OutDir(filepath.Join(filepath.Dir(executablePath), "../config/"))
if err != nil {
- fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err)
- os.Exit(1)
+ return "", errs.WrapMsg(err, "failed to get output directory", "outDir", filepath.Join(filepath.Dir(executablePath), "../config/"))
}
- return configPath
+ return configPath, nil
}
// getProjectRoot returns the absolute path of the project root directory.
-func GetProjectRoot() string {
- executablePath, _ := os.Executable()
-
- projectRoot, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../../../../.."))
+func GetProjectRoot() (string, error) {
+ executablePath, err := os.Executable()
+ if err != nil {
+ return "", errs.Wrap(err)
+ }
+ projectRoot, err := field.OutDir(filepath.Join(filepath.Dir(executablePath), "../../../../.."))
if err != nil {
- fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err)
- os.Exit(1)
+ return "", errs.Wrap(err)
}
- return projectRoot
+ return projectRoot, nil
}
-func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options {
+func GetOptionsByNotification(cfg NotificationConfig) msgprocessor.Options {
opts := msgprocessor.NewOptions()
if cfg.UnreadCount {
@@ -91,37 +89,21 @@ func initConfig(config any, configName, configFolderPath string) error {
_, err := os.Stat(configFolderPath)
if err != nil {
if !os.IsNotExist(err) {
- fmt.Println("stat config path error:", err.Error())
- return fmt.Errorf("stat config path error: %w", err)
+ return errs.WrapMsg(err, "stat config path error", "config Folder Path", configFolderPath)
+ }
+ path, err := GetProjectRoot()
+ if err != nil {
+ return err
}
- configFolderPath = filepath.Join(GetProjectRoot(), "config", configName)
- fmt.Println("flag's path,enviment's path,default path all is not exist,using project path:", configFolderPath)
+ configFolderPath = filepath.Join(path, "config", configName)
}
data, err := os.ReadFile(configFolderPath)
if err != nil {
- return fmt.Errorf("read file error: %w", err)
+ return errs.WrapMsg(err, "read file error", "config Folder Path", configFolderPath)
}
if err = yaml.Unmarshal(data, config); err != nil {
- return fmt.Errorf("unmarshal yaml error: %w", err)
+ return errs.WrapMsg(err, "unmarshal yaml error", "config Folder Path", configFolderPath)
}
- fmt.Println("The path of the configuration file to start the process:", configFolderPath)
return nil
}
-
-func InitConfig(config *GlobalConfig, configFolderPath string) error {
- if configFolderPath == "" {
- envConfigPath := os.Getenv("OPENIMCONFIG")
- if envConfigPath != "" {
- configFolderPath = envConfigPath
- } else {
- configFolderPath = GetDefaultConfigPath()
- }
- }
-
- if err := initConfig(config, FileName, configFolderPath); err != nil {
- return err
- }
-
- return initConfig(&config.Notification, NotificationFileName, configFolderPath)
-}
diff --git a/pkg/common/config/parse_test.go b/pkg/common/config/parse_test.go
deleted file mode 100644
index 35b019b837..0000000000
--- a/pkg/common/config/parse_test.go
+++ /dev/null
@@ -1,131 +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 config
-
-import (
- _ "embed"
- "fmt"
- "reflect"
- "testing"
-
- "gopkg.in/yaml.v3"
-
- "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
-)
-
-func TestGetDefaultConfigPath(t *testing.T) {
- tests := []struct {
- name string
- want string
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got, _ := GetDefaultConfigPath(); got != tt.want {
- t.Errorf("GetDefaultConfigPath() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestGetProjectRoot(t *testing.T) {
- tests := []struct {
- name string
- want string
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got, _ := GetProjectRoot(); got != tt.want {
- t.Errorf("GetProjectRoot() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestGetOptionsByNotification(t *testing.T) {
- type args struct {
- cfg NotificationConf
- }
- tests := []struct {
- name string
- args args
- want msgprocessor.Options
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := GetOptionsByNotification(tt.args.cfg); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("GetOptionsByNotification() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func Test_initConfig(t *testing.T) {
- type args struct {
- config any
- configName string
- configFolderPath string
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := initConfig(tt.args.config, tt.args.configName, tt.args.configFolderPath); (err != nil) != tt.wantErr {
- t.Errorf("initConfig() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
-
-func TestInitConfig(t *testing.T) {
- type args struct {
- configFolderPath string
- }
- tests := []struct {
- name string
- args args
- config *GlobalConfig
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := InitConfig(tt.config, tt.args.configFolderPath); (err != nil) != tt.wantErr {
- t.Errorf("InitConfig() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
-
-func TestName(t *testing.T) {
- Config.LocalCache.Friend.Topic = "friend"
- Config.LocalCache.Friend.SlotNum = 500
- Config.LocalCache.Friend.SlotSize = 20000
-
- data, _ := yaml.Marshal(&Config)
-
- fmt.Println(string(data))
-}
diff --git a/pkg/common/config/version b/pkg/common/config/version
index 40c341bdcd..240bba9069 100644
--- a/pkg/common/config/version
+++ b/pkg/common/config/version
@@ -1 +1 @@
-3.6.0
+3.7.0
\ No newline at end of file
diff --git a/pkg/common/convert/black.go b/pkg/common/convert/black.go
index 683517fbca..635b1a5862 100644
--- a/pkg/common/convert/black.go
+++ b/pkg/common/convert/black.go
@@ -17,9 +17,9 @@ package convert
import (
"context"
- "github.com/OpenIMSDK/protocol/sdkws"
- sdk "github.com/OpenIMSDK/protocol/sdkws"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/sdkws"
+ sdk "github.com/openimsdk/protocol/sdkws"
)
func BlackDB2Pb(ctx context.Context, blackDBs []*relation.BlackModel, f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (blackPbs []*sdk.BlackInfo, err error) {
diff --git a/pkg/common/convert/conversation.go b/pkg/common/convert/conversation.go
index b3f4913ebb..510f40d700 100644
--- a/pkg/common/convert/conversation.go
+++ b/pkg/common/convert/conversation.go
@@ -15,15 +15,15 @@
package convert
import (
- "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/tools/utils/datautil"
)
func ConversationDB2Pb(conversationDB *relation.ConversationModel) *conversation.Conversation {
conversationPB := &conversation.Conversation{}
conversationPB.LatestMsgDestructTime = conversationDB.LatestMsgDestructTime.Unix()
- if err := utils.CopyStructFields(conversationPB, conversationDB); err != nil {
+ if err := datautil.CopyStructFields(conversationPB, conversationDB); err != nil {
return nil
}
return conversationPB
@@ -32,7 +32,7 @@ func ConversationDB2Pb(conversationDB *relation.ConversationModel) *conversation
func ConversationsDB2Pb(conversationsDB []*relation.ConversationModel) (conversationsPB []*conversation.Conversation) {
for _, conversationDB := range conversationsDB {
conversationPB := &conversation.Conversation{}
- if err := utils.CopyStructFields(conversationPB, conversationDB); err != nil {
+ if err := datautil.CopyStructFields(conversationPB, conversationDB); err != nil {
continue
}
conversationPB.LatestMsgDestructTime = conversationDB.LatestMsgDestructTime.Unix()
@@ -43,7 +43,7 @@ func ConversationsDB2Pb(conversationsDB []*relation.ConversationModel) (conversa
func ConversationPb2DB(conversationPB *conversation.Conversation) *relation.ConversationModel {
conversationDB := &relation.ConversationModel{}
- if err := utils.CopyStructFields(conversationDB, conversationPB); err != nil {
+ if err := datautil.CopyStructFields(conversationDB, conversationPB); err != nil {
return nil
}
return conversationDB
@@ -52,7 +52,7 @@ func ConversationPb2DB(conversationPB *conversation.Conversation) *relation.Conv
func ConversationsPb2DB(conversationsPB []*conversation.Conversation) (conversationsDB []*relation.ConversationModel) {
for _, conversationPB := range conversationsPB {
conversationDB := &relation.ConversationModel{}
- if err := utils.CopyStructFields(conversationDB, conversationPB); err != nil {
+ if err := datautil.CopyStructFields(conversationDB, conversationPB); err != nil {
continue
}
conversationsDB = append(conversationsDB, conversationDB)
diff --git a/pkg/common/convert/doc.go b/pkg/common/convert/doc.go
index 29c1144c1a..623b4a5c05 100644
--- a/pkg/common/convert/doc.go
+++ b/pkg/common/convert/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go
index 4231eb49d1..ad8f9071e1 100644
--- a/pkg/common/convert/friend.go
+++ b/pkg/common/convert/friend.go
@@ -18,19 +18,20 @@ import (
"context"
"fmt"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/timeutil"
)
func FriendPb2DB(friend *sdkws.FriendInfo) *relation.FriendModel {
dbFriend := &relation.FriendModel{}
- err := utils.CopyStructFields(dbFriend, friend)
+ err := datautil.CopyStructFields(dbFriend, friend)
if err != nil {
return nil
}
dbFriend.FriendUserID = friend.FriendUser.UserID
- dbFriend.CreateTime = utils.UnixSecondToTime(friend.CreateTime)
+ dbFriend.CreateTime = timeutil.UnixSecondToTime(friend.CreateTime)
return dbFriend
}
@@ -71,7 +72,7 @@ func FriendsDB2Pb(
}
for _, friend := range friendsDB {
friendPb := &sdkws.FriendInfo{FriendUser: &sdkws.UserInfo{}}
- err := utils.CopyStructFields(friendPb, friend)
+ err := datautil.CopyStructFields(friendPb, friend)
if err != nil {
return nil, err
}
@@ -88,10 +89,7 @@ func FriendsDB2Pb(
}
-func FriendRequestDB2Pb(ctx context.Context,
- friendRequests []*relation.FriendRequestModel,
- getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error),
-) ([]*sdkws.FriendRequest, error) {
+func FriendRequestDB2Pb(ctx context.Context, friendRequests []*relation.FriendRequestModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) ([]*sdkws.FriendRequest, error) {
if len(friendRequests) == 0 {
return nil, nil
}
@@ -100,7 +98,7 @@ func FriendRequestDB2Pb(ctx context.Context,
userIDMap[friendRequest.ToUserID] = struct{}{}
userIDMap[friendRequest.FromUserID] = struct{}{}
}
- users, err := getUsers(ctx, utils.Keys(userIDMap))
+ users, err := getUsers(ctx, datautil.Keys(userIDMap))
if err != nil {
return nil, err
}
diff --git a/pkg/common/convert/group.go b/pkg/common/convert/group.go
index 63372f21dc..9b7353cfdc 100644
--- a/pkg/common/convert/group.go
+++ b/pkg/common/convert/group.go
@@ -17,9 +17,9 @@ package convert
import (
"time"
- pbgroup "github.com/OpenIMSDK/protocol/group"
- sdkws "github.com/OpenIMSDK/protocol/sdkws"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ pbgroup "github.com/openimsdk/protocol/group"
+ sdkws "github.com/openimsdk/protocol/sdkws"
)
func Db2PbGroupInfo(m *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo {
@@ -55,12 +55,7 @@ func Pb2DbGroupRequest(req *pbgroup.GroupApplicationResponseReq, handleUserID st
}
}
-func Db2PbCMSGroup(
- m *relation.GroupModel,
- ownerUserID string,
- ownerUserName string,
- memberCount uint32,
-) *pbgroup.CMSGroup {
+func Db2PbCMSGroup(m *relation.GroupModel, ownerUserID string, ownerUserName string, memberCount uint32) *pbgroup.CMSGroup {
return &pbgroup.CMSGroup{
GroupInfo: Db2PbGroupInfo(m, ownerUserID, memberCount),
GroupOwnerUserID: ownerUserID,
@@ -85,11 +80,7 @@ func Db2PbGroupMember(m *relation.GroupMemberModel) *sdkws.GroupMemberFullInfo {
}
}
-func Db2PbGroupRequest(
- m *relation.GroupRequestModel,
- user *sdkws.PublicUserInfo,
- group *sdkws.GroupInfo,
-) *sdkws.GroupRequest {
+func Db2PbGroupRequest(m *relation.GroupRequestModel, user *sdkws.PublicUserInfo, group *sdkws.GroupInfo) *sdkws.GroupRequest {
return &sdkws.GroupRequest{
UserInfo: user,
GroupInfo: group,
@@ -137,7 +128,7 @@ func Pb2DBGroupInfo(m *sdkws.GroupInfo) *relation.GroupModel {
}
}
-//func Pb2DbGroupMember(m *sdkws.UserInfo) *relation.GroupMemberModel {
+// func Pb2DbGroupMember(m *sdkws.UserInfo) *relation.GroupMemberModel {
// return &relation.GroupMemberModel{
// UserID: m.UserID,
// Nickname: m.Nickname,
diff --git a/pkg/common/convert/msg.go b/pkg/common/convert/msg.go
index 34638049b2..594a0ffc2c 100644
--- a/pkg/common/convert/msg.go
+++ b/pkg/common/convert/msg.go
@@ -15,16 +15,16 @@
package convert
import (
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
)
-func MsgPb2DB(msg *sdkws.MsgData) *unrelation.MsgDataModel {
+func MsgPb2DB(msg *sdkws.MsgData) *relation.MsgDataModel {
if msg == nil {
return nil
}
- var msgDataModel unrelation.MsgDataModel
+ var msgDataModel relation.MsgDataModel
msgDataModel.SendID = msg.SendID
msgDataModel.RecvID = msg.RecvID
msgDataModel.GroupID = msg.GroupID
@@ -43,7 +43,7 @@ func MsgPb2DB(msg *sdkws.MsgData) *unrelation.MsgDataModel {
msgDataModel.Status = msg.Status
msgDataModel.Options = msg.Options
if msg.OfflinePushInfo != nil {
- msgDataModel.OfflinePush = &unrelation.OfflinePushModel{
+ msgDataModel.OfflinePush = &relation.OfflinePushModel{
Title: msg.OfflinePushInfo.Title,
Desc: msg.OfflinePushInfo.Desc,
Ex: msg.OfflinePushInfo.Ex,
@@ -57,7 +57,7 @@ func MsgPb2DB(msg *sdkws.MsgData) *unrelation.MsgDataModel {
return &msgDataModel
}
-func MsgDB2Pb(msgModel *unrelation.MsgDataModel) *sdkws.MsgData {
+func MsgDB2Pb(msgModel *relation.MsgDataModel) *sdkws.MsgData {
if msgModel == nil {
return nil
}
diff --git a/pkg/common/convert/user.go b/pkg/common/convert/user.go
index 38afd8c199..a9378e1a0f 100644
--- a/pkg/common/convert/user.go
+++ b/pkg/common/convert/user.go
@@ -17,8 +17,8 @@ package convert
import (
"time"
- "github.com/OpenIMSDK/protocol/sdkws"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/sdkws"
)
func UsersDB2Pb(users []*relationtb.UserModel) []*sdkws.UserInfo {
diff --git a/pkg/common/convert/user_test.go b/pkg/common/convert/user_test.go
index a24efb53cc..88eb812d27 100644
--- a/pkg/common/convert/user_test.go
+++ b/pkg/common/convert/user_test.go
@@ -18,7 +18,7 @@ import (
"reflect"
"testing"
- "github.com/OpenIMSDK/protocol/sdkws"
+ "github.com/openimsdk/protocol/sdkws"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
)
diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go
index a5304b4b0d..615f2cbf1e 100644
--- a/pkg/common/db/cache/black.go
+++ b/pkg/common/db/cache/black.go
@@ -18,11 +18,11 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/log"
"github.com/dtm-labs/rockscache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
@@ -48,10 +48,10 @@ type BlackCacheRedis struct {
blackDB relationtb.BlackModelInterface
}
-func NewBlackCacheRedis(rdb redis.UniversalClient, blackDB relationtb.BlackModelInterface, options rockscache.Options) BlackCache {
+func NewBlackCacheRedis(rdb redis.UniversalClient, localCache *config.LocalCache, blackDB relationtb.BlackModelInterface, options rockscache.Options) BlackCache {
rcClient := rockscache.NewClient(rdb, options)
mc := NewMetaCacheRedis(rcClient)
- b := config.Config.LocalCache.Friend
+ b := localCache.Friend
log.ZDebug(context.Background(), "black local cache init", "Topic", b.Topic, "SlotNum", b.SlotNum, "SlotSize", b.SlotSize, "enable", b.Enable())
mc.SetTopic(b.Topic)
mc.SetRawRedisClient(rdb)
diff --git a/pkg/common/db/cache/config.go b/pkg/common/db/cache/config.go
index c760e0c033..bb5bd449b9 100644
--- a/pkg/common/db/cache/config.go
+++ b/pkg/common/db/cache/config.go
@@ -27,29 +27,26 @@ var (
subscribe map[string][]string
)
-func getPublishKey(topic string, key []string) []string {
- if topic == "" || len(key) == 0 {
- return nil
- }
+func InitLocalCache(localCache *config.LocalCache) {
once.Do(func() {
list := []struct {
- Local config.LocalCache
+ Local config.CacheConfig
Keys []string
}{
{
- Local: config.Config.LocalCache.User,
+ Local: localCache.User,
Keys: []string{cachekey.UserInfoKey, cachekey.UserGlobalRecvMsgOptKey},
},
{
- Local: config.Config.LocalCache.Group,
+ Local: localCache.Group,
Keys: []string{cachekey.GroupMemberIDsKey, cachekey.GroupInfoKey, cachekey.GroupMemberInfoKey},
},
{
- Local: config.Config.LocalCache.Friend,
+ Local: localCache.Friend,
Keys: []string{cachekey.FriendIDsKey, cachekey.BlackIDsKey},
},
{
- Local: config.Config.LocalCache.Conversation,
+ Local: localCache.Conversation,
Keys: []string{cachekey.ConversationKey, cachekey.ConversationIDsKey, cachekey.ConversationNotReceiveMessageUserIDsKey},
},
}
@@ -60,6 +57,12 @@ func getPublishKey(topic string, key []string) []string {
}
}
})
+}
+
+func getPublishKey(topic string, key []string) []string {
+ if topic == "" || len(key) == 0 {
+ return nil
+ }
prefix, ok := subscribe[topic]
if !ok {
return nil
diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go
index c8f752cd39..bd189f2a9c 100644
--- a/pkg/common/db/cache/conversation.go
+++ b/pkg/common/db/cache/conversation.go
@@ -20,24 +20,25 @@ import (
"strings"
"time"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/dtm-labs/rockscache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/encrypt"
"github.com/redis/go-redis/v9"
)
const (
- //conversationKey = "CONVERSATION:"
- //conversationIDsKey = "CONVERSATION_IDS:"
- //conversationIDsHashKey = "CONVERSATION_IDS_HASH:"
- //conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
- //recvMsgOptKey = "RECV_MSG_OPT:"
- //superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
- //superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
- //conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:".
+ // ConversationKey = "CONVERSATION:"
+ // conversationIDsKey = "CONVERSATION_IDS:"
+ // conversationIDsHashKey = "CONVERSATION_IDS_HASH:"
+ // conversationHasReadSeqKey = "CONVERSATION_HAS_READ_SEQ:"
+ // recvMsgOptKey = "RECV_MSG_OPT:"
+ // superGroupRecvMsgNotNotifyUserIDsKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS:"
+ // superGroupRecvMsgNotNotifyUserIDsHashKey = "SUPER_GROUP_RECV_MSG_NOT_NOTIFY_USER_IDS_HASH:"
+ // conversationNotReceiveMessageUserIDsKey = "CONVERSATION_NOT_RECEIVE_MESSAGE_USER_IDS:".
conversationExpireTime = time.Second * 60 * 60 * 12
)
@@ -66,13 +67,13 @@ type ConversationCache interface {
GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error)
DelUserRecvMsgOpt(ownerUserID, conversationID string) ConversationCache
// get one super group recv msg but do not notification userID list
- //GetSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error)
+ // GetSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error)
DelSuperGroupRecvMsgNotNotifyUserIDs(groupID string) ConversationCache
// get one super group recv msg but do not notification userID list hash
- //GetSuperGroupRecvMsgNotNotifyUserIDsHash(ctx context.Context, groupID string) (hash uint64, err error)
+ // GetSuperGroupRecvMsgNotNotifyUserIDsHash(ctx context.Context, groupID string) (hash uint64, err error)
DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupID string) ConversationCache
- //GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
+ // GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache
GetConversationsByConversationID(ctx context.Context,
@@ -82,10 +83,10 @@ type ConversationCache interface {
DelConversationNotReceiveMessageUserIDs(conversationIDs ...string) ConversationCache
}
-func NewConversationRedis(rdb redis.UniversalClient, opts rockscache.Options, db relationtb.ConversationModelInterface) ConversationCache {
+func NewConversationRedis(rdb redis.UniversalClient, localCache *config.LocalCache, opts rockscache.Options, db relationtb.ConversationModelInterface) ConversationCache {
rcClient := rockscache.NewClient(rdb, opts)
mc := NewMetaCacheRedis(rcClient)
- c := config.Config.LocalCache.Conversation
+ c := localCache.Conversation
log.ZDebug(context.Background(), "black local cache init", "Topic", c.Topic, "SlotNum", c.SlotNum, "SlotSize", c.SlotSize, "enable", c.Enable())
mc.SetTopic(c.Topic)
mc.SetRawRedisClient(rdb)
@@ -104,11 +105,11 @@ type ConversationRedisCache struct {
expireTime time.Duration
}
-//func NewNewConversationRedis(
+// func NewNewConversationRedis(
// rdb redis.UniversalClient,
// conversationDB *relation.ConversationGorm,
// options rockscache.Options,
-//) ConversationCache {
+// ) ConversationCache {
// rcClient := rockscache.NewClient(rdb, options)
//
// return &ConversationRedisCache{
@@ -188,9 +189,9 @@ func (c *ConversationRedisCache) GetUserConversationIDsHash(ctx context.Context,
if err != nil {
return 0, err
}
- utils.Sort(conversationIDs, true)
+ datautil.Sort(conversationIDs, true)
bi := big.NewInt(0)
- bi.SetString(utils.Md5(strings.Join(conversationIDs, ";"))[0:8], 16)
+ bi.SetString(encrypt.Md5(strings.Join(conversationIDs, ";"))[0:8], 16)
return bi.Uint64(), nil
},
)
@@ -232,15 +233,15 @@ func (c *ConversationRedisCache) DelConversations(ownerUserID string, conversati
// }
// }
-// return 0, errors.New("not found key:" + key + " in keys")
+// return 0, errs.New("not found key:" + key + " in keys")
// }
func (c *ConversationRedisCache) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationtb.ConversationModel, error) {
- //var keys []string
- //for _, conversarionID := range conversationIDs {
+ // var keys []string
+ // for _, conversarionID := range conversationIDs {
// keys = append(keys, c.getConversationKey(ownerUserID, conversarionID))
//}
- //return batchGetCache(
+ // return batchGetCache(
// ctx,
// c.rcClient,
// keys,
@@ -262,11 +263,11 @@ func (c *ConversationRedisCache) GetUserAllConversations(ctx context.Context, ow
if err != nil {
return nil, err
}
- //var keys []string
- //for _, conversarionID := range conversationIDs {
+ // var keys []string
+ // for _, conversarionID := range conversationIDs {
// keys = append(keys, c.getConversationKey(ownerUserID, conversarionID))
//}
- //return batchGetCache(
+ // return batchGetCache(
// ctx,
// c.rcClient,
// keys,
@@ -285,7 +286,7 @@ func (c *ConversationRedisCache) GetUserRecvMsgOpt(ctx context.Context, ownerUse
})
}
-//func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) {
+// func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) (userIDs []string, err error) {
// return getCache(ctx, c.rcClient, c.getSuperGroupRecvNotNotifyUserIDsKey(groupID), c.expireTime, func(ctx context.Context) (userIDs []string, err error) {
// return c.conversationDB.FindSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
// })
@@ -316,7 +317,7 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDs(groupID st
return cache
}
-//func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDsHash(ctx context.Context, groupID string) (hash uint64, err error) {
+// func (c *ConversationRedisCache) GetSuperGroupRecvMsgNotNotifyUserIDsHash(ctx context.Context, groupID string) (hash uint64, err error) {
// return getCache(ctx, c.rcClient, c.getSuperGroupRecvNotNotifyUserIDsHashKey(groupID), c.expireTime, func(ctx context.Context) (hash uint64, err error) {
// userIDs, err := c.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
// if err != nil {
@@ -337,30 +338,6 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupI
return cache
}
-/* func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID string, conversationIDs []string) (int, error) {
- for _i, _conversationID := range conversationIDs {
- if _conversationID == conversationID {
- return _i, nil
- }
- }
-
- return 0, errors.New("not found key:" + conversationID + " in keys")
-} */
-
-/* func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) {
- conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID)
- if err != nil {
- return nil, err
- }
- var keys []string
- for _, conversarionID := range conversationIDs {
- keys = append(keys, c.getConversationHasReadSeqKey(ownerUserID, conversarionID))
- }
- return batchGetCacheMap(ctx, c.rcClient, keys, conversationIDs, c.expireTime, c.getUserAllHasReadSeqsIndex, func(ctx context.Context) (map[string]int64, error) {
- return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID)
- })
-} */
-
func (c *ConversationRedisCache) DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache {
cache := c.NewCache()
for _, conversationID := range conversationIDs {
diff --git a/pkg/common/db/cache/doc.go b/pkg/common/db/cache/doc.go
index 77651e3dee..a5c237249e 100644
--- a/pkg/common/db/cache/doc.go
+++ b/pkg/common/db/cache/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go
index 2d20705ef8..73fe5ea694 100644
--- a/pkg/common/db/cache/friend.go
+++ b/pkg/common/db/cache/friend.go
@@ -18,20 +18,20 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/dtm-labs/rockscache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
"github.com/redis/go-redis/v9"
)
const (
friendExpireTime = time.Second * 60 * 60 * 12
- //friendIDsKey = "FRIEND_IDS:"
- //TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
- //friendKey = "FRIEND_INFO:".
+ // FriendIDsKey = "FRIEND_IDS:"
+ // TwoWayFriendsIDsKey = "COMMON_FRIENDS_IDS:"
+ // friendKey = "FRIEND_INFO:".
)
// FriendCache is an interface for caching friend-related data.
@@ -58,11 +58,11 @@ type FriendCacheRedis struct {
}
// NewFriendCacheRedis creates a new instance of FriendCacheRedis.
-func NewFriendCacheRedis(rdb redis.UniversalClient, friendDB relationtb.FriendModelInterface,
+func NewFriendCacheRedis(rdb redis.UniversalClient, localCache *config.LocalCache, friendDB relationtb.FriendModelInterface,
options rockscache.Options) FriendCache {
rcClient := rockscache.NewClient(rdb, options)
mc := NewMetaCacheRedis(rcClient)
- f := config.Config.LocalCache.Friend
+ f := localCache.Friend
log.ZDebug(context.Background(), "friend local cache init", "Topic", f.Topic, "SlotNum", f.SlotNum, "SlotSize", f.SlotSize, "enable", f.Enable())
mc.SetTopic(f.Topic)
mc.SetRawRedisClient(rdb)
@@ -129,7 +129,7 @@ func (f *FriendCacheRedis) GetTwoWayFriendIDs(ctx context.Context, ownerUserID s
if err != nil {
return nil, err
}
- if utils.IsContain(ownerUserID, friendFriendID) {
+ if datautil.Contain(ownerUserID, friendFriendID...) {
twoWayFriendIDs = append(twoWayFriendIDs, ownerUserID)
}
}
diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go
index 7022461cbb..66c2d65c48 100644
--- a/pkg/common/db/cache/group.go
+++ b/pkg/common/db/cache/group.go
@@ -19,26 +19,19 @@ import (
"fmt"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/dtm-labs/rockscache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
"github.com/redis/go-redis/v9"
)
const (
groupExpireTime = time.Second * 60 * 60 * 12
- //groupInfoKey = "GROUP_INFO:"
- //groupMemberIDsKey = "GROUP_MEMBER_IDS:"
- //groupMembersHashKey = "GROUP_MEMBERS_HASH2:"
- //groupMemberInfoKey = "GROUP_MEMBER_INFO:"
- //joinedGroupsKey = "JOIN_GROUPS_KEY:"
- //groupMemberNumKey = "GROUP_MEMBER_NUM_CACHE:"
- //groupRoleLevelMemberIDsKey = "GROUP_ROLE_LEVEL_MEMBER_IDS:".
)
type GroupHash interface {
@@ -94,6 +87,7 @@ type GroupCacheRedis struct {
func NewGroupCacheRedis(
rdb redis.UniversalClient,
+ localCache *config.LocalCache,
groupDB relationtb.GroupModelInterface,
groupMemberDB relationtb.GroupMemberModelInterface,
groupRequestDB relationtb.GroupRequestModelInterface,
@@ -102,7 +96,7 @@ func NewGroupCacheRedis(
) GroupCache {
rcClient := rockscache.NewClient(rdb, opts)
mc := NewMetaCacheRedis(rcClient)
- g := config.Config.LocalCache.Group
+ g := localCache.Group
mc.SetTopic(g.Topic)
log.ZDebug(context.Background(), "group local cache init", "Topic", g.Topic, "SlotNum", g.SlotNum, "SlotSize", g.SlotSize, "enable", g.Enable())
mc.SetRawRedisClient(rdb)
@@ -227,7 +221,7 @@ func (g *GroupCacheRedis) DelGroupAllRoleLevel(groupID string) GroupCache {
func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID string) (hashCode uint64, err error) {
if g.groupHash == nil {
- return 0, errs.ErrInternalServer.Wrap("group hash is nil")
+ return 0, errs.ErrInternalServer.WrapMsg("group hash is nil")
}
return getCache(ctx, g.rcClient, g.getGroupMembersHashKey(groupID), g.expireTime, func(ctx context.Context) (uint64, error) {
return g.groupHash.GetGroupHash(ctx, groupID)
@@ -236,7 +230,7 @@ func (g *GroupCacheRedis) GetGroupMembersHash(ctx context.Context, groupID strin
func (g *GroupCacheRedis) GetGroupMemberHashMap(ctx context.Context, groupIDs []string) (map[string]*relationtb.GroupSimpleUserID, error) {
if g.groupHash == nil {
- return nil, errs.ErrInternalServer.Wrap("group hash is nil")
+ return nil, errs.ErrInternalServer.WrapMsg("group hash is nil")
}
res := make(map[string]*relationtb.GroupSimpleUserID)
for _, groupID := range groupIDs {
@@ -244,7 +238,7 @@ func (g *GroupCacheRedis) GetGroupMemberHashMap(ctx context.Context, groupIDs []
if err != nil {
return nil, err
}
- log.ZInfo(ctx, "GetGroupMemberHashMap", "groupID", groupID, "hash", hash)
+ log.ZDebug(ctx, "GetGroupMemberHashMap", "groupID", groupID, "hash", hash)
num, err := g.GetGroupMemberNum(ctx, groupID)
if err != nil {
return nil, err
@@ -330,11 +324,11 @@ func (g *GroupCacheRedis) GetGroupMembersPage(
return 0, nil, err
}
if userIDs != nil {
- userIDs = utils.BothExist(userIDs, groupMemberIDs)
+ userIDs = datautil.BothExist(userIDs, groupMemberIDs)
} else {
userIDs = groupMemberIDs
}
- groupMembers, err = g.GetGroupMembersInfo(ctx, groupID, utils.Paginate(userIDs, int(showNumber), int(showNumber)))
+ groupMembers, err = g.GetGroupMembersInfo(ctx, groupID, datautil.Paginate(userIDs, int(showNumber), int(showNumber)))
return uint32(len(userIDs)), groupMembers, err
}
@@ -390,7 +384,7 @@ func (g *GroupCacheRedis) GetGroupOwner(ctx context.Context, groupID string) (*r
return nil, err
}
if len(members) == 0 {
- return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("group %s owner not found", groupID))
+ return nil, errs.ErrRecordNotFound.WrapMsg(fmt.Sprintf("group %s owner not found", groupID))
}
return members[0], nil
}
diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go
deleted file mode 100644
index 8f4e2c592c..0000000000
--- a/pkg/common/db/cache/init_redis.go
+++ /dev/null
@@ -1,107 +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 cache
-
-import (
- "context"
- "errors"
- "fmt"
- "os"
- "strings"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mw/specialerror"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/redis/go-redis/v9"
-)
-
-var (
- // singleton pattern.
- redisClient redis.UniversalClient
-)
-
-const (
- maxRetry = 10 // number of retries
-)
-
-// NewRedis Initialize redis connection.
-func NewRedis(config *config.GlobalConfig) (redis.UniversalClient, error) {
- if redisClient != nil {
- return redisClient, nil
- }
-
- // Read configuration from environment variables
- overrideConfigFromEnv(config)
-
- if len(config.Redis.Address) == 0 {
- return nil, errs.Wrap(errors.New("redis address is empty"))
- }
- specialerror.AddReplace(redis.Nil, errs.ErrRecordNotFound)
- var rdb redis.UniversalClient
- if len(config.Redis.Address) > 1 || config.Redis.ClusterMode {
- rdb = redis.NewClusterClient(&redis.ClusterOptions{
- Addrs: config.Redis.Address,
- Username: config.Redis.Username,
- Password: config.Redis.Password, // no password set
- PoolSize: 50,
- MaxRetries: maxRetry,
- })
- } else {
- rdb = redis.NewClient(&redis.Options{
- Addr: config.Redis.Address[0],
- Username: config.Redis.Username,
- Password: config.Redis.Password,
- DB: 0, // use default DB
- PoolSize: 100, // connection pool size
- MaxRetries: maxRetry,
- })
- }
-
- var err error
- ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
- defer cancel()
- err = rdb.Ping(ctx).Err()
- if err != nil {
- errMsg := fmt.Sprintf("address:%s, username:%s, password:%s, clusterMode:%t, enablePipeline:%t", config.Redis.Address, config.Redis.Username,
- config.Redis.Password, config.Redis.ClusterMode, config.Redis.EnablePipeline)
- return nil, errs.Wrap(err, errMsg)
- }
- redisClient = rdb
- return rdb, err
-}
-
-// overrideConfigFromEnv overrides configuration fields with environment variables if present.
-func overrideConfigFromEnv(config *config.GlobalConfig) {
- if envAddr := os.Getenv("REDIS_ADDRESS"); envAddr != "" {
- if envPort := os.Getenv("REDIS_PORT"); envPort != "" {
- addresses := strings.Split(envAddr, ",")
- for i, addr := range addresses {
- addresses[i] = addr + ":" + envPort
- }
- config.Redis.Address = addresses
- } else {
- config.Redis.Address = strings.Split(envAddr, ",")
- }
- }
-
- if envUser := os.Getenv("REDIS_USERNAME"); envUser != "" {
- config.Redis.Username = envUser
- }
-
- if envPass := os.Getenv("REDIS_PASSWORD"); envPass != "" {
- config.Redis.Password = envPass
- }
-}
diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go
index 431b7c867e..e633e4fbd0 100644
--- a/pkg/common/db/cache/meta_cache.go
+++ b/pkg/common/db/cache/meta_cache.go
@@ -17,15 +17,14 @@ package cache
import (
"context"
"encoding/json"
- "errors"
"fmt"
"time"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mw/specialerror"
- "github.com/OpenIMSDK/tools/utils"
"github.com/dtm-labs/rockscache"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mw/specialerror"
+ "github.com/openimsdk/tools/utils/datautil"
"github.com/redis/go-redis/v9"
)
@@ -35,7 +34,7 @@ const (
retryInterval = time.Millisecond * 100
)
-var errIndex = errors.New("err index")
+var errIndex = errs.New("err index")
type metaCache interface {
ExecDel(ctx context.Context, distinct ...bool) error
@@ -74,7 +73,7 @@ func (m *metaCacheRedis) Copy() metaCache {
keys: keys,
maxRetryTimes: m.maxRetryTimes,
retryInterval: m.retryInterval,
- redisClient: redisClient,
+ redisClient: m.redisClient,
}
}
@@ -88,7 +87,7 @@ func (m *metaCacheRedis) SetRawRedisClient(cli redis.UniversalClient) {
func (m *metaCacheRedis) ExecDel(ctx context.Context, distinct ...bool) error {
if len(distinct) > 0 && distinct[0] {
- m.keys = utils.Distinct(m.keys)
+ m.keys = datautil.Distinct(m.keys)
}
if len(m.keys) > 0 {
log.ZDebug(ctx, "delete cache", "topic", m.topic, "keys", m.keys)
@@ -150,7 +149,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin
}
bs, err := json.Marshal(t)
if err != nil {
- return "", errs.Wrap(err, "marshal failed")
+ return "", errs.WrapMsg(err, "marshal failed")
}
write = true
@@ -163,12 +162,12 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin
return t, nil
}
if v == "" {
- return t, errs.ErrRecordNotFound.Wrap("cache is not found")
+ return t, errs.ErrRecordNotFound.WrapMsg("cache is not found")
}
err = json.Unmarshal([]byte(v), &t)
if err != nil {
errInfo := fmt.Sprintf("cache json.Unmarshal failed, key:%s, value:%s, expire:%s", key, v, expire)
- return t, errs.Wrap(err, errInfo)
+ return t, errs.WrapMsg(err, errInfo)
}
return t, nil
@@ -240,14 +239,14 @@ func batchGetCache2[T any, K comparable](
return res, nil
}
-//func batchGetCacheMap[T any](
+// func batchGetCacheMap[T any](
// ctx context.Context,
// rcClient *rockscache.Client,
// keys, originKeys []string,
// expire time.Duration,
// keyIndexFn func(s string, keys []string) (int, error),
// fn func(ctx context.Context) (map[string]T, error),
-//) (map[string]T, error) {
+// ) (map[string]T, error) {
// batchMap, err := rcClient.FetchBatch2(ctx, keys, expire, func(idxs []int) (m map[int]string, err error) {
// tArrays, err := fn(ctx)
// if err != nil {
diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go
index b8e9dbc774..e8a86b71bd 100644
--- a/pkg/common/db/cache/msg.go
+++ b/pkg/common/db/cache/msg.go
@@ -20,30 +20,28 @@ import (
"strconv"
"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/utils"
"github.com/gogo/protobuf/jsonpb"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/stringutil"
"github.com/redis/go-redis/v9"
"golang.org/x/sync/errgroup"
)
+const msgCacheTimeout = 86400 * time.Second
+
const (
maxSeq = "MAX_SEQ:"
minSeq = "MIN_SEQ:"
conversationUserMinSeq = "CON_USER_MIN_SEQ:"
hasReadSeq = "HAS_READ_SEQ:"
- //appleDeviceToken = "DEVICE_TOKEN".
getuiToken = "GETUI_TOKEN"
getuiTaskID = "GETUI_TASK_ID"
- //signalCache = "SIGNAL_CACHE:"
- //signalListCache = "SIGNAL_LIST_CACHE:".
- FCM_TOKEN = "FCM_TOKEN:"
+ FCM_TOKEN = "FCM_TOKEN:"
messageCache = "MESSAGE_CACHE:"
messageDelUserList = "MESSAGE_DEL_USER_LIST:"
@@ -51,56 +49,17 @@ const (
sendMsgFailedFlag = "SEND_MSG_FAILED_FLAG:"
userBadgeUnreadCountSum = "USER_BADGE_UNREAD_COUNT_SUM:"
exTypeKeyLocker = "EX_LOCK:"
- uidPidToken = "UID_PID_TOKEN_STATUS:"
)
var concurrentLimit = 3
-type SeqCache interface {
- SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error
- GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error)
- GetMaxSeq(ctx context.Context, conversationID string) (int64, error)
- SetMinSeq(ctx context.Context, conversationID string, minSeq int64) error
- SetMinSeqs(ctx context.Context, seqs map[string]int64) error
- GetMinSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error)
- GetMinSeq(ctx context.Context, conversationID string) (int64, error)
- GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error)
- GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (map[string]int64, error)
- SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error
- // seqs map: key userID value minSeq
- SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error)
- // seqs map: key conversationID value minSeq
- SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error
- // has read seq
- SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error
- // k: user, v: seq
- SetHasReadSeqs(ctx context.Context, conversationID string, hasReadSeqs map[string]int64) error
- // k: conversation, v :seq
- UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error
- GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error)
- GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error)
-}
-
-type thirdCache interface {
- SetFcmToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) (err error)
- GetFcmToken(ctx context.Context, account string, platformID int) (string, error)
- DelFcmToken(ctx context.Context, account string, platformID int) error
- IncrUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error)
- SetUserBadgeUnreadCountSum(ctx context.Context, userID string, value int) error
- GetUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error)
- SetGetuiToken(ctx context.Context, token string, expireTime int64) error
- GetGetuiToken(ctx context.Context) (string, error)
- SetGetuiTaskID(ctx context.Context, taskID string, expireTime int64) error
- GetGetuiTaskID(ctx context.Context) (string, error)
-}
-
-type MsgModel interface {
- SeqCache
- thirdCache
- AddTokenFlag(ctx context.Context, userID string, platformID int, token string, flag int) error
- GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error)
- SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error
- DeleteTokenByUidPid(ctx context.Context, userID string, platformID int, fields []string) error
+//type MsgModel interface {
+// SeqCache
+// ThirdCache
+// MsgCache
+//}
+
+type MsgCache interface {
GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsg []*sdkws.MsgData, failedSeqList []int64, err error)
SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error)
UserDeleteMsgs(ctx context.Context, conversationID string, seqs []int64, userID string) error
@@ -121,303 +80,31 @@ type MsgModel interface {
UnLockMessageTypeKey(ctx context.Context, clientMsgID string, TypeKey string) error
}
-func NewMsgCacheModel(client redis.UniversalClient, config *config.GlobalConfig) MsgModel {
- return &msgCache{rdb: client, config: config}
+//func NewMsgCacheModel(client redis.UniversalClient, msgCacheTimeout int, redisConf *config.Redis) MsgModel {
+// return &msgCache{rdb: client, msgCacheTimeout: msgCacheTimeout, redisConf: redisConf}
+//}
+
+func NewMsgCache(client redis.UniversalClient, redisEnablePipeline bool) MsgCache {
+ return &msgCache{rdb: client, msgCacheTimeout: msgCacheTimeout, redisEnablePipeline: redisEnablePipeline}
}
type msgCache struct {
metaCache
- rdb redis.UniversalClient
- config *config.GlobalConfig
-}
-
-func (c *msgCache) getMaxSeqKey(conversationID string) string {
- return maxSeq + conversationID
-}
-
-func (c *msgCache) getMinSeqKey(conversationID string) string {
- return minSeq + conversationID
-}
-
-func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string {
- return hasReadSeq + userID + ":" + conversationID
-}
-
-func (c *msgCache) getConversationUserMinSeqKey(conversationID, userID string) string {
- return conversationUserMinSeq + conversationID + "u:" + userID
-}
-
-func (c *msgCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error {
- return errs.Wrap(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err())
-}
-
-func (c *msgCache) getSeq(ctx context.Context, conversationID string, getkey func(conversationID string) string) (int64, error) {
- val, err := c.rdb.Get(ctx, getkey(conversationID)).Int64()
- if err != nil {
- return 0, errs.Wrap(err)
- }
- return val, nil
-}
-
-func (c *msgCache) getSeqs(ctx context.Context, items []string, getkey func(s string) string) (m map[string]int64, err error) {
- m = make(map[string]int64, len(items))
- for i, v := range items {
- res, err := c.rdb.Get(ctx, getkey(v)).Result()
- if err != nil && err != redis.Nil {
- return nil, errs.Wrap(err)
- }
- val := utils.StringToInt64(res)
- if val != 0 {
- m[items[i]] = val
- }
- }
-
- return m, nil
-}
-
-func (c *msgCache) SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error {
- return c.setSeq(ctx, conversationID, maxSeq, c.getMaxSeqKey)
-}
-
-func (c *msgCache) GetMaxSeqs(ctx context.Context, conversationIDs []string) (m map[string]int64, err error) {
- return c.getSeqs(ctx, conversationIDs, c.getMaxSeqKey)
-}
-
-func (c *msgCache) GetMaxSeq(ctx context.Context, conversationID string) (int64, error) {
- return c.getSeq(ctx, conversationID, c.getMaxSeqKey)
-}
-
-func (c *msgCache) SetMinSeq(ctx context.Context, conversationID string, minSeq int64) error {
- return c.setSeq(ctx, conversationID, minSeq, c.getMinSeqKey)
-}
-
-func (c *msgCache) setSeqs(ctx context.Context, seqs map[string]int64, getkey func(key string) string) error {
- for conversationID, seq := range seqs {
- if err := c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err(); err != nil {
- return errs.Wrap(err)
- }
- }
- return nil
-}
-
-func (c *msgCache) SetMinSeqs(ctx context.Context, seqs map[string]int64) error {
- return c.setSeqs(ctx, seqs, c.getMinSeqKey)
-}
-
-func (c *msgCache) GetMinSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) {
- return c.getSeqs(ctx, conversationIDs, c.getMinSeqKey)
-}
-
-func (c *msgCache) GetMinSeq(ctx context.Context, conversationID string) (int64, error) {
- return c.getSeq(ctx, conversationID, c.getMinSeqKey)
-}
-
-func (c *msgCache) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
- val, err := c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64()
- if err != nil {
- return 0, errs.Wrap(err)
- }
- return val, nil
-}
-
-func (c *msgCache) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (m map[string]int64, err error) {
- return c.getSeqs(ctx, userIDs, func(userID string) string {
- return c.getConversationUserMinSeqKey(conversationID, userID)
- })
-}
-
-func (c *msgCache) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error {
- return errs.Wrap(c.rdb.Set(ctx, c.getConversationUserMinSeqKey(conversationID, userID), minSeq, 0).Err())
-}
-
-func (c *msgCache) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) {
- return c.setSeqs(ctx, seqs, func(userID string) string {
- return c.getConversationUserMinSeqKey(conversationID, userID)
- })
-}
-
-func (c *msgCache) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) (err error) {
- return c.setSeqs(ctx, seqs, func(conversationID string) string {
- return c.getConversationUserMinSeqKey(conversationID, userID)
- })
-}
-
-func (c *msgCache) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error {
- return errs.Wrap(c.rdb.Set(ctx, c.getHasReadSeqKey(conversationID, userID), hasReadSeq, 0).Err())
-}
-
-func (c *msgCache) SetHasReadSeqs(ctx context.Context, conversationID string, hasReadSeqs map[string]int64) error {
- return c.setSeqs(ctx, hasReadSeqs, func(userID string) string {
- return c.getHasReadSeqKey(conversationID, userID)
- })
-}
-
-func (c *msgCache) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error {
- return c.setSeqs(ctx, hasReadSeqs, func(conversationID string) string {
- return c.getHasReadSeqKey(conversationID, userID)
- })
-}
-
-func (c *msgCache) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
- return c.getSeqs(ctx, conversationIDs, func(conversationID string) string {
- return c.getHasReadSeqKey(conversationID, userID)
- })
-}
-
-func (c *msgCache) GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) {
- val, err := c.rdb.Get(ctx, c.getHasReadSeqKey(conversationID, userID)).Int64()
- if err != nil {
- return 0, err
- }
- return val, nil
-}
-
-func (c *msgCache) AddTokenFlag(ctx context.Context, userID string, platformID int, token string, flag int) error {
- key := uidPidToken + userID + ":" + constant.PlatformIDToName(platformID)
- return errs.Wrap(c.rdb.HSet(ctx, key, token, flag).Err())
-}
-
-func (c *msgCache) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) {
- key := uidPidToken + userID + ":" + constant.PlatformIDToName(platformID)
- m, err := c.rdb.HGetAll(ctx, key).Result()
- if err != nil {
- return nil, errs.Wrap(err)
- }
- mm := make(map[string]int)
- for k, v := range m {
- mm[k] = utils.StringToInt(v)
- }
-
- return mm, nil
-}
-
-func (c *msgCache) SetTokenMapByUidPid(ctx context.Context, userID string, platform int, m map[string]int) error {
- key := uidPidToken + userID + ":" + constant.PlatformIDToName(platform)
- mm := make(map[string]any)
- for k, v := range m {
- mm[k] = v
- }
-
- return errs.Wrap(c.rdb.HSet(ctx, key, mm).Err())
-}
-
-func (c *msgCache) DeleteTokenByUidPid(ctx context.Context, userID string, platform int, fields []string) error {
- key := uidPidToken + userID + ":" + constant.PlatformIDToName(platform)
-
- return errs.Wrap(c.rdb.HDel(ctx, key, fields...).Err())
-}
-
-func (c *msgCache) getMessageCacheKey(conversationID string, seq int64) string {
- return messageCache + conversationID + "_" + strconv.Itoa(int(seq))
+ rdb redis.UniversalClient
+ msgCacheTimeout time.Duration
+ redisEnablePipeline bool
}
func (c *msgCache) allMessageCacheKey(conversationID string) string {
return messageCache + conversationID + "_*"
}
-func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
- if c.config.Redis.EnablePipeline {
- return c.PipeGetMessagesBySeq(ctx, conversationID, seqs)
- }
-
- return c.ParallelGetMessagesBySeq(ctx, conversationID, seqs)
-}
-
-func (c *msgCache) PipeGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
- pipe := c.rdb.Pipeline()
-
- results := []*redis.StringCmd{}
- for _, seq := range seqs {
- results = append(results, pipe.Get(ctx, c.getMessageCacheKey(conversationID, seq)))
- }
-
- _, err = pipe.Exec(ctx)
- if err != nil && err != redis.Nil {
- return seqMsgs, failedSeqs, errs.Wrap(err, "pipe.get")
- }
-
- for idx, res := range results {
- seq := seqs[idx]
- if res.Err() != nil {
- log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq, "err", res.Err())
- failedSeqs = append(failedSeqs, seq)
- continue
- }
-
- msg := sdkws.MsgData{}
- if err = msgprocessor.String2Pb(res.Val(), &msg); err != nil {
- log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
- failedSeqs = append(failedSeqs, seq)
- continue
- }
-
- if msg.Status == constant.MsgDeleted {
- failedSeqs = append(failedSeqs, seq)
- continue
- }
-
- seqMsgs = append(seqMsgs, &msg)
- }
-
- return
-}
-
-func (c *msgCache) ParallelGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
- type entry struct {
- err error
- msg *sdkws.MsgData
- }
-
- wg := errgroup.Group{}
- wg.SetLimit(concurrentLimit)
-
- results := make([]entry, len(seqs)) // set slice len/cap to length of seqs.
- for idx, seq := range seqs {
- // closure safe var
- idx := idx
- seq := seq
-
- wg.Go(func() error {
- res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
- if err != nil {
- log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
- results[idx] = entry{err: err}
- return nil
- }
-
- msg := sdkws.MsgData{}
- if err = msgprocessor.String2Pb(res, &msg); err != nil {
- log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
- results[idx] = entry{err: err}
- return nil
- }
-
- if msg.Status == constant.MsgDeleted {
- results[idx] = entry{err: err}
- return nil
- }
-
- results[idx] = entry{msg: &msg}
- return nil
- })
- }
-
- _ = wg.Wait()
-
- for idx, res := range results {
- if res.err != nil {
- failedSeqs = append(failedSeqs, seqs[idx])
- continue
- }
-
- seqMsgs = append(seqMsgs, res.msg)
- }
-
- return
+func (c *msgCache) getMessageCacheKey(conversationID string, seq int64) string {
+ return messageCache + conversationID + "_" + strconv.Itoa(int(seq))
}
func (c *msgCache) SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) {
- if c.config.Redis.EnablePipeline {
+ if c.redisEnablePipeline {
return c.PipeSetMessageToCache(ctx, conversationID, msgs)
}
return c.ParallelSetMessageToCache(ctx, conversationID, msgs)
@@ -432,7 +119,7 @@ func (c *msgCache) PipeSetMessageToCache(ctx context.Context, conversationID str
}
key := c.getMessageCacheKey(conversationID, msg.Seq)
- _ = pipe.Set(ctx, key, s, time.Duration(c.config.MsgCacheTimeout)*time.Second)
+ _ = pipe.Set(ctx, key, s, c.msgCacheTimeout)
}
results, err := pipe.Exec(ctx)
@@ -462,7 +149,7 @@ func (c *msgCache) ParallelSetMessageToCache(ctx context.Context, conversationID
}
key := c.getMessageCacheKey(conversationID, msg.Seq)
- if err := c.rdb.Set(ctx, key, s, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil {
+ if err := c.rdb.Set(ctx, key, s, c.msgCacheTimeout).Err(); err != nil {
return errs.Wrap(err)
}
return nil
@@ -471,7 +158,7 @@ func (c *msgCache) ParallelSetMessageToCache(ctx context.Context, conversationID
err := wg.Wait()
if err != nil {
- return 0, errs.Wrap(err, "wg.Wait failed")
+ return 0, errs.WrapMsg(err, "wg.Wait failed")
}
return len(msgs), nil
@@ -497,17 +184,17 @@ func (c *msgCache) UserDeleteMsgs(ctx context.Context, conversationID string, se
if err != nil {
return errs.Wrap(err)
}
- if err := c.rdb.Expire(ctx, delUserListKey, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil {
+ if err := c.rdb.Expire(ctx, delUserListKey, c.msgCacheTimeout).Err(); err != nil {
return errs.Wrap(err)
}
- if err := c.rdb.Expire(ctx, userDelListKey, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil {
+ if err := c.rdb.Expire(ctx, userDelListKey, c.msgCacheTimeout).Err(); err != nil {
return errs.Wrap(err)
}
}
return nil
- //pipe := c.rdb.Pipeline()
- //for _, seq := range seqs {
+ // pipe := c.rdb.Pipeline()
+ // for _, seq := range seqs {
// delUserListKey := c.getMessageDelUserListKey(conversationID, seq)
// userDelListKey := c.getUserDelList(conversationID, userID)
// err := pipe.SAdd(ctx, delUserListKey, userID).Err()
@@ -525,8 +212,8 @@ func (c *msgCache) UserDeleteMsgs(ctx context.Context, conversationID string, se
// return errs.Wrap(err)
// }
//}
- //_, err := pipe.Exec(ctx)
- //return errs.Wrap(err)
+ // _, err := pipe.Exec(ctx)
+ // return errs.Wrap(err)
}
func (c *msgCache) GetUserDelList(ctx context.Context, userID, conversationID string) (seqs []int64, err error) {
@@ -536,7 +223,7 @@ func (c *msgCache) GetUserDelList(ctx context.Context, userID, conversationID st
}
seqs = make([]int64, len(result))
for i, v := range result {
- seqs[i] = utils.StringToInt64(v)
+ seqs[i] = stringutil.StringToInt64(v)
}
return seqs, nil
@@ -566,7 +253,7 @@ func (c *msgCache) DelUserDeleteMsgsList(ctx context.Context, conversationID str
}
}
}
- //for _, seq := range seqs {
+ // for _, seq := range seqs {
// delUsers, err := c.rdb.SMembers(ctx, c.getMessageDelUserListKey(conversationID, seq)).Result()
// if err != nil {
// log.ZWarn(ctx, "DelUserDeleteMsgsList failed", err, "conversationID", conversationID, "seq", seq)
@@ -605,7 +292,7 @@ func (c *msgCache) DelUserDeleteMsgsList(ctx context.Context, conversationID str
}
func (c *msgCache) DeleteMessages(ctx context.Context, conversationID string, seqs []int64) error {
- if c.config.Redis.EnablePipeline {
+ if c.redisEnablePipeline {
return c.PipeDeleteMessages(ctx, conversationID, seqs)
}
@@ -638,7 +325,7 @@ func (c *msgCache) PipeDeleteMessages(ctx context.Context, conversationID string
results, err := pipe.Exec(ctx)
if err != nil {
- return errs.Wrap(err, "pipe.del")
+ return errs.WrapMsg(err, "pipe.del")
}
for _, res := range results {
@@ -687,7 +374,7 @@ func (c *msgCache) DelMsgFromCache(ctx context.Context, userID string, seqs []in
if err != nil {
return errs.Wrap(err)
}
- if err := c.rdb.Set(ctx, key, s, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil {
+ if err := c.rdb.Set(ctx, key, s, c.msgCacheTimeout).Err(); err != nil {
return errs.Wrap(err)
}
}
@@ -695,30 +382,6 @@ func (c *msgCache) DelMsgFromCache(ctx context.Context, userID string, seqs []in
return nil
}
-func (c *msgCache) SetGetuiToken(ctx context.Context, token string, expireTime int64) error {
- return errs.Wrap(c.rdb.Set(ctx, getuiToken, token, time.Duration(expireTime)*time.Second).Err())
-}
-
-func (c *msgCache) GetGetuiToken(ctx context.Context) (string, error) {
- val, err := c.rdb.Get(ctx, getuiToken).Result()
- if err != nil {
- return "", errs.Wrap(err)
- }
- return val, nil
-}
-
-func (c *msgCache) SetGetuiTaskID(ctx context.Context, taskID string, expireTime int64) error {
- return errs.Wrap(c.rdb.Set(ctx, getuiTaskID, taskID, time.Duration(expireTime)*time.Second).Err())
-}
-
-func (c *msgCache) GetGetuiTaskID(ctx context.Context) (string, error) {
- val, err := c.rdb.Get(ctx, getuiTaskID).Result()
- if err != nil {
- return "", errs.Wrap(err)
- }
- return val, nil
-}
-
func (c *msgCache) SetSendMsgStatus(ctx context.Context, id string, status int32) error {
return errs.Wrap(c.rdb.Set(ctx, sendMsgFailedFlag+id, status, time.Hour*24).Err())
}
@@ -729,37 +392,6 @@ func (c *msgCache) GetSendMsgStatus(ctx context.Context, id string) (int32, erro
return int32(result), errs.Wrap(err)
}
-func (c *msgCache) SetFcmToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) (err error) {
- return errs.Wrap(c.rdb.Set(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID), fcmToken, time.Duration(expireTime)*time.Second).Err())
-}
-
-func (c *msgCache) GetFcmToken(ctx context.Context, account string, platformID int) (string, error) {
- val, err := c.rdb.Get(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID)).Result()
- if err != nil {
- return "", errs.Wrap(err)
- }
- return val, nil
-}
-
-func (c *msgCache) DelFcmToken(ctx context.Context, account string, platformID int) error {
- return errs.Wrap(c.rdb.Del(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID)).Err())
-}
-
-func (c *msgCache) IncrUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error) {
- seq, err := c.rdb.Incr(ctx, userBadgeUnreadCountSum+userID).Result()
-
- return int(seq), errs.Wrap(err)
-}
-
-func (c *msgCache) SetUserBadgeUnreadCountSum(ctx context.Context, userID string, value int) error {
- return errs.Wrap(c.rdb.Set(ctx, userBadgeUnreadCountSum+userID, value, 0).Err())
-}
-
-func (c *msgCache) GetUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error) {
- val, err := c.rdb.Get(ctx, userBadgeUnreadCountSum+userID).Int()
- return val, errs.Wrap(err)
-}
-
func (c *msgCache) LockMessageTypeKey(ctx context.Context, clientMsgID string, TypeKey string) error {
key := exTypeKeyLocker + clientMsgID + "_" + TypeKey
@@ -776,9 +408,9 @@ func (c *msgCache) getMessageReactionExPrefix(clientMsgID string, sessionType in
switch sessionType {
case constant.SingleChatType:
return "EX_SINGLE_" + clientMsgID
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
return "EX_GROUP_" + clientMsgID
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "EX_SUPER_GROUP_" + clientMsgID
case constant.NotificationChatType:
return "EX_NOTIFICATION" + clientMsgID
@@ -818,3 +450,104 @@ func (c *msgCache) GetOneMessageAllReactionList(ctx context.Context, clientMsgID
func (c *msgCache) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error {
return errs.Wrap(c.rdb.HDel(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), subKey).Err())
}
+
+func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
+ if c.redisEnablePipeline {
+ return c.PipeGetMessagesBySeq(ctx, conversationID, seqs)
+ }
+
+ return c.ParallelGetMessagesBySeq(ctx, conversationID, seqs)
+}
+
+func (c *msgCache) PipeGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
+ pipe := c.rdb.Pipeline()
+
+ results := []*redis.StringCmd{}
+ for _, seq := range seqs {
+ results = append(results, pipe.Get(ctx, c.getMessageCacheKey(conversationID, seq)))
+ }
+
+ _, err = pipe.Exec(ctx)
+ if err != nil && err != redis.Nil {
+ return seqMsgs, failedSeqs, errs.WrapMsg(err, "pipe.get")
+ }
+
+ for idx, res := range results {
+ seq := seqs[idx]
+ if res.Err() != nil {
+ log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq, "err", res.Err())
+ failedSeqs = append(failedSeqs, seq)
+ continue
+ }
+
+ msg := sdkws.MsgData{}
+ if err = msgprocessor.String2Pb(res.Val(), &msg); err != nil {
+ log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
+ failedSeqs = append(failedSeqs, seq)
+ continue
+ }
+
+ if msg.Status == constant.MsgDeleted {
+ failedSeqs = append(failedSeqs, seq)
+ continue
+ }
+
+ seqMsgs = append(seqMsgs, &msg)
+ }
+
+ return
+}
+
+func (c *msgCache) ParallelGetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) {
+ type entry struct {
+ err error
+ msg *sdkws.MsgData
+ }
+
+ wg := errgroup.Group{}
+ wg.SetLimit(concurrentLimit)
+
+ results := make([]entry, len(seqs)) // set slice len/cap to length of seqs.
+ for idx, seq := range seqs {
+ // closure safe var
+ idx := idx
+ seq := seq
+
+ wg.Go(func() error {
+ res, err := c.rdb.Get(ctx, c.getMessageCacheKey(conversationID, seq)).Result()
+ if err != nil {
+ log.ZError(ctx, "GetMessagesBySeq failed", err, "conversationID", conversationID, "seq", seq)
+ results[idx] = entry{err: err}
+ return nil
+ }
+
+ msg := sdkws.MsgData{}
+ if err = msgprocessor.String2Pb(res, &msg); err != nil {
+ log.ZError(ctx, "GetMessagesBySeq Unmarshal failed", err, "res", res, "conversationID", conversationID, "seq", seq)
+ results[idx] = entry{err: err}
+ return nil
+ }
+
+ if msg.Status == constant.MsgDeleted {
+ results[idx] = entry{err: err}
+ return nil
+ }
+
+ results[idx] = entry{msg: &msg}
+ return nil
+ })
+ }
+
+ _ = wg.Wait()
+
+ for idx, res := range results {
+ if res.err != nil {
+ failedSeqs = append(failedSeqs, seqs[idx])
+ continue
+ }
+
+ seqMsgs = append(seqMsgs, res.msg)
+ }
+
+ return
+}
diff --git a/pkg/common/db/cache/msg_test.go b/pkg/common/db/cache/msg_test.go
index 65413199ab..481b4012c1 100644
--- a/pkg/common/db/cache/msg_test.go
+++ b/pkg/common/db/cache/msg_test.go
@@ -20,7 +20,7 @@ import (
"math/rand"
"testing"
- "github.com/OpenIMSDK/protocol/sdkws"
+ "github.com/openimsdk/protocol/sdkws"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
)
diff --git a/pkg/common/db/cache/s3.go b/pkg/common/db/cache/s3.go
index 28e993be09..1610283ca4 100644
--- a/pkg/common/db/cache/s3.go
+++ b/pkg/common/db/cache/s3.go
@@ -16,12 +16,14 @@ package cache
import (
"context"
+ "github.com/openimsdk/tools/s3/cont"
+ "github.com/openimsdk/tools/s3/minio"
"strconv"
"time"
"github.com/dtm-labs/rockscache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/s3"
"github.com/redis/go-redis/v9"
)
@@ -83,7 +85,7 @@ type S3Cache interface {
DelS3Key(engine string, keys ...string) S3Cache
}
-func NewS3Cache(rdb redis.UniversalClient, s3 s3.Interface) S3Cache {
+func NewS3Cache(rdb redis.UniversalClient, s3 s3.Interface) cont.S3Cache {
rcClient := rockscache.NewClient(rdb, rockscache.NewDefaultOptions())
return &s3CacheRedis{
rcClient: rcClient,
@@ -100,7 +102,7 @@ type s3CacheRedis struct {
expireTime time.Duration
}
-func (g *s3CacheRedis) NewCache() S3Cache {
+func (g *s3CacheRedis) newCache() *s3CacheRedis {
return &s3CacheRedis{
rcClient: g.rcClient,
expireTime: g.expireTime,
@@ -109,14 +111,14 @@ func (g *s3CacheRedis) NewCache() S3Cache {
}
}
-func (g *s3CacheRedis) DelS3Key(engine string, keys ...string) S3Cache {
- s3cache := g.NewCache()
+func (g *s3CacheRedis) DelS3Key(ctx context.Context, engine string, keys ...string) error {
+ s3cache := g.newCache()
ks := make([]string, 0, len(keys))
for _, key := range keys {
ks = append(ks, g.getS3Key(engine, key))
}
s3cache.AddKeys(ks...)
- return s3cache
+ return s3cache.ExecDel(ctx)
}
func (g *s3CacheRedis) getS3Key(engine string, name string) string {
@@ -137,7 +139,7 @@ type MinioCache interface {
DelImageThumbnailKey(key string, format string, width int, height int) MinioCache
}
-func NewMinioCache(rdb redis.UniversalClient) MinioCache {
+func NewMinioCache(rdb redis.UniversalClient) minio.Cache {
rcClient := rockscache.NewClient(rdb, rockscache.NewDefaultOptions())
return &minioCacheRedis{
rcClient: rcClient,
@@ -152,7 +154,7 @@ type minioCacheRedis struct {
expireTime time.Duration
}
-func (g *minioCacheRedis) NewCache() MinioCache {
+func (g *minioCacheRedis) newCache() *minioCacheRedis {
return &minioCacheRedis{
rcClient: g.rcClient,
expireTime: g.expireTime,
@@ -160,20 +162,20 @@ func (g *minioCacheRedis) NewCache() MinioCache {
}
}
-func (g *minioCacheRedis) DelObjectImageInfoKey(keys ...string) MinioCache {
- s3cache := g.NewCache()
+func (g *minioCacheRedis) DelObjectImageInfoKey(ctx context.Context, keys ...string) error {
+ s3cache := g.newCache()
ks := make([]string, 0, len(keys))
for _, key := range keys {
ks = append(ks, g.getObjectImageInfoKey(key))
}
s3cache.AddKeys(ks...)
- return s3cache
+ return s3cache.ExecDel(ctx)
}
-func (g *minioCacheRedis) DelImageThumbnailKey(key string, format string, width int, height int) MinioCache {
- s3cache := g.NewCache()
+func (g *minioCacheRedis) DelImageThumbnailKey(ctx context.Context, key string, format string, width int, height int) error {
+ s3cache := g.newCache()
s3cache.AddKeys(g.getMinioImageThumbnailKey(key, format, width, height))
- return s3cache
+ return s3cache.ExecDel(ctx)
}
func (g *minioCacheRedis) getObjectImageInfoKey(key string) string {
@@ -184,7 +186,7 @@ func (g *minioCacheRedis) getMinioImageThumbnailKey(key string, format string, w
return "MINIO:THUMBNAIL:" + format + ":w" + strconv.Itoa(width) + ":h" + strconv.Itoa(height) + ":" + key
}
-func (g *minioCacheRedis) GetImageObjectKeyInfo(ctx context.Context, key string, fn func(ctx context.Context) (*MinioImageInfo, error)) (*MinioImageInfo, error) {
+func (g *minioCacheRedis) GetImageObjectKeyInfo(ctx context.Context, key string, fn func(ctx context.Context) (*minio.ImageInfo, error)) (*minio.ImageInfo, error) {
info, err := getCache(ctx, g.rcClient, g.getObjectImageInfoKey(key), g.expireTime, fn)
if err != nil {
return nil, err
diff --git a/pkg/common/db/cache/seq.go b/pkg/common/db/cache/seq.go
new file mode 100644
index 0000000000..6fbb091836
--- /dev/null
+++ b/pkg/common/db/cache/seq.go
@@ -0,0 +1,182 @@
+package cache
+
+import (
+ "context"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/utils/stringutil"
+ "github.com/redis/go-redis/v9"
+)
+
+type SeqCache interface {
+ SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error
+ GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error)
+ GetMaxSeq(ctx context.Context, conversationID string) (int64, error)
+ SetMinSeq(ctx context.Context, conversationID string, minSeq int64) error
+ SetMinSeqs(ctx context.Context, seqs map[string]int64) error
+ GetMinSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error)
+ GetMinSeq(ctx context.Context, conversationID string) (int64, error)
+ GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error)
+ GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (map[string]int64, error)
+ SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error
+ // seqs map: key userID value minSeq
+ SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error)
+ // seqs map: key conversationID value minSeq
+ SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error
+ // has read seq
+ SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error
+ // k: user, v: seq
+ SetHasReadSeqs(ctx context.Context, conversationID string, hasReadSeqs map[string]int64) error
+ // k: conversation, v :seq
+ UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error
+ GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error)
+ GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error)
+}
+
+func NewSeqCache(rdb redis.UniversalClient) SeqCache {
+ return &seqCache{rdb: rdb}
+}
+
+type seqCache struct {
+ rdb redis.UniversalClient
+}
+
+func (c *seqCache) getMaxSeqKey(conversationID string) string {
+ return maxSeq + conversationID
+}
+
+func (c *seqCache) getMinSeqKey(conversationID string) string {
+ return minSeq + conversationID
+}
+
+func (c *seqCache) getHasReadSeqKey(conversationID string, userID string) string {
+ return hasReadSeq + userID + ":" + conversationID
+}
+
+func (c *seqCache) getConversationUserMinSeqKey(conversationID, userID string) string {
+ return conversationUserMinSeq + conversationID + "u:" + userID
+}
+
+func (c *seqCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error {
+ return errs.Wrap(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err())
+}
+
+func (c *seqCache) getSeq(ctx context.Context, conversationID string, getkey func(conversationID string) string) (int64, error) {
+ val, err := c.rdb.Get(ctx, getkey(conversationID)).Int64()
+ if err != nil {
+ return 0, errs.Wrap(err)
+ }
+ return val, nil
+}
+
+func (c *seqCache) getSeqs(ctx context.Context, items []string, getkey func(s string) string) (m map[string]int64, err error) {
+ m = make(map[string]int64, len(items))
+ for i, v := range items {
+ res, err := c.rdb.Get(ctx, getkey(v)).Result()
+ if err != nil && err != redis.Nil {
+ return nil, errs.Wrap(err)
+ }
+ val := stringutil.StringToInt64(res)
+ if val != 0 {
+ m[items[i]] = val
+ }
+ }
+
+ return m, nil
+}
+
+func (c *seqCache) SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error {
+ return c.setSeq(ctx, conversationID, maxSeq, c.getMaxSeqKey)
+}
+
+func (c *seqCache) GetMaxSeqs(ctx context.Context, conversationIDs []string) (m map[string]int64, err error) {
+ return c.getSeqs(ctx, conversationIDs, c.getMaxSeqKey)
+}
+
+func (c *seqCache) GetMaxSeq(ctx context.Context, conversationID string) (int64, error) {
+ return c.getSeq(ctx, conversationID, c.getMaxSeqKey)
+}
+
+func (c *seqCache) SetMinSeq(ctx context.Context, conversationID string, minSeq int64) error {
+ return c.setSeq(ctx, conversationID, minSeq, c.getMinSeqKey)
+}
+
+func (c *seqCache) setSeqs(ctx context.Context, seqs map[string]int64, getkey func(key string) string) error {
+ for conversationID, seq := range seqs {
+ if err := c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err(); err != nil {
+ return errs.Wrap(err)
+ }
+ }
+ return nil
+}
+
+func (c *seqCache) SetMinSeqs(ctx context.Context, seqs map[string]int64) error {
+ return c.setSeqs(ctx, seqs, c.getMinSeqKey)
+}
+
+func (c *seqCache) GetMinSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) {
+ return c.getSeqs(ctx, conversationIDs, c.getMinSeqKey)
+}
+
+func (c *seqCache) GetMinSeq(ctx context.Context, conversationID string) (int64, error) {
+ return c.getSeq(ctx, conversationID, c.getMinSeqKey)
+}
+
+func (c *seqCache) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
+ val, err := c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64()
+ if err != nil {
+ return 0, errs.Wrap(err)
+ }
+ return val, nil
+}
+
+func (c *seqCache) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (m map[string]int64, err error) {
+ return c.getSeqs(ctx, userIDs, func(userID string) string {
+ return c.getConversationUserMinSeqKey(conversationID, userID)
+ })
+}
+
+func (c *seqCache) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error {
+ return errs.Wrap(c.rdb.Set(ctx, c.getConversationUserMinSeqKey(conversationID, userID), minSeq, 0).Err())
+}
+
+func (c *seqCache) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) {
+ return c.setSeqs(ctx, seqs, func(userID string) string {
+ return c.getConversationUserMinSeqKey(conversationID, userID)
+ })
+}
+
+func (c *seqCache) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) (err error) {
+ return c.setSeqs(ctx, seqs, func(conversationID string) string {
+ return c.getConversationUserMinSeqKey(conversationID, userID)
+ })
+}
+
+func (c *seqCache) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error {
+ return errs.Wrap(c.rdb.Set(ctx, c.getHasReadSeqKey(conversationID, userID), hasReadSeq, 0).Err())
+}
+
+func (c *seqCache) SetHasReadSeqs(ctx context.Context, conversationID string, hasReadSeqs map[string]int64) error {
+ return c.setSeqs(ctx, hasReadSeqs, func(userID string) string {
+ return c.getHasReadSeqKey(conversationID, userID)
+ })
+}
+
+func (c *seqCache) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error {
+ return c.setSeqs(ctx, hasReadSeqs, func(conversationID string) string {
+ return c.getHasReadSeqKey(conversationID, userID)
+ })
+}
+
+func (c *seqCache) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
+ return c.getSeqs(ctx, conversationIDs, func(conversationID string) string {
+ return c.getHasReadSeqKey(conversationID, userID)
+ })
+}
+
+func (c *seqCache) GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) {
+ val, err := c.rdb.Get(ctx, c.getHasReadSeqKey(conversationID, userID)).Int64()
+ if err != nil {
+ return 0, err
+ }
+ return val, nil
+}
diff --git a/pkg/common/db/cache/third.go b/pkg/common/db/cache/third.go
new file mode 100644
index 0000000000..d2900a32de
--- /dev/null
+++ b/pkg/common/db/cache/third.go
@@ -0,0 +1,85 @@
+package cache
+
+import (
+ "context"
+ "github.com/openimsdk/tools/errs"
+ "github.com/redis/go-redis/v9"
+ "strconv"
+ "time"
+)
+
+type ThirdCache interface {
+ SetFcmToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) (err error)
+ GetFcmToken(ctx context.Context, account string, platformID int) (string, error)
+ DelFcmToken(ctx context.Context, account string, platformID int) error
+ IncrUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error)
+ SetUserBadgeUnreadCountSum(ctx context.Context, userID string, value int) error
+ GetUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error)
+ SetGetuiToken(ctx context.Context, token string, expireTime int64) error
+ GetGetuiToken(ctx context.Context) (string, error)
+ SetGetuiTaskID(ctx context.Context, taskID string, expireTime int64) error
+ GetGetuiTaskID(ctx context.Context) (string, error)
+}
+
+func NewThirdCache(rdb redis.UniversalClient) ThirdCache {
+ return &thirdCache{rdb: rdb}
+}
+
+type thirdCache struct {
+ rdb redis.UniversalClient
+}
+
+func (c *thirdCache) SetFcmToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) (err error) {
+ return errs.Wrap(c.rdb.Set(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID), fcmToken, time.Duration(expireTime)*time.Second).Err())
+}
+
+func (c *thirdCache) GetFcmToken(ctx context.Context, account string, platformID int) (string, error) {
+ val, err := c.rdb.Get(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID)).Result()
+ if err != nil {
+ return "", errs.Wrap(err)
+ }
+ return val, nil
+}
+
+func (c *thirdCache) DelFcmToken(ctx context.Context, account string, platformID int) error {
+ return errs.Wrap(c.rdb.Del(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID)).Err())
+}
+
+func (c *thirdCache) IncrUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error) {
+ seq, err := c.rdb.Incr(ctx, userBadgeUnreadCountSum+userID).Result()
+
+ return int(seq), errs.Wrap(err)
+}
+
+func (c *thirdCache) SetUserBadgeUnreadCountSum(ctx context.Context, userID string, value int) error {
+ return errs.Wrap(c.rdb.Set(ctx, userBadgeUnreadCountSum+userID, value, 0).Err())
+}
+
+func (c *thirdCache) GetUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error) {
+ val, err := c.rdb.Get(ctx, userBadgeUnreadCountSum+userID).Int()
+ return val, errs.Wrap(err)
+}
+
+func (c *thirdCache) SetGetuiToken(ctx context.Context, token string, expireTime int64) error {
+ return errs.Wrap(c.rdb.Set(ctx, getuiToken, token, time.Duration(expireTime)*time.Second).Err())
+}
+
+func (c *thirdCache) GetGetuiToken(ctx context.Context) (string, error) {
+ val, err := c.rdb.Get(ctx, getuiToken).Result()
+ if err != nil {
+ return "", errs.Wrap(err)
+ }
+ return val, nil
+}
+
+func (c *thirdCache) SetGetuiTaskID(ctx context.Context, taskID string, expireTime int64) error {
+ return errs.Wrap(c.rdb.Set(ctx, getuiTaskID, taskID, time.Duration(expireTime)*time.Second).Err())
+}
+
+func (c *thirdCache) GetGetuiTaskID(ctx context.Context) (string, error) {
+ val, err := c.rdb.Get(ctx, getuiTaskID).Result()
+ if err != nil {
+ return "", errs.Wrap(err)
+ }
+ return val, nil
+}
diff --git a/pkg/common/db/cache/token.go b/pkg/common/db/cache/token.go
new file mode 100644
index 0000000000..88580e932f
--- /dev/null
+++ b/pkg/common/db/cache/token.go
@@ -0,0 +1,56 @@
+package cache
+
+import (
+ "context"
+
+ "github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/utils/stringutil"
+ "github.com/redis/go-redis/v9"
+)
+
+func NewTokenCacheModel(rdb redis.UniversalClient) TokenModel {
+ return &tokenCache{
+ rdb: rdb,
+ }
+}
+
+type TokenModel interface {
+ AddTokenFlag(ctx context.Context, userID string, platformID int, token string, flag int) error
+ GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error)
+ SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error
+ DeleteTokenByUidPid(ctx context.Context, userID string, platformID int, fields []string) error
+}
+
+type tokenCache struct {
+ rdb redis.UniversalClient
+}
+
+func (c *tokenCache) AddTokenFlag(ctx context.Context, userID string, platformID int, token string, flag int) error {
+ return errs.Wrap(c.rdb.HSet(ctx, cachekey.GetTokenKey(userID, platformID), token, flag).Err())
+}
+
+func (c *tokenCache) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) {
+ m, err := c.rdb.HGetAll(ctx, cachekey.GetTokenKey(userID, platformID)).Result()
+ if err != nil {
+ return nil, errs.Wrap(err)
+ }
+ mm := make(map[string]int)
+ for k, v := range m {
+ mm[k] = stringutil.StringToInt(v)
+ }
+
+ return mm, nil
+}
+
+func (c *tokenCache) SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error {
+ mm := make(map[string]any)
+ for k, v := range m {
+ mm[k] = v
+ }
+ return errs.Wrap(c.rdb.HSet(ctx, cachekey.GetTokenKey(userID, platformID), mm).Err())
+}
+
+func (c *tokenCache) DeleteTokenByUidPid(ctx context.Context, userID string, platformID int, fields []string) error {
+ return errs.Wrap(c.rdb.HDel(ctx, cachekey.GetTokenKey(userID, platformID), fields...).Err())
+}
diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go
index a60f558361..c10e9611a4 100644
--- a/pkg/common/db/cache/user.go
+++ b/pkg/common/db/cache/user.go
@@ -22,25 +22,22 @@ import (
"strconv"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
"github.com/dtm-labs/rockscache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
const (
- userExpireTime = time.Second * 60 * 60 * 12
- //userInfoKey = "USER_INFO:".
- userGlobalRecvMsgOptKey = "USER_GLOBAL_RECV_MSG_OPT_KEY:"
+ userExpireTime = time.Second * 60 * 60 * 12
olineStatusKey = "ONLINE_STATUS:"
userOlineStatusExpireTime = time.Second * 60 * 60 * 24
statusMod = 501
- platformID = "_PlatformIDSuffix"
)
type UserCache interface {
@@ -58,20 +55,16 @@ type UserCache interface {
type UserCacheRedis struct {
metaCache
rdb redis.UniversalClient
- //userDB relationtb.UserModelInterface
+ // userDB relationtb.UserModelInterface
userDB relationtb.UserModelInterface
expireTime time.Duration
rcClient *rockscache.Client
}
-func NewUserCacheRedis(
- rdb redis.UniversalClient,
- userDB relationtb.UserModelInterface,
- options rockscache.Options,
-) UserCache {
+func NewUserCacheRedis(rdb redis.UniversalClient, localCache *config.LocalCache, userDB relationtb.UserModelInterface, options rockscache.Options) UserCache {
rcClient := rockscache.NewClient(rdb, options)
mc := NewMetaCacheRedis(rcClient)
- u := config.Config.LocalCache.User
+ u := localCache.User
log.ZDebug(context.Background(), "user local cache init", "Topic", u.Topic, "SlotNum", u.SlotNum, "SlotSize", u.SlotSize, "enable", u.Enable())
mc.SetTopic(u.Topic)
mc.SetRawRedisClient(rdb)
@@ -294,7 +287,7 @@ func (u *UserCacheRedis) refreshStatusOnline(ctx context.Context, userID string,
onlineStatus.UserID = userID
newjsonData, err := json.Marshal(&onlineStatus)
if err != nil {
- return errs.Wrap(err, "json.Marshal failed")
+ return errs.WrapMsg(err, "json.Marshal failed")
}
_, err = u.rdb.HSet(ctx, key, userID, string(newjsonData)).Result()
if err != nil {
diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go
index 7cafa1c487..8190e5017a 100644
--- a/pkg/common/db/controller/auth.go
+++ b/pkg/common/db/controller/auth.go
@@ -17,13 +17,12 @@ package controller
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/tokenverify"
"github.com/golang-jwt/jwt/v4"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/tokenverify"
)
type AuthDatabase interface {
@@ -31,17 +30,18 @@ type AuthDatabase interface {
GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error)
// Create token
CreateToken(ctx context.Context, userID string, platformID int) (string, error)
+
+ SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error
}
type authDatabase struct {
- cache cache.MsgModel
+ cache cache.TokenModel
accessSecret string
accessExpire int64
- config *config.GlobalConfig
}
-func NewAuthDatabase(cache cache.MsgModel, accessSecret string, accessExpire int64, config *config.GlobalConfig) AuthDatabase {
- return &authDatabase{cache: cache, accessSecret: accessSecret, accessExpire: accessExpire, config: config}
+func NewAuthDatabase(cache cache.TokenModel, accessSecret string, accessExpire int64) AuthDatabase {
+ return &authDatabase{cache: cache, accessSecret: accessSecret, accessExpire: accessExpire}
}
// If the result is empty.
@@ -49,6 +49,10 @@ func (a *authDatabase) GetTokensWithoutError(ctx context.Context, userID string,
return a.cache.GetTokensWithoutError(ctx, userID, platformID)
}
+func (a *authDatabase) SetTokenMapByUidPid(ctx context.Context, userID string, platformID int, m map[string]int) error {
+ return a.cache.SetTokenMapByUidPid(ctx, userID, platformID, m)
+}
+
// Create Token.
func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformID int) (string, error) {
tokens, err := a.cache.GetTokensWithoutError(ctx, userID, platformID)
@@ -57,7 +61,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI
}
var deleteTokenKey []string
for k, v := range tokens {
- _, err = tokenverify.GetClaimFromToken(k, authverify.Secret(a.config.Secret))
+ _, err = tokenverify.GetClaimFromToken(k, authverify.Secret(a.accessSecret))
if err != nil || v != constant.NormalToken {
deleteTokenKey = append(deleteTokenKey, k)
}
@@ -73,7 +77,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err := token.SignedString([]byte(a.accessSecret))
if err != nil {
- return "", errs.Wrap(err, "token.SignedString")
+ return "", errs.WrapMsg(err, "token.SignedString")
}
return tokenString, a.cache.AddTokenFlag(ctx, userID, platformID, tokenString, constant.NormalToken)
}
diff --git a/pkg/common/db/controller/black.go b/pkg/common/db/controller/black.go
index c4b253c2f7..5991a9dfe5 100644
--- a/pkg/common/db/controller/black.go
+++ b/pkg/common/db/controller/black.go
@@ -17,11 +17,11 @@ package controller
import (
"context"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/pagination"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
)
type BlackDatabase interface {
@@ -86,7 +86,7 @@ func (b *blackDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (i
return
}
log.ZDebug(ctx, "blackIDs", "user1BlackIDs", userID1BlackIDs, "user2BlackIDs", userID2BlackIDs)
- return utils.IsContain(userID2, userID1BlackIDs), utils.IsContain(userID1, userID2BlackIDs), nil
+ return datautil.Contain(userID2, userID1BlackIDs...), datautil.Contain(userID1, userID2BlackIDs...), nil
}
// FindBlackIDs Get Blacklist List.
diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go
index 3d46e4fbc4..567bcb2704 100644
--- a/pkg/common/db/controller/conversation.go
+++ b/pkg/common/db/controller/conversation.go
@@ -18,14 +18,15 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/pagination"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/db/tx"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/stringutil"
)
type ConversationDatabase interface {
@@ -62,11 +63,11 @@ type ConversationDatabase interface {
GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationtb.ConversationModel, error)
// GetConversationNotReceiveMessageUserIDs gets user IDs for users in a conversation who have not received messages.
GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error)
- //GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
- //FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error)
+ // GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error)
+ // FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error)
}
-func NewConversationDatabase(conversation relationtb.ConversationModelInterface, cache cache.ConversationCache, tx tx.CtxTx) ConversationDatabase {
+func NewConversationDatabase(conversation relationtb.ConversationModelInterface, cache cache.ConversationCache, tx tx.Tx) ConversationDatabase {
return &conversationDatabase{
conversationDB: conversation,
cache: cache,
@@ -77,7 +78,7 @@ func NewConversationDatabase(conversation relationtb.ConversationModelInterface,
type conversationDatabase struct {
conversationDB relationtb.ConversationModelInterface
cache cache.ConversationCache
- tx tx.CtxTx
+ tx tx.Tx
}
func (c *conversationDatabase) SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.ConversationModel, fieldMap map[string]any) (err error) {
@@ -105,13 +106,13 @@ func (c *conversationDatabase) SetUsersConversationFieldTx(ctx context.Context,
cache = cache.DelConversationNotReceiveMessageUserIDs(conversation.ConversationID)
}
}
- NotUserIDs := utils.DifferenceString(haveUserIDs, userIDs)
+ NotUserIDs := stringutil.DifferenceString(haveUserIDs, userIDs)
log.ZDebug(ctx, "SetUsersConversationFieldTx", "NotUserIDs", NotUserIDs, "haveUserIDs", haveUserIDs, "userIDs", userIDs)
var conversations []*relationtb.ConversationModel
now := time.Now()
for _, v := range NotUserIDs {
temp := new(relationtb.ConversationModel)
- if err = utils.CopyStructFields(temp, conversation); err != nil {
+ if err = datautil.CopyStructFields(temp, conversation); err != nil {
return err
}
temp.OwnerUserID = v
@@ -205,7 +206,7 @@ func (c *conversationDatabase) GetUserAllConversation(ctx context.Context, owner
func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationtb.ConversationModel) error {
return c.tx.Transaction(ctx, func(ctx context.Context) error {
cache := c.cache.NewCache()
- groupIDs := utils.Distinct(utils.Filter(conversations, func(e *relationtb.ConversationModel) (string, bool) {
+ groupIDs := datautil.Distinct(datautil.Filter(conversations, func(e *relationtb.ConversationModel) (string, bool) {
return e.GroupID, e.GroupID != ""
}))
for _, groupID := range groupIDs {
@@ -235,7 +236,7 @@ func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUs
var notExistConversations []*relationtb.ConversationModel
for _, conversation := range conversations {
- if !utils.IsContain(conversation.ConversationID, existConversationIDs) {
+ if !datautil.Contain(conversation.ConversationID, existConversationIDs...) {
notExistConversations = append(notExistConversations, conversation)
}
}
@@ -246,28 +247,28 @@ func (c *conversationDatabase) SetUserConversations(ctx context.Context, ownerUs
}
cache = cache.DelConversationIDs(ownerUserID).
DelUserConversationIDsHash(ownerUserID).
- DelConversationNotReceiveMessageUserIDs(utils.Slice(notExistConversations, func(e *relationtb.ConversationModel) string { return e.ConversationID })...)
+ DelConversationNotReceiveMessageUserIDs(datautil.Slice(notExistConversations, func(e *relationtb.ConversationModel) string { return e.ConversationID })...)
}
return cache.ExecDel(ctx)
})
}
-//func (c *conversationDatabase) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) {
+// func (c *conversationDatabase) FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) {
// return c.cache.GetSuperGroupRecvMsgNotNotifyUserIDs(ctx, groupID)
//}
func (c *conversationDatabase) CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error {
return c.tx.Transaction(ctx, func(ctx context.Context) error {
cache := c.cache.NewCache()
- conversationID := msgprocessor.GetConversationIDBySessionType(constant.SuperGroupChatType, groupID)
+ conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, groupID)
existConversationUserIDs, err := c.conversationDB.FindUserID(ctx, userIDs, []string{conversationID})
if err != nil {
return err
}
- notExistUserIDs := utils.DifferenceString(userIDs, existConversationUserIDs)
+ notExistUserIDs := stringutil.DifferenceString(userIDs, existConversationUserIDs)
var conversations []*relationtb.ConversationModel
for _, v := range notExistUserIDs {
- conversation := relationtb.ConversationModel{ConversationType: constant.SuperGroupChatType, GroupID: groupID, OwnerUserID: v, ConversationID: conversationID}
+ conversation := relationtb.ConversationModel{ConversationType: constant.ReadGroupChatType, GroupID: groupID, OwnerUserID: v, ConversationID: conversationID}
conversations = append(conversations, &conversation)
cache = cache.DelConversations(v, conversationID).DelConversationNotReceiveMessageUserIDs(conversationID)
}
diff --git a/pkg/common/db/controller/doc.go b/pkg/common/db/controller/doc.go
index 72316f1280..97ec08799f 100644
--- a/pkg/common/db/controller/doc.go
+++ b/pkg/common/db/controller/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go
index 3c81d922ce..49136f2288 100644
--- a/pkg/common/db/controller/friend.go
+++ b/pkg/common/db/controller/friend.go
@@ -19,15 +19,15 @@ import (
"fmt"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "github.com/OpenIMSDK/tools/pagination"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/db/tx"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/utils/datautil"
)
type FriendDatabase interface {
@@ -80,11 +80,11 @@ type FriendDatabase interface {
type friendDatabase struct {
friend relation.FriendModelInterface
friendRequest relation.FriendRequestModelInterface
- tx tx.CtxTx
+ tx tx.Tx
cache cache.FriendCache
}
-func NewFriendDatabase(friend relation.FriendModelInterface, friendRequest relation.FriendRequestModelInterface, cache cache.FriendCache, tx tx.CtxTx) FriendDatabase {
+func NewFriendDatabase(friend relation.FriendModelInterface, friendRequest relation.FriendRequestModelInterface, cache cache.FriendCache, tx tx.Tx) FriendDatabase {
return &friendDatabase{friend: friend, friendRequest: friendRequest, cache: cache, tx: tx}
}
@@ -106,8 +106,8 @@ func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (
}
// Check if userID2 is in userID1's friend list and vice versa
- inUser1Friends = utils.IsContain(userID2, userID1FriendIDs)
- inUser2Friends = utils.IsContain(userID1, userID2FriendIDs)
+ inUser1Friends = datautil.Contain(userID2, userID1FriendIDs...)
+ inUser2Friends = datautil.Contain(userID1, userID2FriendIDs...)
return inUser1Friends, inUser2Friends, nil
}
@@ -139,7 +139,7 @@ func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUse
func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error) {
return f.tx.Transaction(ctx, func(ctx context.Context) error {
cache := f.cache.NewCache()
- // User find friends
+ // user find friends
fs1, err := f.friend.FindFriends(ctx, ownerUserID, friendUserIDs)
if err != nil {
return err
@@ -148,7 +148,7 @@ func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string,
for _, v := range friendUserIDs {
fs1 = append(fs1, &relation.FriendModel{OwnerUserID: ownerUserID, FriendUserID: v, AddSource: addSource, OperatorUserID: opUserID})
}
- fs11 := utils.DistinctAny(fs1, func(e *relation.FriendModel) string {
+ fs11 := datautil.DistinctAny(fs1, func(e *relation.FriendModel) string {
return e.FriendUserID
})
@@ -165,7 +165,7 @@ func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string,
fs2 = append(fs2, &relation.FriendModel{OwnerUserID: v, FriendUserID: ownerUserID, AddSource: addSource, OperatorUserID: opUserID})
newFriendIDs = append(newFriendIDs, v)
}
- fs22 := utils.DistinctAny(fs2, func(e *relation.FriendModel) string {
+ fs22 := datautil.DistinctAny(fs2, func(e *relation.FriendModel) string {
return e.OwnerUserID
})
err = f.friend.Create(ctx, fs22)
@@ -212,14 +212,13 @@ func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest
// AgreeFriendRequest accepts a friend request. It first checks for an existing, unprocessed request.
func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
return f.tx.Transaction(ctx, func(ctx context.Context) error {
- defer log.ZDebug(ctx, "return line")
now := time.Now()
fr, err := f.friendRequest.Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID)
if err != nil {
return err
}
if fr.HandleResult != 0 {
- return errs.ErrArgs.Wrap("the friend request has been processed")
+ return errs.ErrArgs.WrapMsg("the friend request has been processed")
}
friendRequest.HandlerUserID = mcontext.GetOpUserID(ctx)
friendRequest.HandleResult = constant.FriendResponseAgree
@@ -246,7 +245,7 @@ func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *
if err != nil {
return err
}
- existsMap := utils.SliceSet(utils.Slice(exists, func(friend *relation.FriendModel) [2]string {
+ existsMap := datautil.SliceSet(datautil.Slice(exists, func(friend *relation.FriendModel) [2]string {
return [...]string{friend.OwnerUserID, friend.FriendUserID} // My - Friend
}))
var adds []*relation.FriendModel
diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go
index 45bf87b6f1..ddf72b7bf1 100644
--- a/pkg/common/db/controller/group.go
+++ b/pkg/common/db/controller/group.go
@@ -16,15 +16,16 @@ package controller
import (
"context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/pagination"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
"github.com/dtm-labs/rockscache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/db/tx"
+ "github.com/openimsdk/tools/utils/datautil"
"github.com/redis/go-redis/v9"
)
@@ -107,10 +108,11 @@ type GroupDatabase interface {
func NewGroupDatabase(
rdb redis.UniversalClient,
+ localCache *config.LocalCache,
groupDB relationtb.GroupModelInterface,
groupMemberDB relationtb.GroupMemberModelInterface,
groupRequestDB relationtb.GroupRequestModelInterface,
- ctxTx tx.CtxTx,
+ ctxTx tx.Tx,
groupHash cache.GroupHash,
) GroupDatabase {
rcOptions := rockscache.NewDefaultOptions()
@@ -121,7 +123,7 @@ func NewGroupDatabase(
groupMemberDB: groupMemberDB,
groupRequestDB: groupRequestDB,
ctxTx: ctxTx,
- cache: cache.NewGroupCacheRedis(rdb, groupDB, groupMemberDB, groupRequestDB, groupHash, rcOptions),
+ cache: cache.NewGroupCacheRedis(rdb, localCache, groupDB, groupMemberDB, groupRequestDB, groupHash, rcOptions),
}
}
@@ -129,7 +131,7 @@ type groupDatabase struct {
groupDB relationtb.GroupModelInterface
groupMemberDB relationtb.GroupMemberModelInterface
groupRequestDB relationtb.GroupRequestModelInterface
- ctxTx tx.CtxTx
+ ctxTx tx.Tx
cache cache.GroupCache
}
@@ -270,7 +272,7 @@ func (g *groupDatabase) PageGetJoinGroup(ctx context.Context, userID string, pag
if err != nil {
return 0, nil, err
}
- for _, groupID := range utils.Paginate(groupIDs, int(pagination.GetPageNumber()), int(pagination.GetShowNumber())) {
+ for _, groupID := range datautil.Paginate(groupIDs, int(pagination.GetPageNumber()), int(pagination.GetShowNumber())) {
groupMembers, err := g.cache.GetGroupMembersInfo(ctx, groupID, []string{userID})
if err != nil {
return 0, nil, err
@@ -285,7 +287,7 @@ func (g *groupDatabase) PageGetGroupMember(ctx context.Context, groupID string,
if err != nil {
return 0, nil, err
}
- pageIDs := utils.Paginate(groupMemberIDs, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
+ pageIDs := datautil.Paginate(groupMemberIDs, int(pagination.GetPageNumber()), int(pagination.GetShowNumber()))
if len(pageIDs) == 0 {
return int64(len(groupMemberIDs)), nil, nil
}
diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go
index 722fc8f903..8fbe3ee771 100644
--- a/pkg/common/db/controller/msg.go
+++ b/pkg/common/db/controller/msg.go
@@ -18,21 +18,22 @@ import (
"context"
"encoding/json"
"errors"
+ "strings"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- pbmsg "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
"github.com/openimsdk/open-im-server/v3/pkg/common/convert"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
+ "github.com/openimsdk/protocol/constant"
+ pbmsg "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mq/kafka"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/openimsdk/tools/utils/timeutil"
"github.com/redis/go-redis/v9"
"go.mongodb.org/mongo-driver/mongo"
)
@@ -47,7 +48,7 @@ type CommonMsgDatabase interface {
// BatchInsertChat2DB inserts a batch of messages into the database for a specific conversation.
BatchInsertChat2DB(ctx context.Context, conversationID string, msgs []*sdkws.MsgData, currentMaxSeq int64) error
// RevokeMsg revokes a message in a conversation.
- RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *unrelationtb.RevokeModel) error
+ RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *relation.RevokeModel) error
// MarkSingleChatMsgsAsRead marks messages as read for a single chat by sequence numbers.
MarkSingleChatMsgsAsRead(ctx context.Context, userID string, conversationID string, seqs []int64) error
// DeleteMessagesFromCache deletes message caches from Redis by sequence numbers.
@@ -100,75 +101,58 @@ type CommonMsgDatabase interface {
MsgToPushMQ(ctx context.Context, key, conversarionID string, msg2mq *sdkws.MsgData) (int32, int64, error)
MsgToMongoMQ(ctx context.Context, key, conversarionID string, msgs []*sdkws.MsgData, lastSeq int64) error
- RangeUserSendCount(
- ctx context.Context,
- start time.Time,
- end time.Time,
- group bool,
- ase bool,
- pageNumber int32,
- showNumber int32,
- ) (msgCount int64, userCount int64, users []*unrelationtb.UserCount, dateCount map[string]int64, err error)
- RangeGroupSendCount(
- ctx context.Context,
- start time.Time,
- end time.Time,
- ase bool,
- pageNumber int32,
- showNumber int32,
- ) (msgCount int64, userCount int64, groups []*unrelationtb.GroupCount, dateCount map[string]int64, err error)
+ RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*relation.UserCount, dateCount map[string]int64, err error)
+ RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*relation.GroupCount, dateCount map[string]int64, err error)
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)
+
+ // clear msg
+ GetBeforeMsg(ctx context.Context, ts int64, limit int) ([]*relation.MsgDocModel, error)
+ DeleteDocMsgBefore(ctx context.Context, ts int64, doc *relation.MsgDocModel) ([]int, error)
}
-func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel, config *config.GlobalConfig) (CommonMsgDatabase, error) {
- producerConfig := &kafka.ProducerConfig{
- ProducerAck: config.Kafka.ProducerAck,
- CompressType: config.Kafka.CompressType,
- Username: config.Kafka.Username,
- Password: config.Kafka.Password,
- }
-
- var tlsConfig *kafka.TLSConfig
- if config.Kafka.TLS != nil {
- tlsConfig = &kafka.TLSConfig{
- CACrt: config.Kafka.TLS.CACrt,
- ClientCrt: config.Kafka.TLS.ClientCrt,
- ClientKey: config.Kafka.TLS.ClientKey,
- ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
- InsecureSkipVerify: false,
- }
+func NewCommonMsgDatabase(msgDocModel relation.MsgDocModelInterface, msg cache.MsgCache, seq cache.SeqCache, kafkaConf *config.Kafka) (CommonMsgDatabase, error) {
+ conf, err := kafka.BuildProducerConfig(*kafkaConf.Build())
+ if err != nil {
+ return nil, err
}
- producerToRedis, err := kafka.NewKafkaProducer(config.Kafka.Addr, config.Kafka.LatestMsgToRedis.Topic, producerConfig, tlsConfig)
+ producerToRedis, err := kafka.NewKafkaProducer(conf, kafkaConf.Address, kafkaConf.ToRedisTopic)
if err != nil {
return nil, err
}
- producerToMongo, err := kafka.NewKafkaProducer(config.Kafka.Addr, config.Kafka.MsgToMongo.Topic, producerConfig, tlsConfig)
+ producerToMongo, err := kafka.NewKafkaProducer(conf, kafkaConf.Address, kafkaConf.ToMongoTopic)
if err != nil {
return nil, err
}
- producerToPush, err := kafka.NewKafkaProducer(config.Kafka.Addr, config.Kafka.MsgToPush.Topic, producerConfig, tlsConfig)
+ producerToPush, err := kafka.NewKafkaProducer(conf, kafkaConf.Address, kafkaConf.ToPushTopic)
if err != nil {
return nil, err
}
return &commonMsgDatabase{
msgDocDatabase: msgDocModel,
- cache: cacheModel,
+ msg: msg,
+ seq: seq,
producer: producerToRedis,
producerToMongo: producerToMongo,
producerToPush: producerToPush,
}, nil
}
-func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database, config *config.GlobalConfig) (CommonMsgDatabase, error) {
- cacheModel := cache.NewMsgCacheModel(rdb, config)
- msgDocModel := unrelation.NewMsgMongoDriver(database)
- return NewCommonMsgDatabase(msgDocModel, cacheModel, config)
-}
+//func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database, config *tools.CronTaskConfig) (CommonMsgDatabase, error) {
+// msgDocModel, err := mgo.NewMsgMongo(database)
+// if err != nil {
+// return nil, err
+// }
+// //todo MsgCacheTimeout
+// msg := cache.NewMsgCache(rdb, 86400, config.RedisConfig.EnablePipeline)
+// seq := cache.NewSeqCache(rdb)
+// return NewCommonMsgDatabase(msgDocModel, msg, seq, &config.KafkaConfig)
+//}
type commonMsgDatabase struct {
- msgDocDatabase unrelationtb.MsgDocModelInterface
- msg unrelationtb.MsgDocModel
- cache cache.MsgModel
+ msgDocDatabase relation.MsgDocModelInterface
+ msgTable relation.MsgDocModel
+ msg cache.MsgCache
+ seq cache.SeqCache
producer *kafka.Producer
producerToMongo *kafka.Producer
producerToModify *kafka.Producer
@@ -209,24 +193,24 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI
if len(fields) == 0 {
return nil
}
- num := db.msg.GetSingleGocMsgNum()
+ num := db.msgTable.GetSingleGocMsgNum()
// num = 100
for i, field := range fields { // Check the type of the field
var ok bool
switch key {
case updateKeyMsg:
- var msg *unrelationtb.MsgDataModel
- msg, ok = field.(*unrelationtb.MsgDataModel)
+ var msg *relation.MsgDataModel
+ msg, ok = field.(*relation.MsgDataModel)
if msg != nil && msg.Seq != firstSeq+int64(i) {
- return errs.ErrInternalServer.Wrap("seq is invalid")
+ return errs.ErrInternalServer.WrapMsg("seq is invalid")
}
case updateKeyRevoke:
- _, ok = field.(*unrelationtb.RevokeModel)
+ _, ok = field.(*relation.RevokeModel)
default:
- return errs.ErrInternalServer.Wrap("key is invalid")
+ return errs.ErrInternalServer.WrapMsg("key is invalid")
}
if !ok {
- return errs.ErrInternalServer.Wrap("field type is invalid")
+ return errs.ErrInternalServer.WrapMsg("field type is invalid")
}
}
// Returns true if the document exists in the database, false if the document does not exist in the database
@@ -235,8 +219,8 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI
res *mongo.UpdateResult
err error
)
- docID := db.msg.GetDocID(conversationID, seq)
- index := db.msg.GetMsgIndex(seq)
+ docID := db.msgTable.GetDocID(conversationID, seq)
+ index := db.msgTable.GetMsgIndex(seq)
field := fields[i]
switch key {
case updateKeyMsg:
@@ -261,31 +245,31 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI
continue // The current data has been updated, skip the current data
}
}
- doc := unrelationtb.MsgDocModel{
- DocID: db.msg.GetDocID(conversationID, seq),
- Msg: make([]*unrelationtb.MsgInfoModel, num),
+ doc := relation.MsgDocModel{
+ DocID: db.msgTable.GetDocID(conversationID, seq),
+ Msg: make([]*relation.MsgInfoModel, num),
}
var insert int // Inserted data number
for j := i; j < len(fields); j++ {
seq = firstSeq + int64(j)
- if db.msg.GetDocID(conversationID, seq) != doc.DocID {
+ if db.msgTable.GetDocID(conversationID, seq) != doc.DocID {
break
}
insert++
switch key {
case updateKeyMsg:
- doc.Msg[db.msg.GetMsgIndex(seq)] = &unrelationtb.MsgInfoModel{
- Msg: fields[j].(*unrelationtb.MsgDataModel),
+ doc.Msg[db.msgTable.GetMsgIndex(seq)] = &relation.MsgInfoModel{
+ Msg: fields[j].(*relation.MsgDataModel),
}
case updateKeyRevoke:
- doc.Msg[db.msg.GetMsgIndex(seq)] = &unrelationtb.MsgInfoModel{
- Revoke: fields[j].(*unrelationtb.RevokeModel),
+ doc.Msg[db.msgTable.GetMsgIndex(seq)] = &relation.MsgInfoModel{
+ Revoke: fields[j].(*relation.RevokeModel),
}
}
}
for i, model := range doc.Msg {
if model == nil {
- model = &unrelationtb.MsgInfoModel{}
+ model = &relation.MsgInfoModel{}
doc.Msg[i] = model
}
if model.DelList == nil {
@@ -308,16 +292,16 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI
func (db *commonMsgDatabase) BatchInsertChat2DB(ctx context.Context, conversationID string, msgList []*sdkws.MsgData, currentMaxSeq int64) error {
if len(msgList) == 0 {
- return errs.ErrArgs.Wrap("msgList is empty")
+ return errs.ErrArgs.WrapMsg("msgList is empty")
}
msgs := make([]any, len(msgList))
for i, msg := range msgList {
if msg == nil {
continue
}
- var offlinePushModel *unrelationtb.OfflinePushModel
+ var offlinePushModel *relation.OfflinePushModel
if msg.OfflinePushInfo != nil {
- offlinePushModel = &unrelationtb.OfflinePushModel{
+ offlinePushModel = &relation.OfflinePushModel{
Title: msg.OfflinePushInfo.Title,
Desc: msg.OfflinePushInfo.Desc,
Ex: msg.OfflinePushInfo.Ex,
@@ -325,7 +309,7 @@ func (db *commonMsgDatabase) BatchInsertChat2DB(ctx context.Context, conversatio
IOSBadgeCount: msg.OfflinePushInfo.IOSBadgeCount,
}
}
- msgs[i] = &unrelationtb.MsgDataModel{
+ msgs[i] = &relation.MsgDataModel{
SendID: msg.SendID,
RecvID: msg.RecvID,
GroupID: msg.GroupID,
@@ -352,15 +336,15 @@ func (db *commonMsgDatabase) BatchInsertChat2DB(ctx context.Context, conversatio
return db.BatchInsertBlock(ctx, conversationID, msgs, updateKeyMsg, msgList[0].Seq)
}
-func (db *commonMsgDatabase) RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *unrelationtb.RevokeModel) error {
+func (db *commonMsgDatabase) RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *relation.RevokeModel) error {
return db.BatchInsertBlock(ctx, conversationID, []any{revoke}, updateKeyRevoke, seq)
}
func (db *commonMsgDatabase) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, conversationID string, totalSeqs []int64) error {
- for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, totalSeqs) {
+ for docID, seqs := range db.msgTable.GetDocIDSeqsMap(conversationID, totalSeqs) {
var indexes []int64
for _, seq := range seqs {
- indexes = append(indexes, db.msg.GetMsgIndex(seq))
+ indexes = append(indexes, db.msgTable.GetMsgIndex(seq))
}
log.ZDebug(ctx, "MarkSingleChatMsgsAsRead", "userID", userID, "docID", docID, "indexes", indexes)
if err := db.msgDocDatabase.MarkSingleChatMsgsAsRead(ctx, userID, docID, indexes); err != nil {
@@ -372,25 +356,25 @@ func (db *commonMsgDatabase) MarkSingleChatMsgsAsRead(ctx context.Context, userI
}
func (db *commonMsgDatabase) DeleteMessagesFromCache(ctx context.Context, conversationID string, seqs []int64) error {
- return db.cache.DeleteMessages(ctx, conversationID, seqs)
+ return db.msg.DeleteMessages(ctx, conversationID, seqs)
}
func (db *commonMsgDatabase) DelUserDeleteMsgsList(ctx context.Context, conversationID string, seqs []int64) {
- db.cache.DelUserDeleteMsgsList(ctx, conversationID, seqs)
+ db.msg.DelUserDeleteMsgsList(ctx, conversationID, seqs)
}
func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (seq int64, isNew bool, err error) {
- currentMaxSeq, err := db.cache.GetMaxSeq(ctx, conversationID)
+ currentMaxSeq, err := db.seq.GetMaxSeq(ctx, conversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
- log.ZError(ctx, "db.cache.GetMaxSeq", err)
+ log.ZError(ctx, "db.seq.GetMaxSeq", err)
return 0, false, err
}
lenList := len(msgs)
- if int64(lenList) > db.msg.GetSingleGocMsgNum() {
- return 0, false, errors.New("too large")
+ if int64(lenList) > db.msgTable.GetSingleGocMsgNum() {
+ return 0, false, errs.New("message count exceeds limit", "limit", db.msgTable.GetSingleGocMsgNum()).Wrap()
}
if lenList < 1 {
- return 0, false, errors.New("too short as 0")
+ return 0, false, errs.New("no messages to insert", "minCount", 1).Wrap()
}
if errs.Unwrap(err) == redis.Nil {
isNew = true
@@ -402,19 +386,22 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa
m.Seq = currentMaxSeq
userSeqMap[m.SendID] = m.Seq
}
- failedNum, err := db.cache.SetMessageToCache(ctx, conversationID, msgs)
+
+ failedNum, err := db.msg.SetMessageToCache(ctx, conversationID, msgs)
if err != nil {
prommetrics.MsgInsertRedisFailedCounter.Add(float64(failedNum))
log.ZError(ctx, "setMessageToCache error", err, "len", len(msgs), "conversationID", conversationID)
} else {
prommetrics.MsgInsertRedisSuccessCounter.Inc()
}
- err = db.cache.SetMaxSeq(ctx, conversationID, currentMaxSeq)
+
+ err = db.seq.SetMaxSeq(ctx, conversationID, currentMaxSeq)
if err != nil {
- log.ZError(ctx, "db.cache.SetMaxSeq error", err, "conversationID", conversationID)
+ log.ZError(ctx, "db.seq.SetMaxSeq error", err, "conversationID", conversationID)
prommetrics.SeqSetFailedCounter.Inc()
}
- err = db.cache.SetHasReadSeqs(ctx, conversationID, userSeqMap)
+
+ err = db.seq.SetHasReadSeqs(ctx, conversationID, userSeqMap)
if err != nil {
log.ZError(ctx, "SetHasReadSeqs error", err, "userSeqMap", userSeqMap, "conversationID", conversationID)
prommetrics.SeqSetFailedCounter.Inc()
@@ -423,7 +410,7 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa
}
func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversationID string, seqs []int64) (totalMsgs []*sdkws.MsgData, err error) {
- for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, seqs) {
+ for docID, seqs := range db.msgTable.GetDocIDSeqsMap(conversationID, seqs) {
// log.ZDebug(ctx, "getMsgBySeqs", "docID", docID, "seqs", seqs)
msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, conversationID, seqs)
if err != nil {
@@ -436,7 +423,7 @@ func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversat
return totalMsgs, nil
}
-func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][]*unrelationtb.MsgInfoModel, userID, conversationID string, msg *unrelationtb.MsgInfoModel) {
+func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][]*relation.MsgInfoModel, userID, conversationID string, msg *relation.MsgInfoModel) {
if msg.IsRead {
msg.Msg.IsRead = true
}
@@ -458,12 +445,12 @@ func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][
if quoteMsg.QuoteMessage == nil || quoteMsg.QuoteMessage.ContentType == constant.MsgRevokeNotification {
return
}
- var msgs []*unrelationtb.MsgInfoModel
+ var msgs []*relation.MsgInfoModel
if v, ok := cache[quoteMsg.QuoteMessage.Seq]; ok {
msgs = v
} else {
if quoteMsg.QuoteMessage.Seq > 0 {
- ms, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, db.msg.GetDocID(conversationID, quoteMsg.QuoteMessage.Seq), []int64{quoteMsg.QuoteMessage.Seq})
+ ms, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, db.msgTable.GetDocID(conversationID, quoteMsg.QuoteMessage.Seq), []int64{quoteMsg.QuoteMessage.Seq})
if err != nil {
log.ZError(ctx, "GetMsgBySeqIndexIn1Doc", err, "conversationID", conversationID, "seq", quoteMsg.QuoteMessage.Seq)
return
@@ -487,17 +474,17 @@ func (db *commonMsgDatabase) handlerDBMsg(ctx context.Context, cache map[int64][
return
}
msg.Msg.Content = string(data)
- if _, err := db.msgDocDatabase.UpdateMsg(ctx, db.msg.GetDocID(conversationID, msg.Msg.Seq), db.msg.GetMsgIndex(msg.Msg.Seq), "msg", msg.Msg); err != nil {
+ if _, err := db.msgDocDatabase.UpdateMsg(ctx, db.msgTable.GetDocID(conversationID, msg.Msg.Seq), db.msgTable.GetMsgIndex(msg.Msg.Seq), "msg", msg.Msg); err != nil {
log.ZError(ctx, "UpdateMsgContent", err)
}
}
-func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, conversationID string, seqs []int64) (totalMsgs []*unrelationtb.MsgInfoModel, err error) {
+func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID string, conversationID string, seqs []int64) (totalMsgs []*relation.MsgInfoModel, err error) {
msgs, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, userID, docID, seqs)
if err != nil {
return nil, err
}
- tempCache := make(map[int64][]*unrelationtb.MsgInfoModel)
+ tempCache := make(map[int64][]*relation.MsgInfoModel)
for _, msg := range msgs {
db.handlerDBMsg(ctx, tempCache, userID, conversationID, msg)
}
@@ -506,7 +493,7 @@ func (db *commonMsgDatabase) findMsgInfoBySeq(ctx context.Context, userID, docID
func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID string, conversationID string, allSeqs []int64, begin, end int64) (seqMsgs []*sdkws.MsgData, err error) {
log.ZDebug(ctx, "getMsgBySeqsRange", "conversationID", conversationID, "allSeqs", allSeqs, "begin", begin, "end", end)
- for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) {
+ for docID, seqs := range db.msgTable.GetDocIDSeqsMap(conversationID, allSeqs) {
log.ZDebug(ctx, "getMsgBySeqsRange", "docID", docID, "seqs", seqs)
msgs, err := db.findMsgInfoBySeq(ctx, userID, docID, conversationID, seqs)
if err != nil {
@@ -542,23 +529,23 @@ func (db *commonMsgDatabase) getMsgBySeqsRange(ctx context.Context, userID strin
// "userMinSeq" can be set as the same value as the conversation's "maxSeq" at the moment they join the group.
// This ensures that their message retrieval starts from the point they joined.
func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (int64, int64, []*sdkws.MsgData, error) {
- userMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID)
+ userMinSeq, err := db.seq.GetConversationUserMinSeq(ctx, conversationID, userID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
- minSeq, err := db.cache.GetMinSeq(ctx, conversationID)
+ minSeq, err := db.seq.GetMinSeq(ctx, conversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
if userMinSeq > minSeq {
minSeq = userMinSeq
}
- //"minSeq" represents the startSeq value that the user can retrieve.
+ // "minSeq" represents the startSeq value that the user can retrieve.
if minSeq > end {
- log.ZInfo(ctx, "minSeq > end", "minSeq", minSeq, "end", end)
+ log.ZWarn(ctx, "minSeq > end", errs.New("minSeq>end"), "minSeq", minSeq, "end", end)
return 0, 0, nil, nil
}
- maxSeq, err := db.cache.GetMaxSeq(ctx, conversationID)
+ maxSeq, err := db.seq.GetMaxSeq(ctx, conversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
@@ -568,7 +555,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
maxSeq = userMaxSeq
}
}
- //"maxSeq" represents the endSeq value that the user can retrieve.
+ // "maxSeq" represents the endSeq value that the user can retrieve.
if begin < minSeq {
begin = minSeq
@@ -576,9 +563,9 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
if end > maxSeq {
end = maxSeq
}
- //"begin" and "end" represent the actual startSeq and endSeq values that the user can retrieve.
+ // "begin" and "end" represent the actual startSeq and endSeq values that the user can retrieve.
if end < begin {
- return 0, 0, nil, errs.ErrArgs.Wrap("seq end < begin")
+ return 0, 0, nil, errs.ErrArgs.WrapMsg("seq end < begin")
}
var seqs []int64
if end-begin+1 <= num {
@@ -591,25 +578,13 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
}
}
- //167 178 10
- //if end-num < {
- //
- //}
- //var seqs []int64
- //for i := end; i > end-num; i-- {
- // if i >= begin {
- // seqs = append([]int64{i}, seqs...)
- // } else {
- // break
- // }
- //}
if len(seqs) == 0 {
return 0, 0, nil, nil
}
newBegin := seqs[0]
newEnd := seqs[len(seqs)-1]
log.ZDebug(ctx, "GetMsgBySeqsRange", "first seqs", seqs, "newBegin", newBegin, "newEnd", newEnd)
- cachedMsgs, failedSeqs, err := db.cache.GetMessagesBySeq(ctx, conversationID, seqs)
+ cachedMsgs, failedSeqs, err := db.msg.GetMessagesBySeq(ctx, conversationID, seqs)
if err != nil {
if err != redis.Nil {
@@ -618,13 +593,13 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
}
var successMsgs []*sdkws.MsgData
if len(cachedMsgs) > 0 {
- delSeqs, err := db.cache.GetUserDelList(ctx, userID, conversationID)
+ delSeqs, err := db.msg.GetUserDelList(ctx, userID, conversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
var cacheDelNum int
for _, msg := range cachedMsgs {
- if !utils.Contain(msg.Seq, delSeqs...) {
+ if !datautil.Contain(msg.Seq, delSeqs...) {
successMsgs = append(successMsgs, msg)
} else {
cacheDelNum += 1
@@ -635,7 +610,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
for i := 1; i <= cacheDelNum; {
newSeq := newBegin - int64(i)
if newSeq >= begin {
- if !utils.Contain(newSeq, delSeqs...) {
+ if !datautil.Contain(newSeq, delSeqs...) {
log.ZDebug(ctx, "seq del in cache, a new seq in range append", "new seq", newSeq)
reGetSeqsCache = append(reGetSeqsCache, newSeq)
i++
@@ -646,7 +621,7 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
}
if len(reGetSeqsCache) > 0 {
log.ZDebug(ctx, "reGetSeqsCache", "reGetSeqsCache", reGetSeqsCache)
- cachedMsgs, failedSeqs2, err := db.cache.GetMessagesBySeq(ctx, conversationID, reGetSeqsCache)
+ cachedMsgs, failedSeqs2, err := db.msg.GetMessagesBySeq(ctx, conversationID, reGetSeqsCache)
if err != nil {
if err != redis.Nil {
@@ -676,15 +651,15 @@ func (db *commonMsgDatabase) GetMsgBySeqsRange(ctx context.Context, userID strin
}
func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (int64, int64, []*sdkws.MsgData, error) {
- userMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID)
+ userMinSeq, err := db.seq.GetConversationUserMinSeq(ctx, conversationID, userID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
- minSeq, err := db.cache.GetMinSeq(ctx, conversationID)
+ minSeq, err := db.seq.GetMinSeq(ctx, conversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
- maxSeq, err := db.cache.GetMaxSeq(ctx, conversationID)
+ maxSeq, err := db.seq.GetMaxSeq(ctx, conversationID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return 0, 0, nil, err
}
@@ -697,28 +672,14 @@ func (db *commonMsgDatabase) GetMsgBySeqs(ctx context.Context, userID string, co
newSeqs = append(newSeqs, seq)
}
}
- successMsgs, failedSeqs, err := db.cache.GetMessagesBySeq(ctx, conversationID, newSeqs)
+ successMsgs, failedSeqs, err := db.msg.GetMessagesBySeq(ctx, conversationID, newSeqs)
if err != nil {
if err != redis.Nil {
log.ZError(ctx, "get message from redis exception", err, "failedSeqs", failedSeqs, "conversationID", conversationID)
}
}
- log.ZInfo(
- ctx,
- "db.cache.GetMessagesBySeq",
- "userID",
- userID,
- "conversationID",
- conversationID,
- "seqs",
- seqs,
- "successMsgs",
- len(successMsgs),
- "failedSeqs",
- failedSeqs,
- "conversationID",
- conversationID,
- )
+ log.ZDebug(ctx, "db.seq.GetMessagesBySeq", "userID", userID, "conversationID", conversationID, "seqs",
+ seqs, "len(successMsgs)", len(successMsgs), "failedSeqs", failedSeqs)
if len(failedSeqs) > 0 {
mongoMsgs, err := db.getMsgBySeqs(ctx, userID, conversationID, failedSeqs)
@@ -739,17 +700,17 @@ func (db *commonMsgDatabase) DeleteConversationMsgsAndSetMinSeq(ctx context.Cont
if err != nil {
return err
}
- log.ZInfo(ctx, "DeleteConversationMsgsAndSetMinSeq", "conversationID", conversationID, "minSeq", minSeq)
+ log.ZDebug(ctx, "DeleteConversationMsgsAndSetMinSeq", "conversationID", conversationID, "minSeq", minSeq)
if minSeq == 0 {
return nil
}
if remainTime == 0 {
- err = db.cache.CleanUpOneConversationAllMsg(ctx, conversationID)
+ err = db.msg.CleanUpOneConversationAllMsg(ctx, conversationID)
if err != nil {
log.ZWarn(ctx, "CleanUpOneUserAllMsg", err, "conversationID", conversationID)
}
}
- return db.cache.SetMinSeq(ctx, conversationID, minSeq)
+ return db.seq.SetMinSeq(ctx, conversationID, minSeq)
}
func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error) {
@@ -759,7 +720,7 @@ func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string
msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1)
if err != nil || msgDocModel.DocID == "" {
if err != nil {
- if err == unrelation.ErrMsgListNotExist {
+ if err == relation.ErrMsgListNotExist {
log.ZDebug(ctx, "not doc find", "conversationID", conversationID, "userID", userID, "index", index)
} else {
log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index)
@@ -769,14 +730,14 @@ func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string
break
}
index++
- //&& msgDocModel.Msg[0].Msg.SendTime > lastMsgDestructTime.UnixMilli()
+ // && msgDocModel.Msg[0].Msg.SendTime > lastMsgDestructTime.UnixMilli()
if len(msgDocModel.Msg) > 0 {
i := 0
var over bool
for _, msg := range msgDocModel.Msg {
i++
if msg != nil && msg.Msg != nil && msg.Msg.SendTime+destructTime*1000 <= time.Now().UnixMilli() {
- if msg.Msg.SendTime+destructTime*1000 > lastMsgDestructTime.UnixMilli() && !utils.Contain(userID, msg.DelList...) {
+ if msg.Msg.SendTime+destructTime*1000 > lastMsgDestructTime.UnixMilli() && !datautil.Contain(userID, msg.DelList...) {
seqs = append(seqs, msg.Msg.Seq)
}
} else {
@@ -794,12 +755,12 @@ func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string
log.ZDebug(ctx, "UserMsgsDestruct", "conversationID", conversationID, "userID", userID, "seqs", seqs)
if len(seqs) > 0 {
userMinSeq := seqs[len(seqs)-1] + 1
- currentUserMinSeq, err := db.cache.GetConversationUserMinSeq(ctx, conversationID, userID)
+ currentUserMinSeq, err := db.seq.GetConversationUserMinSeq(ctx, conversationID, userID)
if err != nil && errs.Unwrap(err) != redis.Nil {
return nil, err
}
if currentUserMinSeq < userMinSeq {
- if err := db.cache.SetConversationUserMinSeq(ctx, conversationID, userID, userMinSeq); err != nil {
+ if err := db.seq.SetConversationUserMinSeq(ctx, conversationID, userID, userMinSeq); err != nil {
return nil, err
}
}
@@ -826,7 +787,7 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio
msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1)
if err != nil || msgDocModel.DocID == "" {
if err != nil {
- if err == unrelation.ErrMsgListNotExist {
+ if err == relation.ErrMsgListNotExist {
log.ZDebug(ctx, "deleteMsgRecursion ErrMsgListNotExist", "conversationID", conversationID, "index:", index)
} else {
log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index)
@@ -840,10 +801,10 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio
return delStruct.getSetMinSeq() + 1, nil
}
log.ZDebug(ctx, "doc info", "conversationID", conversationID, "index", index, "docID", msgDocModel.DocID, "len", len(msgDocModel.Msg))
- if int64(len(msgDocModel.Msg)) > db.msg.GetSingleGocMsgNum() {
+ if int64(len(msgDocModel.Msg)) > db.msgTable.GetSingleGocMsgNum() {
log.ZWarn(ctx, "msgs too large", nil, "length", len(msgDocModel.Msg), "docID:", msgDocModel.DocID)
}
- if msgDocModel.IsFull() && msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < utils.GetCurrentTimestampByMill() {
+ if msgDocModel.IsFull() && msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.SendTime+(remainTime*1000) < timeutil.GetCurrentTimestampByMill() {
log.ZDebug(ctx, "doc is full and all msg is expired", "docID", msgDocModel.DocID)
delStruct.delDocIDs = append(delStruct.delDocIDs, msgDocModel.DocID)
delStruct.minSeq = msgDocModel.Msg[len(msgDocModel.Msg)-1].Msg.Seq
@@ -851,7 +812,7 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio
var delMsgIndexs []int
for i, MsgInfoModel := range msgDocModel.Msg {
if MsgInfoModel != nil && MsgInfoModel.Msg != nil {
- if utils.GetCurrentTimestampByMill() > MsgInfoModel.Msg.SendTime+(remainTime*1000) {
+ if timeutil.GetCurrentTimestampByMill() > MsgInfoModel.Msg.SendTime+(remainTime*1000) {
delMsgIndexs = append(delMsgIndexs, i)
}
}
@@ -868,13 +829,13 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio
}
func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs(ctx context.Context, conversationID string, allSeqs []int64) error {
- if err := db.cache.DeleteMessages(ctx, conversationID, allSeqs); err != nil {
+ if err := db.msg.DeleteMessages(ctx, conversationID, allSeqs); err != nil {
return err
}
- for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, allSeqs) {
+ for docID, seqs := range db.msgTable.GetDocIDSeqsMap(conversationID, allSeqs) {
var indexes []int
for _, seq := range seqs {
- indexes = append(indexes, int(db.msg.GetMsgIndex(seq)))
+ indexes = append(indexes, int(db.msgTable.GetMsgIndex(seq)))
}
if err := db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, docID, indexes); err != nil {
return err
@@ -884,7 +845,7 @@ func (db *commonMsgDatabase) DeleteMsgsPhysicalBySeqs(ctx context.Context, conve
}
func (db *commonMsgDatabase) DeleteUserMsgsBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) error {
- cachedMsgs, _, err := db.cache.GetMessagesBySeq(ctx, conversationID, seqs)
+ cachedMsgs, _, err := db.msg.GetMessagesBySeq(ctx, conversationID, seqs)
if err != nil && errs.Unwrap(err) != redis.Nil {
log.ZWarn(ctx, "DeleteUserMsgsBySeqs", err, "conversationID", conversationID, "seqs", seqs)
return err
@@ -894,14 +855,14 @@ func (db *commonMsgDatabase) DeleteUserMsgsBySeqs(ctx context.Context, userID st
for _, msg := range cachedMsgs {
cacheSeqs = append(cacheSeqs, msg.Seq)
}
- if err := db.cache.UserDeleteMsgs(ctx, conversationID, cacheSeqs, userID); err != nil {
+ if err := db.msg.UserDeleteMsgs(ctx, conversationID, cacheSeqs, userID); err != nil {
return err
}
}
- for docID, seqs := range db.msg.GetDocIDSeqsMap(conversationID, seqs) {
+ for docID, seqs := range db.msgTable.GetDocIDSeqsMap(conversationID, seqs) {
for _, seq := range seqs {
- if _, err := db.msgDocDatabase.PushUnique(ctx, docID, db.msg.GetMsgIndex(seq), "del_list", []string{userID}); err != nil {
+ if _, err := db.msgDocDatabase.PushUnique(ctx, docID, db.msgTable.GetMsgIndex(seq), "del_list", []string{userID}); err != nil {
return err
}
}
@@ -915,91 +876,91 @@ func (db *commonMsgDatabase) DeleteMsgsBySeqs(ctx context.Context, conversationI
func (db *commonMsgDatabase) CleanUpUserConversationsMsgs(ctx context.Context, user string, conversationIDs []string) {
for _, conversationID := range conversationIDs {
- maxSeq, err := db.cache.GetMaxSeq(ctx, conversationID)
+ maxSeq, err := db.seq.GetMaxSeq(ctx, conversationID)
if err != nil {
if err == redis.Nil {
- log.ZInfo(ctx, "max seq is nil", "conversationID", conversationID)
+ log.ZDebug(ctx, "max seq is nil", "conversationID", conversationID)
} else {
log.ZError(ctx, "get max seq failed", err, "conversationID", conversationID)
}
continue
}
- if err := db.cache.SetMinSeq(ctx, conversationID, maxSeq+1); err != nil {
+ if err := db.seq.SetMinSeq(ctx, conversationID, maxSeq+1); err != nil {
log.ZError(ctx, "set min seq failed", err, "conversationID", conversationID, "minSeq", maxSeq+1)
}
}
}
func (db *commonMsgDatabase) SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error {
- return db.cache.SetMaxSeq(ctx, conversationID, maxSeq)
+ return db.seq.SetMaxSeq(ctx, conversationID, maxSeq)
}
func (db *commonMsgDatabase) GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) {
- return db.cache.GetMaxSeqs(ctx, conversationIDs)
+ return db.seq.GetMaxSeqs(ctx, conversationIDs)
}
func (db *commonMsgDatabase) GetMaxSeq(ctx context.Context, conversationID string) (int64, error) {
- return db.cache.GetMaxSeq(ctx, conversationID)
+ return db.seq.GetMaxSeq(ctx, conversationID)
}
func (db *commonMsgDatabase) SetMinSeq(ctx context.Context, conversationID string, minSeq int64) error {
- return db.cache.SetMinSeq(ctx, conversationID, minSeq)
+ return db.seq.SetMinSeq(ctx, conversationID, minSeq)
}
func (db *commonMsgDatabase) SetMinSeqs(ctx context.Context, seqs map[string]int64) error {
- return db.cache.SetMinSeqs(ctx, seqs)
+ return db.seq.SetMinSeqs(ctx, seqs)
}
func (db *commonMsgDatabase) GetMinSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) {
- return db.cache.GetMinSeqs(ctx, conversationIDs)
+ return db.seq.GetMinSeqs(ctx, conversationIDs)
}
func (db *commonMsgDatabase) GetMinSeq(ctx context.Context, conversationID string) (int64, error) {
- return db.cache.GetMinSeq(ctx, conversationID)
+ return db.seq.GetMinSeq(ctx, conversationID)
}
func (db *commonMsgDatabase) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) {
- return db.cache.GetConversationUserMinSeq(ctx, conversationID, userID)
+ return db.seq.GetConversationUserMinSeq(ctx, conversationID, userID)
}
func (db *commonMsgDatabase) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (map[string]int64, error) {
- return db.cache.GetConversationUserMinSeqs(ctx, conversationID, userIDs)
+ return db.seq.GetConversationUserMinSeqs(ctx, conversationID, userIDs)
}
func (db *commonMsgDatabase) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error {
- return db.cache.SetConversationUserMinSeq(ctx, conversationID, userID, minSeq)
+ return db.seq.SetConversationUserMinSeq(ctx, conversationID, userID, minSeq)
}
func (db *commonMsgDatabase) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) {
- return db.cache.SetConversationUserMinSeqs(ctx, conversationID, seqs)
+ return db.seq.SetConversationUserMinSeqs(ctx, conversationID, seqs)
}
func (db *commonMsgDatabase) SetUserConversationsMinSeqs(ctx context.Context, userID string, seqs map[string]int64) error {
- return db.cache.SetUserConversationsMinSeqs(ctx, userID, seqs)
+ return db.seq.SetUserConversationsMinSeqs(ctx, userID, seqs)
}
func (db *commonMsgDatabase) UserSetHasReadSeqs(ctx context.Context, userID string, hasReadSeqs map[string]int64) error {
- return db.cache.UserSetHasReadSeqs(ctx, userID, hasReadSeqs)
+ return db.seq.UserSetHasReadSeqs(ctx, userID, hasReadSeqs)
}
func (db *commonMsgDatabase) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error {
- return db.cache.SetHasReadSeq(ctx, userID, conversationID, hasReadSeq)
+ return db.seq.SetHasReadSeq(ctx, userID, conversationID, hasReadSeq)
}
func (db *commonMsgDatabase) GetHasReadSeqs(ctx context.Context, userID string, conversationIDs []string) (map[string]int64, error) {
- return db.cache.GetHasReadSeqs(ctx, userID, conversationIDs)
+ return db.seq.GetHasReadSeqs(ctx, userID, conversationIDs)
}
func (db *commonMsgDatabase) GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) {
- return db.cache.GetHasReadSeq(ctx, userID, conversationID)
+ return db.seq.GetHasReadSeq(ctx, userID, conversationID)
}
func (db *commonMsgDatabase) SetSendMsgStatus(ctx context.Context, id string, status int32) error {
- return db.cache.SetSendMsgStatus(ctx, id, status)
+ return db.msg.SetSendMsgStatus(ctx, id, status)
}
func (db *commonMsgDatabase) GetSendMsgStatus(ctx context.Context, id string) (int32, error) {
- return db.cache.GetSendMsgStatus(ctx, id)
+ return db.msg.GetSendMsgStatus(ctx, id)
}
func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache(ctx context.Context, conversationID string) (minSeqMongo, maxSeqMongo, minSeqCache, maxSeqCache int64, err error) {
@@ -1007,11 +968,11 @@ func (db *commonMsgDatabase) GetConversationMinMaxSeqInMongoAndCache(ctx context
if err != nil {
return
}
- minSeqCache, err = db.cache.GetMinSeq(ctx, conversationID)
+ minSeqCache, err = db.seq.GetMinSeq(ctx, conversationID)
if err != nil {
return
}
- maxSeqCache, err = db.cache.GetMaxSeq(ctx, conversationID)
+ maxSeqCache, err = db.seq.GetMaxSeq(ctx, conversationID)
if err != nil {
return
}
@@ -1044,7 +1005,7 @@ func (db *commonMsgDatabase) RangeUserSendCount(
ase bool,
pageNumber int32,
showNumber int32,
-) (msgCount int64, userCount int64, users []*unrelationtb.UserCount, dateCount map[string]int64, err error) {
+) (msgCount int64, userCount int64, users []*relation.UserCount, dateCount map[string]int64, err error) {
return db.msgDocDatabase.RangeUserSendCount(ctx, start, end, group, ase, pageNumber, showNumber)
}
@@ -1055,7 +1016,7 @@ func (db *commonMsgDatabase) RangeGroupSendCount(
ase bool,
pageNumber int32,
showNumber int32,
-) (msgCount int64, userCount int64, groups []*unrelationtb.GroupCount, dateCount map[string]int64, err error) {
+) (msgCount int64, userCount int64, groups []*relation.GroupCount, dateCount map[string]int64, err error) {
return db.msgDocDatabase.RangeGroupSendCount(ctx, start, end, ase, pageNumber, showNumber)
}
@@ -1078,12 +1039,12 @@ func (db *commonMsgDatabase) FindOneByDocIDs(ctx context.Context, conversationID
totalMsgs := make(map[string]*sdkws.MsgData)
for _, conversationID := range conversationIDs {
seq := seqs[conversationID]
- docID := db.msg.GetDocID(conversationID, seq)
+ docID := db.msgTable.GetDocID(conversationID, seq)
msgs, err := db.msgDocDatabase.FindOneByDocID(ctx, docID)
if err != nil {
return nil, err
}
- index := db.msg.GetMsgIndex(seq)
+ index := db.msgTable.GetMsgIndex(seq)
totalMsgs[conversationID] = convert.MsgDB2Pb(msgs.Msg[index].Msg)
}
return totalMsgs, nil
@@ -1092,3 +1053,72 @@ func (db *commonMsgDatabase) FindOneByDocIDs(ctx context.Context, conversationID
func (db *commonMsgDatabase) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) {
db.msgDocDatabase.ConvertMsgsDocLen(ctx, conversationIDs)
}
+
+func (db *commonMsgDatabase) GetBeforeMsg(ctx context.Context, ts int64, limit int) ([]*relation.MsgDocModel, error) {
+ return db.msgDocDatabase.GetBeforeMsg(ctx, ts, limit)
+}
+
+func (db *commonMsgDatabase) DeleteDocMsgBefore(ctx context.Context, ts int64, doc *relation.MsgDocModel) ([]int, error) {
+ var notNull int
+ index := make([]int, 0, len(doc.Msg))
+ for i, message := range doc.Msg {
+ if message.Msg != nil {
+ notNull++
+ if message.Msg.SendTime < ts {
+ index = append(index, i)
+ }
+ }
+ }
+ if len(index) == 0 {
+ return index, nil
+ }
+ maxSeq := doc.Msg[index[len(index)-1]].Msg.Seq
+ conversationID := doc.DocID[:strings.LastIndex(doc.DocID, ":")]
+ if err := db.setMinSeq(ctx, conversationID, maxSeq+1); err != nil {
+ return index, err
+ }
+ if len(index) == notNull {
+ return index, db.msgDocDatabase.DeleteDoc(ctx, doc.DocID)
+ } else {
+ return index, db.msgDocDatabase.DeleteMsgByIndex(ctx, doc.DocID, index)
+ }
+}
+
+//func (db *commonMsgDatabase) ClearMsg(ctx context.Context, ts int64) (err error) {
+// var (
+// docNum int
+// msgNum int
+// start = time.Now()
+// )
+// for {
+// msgs, err := db.msgDocDatabase.GetBeforeMsg(ctx, ts, 100)
+// if err != nil {
+// return err
+// }
+// if len(msgs) == 0 {
+// return nil
+// }
+// for _, msg := range msgs {
+// num, err := db.deleteOneMsg(ctx, ts, msg)
+// if err != nil {
+// return err
+// }
+// docNum++
+// msgNum += num
+// }
+// }
+//}
+
+func (db *commonMsgDatabase) setMinSeq(ctx context.Context, conversationID string, seq int64) error {
+ dbSeq, err := db.seq.GetMinSeq(ctx, conversationID)
+ if err != nil {
+ if errors.Is(errs.Unwrap(err), redis.Nil) {
+ return nil
+ }
+ return err
+ }
+ if dbSeq >= seq {
+ return nil
+ }
+ return db.seq.SetMinSeq(ctx, conversationID, seq)
+}
diff --git a/pkg/common/db/controller/msg_test.go b/pkg/common/db/controller/msg_test.go
deleted file mode 100644
index 4c2ab20da3..0000000000
--- a/pkg/common/db/controller/msg_test.go
+++ /dev/null
@@ -1,250 +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 controller
-
-import (
- "context"
- "fmt"
- "math/rand"
- "strconv"
- "sync"
- "testing"
- "time"
-
- "github.com/OpenIMSDK/tools/log"
-
- "go.mongodb.org/mongo-driver/bson"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
-)
-
-func Test_BatchInsertChat2DB(t *testing.T) {
- conf := config.NewGlobalConfig()
- conf.Mongo.Address = []string{"192.168.44.128:37017"}
- // conf.Mongo.Timeout = 60
- conf.Mongo.Database = "openIM"
- // conf.Mongo.Source = "admin"
- conf.Mongo.Username = "root"
- conf.Mongo.Password = "openIM123"
- conf.Mongo.MaxPoolSize = 100
- conf.RetainChatRecords = 3650
- conf.ChatRecordsClearTime = "0 2 * * 3"
-
- mongo, err := unrelation.NewMongo(conf)
- if err != nil {
- t.Fatal(err)
- }
- err = mongo.GetDatabase(conf.Mongo.Database).Client().Ping(context.Background(), nil)
- if err != nil {
- panic(err)
- }
-
- db := &commonMsgDatabase{
- msgDocDatabase: unrelation.NewMsgMongoDriver(mongo.GetDatabase(conf.Mongo.Database)),
- }
-
- //ctx := context.Background()
- //msgs := make([]*sdkws.MsgData, 0, 1)
- //for i := 0; i < cap(msgs); i++ {
- // msgs = append(msgs, &sdkws.MsgData{
- // Content: []byte(fmt.Sprintf("test-%d", i)),
- // SendTime: time.Now().UnixMilli(),
- // })
- //}
- //err = db.BatchInsertChat2DB(ctx, "test", msgs, 0)
- //if err != nil {
- // panic(err)
- //}
-
- _ = db.BatchInsertChat2DB
- c := mongo.GetDatabase(conf.Mongo.Database).Collection("msg")
-
- ch := make(chan int)
- rand.Seed(time.Now().UnixNano())
-
- index := 10
-
- var wg sync.WaitGroup
- for i := 0; i < 1000; i++ {
- wg.Add(1)
- go func(channelID int) {
- defer wg.Done()
- <-ch
- var arr []string
- for i := 0; i < 500; i++ {
- arr = append(arr, strconv.Itoa(i+1))
- }
- rand.Shuffle(len(arr), func(i, j int) {
- arr[i], arr[j] = arr[j], arr[i]
- })
- for j, s := range arr {
- if j == 0 {
- fmt.Printf("channnelID: %d, arr[0]: %s\n", channelID, arr[j])
- }
- filter := bson.M{"doc_id": "test:0"}
- update := bson.M{
- "$addToSet": bson.M{
- fmt.Sprintf("msgs.%d.del_list", index): bson.M{"$each": []string{s}},
- },
- }
- _, err := c.UpdateOne(context.Background(), filter, update)
- if err != nil {
- t.Fatal(err)
- }
- }
- }(i)
- }
-
- for i := 0; i < 1000; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- <-ch
- var arr []string
- for i := 0; i < 500; i++ {
- arr = append(arr, strconv.Itoa(1001+i))
- }
- rand.Shuffle(len(arr), func(i, j int) {
- arr[i], arr[j] = arr[j], arr[i]
- })
- for _, s := range arr {
- filter := bson.M{"doc_id": "test:0"}
- update := bson.M{
- "$addToSet": bson.M{
- fmt.Sprintf("msgs.%d.read_list", index): bson.M{"$each": []string{s}},
- },
- }
- _, err := c.UpdateOne(context.Background(), filter, update)
- if err != nil {
- t.Fatal(err)
- }
- }
- }()
- }
-
- time.Sleep(time.Second * 2)
-
- close(ch)
-
- wg.Wait()
-
-}
-
-func GetDB() *commonMsgDatabase {
- conf := config.NewGlobalConfig()
- conf.Mongo.Address = []string{"203.56.175.233:37017"}
- // conf.Mongo.Timeout = 60
- conf.Mongo.Database = "openim_v3"
- // conf.Mongo.Source = "admin"
- conf.Mongo.Username = "root"
- conf.Mongo.Password = "openIM123"
- conf.Mongo.MaxPoolSize = 100
- conf.RetainChatRecords = 3650
- conf.ChatRecordsClearTime = "0 2 * * 3"
-
- mongo, err := unrelation.NewMongo(conf)
- if err != nil {
- panic(err)
- }
- err = mongo.GetDatabase(conf.Mongo.Database).Client().Ping(context.Background(), nil)
- if err != nil {
- panic(err)
- }
- return &commonMsgDatabase{
- msgDocDatabase: unrelation.NewMsgMongoDriver(mongo.GetDatabase(conf.Mongo.Database)),
- }
-}
-
-func Test_Insert(t *testing.T) {
- db := GetDB()
- ctx := context.Background()
- var arr []any
- for i := 0; i < 345; i++ {
- if i%2 == 0 {
- arr = append(arr, (*unrelationtb.MsgDataModel)(nil))
- continue
- }
- arr = append(arr, &unrelationtb.MsgDataModel{
- Seq: int64(i),
- Content: fmt.Sprintf("test-%d", i),
- })
- }
- if err := db.BatchInsertBlock(ctx, "test", arr, updateKeyMsg, 1); err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_Revoke(t *testing.T) {
- db := GetDB()
- ctx := context.Background()
- var arr []any
- for i := 0; i < 456; i++ {
- arr = append(arr, &unrelationtb.RevokeModel{
- UserID: "uid_" + strconv.Itoa(i),
- Nickname: "uname_" + strconv.Itoa(i),
- Time: time.Now().UnixMilli(),
- })
- }
- if err := db.BatchInsertBlock(ctx, "test", arr, updateKeyRevoke, 123); err != nil {
- t.Fatal(err)
- }
-}
-
-func Test_FindBySeq(t *testing.T) {
- if err := log.InitFromConfig("", "", 6, true, false, "", 2, 1); err != nil {
- t.Fatal(err)
- }
- db := GetDB()
- ctx := context.Background()
- fmt.Println(
- db.msgDocDatabase.(*unrelation.MsgMongoDriver).GetMsgBySeqIndexIn1Doc(ctx, "100", "si_100_101:0", []int64{1}),
- )
- //res, err := db.msgDocDatabase.GetMsgBySeqIndexIn1Doc(ctx, "123456", "test:0", []int64{1, 2, 3})
- //if err != nil {
- // t.Fatal(err)
- //}
- //db.GetMsgBySeqs(ctx, "100", "si_100_101:0", []int64{6})
- //data, _ := json.Marshal(res)
- //fmt.Println(string(data))
-}
-
-//func Test_Delete(t *testing.T) {
-// db := GetDB()
-// ctx := context.Background()
-// var arr []any
-// for i := 0; i < 123; i++ {
-// arr = append(arr, []string{"uid_1", "uid_2"})
-// }
-// if err := db.BatchInsertBlock(ctx, "test", arr, updateKeyDel, 210); err != nil {
-// t.Fatal(err)
-// }
-//}
-
-func TestName(t *testing.T) {
- db := GetDB()
- var seqs []int64
- for i := int64(1); i <= 50; i++ {
- seqs = append(seqs, i)
- }
- msgs, err := db.getMsgBySeqsRange(context.Background(), "4931176757", "si_3866692501_4931176757", seqs, seqs[0], seqs[len(seqs)-1])
- if err != nil {
- t.Fatal(err)
- }
-
- t.Log(msgs)
-
-}
diff --git a/pkg/common/db/controller/push.go b/pkg/common/db/controller/push.go
index 76d8c3efb9..390d70b7ba 100644
--- a/pkg/common/db/controller/push.go
+++ b/pkg/common/db/controller/push.go
@@ -25,10 +25,10 @@ type PushDatabase interface {
}
type pushDataBase struct {
- cache cache.MsgModel
+ cache cache.ThirdCache
}
-func NewPushDatabase(cache cache.MsgModel) PushDatabase {
+func NewPushDatabase(cache cache.ThirdCache) PushDatabase {
return &pushDataBase{cache: cache}
}
diff --git a/pkg/common/db/controller/s3.go b/pkg/common/db/controller/s3.go
index e847c9c8ff..eae47c4212 100644
--- a/pkg/common/db/controller/s3.go
+++ b/pkg/common/db/controller/s3.go
@@ -20,9 +20,9 @@ import (
"time"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/s3"
+ "github.com/openimsdk/tools/s3/cont"
"github.com/redis/go-redis/v9"
)
diff --git a/pkg/common/db/controller/third.go b/pkg/common/db/controller/third.go
index 996d82c452..be618843fd 100644
--- a/pkg/common/db/controller/third.go
+++ b/pkg/common/db/controller/third.go
@@ -18,9 +18,9 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/pagination"
)
type ThirdDatabase interface {
@@ -34,7 +34,7 @@ type ThirdDatabase interface {
}
type thirdDatabase struct {
- cache cache.MsgModel
+ cache cache.ThirdCache
logdb relation.LogInterface
}
@@ -58,7 +58,7 @@ func (t *thirdDatabase) UploadLogs(ctx context.Context, logs []*relation.LogMode
return t.logdb.Create(ctx, logs)
}
-func NewThirdDatabase(cache cache.MsgModel, logdb relation.LogInterface) ThirdDatabase {
+func NewThirdDatabase(cache cache.ThirdCache, logdb relation.LogInterface) ThirdDatabase {
return &thirdDatabase{cache: cache, logdb: logdb}
}
diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go
index 0e1bdd3147..b2aba41faa 100644
--- a/pkg/common/db/controller/user.go
+++ b/pkg/common/db/controller/user.go
@@ -16,16 +16,16 @@ package controller
import (
"context"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/db/tx"
+ "github.com/openimsdk/tools/utils/datautil"
"time"
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/pagination"
- "github.com/OpenIMSDK/tools/tx"
- "github.com/OpenIMSDK/tools/utils"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/errs"
+
"github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
- unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
)
type UserDatabase interface {
@@ -39,13 +39,11 @@ type UserDatabase interface {
FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error)
// Create Insert multiple external guarantees that the userID is not repeated and does not exist in the db
Create(ctx context.Context, users []*relation.UserModel) (err error)
- // Update update (non-zero value) external guarantee userID exists
- //Update(ctx context.Context, user *relation.UserModel) (err error)
// UpdateByMap update (zero value) external guarantee userID exists
UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error)
// FindUser
PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
- //FindUser with keyword
+ // FindUser with keyword
PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID string, nickName string, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
// Page If not found, no error is returned
Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error)
@@ -74,7 +72,7 @@ type UserDatabase interface {
// SetUserStatus Set the user status and store the user status in redis
SetUserStatus(ctx context.Context, userID string, status, platformID int32) error
- //CRUD user command
+ // CRUD user command
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error
@@ -83,19 +81,19 @@ type UserDatabase interface {
}
type userDatabase struct {
- tx tx.CtxTx
+ tx tx.Tx
userDB relation.UserModelInterface
cache cache.UserCache
- mongoDB unrelationtb.UserModelInterface
+ mongoDB relation.SubscribeUserModelInterface
}
-func NewUserDatabase(userDB relation.UserModelInterface, cache cache.UserCache, tx tx.CtxTx, mongoDB unrelationtb.UserModelInterface) UserDatabase {
+func NewUserDatabase(userDB relation.UserModelInterface, cache cache.UserCache, tx tx.Tx, mongoDB relation.SubscribeUserModelInterface) UserDatabase {
return &userDatabase{userDB: userDB, cache: cache, tx: tx, mongoDB: mongoDB}
}
func (u *userDatabase) InitOnce(ctx context.Context, users []*relation.UserModel) error {
// Extract user IDs from the given user models.
- userIDs := utils.Slice(users, func(e *relation.UserModel) string {
+ userIDs := datautil.Slice(users, func(e *relation.UserModel) string {
return e.UserID
})
@@ -106,7 +104,7 @@ func (u *userDatabase) InitOnce(ctx context.Context, users []*relation.UserModel
}
// Determine which users are missing from the database.
- missingUsers := utils.SliceAnySub(users, existingUsers, func(e *relation.UserModel) string {
+ missingUsers := datautil.SliceAnySub(users, existingUsers, func(e *relation.UserModel) string {
return e.UserID
})
@@ -127,7 +125,7 @@ func (u *userDatabase) FindWithError(ctx context.Context, userIDs []string) (use
return
}
if len(users) != len(userIDs) {
- err = errs.ErrRecordNotFound.Wrap("userID not found")
+ err = errs.ErrRecordNotFound.WrapMsg("userID not found")
}
return
}
@@ -137,12 +135,10 @@ func (u *userDatabase) Find(ctx context.Context, userIDs []string) (users []*rel
return u.cache.GetUsersInfo(ctx, userIDs)
}
-// Find userInfo By Nickname.
func (u *userDatabase) FindByNickname(ctx context.Context, nickname string) (users []*relation.UserModel, err error) {
return u.userDB.TakeByNickname(ctx, nickname)
}
-// Find notificationAccouts.
func (u *userDatabase) FindNotification(ctx context.Context, level int64) (users []*relation.UserModel, err error) {
return u.userDB.TakeNotification(ctx, level)
}
@@ -153,20 +149,12 @@ func (u *userDatabase) Create(ctx context.Context, users []*relation.UserModel)
if err = u.userDB.Create(ctx, users); err != nil {
return err
}
- return u.cache.DelUsersInfo(utils.Slice(users, func(e *relation.UserModel) string {
+ return u.cache.DelUsersInfo(datautil.Slice(users, func(e *relation.UserModel) string {
return e.UserID
})...).ExecDel(ctx)
})
}
-//// Update (non-zero value) externally guarantees that userID exists.
-//func (u *userDatabase) Update(ctx context.Context, user *relation.UserModel) (err error) {
-// if err := u.userDB.Update(ctx, user); err != nil {
-// return err
-// }
-// return u.cache.DelUsersInfo(user.UserID).ExecDel(ctx)
-//}
-
// UpdateByMap update (zero value) externally guarantees that userID exists.
func (u *userDatabase) UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) {
return u.tx.Transaction(ctx, func(ctx context.Context) error {
@@ -186,13 +174,7 @@ func (u *userDatabase) PageFindUser(ctx context.Context, level1 int64, level2 in
return u.userDB.PageFindUser(ctx, level1, level2, pagination)
}
-func (u *userDatabase) PageFindUserWithKeyword(
- ctx context.Context,
- level1 int64,
- level2 int64,
- userID, nickName string,
- pagination pagination.Pagination,
-) (count int64, users []*relation.UserModel, err error) {
+func (u *userDatabase) PageFindUserWithKeyword(ctx context.Context, level1 int64, level2 int64, userID, nickName string, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) {
return u.userDB.PageFindUserWithKeyword(ctx, level1, level2, userID, nickName, pagination)
}
@@ -267,19 +249,24 @@ func (u *userDatabase) GetUserStatus(ctx context.Context, userIDs []string) ([]*
func (u *userDatabase) SetUserStatus(ctx context.Context, userID string, status, platformID int32) error {
return u.cache.SetUserStatus(ctx, userID, status, platformID)
}
+
func (u *userDatabase) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error {
return u.userDB.AddUserCommand(ctx, userID, Type, UUID, value, ex)
}
+
func (u *userDatabase) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error {
return u.userDB.DeleteUserCommand(ctx, userID, Type, UUID)
}
+
func (u *userDatabase) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error {
return u.userDB.UpdateUserCommand(ctx, userID, Type, UUID, val)
}
+
func (u *userDatabase) GetUserCommands(ctx context.Context, userID string, Type int32) ([]*user.CommandInfoResp, error) {
commands, err := u.userDB.GetUserCommand(ctx, userID, Type)
return commands, err
}
+
func (u *userDatabase) GetAllUserCommands(ctx context.Context, userID string) ([]*user.AllCommandInfoResp, error) {
commands, err := u.userDB.GetAllUserCommand(ctx, userID)
return commands, err
diff --git a/pkg/common/db/mgo/black.go b/pkg/common/db/mgo/black.go
index c555e0b776..d588aece6e 100644
--- a/pkg/common/db/mgo/black.go
+++ b/pkg/common/db/mgo/black.go
@@ -17,9 +17,9 @@ package mgo
import (
"context"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -63,42 +63,42 @@ func (b *BlackMgo) blacksFilter(blacks []*relation.BlackModel) bson.M {
}
func (b *BlackMgo) Create(ctx context.Context, blacks []*relation.BlackModel) (err error) {
- return mgoutil.InsertMany(ctx, b.coll, blacks)
+ return mongoutil.InsertMany(ctx, b.coll, blacks)
}
func (b *BlackMgo) Delete(ctx context.Context, blacks []*relation.BlackModel) (err error) {
if len(blacks) == 0 {
return nil
}
- return mgoutil.DeleteMany(ctx, b.coll, b.blacksFilter(blacks))
+ return mongoutil.DeleteMany(ctx, b.coll, b.blacksFilter(blacks))
}
func (b *BlackMgo) UpdateByMap(ctx context.Context, ownerUserID, blockUserID string, args map[string]any) (err error) {
if len(args) == 0 {
return nil
}
- return mgoutil.UpdateOne(ctx, b.coll, b.blackFilter(ownerUserID, blockUserID), bson.M{"$set": args}, false)
+ return mongoutil.UpdateOne(ctx, b.coll, b.blackFilter(ownerUserID, blockUserID), bson.M{"$set": args}, false)
}
func (b *BlackMgo) Find(ctx context.Context, blacks []*relation.BlackModel) (blackList []*relation.BlackModel, err error) {
- return mgoutil.Find[*relation.BlackModel](ctx, b.coll, b.blacksFilter(blacks))
+ return mongoutil.Find[*relation.BlackModel](ctx, b.coll, b.blacksFilter(blacks))
}
func (b *BlackMgo) Take(ctx context.Context, ownerUserID, blockUserID string) (black *relation.BlackModel, err error) {
- return mgoutil.FindOne[*relation.BlackModel](ctx, b.coll, b.blackFilter(ownerUserID, blockUserID))
+ return mongoutil.FindOne[*relation.BlackModel](ctx, b.coll, b.blackFilter(ownerUserID, blockUserID))
}
func (b *BlackMgo) FindOwnerBlacks(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, blacks []*relation.BlackModel, err error) {
- return mgoutil.FindPage[*relation.BlackModel](ctx, b.coll, bson.M{"owner_user_id": ownerUserID}, pagination)
+ return mongoutil.FindPage[*relation.BlackModel](ctx, b.coll, bson.M{"owner_user_id": ownerUserID}, pagination)
}
func (b *BlackMgo) FindOwnerBlackInfos(ctx context.Context, ownerUserID string, userIDs []string) (blacks []*relation.BlackModel, err error) {
if len(userIDs) == 0 {
- return mgoutil.Find[*relation.BlackModel](ctx, b.coll, bson.M{"owner_user_id": ownerUserID})
+ return mongoutil.Find[*relation.BlackModel](ctx, b.coll, bson.M{"owner_user_id": ownerUserID})
}
- return mgoutil.Find[*relation.BlackModel](ctx, b.coll, bson.M{"owner_user_id": ownerUserID, "block_user_id": bson.M{"$in": userIDs}})
+ return mongoutil.Find[*relation.BlackModel](ctx, b.coll, bson.M{"owner_user_id": ownerUserID, "block_user_id": bson.M{"$in": userIDs}})
}
func (b *BlackMgo) FindBlackUserIDs(ctx context.Context, ownerUserID string) (blackUserIDs []string, err error) {
- return mgoutil.Find[string](ctx, b.coll, bson.M{"owner_user_id": ownerUserID}, options.Find().SetProjection(bson.M{"_id": 0, "block_user_id": 1}))
+ return mongoutil.Find[string](ctx, b.coll, bson.M{"owner_user_id": ownerUserID}, options.Find().SetProjection(bson.M{"_id": 0, "block_user_id": 1}))
}
diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go
index bc37ed7591..5292cb60cf 100644
--- a/pkg/common/db/mgo/conversation.go
+++ b/pkg/common/db/mgo/conversation.go
@@ -18,11 +18,11 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -48,15 +48,24 @@ type ConversationMgo struct {
}
func (c *ConversationMgo) Create(ctx context.Context, conversations []*relation.ConversationModel) (err error) {
- return mgoutil.InsertMany(ctx, c.coll, conversations)
+ return mongoutil.InsertMany(ctx, c.coll, conversations)
}
func (c *ConversationMgo) Delete(ctx context.Context, groupIDs []string) (err error) {
- return mgoutil.DeleteMany(ctx, c.coll, bson.M{"group_id": bson.M{"$in": groupIDs}})
+ return mongoutil.DeleteMany(ctx, c.coll, bson.M{"group_id": bson.M{"$in": groupIDs}})
}
func (c *ConversationMgo) UpdateByMap(ctx context.Context, userIDs []string, conversationID string, args map[string]any) (rows int64, err error) {
- res, err := mgoutil.UpdateMany(ctx, c.coll, bson.M{"owner_user_id": bson.M{"$in": userIDs}, "conversation_id": conversationID}, bson.M{"$set": args})
+ if len(args) == 0 {
+ return 0, nil
+ }
+ filter := bson.M{
+ "conversation_id": conversationID,
+ }
+ if len(userIDs) > 0 {
+ filter["owner_user_id"] = bson.M{"$in": userIDs}
+ }
+ res, err := mongoutil.UpdateMany(ctx, c.coll, filter, bson.M{"$set": args})
if err != nil {
return 0, err
}
@@ -64,36 +73,35 @@ func (c *ConversationMgo) UpdateByMap(ctx context.Context, userIDs []string, con
}
func (c *ConversationMgo) Update(ctx context.Context, conversation *relation.ConversationModel) (err error) {
- return mgoutil.UpdateOne(ctx, c.coll, bson.M{"owner_user_id": conversation.OwnerUserID, "conversation_id": conversation.ConversationID}, bson.M{"$set": conversation}, true)
+ return mongoutil.UpdateOne(ctx, c.coll, bson.M{"owner_user_id": conversation.OwnerUserID, "conversation_id": conversation.ConversationID}, bson.M{"$set": conversation}, true)
}
func (c *ConversationMgo) Find(ctx context.Context, ownerUserID string, conversationIDs []string) (conversations []*relation.ConversationModel, err error) {
- return mgoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": ownerUserID, "conversation_id": bson.M{"$in": conversationIDs}})
+ return mongoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": ownerUserID, "conversation_id": bson.M{"$in": conversationIDs}})
}
func (c *ConversationMgo) FindUserID(ctx context.Context, userIDs []string, conversationIDs []string) ([]string, error) {
- return mgoutil.Find[string](
+ return mongoutil.Find[string](
ctx,
c.coll,
bson.M{"owner_user_id": bson.M{"$in": userIDs}, "conversation_id": bson.M{"$in": conversationIDs}},
options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1}),
)
}
-
func (c *ConversationMgo) FindUserIDAllConversationID(ctx context.Context, userID string) ([]string, error) {
- return mgoutil.Find[string](ctx, c.coll, bson.M{"owner_user_id": userID}, options.Find().SetProjection(bson.M{"_id": 0, "conversation_id": 1}))
+ return mongoutil.Find[string](ctx, c.coll, bson.M{"owner_user_id": userID}, options.Find().SetProjection(bson.M{"_id": 0, "conversation_id": 1}))
}
func (c *ConversationMgo) Take(ctx context.Context, userID, conversationID string) (conversation *relation.ConversationModel, err error) {
- return mgoutil.FindOne[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": userID, "conversation_id": conversationID})
+ return mongoutil.FindOne[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": userID, "conversation_id": conversationID})
}
func (c *ConversationMgo) FindConversationID(ctx context.Context, userID string, conversationIDs []string) (existConversationID []string, err error) {
- return mgoutil.Find[string](ctx, c.coll, bson.M{"owner_user_id": userID, "conversation_id": bson.M{"$in": conversationIDs}}, options.Find().SetProjection(bson.M{"_id": 0, "conversation_id": 1}))
+ return mongoutil.Find[string](ctx, c.coll, bson.M{"owner_user_id": userID, "conversation_id": bson.M{"$in": conversationIDs}}, options.Find().SetProjection(bson.M{"_id": 0, "conversation_id": 1}))
}
func (c *ConversationMgo) FindUserIDAllConversations(ctx context.Context, userID string) (conversations []*relation.ConversationModel, err error) {
- return mgoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": userID})
+ return mongoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"owner_user_id": userID})
}
func (c *ConversationMgo) FindRecvMsgUserIDs(ctx context.Context, conversationID string, recvOpts []int) ([]string, error) {
@@ -103,22 +111,22 @@ func (c *ConversationMgo) FindRecvMsgUserIDs(ctx context.Context, conversationID
} else {
filter = bson.M{"conversation_id": conversationID, "recv_msg_opt": bson.M{"$in": recvOpts}}
}
- return mgoutil.Find[string](ctx, c.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1}))
+ return mongoutil.Find[string](ctx, c.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "owner_user_id": 1}))
}
func (c *ConversationMgo) GetUserRecvMsgOpt(ctx context.Context, ownerUserID, conversationID string) (opt int, err error) {
- return mgoutil.FindOne[int](ctx, c.coll, bson.M{"owner_user_id": ownerUserID, "conversation_id": conversationID}, options.FindOne().SetProjection(bson.M{"recv_msg_opt": 1}))
+ return mongoutil.FindOne[int](ctx, c.coll, bson.M{"owner_user_id": ownerUserID, "conversation_id": conversationID}, options.FindOne().SetProjection(bson.M{"recv_msg_opt": 1}))
}
func (c *ConversationMgo) GetAllConversationIDs(ctx context.Context) ([]string, error) {
- return mgoutil.Aggregate[string](ctx, c.coll, []bson.M{
+ return mongoutil.Aggregate[string](ctx, c.coll, []bson.M{
{"$group": bson.M{"_id": "$conversation_id"}},
{"$project": bson.M{"_id": 0, "conversation_id": "$_id"}},
})
}
func (c *ConversationMgo) GetAllConversationIDsNumber(ctx context.Context) (int64, error) {
- counts, err := mgoutil.Aggregate[int64](ctx, c.coll, []bson.M{
+ counts, err := mongoutil.Aggregate[int64](ctx, c.coll, []bson.M{
{"$group": bson.M{"_id": "$conversation_id"}},
{"$group": bson.M{"_id": nil, "count": bson.M{"$sum": 1}}},
{"$project": bson.M{"_id": 0}},
@@ -133,16 +141,16 @@ func (c *ConversationMgo) GetAllConversationIDsNumber(ctx context.Context) (int6
}
func (c *ConversationMgo) PageConversationIDs(ctx context.Context, pagination pagination.Pagination) (conversationIDs []string, err error) {
- return mgoutil.FindPageOnly[string](ctx, c.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"conversation_id": 1}))
+ return mongoutil.FindPageOnly[string](ctx, c.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"conversation_id": 1}))
}
func (c *ConversationMgo) GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relation.ConversationModel, error) {
- return mgoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"conversation_id": bson.M{"$in": conversationIDs}})
+ return mongoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{"conversation_id": bson.M{"$in": conversationIDs}})
}
func (c *ConversationMgo) GetConversationIDsNeedDestruct(ctx context.Context) ([]*relation.ConversationModel, error) {
- //"is_msg_destruct = 1 && msg_destruct_time != 0 && (UNIX_TIMESTAMP(NOW()) > (msg_destruct_time + UNIX_TIMESTAMP(latest_msg_destruct_time)) || latest_msg_destruct_time is NULL)"
- return mgoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{
+ // "is_msg_destruct = 1 && msg_destruct_time != 0 && (UNIX_TIMESTAMP(NOW()) > (msg_destruct_time + UNIX_TIMESTAMP(latest_msg_destruct_time)) || latest_msg_destruct_time is NULL)"
+ return mongoutil.Find[*relation.ConversationModel](ctx, c.coll, bson.M{
"is_msg_destruct": 1,
"msg_destruct_time": bson.M{"$ne": 0},
"$or": []bson.M{
@@ -162,7 +170,7 @@ func (c *ConversationMgo) GetConversationIDsNeedDestruct(ctx context.Context) ([
}
func (c *ConversationMgo) GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error) {
- return mgoutil.Find[string](
+ return mongoutil.Find[string](
ctx,
c.coll,
bson.M{"conversation_id": conversationID, "recv_msg_opt": bson.M{"$ne": constant.ReceiveMessage}},
diff --git a/pkg/common/db/s3/doc.go b/pkg/common/db/mgo/doc.go
similarity index 81%
rename from pkg/common/db/s3/doc.go
rename to pkg/common/db/mgo/doc.go
index a43710676a..f9d43e8858 100644
--- a/pkg/common/db/s3/doc.go
+++ b/pkg/common/db/mgo/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package s3 // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
+package mgo // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo"
diff --git a/pkg/common/db/mgo/friend.go b/pkg/common/db/mgo/friend.go
index aa7775ce04..269bb594ab 100644
--- a/pkg/common/db/mgo/friend.go
+++ b/pkg/common/db/mgo/friend.go
@@ -17,9 +17,9 @@ package mgo
import (
"context"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -48,7 +48,7 @@ func NewFriendMongo(db *mongo.Database) (relation.FriendModelInterface, error) {
// Create inserts multiple friend records.
func (f *FriendMgo) Create(ctx context.Context, friends []*relation.FriendModel) error {
- return mgoutil.InsertMany(ctx, f.coll, friends)
+ return mongoutil.InsertMany(ctx, f.coll, friends)
}
// Delete removes specified friends of the owner user.
@@ -57,7 +57,7 @@ func (f *FriendMgo) Delete(ctx context.Context, ownerUserID string, friendUserID
"owner_user_id": ownerUserID,
"friend_user_id": bson.M{"$in": friendUserIDs},
}
- return mgoutil.DeleteOne(ctx, f.coll, filter)
+ return mongoutil.DeleteOne(ctx, f.coll, filter)
}
// UpdateByMap updates specific fields of a friend document using a map.
@@ -69,7 +69,7 @@ func (f *FriendMgo) UpdateByMap(ctx context.Context, ownerUserID string, friendU
"owner_user_id": ownerUserID,
"friend_user_id": friendUserID,
}
- return mgoutil.UpdateOne(ctx, f.coll, filter, bson.M{"$set": args}, true)
+ return mongoutil.UpdateOne(ctx, f.coll, filter, bson.M{"$set": args}, true)
}
// Update modifies multiple friend documents.
@@ -92,7 +92,7 @@ func (f *FriendMgo) Take(ctx context.Context, ownerUserID, friendUserID string)
"owner_user_id": ownerUserID,
"friend_user_id": friendUserID,
}
- return mgoutil.FindOne[*relation.FriendModel](ctx, f.coll, filter)
+ return mongoutil.FindOne[*relation.FriendModel](ctx, f.coll, filter)
}
// FindUserState finds the friendship status between two users.
@@ -103,7 +103,7 @@ func (f *FriendMgo) FindUserState(ctx context.Context, userID1, userID2 string)
{"owner_user_id": userID2, "friend_user_id": userID1},
},
}
- return mgoutil.Find[*relation.FriendModel](ctx, f.coll, filter)
+ return mongoutil.Find[*relation.FriendModel](ctx, f.coll, filter)
}
// FindFriends retrieves a list of friends for a given owner. Missing friends do not cause an error.
@@ -112,7 +112,7 @@ func (f *FriendMgo) FindFriends(ctx context.Context, ownerUserID string, friendU
"owner_user_id": ownerUserID,
"friend_user_id": bson.M{"$in": friendUserIDs},
}
- return mgoutil.Find[*relation.FriendModel](ctx, f.coll, filter)
+ return mongoutil.Find[*relation.FriendModel](ctx, f.coll, filter)
}
// FindReversalFriends finds users who have added the specified user as a friend.
@@ -121,25 +121,25 @@ func (f *FriendMgo) FindReversalFriends(ctx context.Context, friendUserID string
"owner_user_id": bson.M{"$in": ownerUserIDs},
"friend_user_id": friendUserID,
}
- return mgoutil.Find[*relation.FriendModel](ctx, f.coll, filter)
+ return mongoutil.Find[*relation.FriendModel](ctx, f.coll, filter)
}
// FindOwnerFriends retrieves a paginated list of friends for a given owner.
func (f *FriendMgo) FindOwnerFriends(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (int64, []*relation.FriendModel, error) {
filter := bson.M{"owner_user_id": ownerUserID}
- return mgoutil.FindPage[*relation.FriendModel](ctx, f.coll, filter, pagination)
+ return mongoutil.FindPage[*relation.FriendModel](ctx, f.coll, filter, pagination)
}
// FindInWhoseFriends finds users who have added the specified user as a friend, with pagination.
func (f *FriendMgo) FindInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (int64, []*relation.FriendModel, error) {
filter := bson.M{"friend_user_id": friendUserID}
- return mgoutil.FindPage[*relation.FriendModel](ctx, f.coll, filter, pagination)
+ return mongoutil.FindPage[*relation.FriendModel](ctx, f.coll, filter, pagination)
}
// FindFriendUserIDs retrieves a list of friend user IDs for a given owner.
func (f *FriendMgo) FindFriendUserIDs(ctx context.Context, ownerUserID string) ([]string, error) {
filter := bson.M{"owner_user_id": ownerUserID}
- return mgoutil.Find[string](ctx, f.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1}))
+ return mongoutil.Find[string](ctx, f.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "friend_user_id": 1}))
}
func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, val map[string]any) error {
@@ -158,6 +158,6 @@ func (f *FriendMgo) UpdateFriends(ctx context.Context, ownerUserID string, frien
update := bson.M{"$set": val}
// Perform the update operation for all matching documents
- _, err := mgoutil.UpdateMany(ctx, f.coll, filter, update)
+ _, err := mongoutil.UpdateMany(ctx, f.coll, filter, update)
return err
}
diff --git a/pkg/common/db/mgo/friend_request.go b/pkg/common/db/mgo/friend_request.go
index 3e0588a0b6..704b681267 100644
--- a/pkg/common/db/mgo/friend_request.go
+++ b/pkg/common/db/mgo/friend_request.go
@@ -17,9 +17,9 @@ package mgo
import (
"context"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -45,11 +45,11 @@ type FriendRequestMgo struct {
}
func (f *FriendRequestMgo) FindToUserID(ctx context.Context, toUserID string, pagination pagination.Pagination) (total int64, friendRequests []*relation.FriendRequestModel, err error) {
- return mgoutil.FindPage[*relation.FriendRequestModel](ctx, f.coll, bson.M{"to_user_id": toUserID}, pagination)
+ return mongoutil.FindPage[*relation.FriendRequestModel](ctx, f.coll, bson.M{"to_user_id": toUserID}, pagination)
}
func (f *FriendRequestMgo) FindFromUserID(ctx context.Context, fromUserID string, pagination pagination.Pagination) (total int64, friendRequests []*relation.FriendRequestModel, err error) {
- return mgoutil.FindPage[*relation.FriendRequestModel](ctx, f.coll, bson.M{"from_user_id": fromUserID}, pagination)
+ return mongoutil.FindPage[*relation.FriendRequestModel](ctx, f.coll, bson.M{"from_user_id": fromUserID}, pagination)
}
func (f *FriendRequestMgo) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*relation.FriendRequestModel, err error) {
@@ -57,22 +57,22 @@ func (f *FriendRequestMgo) FindBothFriendRequests(ctx context.Context, fromUserI
{"from_user_id": fromUserID, "to_user_id": toUserID},
{"from_user_id": toUserID, "to_user_id": fromUserID},
}}
- return mgoutil.Find[*relation.FriendRequestModel](ctx, f.coll, filter)
+ return mongoutil.Find[*relation.FriendRequestModel](ctx, f.coll, filter)
}
func (f *FriendRequestMgo) Create(ctx context.Context, friendRequests []*relation.FriendRequestModel) error {
- return mgoutil.InsertMany(ctx, f.coll, friendRequests)
+ return mongoutil.InsertMany(ctx, f.coll, friendRequests)
}
func (f *FriendRequestMgo) Delete(ctx context.Context, fromUserID, toUserID string) (err error) {
- return mgoutil.DeleteOne(ctx, f.coll, bson.M{"from_user_id": fromUserID, "to_user_id": toUserID})
+ return mongoutil.DeleteOne(ctx, f.coll, bson.M{"from_user_id": fromUserID, "to_user_id": toUserID})
}
func (f *FriendRequestMgo) UpdateByMap(ctx context.Context, formUserID, toUserID string, args map[string]any) (err error) {
if len(args) == 0 {
return nil
}
- return mgoutil.UpdateOne(ctx, f.coll, bson.M{"from_user_id": formUserID, "to_user_id": toUserID}, bson.M{"$set": args}, true)
+ return mongoutil.UpdateOne(ctx, f.coll, bson.M{"from_user_id": formUserID, "to_user_id": toUserID}, bson.M{"$set": args}, true)
}
func (f *FriendRequestMgo) Update(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) {
@@ -99,11 +99,11 @@ func (f *FriendRequestMgo) Update(ctx context.Context, friendRequest *relation.F
return nil
}
filter := bson.M{"from_user_id": friendRequest.FromUserID, "to_user_id": friendRequest.ToUserID}
- return mgoutil.UpdateOne(ctx, f.coll, filter, bson.M{"$set": updater}, true)
+ return mongoutil.UpdateOne(ctx, f.coll, filter, bson.M{"$set": updater}, true)
}
func (f *FriendRequestMgo) Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *relation.FriendRequestModel, err error) {
- return mgoutil.FindOne[*relation.FriendRequestModel](ctx, f.coll, bson.M{"from_user_id": fromUserID, "to_user_id": toUserID})
+ return mongoutil.FindOne[*relation.FriendRequestModel](ctx, f.coll, bson.M{"from_user_id": fromUserID, "to_user_id": toUserID})
}
func (f *FriendRequestMgo) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *relation.FriendRequestModel, err error) {
diff --git a/pkg/common/db/mgo/group.go b/pkg/common/db/mgo/group.go
index c63e3c3761..0169c23391 100644
--- a/pkg/common/db/mgo/group.go
+++ b/pkg/common/db/mgo/group.go
@@ -18,11 +18,11 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -47,7 +47,7 @@ type GroupMgo struct {
}
func (g *GroupMgo) Create(ctx context.Context, groups []*relation.GroupModel) (err error) {
- return mgoutil.InsertMany(ctx, g.coll, groups)
+ return mongoutil.InsertMany(ctx, g.coll, groups)
}
func (g *GroupMgo) UpdateStatus(ctx context.Context, groupID string, status int32) (err error) {
@@ -58,21 +58,23 @@ func (g *GroupMgo) UpdateMap(ctx context.Context, groupID string, args map[strin
if len(args) == 0 {
return nil
}
- return mgoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID}, bson.M{"$set": args}, true)
+ return mongoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID}, bson.M{"$set": args}, true)
}
func (g *GroupMgo) Find(ctx context.Context, groupIDs []string) (groups []*relation.GroupModel, err error) {
- return mgoutil.Find[*relation.GroupModel](ctx, g.coll, bson.M{"group_id": bson.M{"$in": groupIDs}})
+ return mongoutil.Find[*relation.GroupModel](ctx, g.coll, bson.M{"group_id": bson.M{"$in": groupIDs}})
}
func (g *GroupMgo) Take(ctx context.Context, groupID string) (group *relation.GroupModel, err error) {
- return mgoutil.FindOne[*relation.GroupModel](ctx, g.coll, bson.M{"group_id": groupID})
+ return mongoutil.FindOne[*relation.GroupModel](ctx, g.coll, bson.M{"group_id": groupID})
}
func (g *GroupMgo) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*relation.GroupModel, err error) {
+ // Define the sorting options
opts := options.Find().SetSort(bson.D{{Key: "created_at", Value: -1}})
- return mgoutil.FindPage[*relation.GroupModel](ctx, g.coll, bson.M{
+ // Perform the search with pagination and sorting
+ return mongoutil.FindPage[*relation.GroupModel](ctx, g.coll, bson.M{
"group_name": bson.M{"$regex": keyword},
"status": bson.M{"$ne": constant.GroupStatusDismissed},
}, pagination, opts)
@@ -80,9 +82,9 @@ func (g *GroupMgo) Search(ctx context.Context, keyword string, pagination pagina
func (g *GroupMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
if before == nil {
- return mgoutil.Count(ctx, g.coll, bson.M{})
+ return mongoutil.Count(ctx, g.coll, bson.M{})
}
- return mgoutil.Count(ctx, g.coll, bson.M{"create_time": bson.M{"$lt": before}})
+ return mongoutil.Count(ctx, g.coll, bson.M{"create_time": bson.M{"$lt": before}})
}
func (g *GroupMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) {
@@ -113,7 +115,7 @@ func (g *GroupMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time,
Date string `bson:"_id"`
Count int64 `bson:"count"`
}
- items, err := mgoutil.Aggregate[Item](ctx, g.coll, pipeline)
+ items, err := mongoutil.Aggregate[Item](ctx, g.coll, pipeline)
if err != nil {
return nil, err
}
diff --git a/pkg/common/db/mgo/group_member.go b/pkg/common/db/mgo/group_member.go
index e1af34f7c1..29d69d0f0b 100644
--- a/pkg/common/db/mgo/group_member.go
+++ b/pkg/common/db/mgo/group_member.go
@@ -17,11 +17,11 @@ package mgo
import (
"context"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -47,7 +47,7 @@ type GroupMemberMgo struct {
}
func (g *GroupMemberMgo) Create(ctx context.Context, groupMembers []*relation.GroupMemberModel) (err error) {
- return mgoutil.InsertMany(ctx, g.coll, groupMembers)
+ return mongoutil.InsertMany(ctx, g.coll, groupMembers)
}
func (g *GroupMemberMgo) Delete(ctx context.Context, groupID string, userIDs []string) (err error) {
@@ -55,7 +55,7 @@ func (g *GroupMemberMgo) Delete(ctx context.Context, groupID string, userIDs []s
if len(userIDs) > 0 {
filter["user_id"] = bson.M{"$in": userIDs}
}
- return mgoutil.DeleteMany(ctx, g.coll, filter)
+ return mongoutil.DeleteMany(ctx, g.coll, filter)
}
func (g *GroupMemberMgo) UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) error {
@@ -63,41 +63,41 @@ func (g *GroupMemberMgo) UpdateRoleLevel(ctx context.Context, groupID string, us
}
func (g *GroupMemberMgo) Update(ctx context.Context, groupID string, userID string, data map[string]any) (err error) {
- return mgoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID}, bson.M{"$set": data}, true)
+ return mongoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID}, bson.M{"$set": data}, true)
}
func (g *GroupMemberMgo) Find(ctx context.Context, groupIDs []string, userIDs []string, roleLevels []int32) (groupMembers []*relation.GroupMemberModel, err error) {
- //TODO implement me
+ // TODO implement me
panic("implement me")
}
func (g *GroupMemberMgo) FindMemberUserID(ctx context.Context, groupID string) (userIDs []string, err error) {
- return mgoutil.Find[string](ctx, g.coll, bson.M{"group_id": groupID}, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
+ return mongoutil.Find[string](ctx, g.coll, bson.M{"group_id": groupID}, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
}
func (g *GroupMemberMgo) Take(ctx context.Context, groupID string, userID string) (groupMember *relation.GroupMemberModel, err error) {
- return mgoutil.FindOne[*relation.GroupMemberModel](ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID})
+ return mongoutil.FindOne[*relation.GroupMemberModel](ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID})
}
func (g *GroupMemberMgo) TakeOwner(ctx context.Context, groupID string) (groupMember *relation.GroupMemberModel, err error) {
- return mgoutil.FindOne[*relation.GroupMemberModel](ctx, g.coll, bson.M{"group_id": groupID, "role_level": constant.GroupOwner})
+ return mongoutil.FindOne[*relation.GroupMemberModel](ctx, g.coll, bson.M{"group_id": groupID, "role_level": constant.GroupOwner})
}
func (g *GroupMemberMgo) FindRoleLevelUserIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error) {
- return mgoutil.Find[string](ctx, g.coll, bson.M{"group_id": groupID, "role_level": roleLevel}, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
+ return mongoutil.Find[string](ctx, g.coll, bson.M{"group_id": groupID, "role_level": roleLevel}, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
}
func (g *GroupMemberMgo) SearchMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (total int64, groupList []*relation.GroupMemberModel, err error) {
filter := bson.M{"group_id": groupID, "nickname": bson.M{"$regex": keyword}}
- return mgoutil.FindPage[*relation.GroupMemberModel](ctx, g.coll, filter, pagination)
+ return mongoutil.FindPage[*relation.GroupMemberModel](ctx, g.coll, filter, pagination)
}
func (g *GroupMemberMgo) FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {
- return mgoutil.Find[string](ctx, g.coll, bson.M{"user_id": userID}, options.Find().SetProjection(bson.M{"_id": 0, "group_id": 1}))
+ return mongoutil.Find[string](ctx, g.coll, bson.M{"user_id": userID}, options.Find().SetProjection(bson.M{"_id": 0, "group_id": 1}))
}
func (g *GroupMemberMgo) TakeGroupMemberNum(ctx context.Context, groupID string) (count int64, err error) {
- return mgoutil.Count(ctx, g.coll, bson.M{"group_id": groupID})
+ return mongoutil.Count(ctx, g.coll, bson.M{"group_id": groupID})
}
func (g *GroupMemberMgo) FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) {
@@ -107,7 +107,7 @@ func (g *GroupMemberMgo) FindUserManagedGroupID(ctx context.Context, userID stri
"$in": []int{constant.GroupOwner, constant.GroupAdmin},
},
}
- return mgoutil.Find[string](ctx, g.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "group_id": 1}))
+ return mongoutil.Find[string](ctx, g.coll, filter, options.Find().SetProjection(bson.M{"_id": 0, "group_id": 1}))
}
func (g *GroupMemberMgo) IsUpdateRoleLevel(data map[string]any) bool {
diff --git a/pkg/common/db/mgo/group_request.go b/pkg/common/db/mgo/group_request.go
index 9aee0e9605..17cbeab179 100644
--- a/pkg/common/db/mgo/group_request.go
+++ b/pkg/common/db/mgo/group_request.go
@@ -17,10 +17,10 @@ package mgo
import (
"context"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -46,29 +46,29 @@ type GroupRequestMgo struct {
}
func (g *GroupRequestMgo) Create(ctx context.Context, groupRequests []*relation.GroupRequestModel) (err error) {
- return mgoutil.InsertMany(ctx, g.coll, groupRequests)
+ return mongoutil.InsertMany(ctx, g.coll, groupRequests)
}
func (g *GroupRequestMgo) Delete(ctx context.Context, groupID string, userID string) (err error) {
- return mgoutil.DeleteOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID})
+ return mongoutil.DeleteOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID})
}
func (g *GroupRequestMgo) UpdateHandler(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32) (err error) {
- return mgoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID}, bson.M{"$set": bson.M{"handle_msg": handledMsg, "handle_result": handleResult}}, true)
+ return mongoutil.UpdateOne(ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID}, bson.M{"$set": bson.M{"handle_msg": handledMsg, "handle_result": handleResult}}, true)
}
func (g *GroupRequestMgo) Take(ctx context.Context, groupID string, userID string) (groupRequest *relation.GroupRequestModel, err error) {
- return mgoutil.FindOne[*relation.GroupRequestModel](ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID})
+ return mongoutil.FindOne[*relation.GroupRequestModel](ctx, g.coll, bson.M{"group_id": groupID, "user_id": userID})
}
func (g *GroupRequestMgo) FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*relation.GroupRequestModel, error) {
- return mgoutil.Find[*relation.GroupRequestModel](ctx, g.coll, bson.M{"group_id": groupID, "user_id": bson.M{"$in": userIDs}})
+ return mongoutil.Find[*relation.GroupRequestModel](ctx, g.coll, bson.M{"group_id": groupID, "user_id": bson.M{"$in": userIDs}})
}
func (g *GroupRequestMgo) Page(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, groups []*relation.GroupRequestModel, err error) {
- return mgoutil.FindPage[*relation.GroupRequestModel](ctx, g.coll, bson.M{"user_id": userID}, pagination)
+ return mongoutil.FindPage[*relation.GroupRequestModel](ctx, g.coll, bson.M{"user_id": userID}, pagination)
}
func (g *GroupRequestMgo) PageGroup(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (total int64, groups []*relation.GroupRequestModel, err error) {
- return mgoutil.FindPage[*relation.GroupRequestModel](ctx, g.coll, bson.M{"group_id": bson.M{"$in": groupIDs}}, pagination)
+ return mongoutil.FindPage[*relation.GroupRequestModel](ctx, g.coll, bson.M{"group_id": bson.M{"$in": groupIDs}}, pagination)
}
diff --git a/pkg/common/db/mgo/log.go b/pkg/common/db/mgo/log.go
index ca28d5964b..36a0bbbc55 100644
--- a/pkg/common/db/mgo/log.go
+++ b/pkg/common/db/mgo/log.go
@@ -18,9 +18,9 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -57,7 +57,7 @@ type LogMgo struct {
}
func (l *LogMgo) Create(ctx context.Context, log []*relation.LogModel) error {
- return mgoutil.InsertMany(ctx, l.coll, log)
+ return mongoutil.InsertMany(ctx, l.coll, log)
}
func (l *LogMgo) Search(ctx context.Context, keyword string, start time.Time, end time.Time, pagination pagination.Pagination) (int64, []*relation.LogModel, error) {
@@ -65,19 +65,19 @@ func (l *LogMgo) Search(ctx context.Context, keyword string, start time.Time, en
if keyword != "" {
filter["user_id"] = bson.M{"$regex": keyword}
}
- return mgoutil.FindPage[*relation.LogModel](ctx, l.coll, filter, pagination, options.Find().SetSort(bson.M{"create_time": -1}))
+ return mongoutil.FindPage[*relation.LogModel](ctx, l.coll, filter, pagination, options.Find().SetSort(bson.M{"create_time": -1}))
}
func (l *LogMgo) Delete(ctx context.Context, logID []string, userID string) error {
if userID == "" {
- return mgoutil.DeleteMany(ctx, l.coll, bson.M{"log_id": bson.M{"$in": logID}})
+ return mongoutil.DeleteMany(ctx, l.coll, bson.M{"log_id": bson.M{"$in": logID}})
}
- return mgoutil.DeleteMany(ctx, l.coll, bson.M{"log_id": bson.M{"$in": logID}, "user_id": userID})
+ return mongoutil.DeleteMany(ctx, l.coll, bson.M{"log_id": bson.M{"$in": logID}, "user_id": userID})
}
func (l *LogMgo) Get(ctx context.Context, logIDs []string, userID string) ([]*relation.LogModel, error) {
if userID == "" {
- return mgoutil.Find[*relation.LogModel](ctx, l.coll, bson.M{"log_id": bson.M{"$in": logIDs}})
+ return mongoutil.Find[*relation.LogModel](ctx, l.coll, bson.M{"log_id": bson.M{"$in": logIDs}})
}
- return mgoutil.Find[*relation.LogModel](ctx, l.coll, bson.M{"log_id": bson.M{"$in": logIDs}, "user_id": userID})
+ return mongoutil.Find[*relation.LogModel](ctx, l.coll, bson.M{"log_id": bson.M{"$in": logIDs}, "user_id": userID})
}
diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/mgo/msg.go
similarity index 56%
rename from pkg/common/db/unrelation/msg.go
rename to pkg/common/db/mgo/msg.go
index a129f3e996..6fe24536bd 100644
--- a/pkg/common/db/unrelation/msg.go
+++ b/pkg/common/db/mgo/msg.go
@@ -1,68 +1,54 @@
-// 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 unrelation
+package mgo
import (
"context"
- "encoding/json"
- "errors"
"fmt"
"time"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/utils/jsonutil"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
- "google.golang.org/protobuf/proto"
)
-var ErrMsgListNotExist = errors.New("user not have msg in mongoDB")
-
-type MsgMongoDriver struct {
- MsgCollection *mongo.Collection
- model table.MsgDocModel
+func NewMsgMongo(db *mongo.Database) (relation.MsgDocModelInterface, error) {
+ coll := db.Collection(new(relation.MsgDocModel).TableName())
+ _, err := coll.Indexes().CreateOne(context.Background(), mongo.IndexModel{
+ Keys: bson.D{
+ {Key: "doc_id", Value: 1},
+ },
+ Options: options.Index().SetUnique(true),
+ })
+ if err != nil {
+ return nil, errs.Wrap(err)
+ }
+ return &MsgMgo{coll: coll}, nil
}
-func NewMsgMongoDriver(database *mongo.Database) table.MsgDocModelInterface {
- collection := database.Collection(table.MsgDocModel{}.TableName())
- return &MsgMongoDriver{MsgCollection: collection}
+type MsgMgo struct {
+ coll *mongo.Collection
+ model relation.MsgDocModel
}
-func (m *MsgMongoDriver) PushMsgsToDoc(ctx context.Context, docID string, msgsToMongo []table.MsgInfoModel) error {
- return m.MsgCollection.FindOneAndUpdate(ctx, bson.M{"doc_id": docID}, bson.M{"$push": bson.M{"msgs": bson.M{"$each": msgsToMongo}}}).
- Err()
+func (m *MsgMgo) PushMsgsToDoc(ctx context.Context, docID string, msgsToMongo []relation.MsgInfoModel) error {
+ filter := bson.M{"doc_id": docID}
+ update := bson.M{"$push": bson.M{"msgs": bson.M{"$each": msgsToMongo}}}
+ return mongoutil.UpdateOne(ctx, m.coll, filter, update, false)
}
-func (m *MsgMongoDriver) Create(ctx context.Context, model *table.MsgDocModel) error {
- _, err := m.MsgCollection.InsertOne(ctx, model)
- return err
+func (m *MsgMgo) Create(ctx context.Context, model *relation.MsgDocModel) error {
+ return mongoutil.InsertMany(ctx, m.coll, []*relation.MsgDocModel{model})
}
-func (m *MsgMongoDriver) UpdateMsg(
- ctx context.Context,
- docID string,
- index int64,
- key string,
- value any,
-) (*mongo.UpdateResult, error) {
+func (m *MsgMgo) UpdateMsg(ctx context.Context, docID string, index int64, key string, value any) (*mongo.UpdateResult, error) {
var field string
if key == "" {
field = fmt.Sprintf("msgs.%d", index)
@@ -71,21 +57,10 @@ func (m *MsgMongoDriver) UpdateMsg(
}
filter := bson.M{"doc_id": docID}
update := bson.M{"$set": bson.M{field: value}}
- res, err := m.MsgCollection.UpdateOne(ctx, filter, update)
- if err != nil {
- return nil, errs.Wrap(err)
- }
- return res, nil
+ return mongoutil.UpdateOneResult(ctx, m.coll, filter, update)
}
-// PushUnique value must slice.
-func (m *MsgMongoDriver) PushUnique(
- ctx context.Context,
- docID string,
- index int64,
- key string,
- value any,
-) (*mongo.UpdateResult, error) {
+func (m *MsgMgo) PushUnique(ctx context.Context, docID string, index int64, key string, value any) (*mongo.UpdateResult, error) {
var field string
if key == "" {
field = fmt.Sprintf("msgs.%d", index)
@@ -98,138 +73,24 @@ func (m *MsgMongoDriver) PushUnique(
field: bson.M{"$each": value},
},
}
- res, err := m.MsgCollection.UpdateOne(ctx, filter, update)
- if err != nil {
- return nil, errs.Wrap(err)
- }
- return res, nil
-}
-
-func (m *MsgMongoDriver) UpdateMsgContent(ctx context.Context, docID string, index int64, msg []byte) error {
- _, err := m.MsgCollection.UpdateOne(
- ctx,
- bson.M{"doc_id": docID},
- bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}},
- )
- if err != nil {
- return errs.Wrap(err)
- }
- return nil
+ return mongoutil.UpdateOneResult(ctx, m.coll, filter, update)
}
-func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc(ctx context.Context, docID string, msg *sdkws.MsgData, seqIndex int, status int32) error {
- msg.Status = status
- bytes, err := proto.Marshal(msg)
- if err != nil {
- return errs.Wrap(err)
- }
- _, err = m.MsgCollection.UpdateOne(
- ctx,
- bson.M{"doc_id": docID},
- bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}},
- )
- if err != nil {
- return errs.Wrap(err, fmt.Sprintf("docID is %s, seqIndex is %d", docID, seqIndex))
- }
- return nil
-}
-
-func (m *MsgMongoDriver) FindOneByDocID(ctx context.Context, docID string) (*table.MsgDocModel, error) {
- doc := &table.MsgDocModel{}
- err := m.MsgCollection.FindOne(ctx, bson.M{"doc_id": docID}).Decode(doc)
- return doc, err
-}
-
-func (m *MsgMongoDriver) GetMsgDocModelByIndex(
- ctx context.Context,
- conversationID string,
- index, sort int64,
-) (*table.MsgDocModel, error) {
- if sort != 1 && sort != -1 {
- return nil, errs.ErrArgs.Wrap("mongo sort must be 1 or -1")
- }
- findOpts := options.Find().SetLimit(1).SetSkip(index).SetSort(bson.M{"doc_id": sort})
- cursor, err := m.MsgCollection.Find(
- ctx,
- bson.M{"doc_id": primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}},
- findOpts,
- )
- if err != nil {
- return nil, errs.Wrap(err, fmt.Sprintf("conversationID is %s", conversationID))
- }
- var msgs []table.MsgDocModel
- err = cursor.All(ctx, &msgs)
- if err != nil {
- return nil, errs.Wrap(err, fmt.Sprintf("cursor is %s", cursor.Current.String()))
- }
- if len(msgs) > 0 {
- return &msgs[0], nil
- }
- return nil, ErrMsgListNotExist
-}
-
-func (m *MsgMongoDriver) GetNewestMsg(ctx context.Context, conversationID string) (*table.MsgInfoModel, error) {
- var skip int64 = 0
- for {
- msgDocModel, err := m.GetMsgDocModelByIndex(ctx, conversationID, skip, -1)
- if err != nil {
- return nil, err
- }
- for i := len(msgDocModel.Msg) - 1; i >= 0; i-- {
- if msgDocModel.Msg[i].Msg != nil {
- return msgDocModel.Msg[i], nil
- }
- }
- skip++
- }
-}
-
-func (m *MsgMongoDriver) GetOldestMsg(ctx context.Context, conversationID string) (*table.MsgInfoModel, error) {
- var skip int64 = 0
- for {
- msgDocModel, err := m.GetMsgDocModelByIndex(ctx, conversationID, skip, 1)
- if err != nil {
- return nil, err
- }
- for i, v := range msgDocModel.Msg {
- if v.Msg != nil {
- return msgDocModel.Msg[i], nil
- }
- }
- skip++
- }
+func (m *MsgMgo) UpdateMsgContent(ctx context.Context, docID string, index int64, msg []byte) error {
+ filter := bson.M{"doc_id": docID}
+ update := bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}}
+ return mongoutil.UpdateOne(ctx, m.coll, filter, update, false)
}
-func (m *MsgMongoDriver) DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error {
- updates := bson.M{
- "$set": bson.M{},
- }
- for _, index := range indexes {
- updates["$set"].(bson.M)[fmt.Sprintf("msgs.%d", index)] = bson.M{
- "msg": nil,
- }
- }
- _, err := m.MsgCollection.UpdateMany(ctx, bson.M{"doc_id": docID}, updates)
- if err != nil {
- return errs.Wrap(err, fmt.Sprintf("docID is %s, indexes is %v", docID, indexes))
- }
- return nil
+func (m *MsgMgo) IsExistDocID(ctx context.Context, docID string) (bool, error) {
+ return mongoutil.Exist(ctx, m.coll, bson.M{"doc_id": docID})
}
-func (m *MsgMongoDriver) DeleteDocs(ctx context.Context, docIDs []string) error {
- if docIDs == nil {
- return nil
- }
- _, err := m.MsgCollection.DeleteMany(ctx, bson.M{"doc_id": bson.M{"$in": docIDs}})
- return err
+func (m *MsgMgo) FindOneByDocID(ctx context.Context, docID string) (*relation.MsgDocModel, error) {
+ return mongoutil.FindOne[*relation.MsgDocModel](ctx, m.coll, bson.M{"doc_id": docID})
}
-func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(
- ctx context.Context,
- userID string,
- docID string,
- seqs []int64,
-) (msgs []*table.MsgInfoModel, err error) {
+func (m *MsgMgo) GetMsgBySeqIndexIn1Doc(ctx context.Context, userID, docID string, seqs []int64) ([]*relation.MsgInfoModel, error) {
indexs := make([]int64, 0, len(seqs))
for _, seq := range seqs {
indexs = append(indexs, m.model.GetMsgIndex(seq))
@@ -270,20 +131,14 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(
{Key: "msgs.del_list", Value: 0},
}}},
}
-
- cur, err := m.MsgCollection.Aggregate(ctx, pipeline)
+ msgDocModel, err := mongoutil.Aggregate[*relation.MsgDocModel](ctx, m.coll, pipeline)
if err != nil {
- return nil, errs.Wrap(err)
- }
- defer cur.Close(ctx)
- var msgDocModel []table.MsgDocModel
- if err := cur.All(ctx, &msgDocModel); err != nil {
- return nil, errs.Wrap(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs))
+ return nil, err
}
if len(msgDocModel) == 0 {
return nil, errs.Wrap(mongo.ErrNoDocuments)
}
- msgs = make([]*table.MsgInfoModel, 0, len(msgDocModel[0].Msg))
+ msgs := make([]*relation.MsgInfoModel, 0, len(msgDocModel[0].Msg))
for i := range msgDocModel[0].Msg {
msg := msgDocModel[0].Msg[i]
if msg == nil || msg.Msg == nil {
@@ -303,16 +158,16 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(
Seq: msg.Msg.Seq,
Ex: msg.Msg.Ex,
}
- data, err := json.Marshal(&revokeContent)
+ data, err := jsonutil.JsonMarshal(&revokeContent)
if err != nil {
- return nil, errs.Wrap(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs))
+ return nil, errs.WrapMsg(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs))
}
elem := sdkws.NotificationElem{
Detail: string(data),
}
- content, err := json.Marshal(&elem)
+ content, err := jsonutil.JsonMarshal(&elem)
if err != nil {
- return nil, errs.Wrap(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs))
+ return nil, errs.WrapMsg(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs))
}
msg.Msg.ContentType = constant.MsgRevokeNotification
msg.Msg.Content = string(content)
@@ -322,16 +177,72 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc(
return msgs, nil
}
-func (m *MsgMongoDriver) IsExistDocID(ctx context.Context, docID string) (bool, error) {
- count, err := m.MsgCollection.CountDocuments(ctx, bson.M{"doc_id": docID})
+func (m *MsgMgo) GetNewestMsg(ctx context.Context, conversationID string) (*relation.MsgInfoModel, error) {
+ for skip := int64(0); ; skip++ {
+ msgDocModel, err := m.GetMsgDocModelByIndex(ctx, conversationID, skip, -1)
+ if err != nil {
+ return nil, err
+ }
+ for i := len(msgDocModel.Msg) - 1; i >= 0; i-- {
+ if msgDocModel.Msg[i].Msg != nil {
+ return msgDocModel.Msg[i], nil
+ }
+ }
+ }
+}
+
+func (m *MsgMgo) GetOldestMsg(ctx context.Context, conversationID string) (*relation.MsgInfoModel, error) {
+ for skip := int64(0); ; skip++ {
+ msgDocModel, err := m.GetMsgDocModelByIndex(ctx, conversationID, skip, 1)
+ if err != nil {
+ return nil, err
+ }
+ for i, v := range msgDocModel.Msg {
+ if v.Msg != nil {
+ return msgDocModel.Msg[i], nil
+ }
+ }
+ }
+}
+
+func (m *MsgMgo) DeleteDocs(ctx context.Context, docIDs []string) error {
+ if len(docIDs) == 0 {
+ return nil
+ }
+ return mongoutil.DeleteMany(ctx, m.coll, bson.M{"doc_id": bson.M{"$in": docIDs}})
+}
+
+func (m *MsgMgo) GetMsgDocModelByIndex(ctx context.Context, conversationID string, index, sort int64) (*relation.MsgDocModel, error) {
+ if sort != 1 && sort != -1 {
+ return nil, errs.ErrArgs.WrapMsg("mongo sort must be 1 or -1")
+ }
+ opt := options.Find().SetLimit(1).SetSkip(index).SetSort(bson.M{"doc_id": sort}).SetLimit(1)
+ filter := bson.M{"doc_id": primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}}
+ msgs, err := mongoutil.Find[*relation.MsgDocModel](ctx, m.coll, filter, opt)
if err != nil {
- return false, errs.Wrap(err, fmt.Sprintf("docID is %s", docID))
+ return nil, err
+ }
+ if len(msgs) > 0 {
+ return msgs[0], nil
+ }
+ return nil, errs.Wrap(relation.ErrMsgListNotExist)
+}
+
+func (m *MsgMgo) DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error {
+ update := bson.M{
+ "$set": bson.M{},
+ }
+ for _, index := range indexes {
+ update["$set"].(bson.M)[fmt.Sprintf("msgs.%d", index)] = bson.M{
+ "msg": nil,
+ }
}
- return count > 0, nil
+ _, err := mongoutil.UpdateMany(ctx, m.coll, bson.M{"doc_id": docID}, update)
+ return err
}
-func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error {
- updates := []mongo.WriteModel{}
+func (m *MsgMgo) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error {
+ var updates []mongo.WriteModel
for _, index := range indexes {
filter := bson.M{
"doc_id": docID,
@@ -349,204 +260,110 @@ func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(ctx context.Context, userID st
SetUpdate(update)
updates = append(updates, updateModel)
}
- _, err := m.MsgCollection.BulkWrite(ctx, updates)
- return errs.Wrap(err, fmt.Sprintf("docID is %s, indexes is %v", docID, indexes))
+ if _, err := m.coll.BulkWrite(ctx, updates); err != nil {
+ return errs.WrapMsg(err, fmt.Sprintf("docID is %s, indexes is %v", docID, indexes))
+ }
+ return nil
}
-// RangeUserSendCount
-// db.msg.aggregate([
-//
-// {
-// $match: {
-// "msgs.msg.send_time": {
-// "$gte": 0,
-// "$lt": 1788122092317
-// }
-// }
-// },
-// {
-// "$addFields": {
-// "msgs": {
-// "$filter": {
-// "input": "$msgs",
-// "as": "item",
-// "cond": {
-// "$and": [
-// {
-// $gte: ["$$item.msg.send_time", 0]
-// },
-// {
-// $lt: ["$$item.msg.send_time", 1788122092317]
-// }
-// ]
-// }
-// }
-// }
-// }
-// },
-// {
-// "$project": {
-// "_id": 0,
-//
-// },
-//
-// },
-// {
-// "$project": {
-// "result": {
-// "$map": {
-// "input": "$msgs",
-// "as": "item",
-// "in": {
-// user_id: "$$item.msg.send_id",
-// send_date: {
-// $dateToString: {
-// format: "%Y-%m-%d",
-// date: {
-// $toDate: "$$item.msg.send_time"
-// }
-// }
-// }
-// }
-// }
-// }
-// },
-//
-// },
-// {
-// "$unwind": "$result"
-// },
-// {
-// "$group": {
-// _id: "$result.send_date",
-// count: {
-// $sum: 1
-// },
-// original: {
-// $push: "$$ROOT"
-// }
-// }
-// },
-// {
-// "$addFields": {
-// "dates": "$$ROOT"
-// }
-// },
-// {
-// "$project": {
-// "_id": 0,
-// "count": 0,
-// "dates.original": 0,
-//
-// },
-//
-// },
-// {
-// "$group": {
-// _id: null,
-// count: {
-// $sum: 1
-// },
-// dates: {
-// $push: "$dates"
-// },
-// original: {
-// $push: "$original"
-// },
-//
-// }
-// },
-// {
-// "$unwind": "$original"
-// },
-// {
-// "$unwind": "$original"
-// },
-// {
-// "$group": {
-// _id: "$original.result.user_id",
-// count: {
-// $sum: 1
-// },
-// original: {
-// $push: "$dates"
-// },
-//
-// }
-// },
-// {
-// "$addFields": {
-// "dates": {
-// $arrayElemAt: ["$original", 0]
-// }
-// }
-// },
-// {
-// "$project": {
-// original: 0
-// }
-// },
-// {
-// $sort: {
-// count: - 1
-// }
-// },
-// {
-// "$group": {
-// _id: null,
-// user_count: {
-// $sum: 1
-// },
-// users: {
-// $push: "$$ROOT"
-// },
-//
-// }
-// },
-// {
-// "$addFields": {
-// "dates": {
-// $arrayElemAt: ["$users", 0]
-// }
-// }
-// },
-// {
-// "$addFields": {
-// "dates": "$dates.dates"
-// }
-// },
-// {
-// "$project": {
-// _id: 0,
-// "users.dates": 0,
-//
-// }
-// },
-// {
-// "$addFields": {
-// "msg_count": {
-// $sum: "$users.count"
-// }
-// }
-// },
-// {
-// "$addFields": {
-// users: {
-// $slice: ["$users", 0, 10]
-// }
-// }
-// }
-//
-// ]).
-func (m *MsgMongoDriver) RangeUserSendCount(
- ctx context.Context,
- start time.Time,
- end time.Time,
- group bool,
- ase bool,
- pageNumber int32,
- showNumber int32,
-) (msgCount int64, userCount int64, users []*table.UserCount, dateCount map[string]int64, err error) {
+func (m *MsgMgo) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*relation.MsgInfoModel, error) {
+ var pipe mongo.Pipeline
+ condition := bson.A{}
+ if req.SendTime != "" {
+ // Changed to keyed fields for bson.M to avoid govet errors
+ condition = append(condition, bson.M{"$eq": bson.A{bson.M{"$dateToString": bson.M{"format": "%Y-%m-%d", "date": bson.M{"$toDate": "$$item.msg.send_time"}}}, req.SendTime}})
+ }
+ if req.ContentType != 0 {
+ condition = append(condition, bson.M{"$eq": bson.A{"$$item.msg.content_type", req.ContentType}})
+ }
+ if req.SessionType != 0 {
+ condition = append(condition, bson.M{"$eq": bson.A{"$$item.msg.session_type", req.SessionType}})
+ }
+ if req.RecvID != "" {
+ condition = append(condition, bson.M{"$regexFind": bson.M{"input": "$$item.msg.recv_id", "regex": req.RecvID}})
+ }
+ if req.SendID != "" {
+ condition = append(condition, bson.M{"$regexFind": bson.M{"input": "$$item.msg.send_id", "regex": req.SendID}})
+ }
+
+ or := bson.A{
+ bson.M{"doc_id": bson.M{"$regex": "^si_", "$options": "i"}},
+ bson.M{"doc_id": bson.M{"$regex": "^g_", "$options": "i"}},
+ bson.M{"doc_id": bson.M{"$regex": "^sg_", "$options": "i"}},
+ }
+
+ // Use bson.D with keyed fields to specify the order explicitly
+ pipe = mongo.Pipeline{
+ {{"$match", bson.D{{Key: "$or", Value: or}}}},
+ {{"$project", bson.D{
+ {Key: "msgs", Value: bson.D{
+ {Key: "$filter", Value: bson.D{
+ {Key: "input", Value: "$msgs"},
+ {Key: "as", Value: "item"},
+ {Key: "cond", Value: bson.D{{Key: "$and", Value: condition}}},
+ }},
+ }},
+ {Key: "doc_id", Value: 1},
+ }}},
+ {{"$unwind", bson.M{"path": "$msgs"}}},
+ {{"$sort", bson.M{"msgs.msg.send_time": -1}}},
+ }
+ type docModel struct {
+ DocID string `bson:"doc_id"`
+ Msg *relation.MsgInfoModel `bson:"msgs"`
+ }
+ msgsDocs, err := mongoutil.Aggregate[*docModel](ctx, m.coll, pipe)
+ if err != nil {
+ return 0, nil, err
+ }
+ msgs := make([]*relation.MsgInfoModel, 0)
+ for _, doc := range msgsDocs {
+ msgInfo := doc.Msg
+ if msgInfo == nil || msgInfo.Msg == nil {
+ continue
+ }
+ if msgInfo.Revoke != nil {
+ revokeContent := sdkws.MessageRevokedContent{
+ RevokerID: msgInfo.Revoke.UserID,
+ RevokerRole: msgInfo.Revoke.Role,
+ ClientMsgID: msgInfo.Msg.ClientMsgID,
+ RevokerNickname: msgInfo.Revoke.Nickname,
+ RevokeTime: msgInfo.Revoke.Time,
+ SourceMessageSendTime: msgInfo.Msg.SendTime,
+ SourceMessageSendID: msgInfo.Msg.SendID,
+ SourceMessageSenderNickname: msgInfo.Msg.SenderNickname,
+ SessionType: msgInfo.Msg.SessionType,
+ Seq: msgInfo.Msg.Seq,
+ Ex: msgInfo.Msg.Ex,
+ }
+ data, err := jsonutil.JsonMarshal(&revokeContent)
+ if err != nil {
+ return 0, nil, errs.WrapMsg(err, "json.Marshal revokeContent")
+ }
+ elem := sdkws.NotificationElem{Detail: string(data)}
+ content, err := jsonutil.JsonMarshal(&elem)
+ if err != nil {
+ return 0, nil, errs.WrapMsg(err, "json.Marshal elem")
+ }
+ msgInfo.Msg.ContentType = constant.MsgRevokeNotification
+ msgInfo.Msg.Content = string(content)
+ }
+ msgs = append(msgs, msgInfo)
+ }
+ start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
+ n := int32(len(msgs))
+ if start >= n {
+ return n, []*relation.MsgInfoModel{}, nil
+ }
+ if start+req.Pagination.ShowNumber < n {
+ msgs = msgs[start : start+req.Pagination.ShowNumber]
+ } else {
+ msgs = msgs[start:]
+ }
+ return n, msgs, nil
+}
+
+func (m *MsgMgo) RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*relation.UserCount, dateCount map[string]int64, err error) {
var sort int
if ase {
sort = 1
@@ -773,21 +590,16 @@ func (m *MsgMongoDriver) RangeUserSendCount(
},
},
}
- cur, err := m.MsgCollection.Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true))
+ result, err := mongoutil.Aggregate[*Result](ctx, m.coll, pipeline, options.Aggregate().SetAllowDiskUse(true))
if err != nil {
- return 0, 0, nil, nil, errs.Wrap(err)
- }
- defer cur.Close(ctx)
- var result []Result
- if err = cur.All(ctx, &result); err != nil {
- return 0, 0, nil, nil, errs.Wrap(err)
+ return 0, 0, nil, nil, err
}
if len(result) == 0 {
return 0, 0, nil, nil, errs.Wrap(err)
}
- users = make([]*table.UserCount, len(result[0].Users))
+ users = make([]*relation.UserCount, len(result[0].Users))
for i, r := range result[0].Users {
- users[i] = &table.UserCount{
+ users[i] = &relation.UserCount{
UserID: r.UserID,
Count: r.Count,
}
@@ -799,14 +611,7 @@ func (m *MsgMongoDriver) RangeUserSendCount(
return result[0].MsgCount, result[0].UserCount, users, dateCount, nil
}
-func (m *MsgMongoDriver) RangeGroupSendCount(
- ctx context.Context,
- start time.Time,
- end time.Time,
- ase bool,
- pageNumber int32,
- showNumber int32,
-) (msgCount int64, userCount int64, groups []*table.GroupCount, dateCount map[string]int64, err error) {
+func (m *MsgMgo) RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*relation.GroupCount, dateCount map[string]int64, err error) {
var sort int
if ase {
sort = 1
@@ -1022,21 +827,16 @@ func (m *MsgMongoDriver) RangeGroupSendCount(
},
},
}
- cur, err := m.MsgCollection.Aggregate(ctx, pipeline, options.Aggregate().SetAllowDiskUse(true))
+ result, err := mongoutil.Aggregate[*Result](ctx, m.coll, pipeline, options.Aggregate().SetAllowDiskUse(true))
if err != nil {
- return 0, 0, nil, nil, errs.Wrap(err)
- }
- defer cur.Close(ctx)
- var result []Result
- if err = cur.All(ctx, &result); err != nil {
- return 0, 0, nil, nil, errs.Wrap(err)
+ return 0, 0, nil, nil, err
}
if len(result) == 0 {
return 0, 0, nil, nil, errs.Wrap(err)
}
- groups = make([]*table.GroupCount, len(result[0].Groups))
+ groups = make([]*relation.GroupCount, len(result[0].Groups))
for i, r := range result[0].Groups {
- groups[i] = &table.GroupCount{
+ groups[i] = &relation.GroupCount{
GroupID: r.GroupID,
Count: r.Count,
}
@@ -1048,113 +848,137 @@ func (m *MsgMongoDriver) RangeGroupSendCount(
return result[0].MsgCount, result[0].UserCount, groups, dateCount, nil
}
-func (m *MsgMongoDriver) SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*table.MsgInfoModel, error) {
- total, msgs, err := m.searchMessage(ctx, req)
- if err != nil {
- return 0, nil, err
- }
- return total, msgs, nil
-}
-
-func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*table.MsgInfoModel, error) {
- var pipe mongo.Pipeline
- condition := bson.A{}
- if req.SendTime != "" {
- // Changed to keyed fields for bson.M to avoid govet errors
- condition = append(condition, bson.M{"$eq": bson.A{bson.M{"$dateToString": bson.M{"format": "%Y-%m-%d", "date": bson.M{"$toDate": "$$item.msg.send_time"}}}, req.SendTime}})
- }
- if req.ContentType != 0 {
- condition = append(condition, bson.M{"$eq": bson.A{"$$item.msg.content_type", req.ContentType}})
- }
- if req.SessionType != 0 {
- condition = append(condition, bson.M{"$eq": bson.A{"$$item.msg.session_type", req.SessionType}})
- }
- if req.RecvID != "" {
- condition = append(condition, bson.M{"$regexFind": bson.M{"input": "$$item.msg.recv_id", "regex": req.RecvID}})
- }
- if req.SendID != "" {
- condition = append(condition, bson.M{"$regexFind": bson.M{"input": "$$item.msg.send_id", "regex": req.SendID}})
- }
-
- or := bson.A{
- bson.M{"doc_id": bson.M{"$regex": "^si_", "$options": "i"}},
- bson.M{"doc_id": bson.M{"$regex": "^g_", "$options": "i"}},
- bson.M{"doc_id": bson.M{"$regex": "^sg_", "$options": "i"}},
- }
-
- // Use bson.D with keyed fields to specify the order explicitly
- pipe = mongo.Pipeline{
- {{"$match", bson.D{{Key: "$or", Value: or}}}},
- {{"$project", bson.D{
- {Key: "msgs", Value: bson.D{
- {Key: "$filter", Value: bson.D{
- {Key: "input", Value: "$msgs"},
- {Key: "as", Value: "item"},
- {Key: "cond", Value: bson.D{{Key: "$and", Value: condition}}},
- }},
- }},
- {Key: "doc_id", Value: 1},
- }}},
- {{"$unwind", bson.M{"path": "$msgs"}}},
- {{"$sort", bson.M{"msgs.msg.send_time": -1}}},
- }
- cursor, err := m.MsgCollection.Aggregate(ctx, pipe)
- if err != nil {
- return 0, nil, err
- }
- type docModel struct {
- DocID string `bson:"doc_id"`
- Msg *table.MsgInfoModel `bson:"msgs"`
- }
- var msgsDocs []docModel
- err = cursor.All(ctx, &msgsDocs)
- if err != nil {
- return 0, nil, errs.Wrap(err, "cursor.All msgsDocs")
- }
- log.ZDebug(ctx, "query mongoDB", "result", msgsDocs)
- msgs := make([]*table.MsgInfoModel, 0)
- for _, doc := range msgsDocs {
- msgInfo := doc.Msg
- if msgInfo == nil || msgInfo.Msg == nil {
+func (m *MsgMgo) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) {
+ for _, conversationID := range conversationIDs {
+ regex := primitive.Regex{Pattern: fmt.Sprintf("^%s:", conversationID)}
+ msgDocs, err := mongoutil.Find[*relation.MsgDocModel](ctx, m.coll, bson.M{"doc_id": regex})
+ if err != nil {
+ log.ZError(ctx, "convertAll find msg doc failed", err, "conversationID", conversationID)
continue
}
- if msgInfo.Revoke != nil {
- revokeContent := sdkws.MessageRevokedContent{
- RevokerID: msgInfo.Revoke.UserID,
- RevokerRole: msgInfo.Revoke.Role,
- ClientMsgID: msgInfo.Msg.ClientMsgID,
- RevokerNickname: msgInfo.Revoke.Nickname,
- RevokeTime: msgInfo.Revoke.Time,
- SourceMessageSendTime: msgInfo.Msg.SendTime,
- SourceMessageSendID: msgInfo.Msg.SendID,
- SourceMessageSenderNickname: msgInfo.Msg.SenderNickname,
- SessionType: msgInfo.Msg.SessionType,
- Seq: msgInfo.Msg.Seq,
- Ex: msgInfo.Msg.Ex,
+ if len(msgDocs) < 1 {
+ continue
+ }
+ log.ZDebug(ctx, "msg doc convert", "conversationID", conversationID, "len(msgDocs)", len(msgDocs))
+ if len(msgDocs[0].Msg) == int(m.model.GetSingleGocMsgNum5000()) {
+ if err := mongoutil.DeleteMany(ctx, m.coll, bson.M{"doc_id": regex}); err != nil {
+ log.ZError(ctx, "convertAll delete many failed", err, "conversationID", conversationID)
+ continue
}
- data, err := json.Marshal(&revokeContent)
- if err != nil {
- return 0, nil, errs.Wrap(err, "json.Marshal revokeContent")
+ var newMsgDocs []any
+ 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 := relation.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
+ }
+ }
}
- elem := sdkws.NotificationElem{Detail: string(data)}
- content, err := json.Marshal(&elem)
- if err != nil {
- return 0, nil, errs.Wrap(err, "json.Marshal elem")
+ if err = mongoutil.InsertMany(ctx, m.coll, newMsgDocs); err != nil {
+ log.ZError(ctx, "convertAll insert many failed", err, "conversationID", conversationID, "len(newMsgDocs)", len(newMsgDocs))
+ } else {
+ log.ZDebug(ctx, "msg doc convert", "conversationID", conversationID, "len(newMsgDocs)", len(newMsgDocs))
}
- msgInfo.Msg.ContentType = constant.MsgRevokeNotification
- msgInfo.Msg.Content = string(content)
}
- msgs = append(msgs, msgInfo)
}
- start := (req.Pagination.PageNumber - 1) * req.Pagination.ShowNumber
- n := int32(len(msgs))
- if start >= n {
- return n, []*table.MsgInfoModel{}, nil
+}
+
+func (m *MsgMgo) GetBeforeMsg(ctx context.Context, ts int64, limit int) ([]*relation.MsgDocModel, error) {
+ return mongoutil.Aggregate[*relation.MsgDocModel](ctx, m.coll, []bson.M{
+ {
+ "$match": bson.M{
+ "msgs.msg.send_time": bson.M{
+ "$lt": ts,
+ },
+ },
+ },
+ {
+ "$project": bson.M{
+ "_id": 0,
+ "doc_id": 1,
+ "msgs.msg.send_time": 1,
+ "msgs.msg.seq": 1,
+ },
+ },
+ {
+ "$limit": limit,
+ },
+ })
+}
+
+func (m *MsgMgo) DeleteMsgByIndex(ctx context.Context, docID string, index []int) error {
+ if len(index) == 0 {
+ return nil
}
- if start+req.Pagination.ShowNumber < n {
- msgs = msgs[start : start+req.Pagination.ShowNumber]
- } else {
- msgs = msgs[start:]
+ model := &relation.MsgInfoModel{DelList: []string{}}
+ set := make(map[string]any)
+ for i := range index {
+ set[fmt.Sprintf("msgs.%d", i)] = model
}
- return n, msgs, nil
+ return mongoutil.UpdateOne(ctx, m.coll, bson.M{"doc_id": docID}, bson.M{"$set": set}, true)
+}
+
+//func (m *MsgMgo) ClearMsg(ctx context.Context, t time.Time) (int64, error) {
+// ts := t.UnixMilli()
+// var count int64
+// for {
+// msgs, err := m.GetBeforeMsg(ctx, ts, 100)
+// if err != nil {
+// return count, err
+// }
+// if len(msgs) == 0 {
+// return count, nil
+// }
+// for _, msg := range msgs {
+// num, err := m.deleteOneMsg(ctx, ts, msg)
+// count += num
+// if err != nil {
+// return count, err
+// }
+// }
+// }
+//}
+
+func (m *MsgMgo) DeleteDoc(ctx context.Context, docID string) error {
+ return mongoutil.DeleteOne(ctx, m.coll, bson.M{"doc_id": docID})
}
+
+//func (m *MsgMgo) DeleteDocMsg(ctx context.Context, ts int64, doc *relation.MsgDocModel) (int64, error) {
+// var notNull int
+// index := make([]int, 0, len(doc.Msg))
+// for i, message := range doc.Msg {
+// if message.Msg != nil {
+// notNull++
+// if message.Msg.SendTime < ts {
+// index = append(index, i)
+// }
+// }
+// }
+// if len(index) == 0 {
+// return 0, errs.New("no msg to delete").WrapMsg("deleteOneMsg", "docID", doc.DocID)
+// }
+// if len(index) == notNull {
+// if err := m.DeleteDoc(ctx, doc.DocID); err != nil {
+// return 0, err
+// }
+// } else {
+// if err := m.setNullMsg(ctx, doc.DocID, index); err != nil {
+// return 0, err
+// }
+// }
+// return int64(len(index)), nil
+//}
diff --git a/pkg/common/db/mgo/object.go b/pkg/common/db/mgo/object.go
index e9d639f198..1c628bb51c 100644
--- a/pkg/common/db/mgo/object.go
+++ b/pkg/common/db/mgo/object.go
@@ -17,9 +17,9 @@ package mgo
import (
"context"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mgoutil"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -54,16 +54,16 @@ func (o *S3Mongo) SetObject(ctx context.Context, obj *relation.ObjectModel) erro
"group": obj.Group,
"create_time": obj.CreateTime,
}
- return mgoutil.UpdateOne(ctx, o.coll, filter, bson.M{"$set": update}, false, options.Update().SetUpsert(true))
+ return mongoutil.UpdateOne(ctx, o.coll, filter, bson.M{"$set": update}, false, options.Update().SetUpsert(true))
}
func (o *S3Mongo) Take(ctx context.Context, engine string, name string) (*relation.ObjectModel, error) {
if engine == "" {
- return mgoutil.FindOne[*relation.ObjectModel](ctx, o.coll, bson.M{"name": name})
+ return mongoutil.FindOne[*relation.ObjectModel](ctx, o.coll, bson.M{"name": name})
}
- return mgoutil.FindOne[*relation.ObjectModel](ctx, o.coll, bson.M{"name": name, "engine": engine})
+ return mongoutil.FindOne[*relation.ObjectModel](ctx, o.coll, bson.M{"name": name, "engine": engine})
}
func (o *S3Mongo) Delete(ctx context.Context, engine string, name string) error {
- return mgoutil.DeleteOne(ctx, o.coll, bson.M{"name": name, "engine": engine})
+ return mongoutil.DeleteOne(ctx, o.coll, bson.M{"name": name, "engine": engine})
}
diff --git a/pkg/common/db/unrelation/user.go b/pkg/common/db/mgo/subscribe.go
similarity index 92%
rename from pkg/common/db/unrelation/user.go
rename to pkg/common/db/mgo/subscribe.go
index cbf395bf84..f2057dc453 100644
--- a/pkg/common/db/unrelation/user.go
+++ b/pkg/common/db/mgo/subscribe.go
@@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package unrelation
+package mgo
import (
"context"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@@ -35,9 +35,9 @@ const (
MaximumSubscription = 3000
)
-func NewUserMongoDriver(database *mongo.Database) unrelation.UserModelInterface {
+func NewUserMongoDriver(database *mongo.Database) relation.SubscribeUserModelInterface {
return &UserMongoDriver{
- userCollection: database.Collection(unrelation.SubscribeUser),
+ userCollection: database.Collection(relation.SubscribeUser),
}
}
@@ -117,7 +117,7 @@ func (u *UserMongoDriver) AddSubscriptionList(ctx context.Context, userID string
opts,
)
if err != nil {
- return errs.Wrap(err, "transaction failed")
+ return errs.WrapMsg(err, "transaction failed")
}
}
return nil
@@ -155,7 +155,7 @@ func (u *UserMongoDriver) RemoveSubscribedListFromUser(ctx context.Context, user
// GetAllSubscribeList Get all users subscribed by this user.
func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string) (userIDList []string, err error) {
- var user unrelation.UserModel
+ var user relation.SubscribeUserModel
cursor := u.userCollection.FindOne(
ctx,
bson.M{"user_id": SubscriptionPrefix + userID})
@@ -172,7 +172,7 @@ func (u *UserMongoDriver) GetAllSubscribeList(ctx context.Context, userID string
// GetSubscribedList Get the user subscribed by those users.
func (u *UserMongoDriver) GetSubscribedList(ctx context.Context, userID string) (userIDList []string, err error) {
- var user unrelation.UserModel
+ var user relation.SubscribeUserModel
cursor := u.userCollection.FindOne(
ctx,
bson.M{"user_id": SubscribedPrefix + userID})
diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go
index 9ca2eb1785..696479871e 100644
--- a/pkg/common/db/mgo/user.go
+++ b/pkg/common/db/mgo/user.go
@@ -18,11 +18,11 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mgoutil"
- "github.com/OpenIMSDK/tools/pagination"
"github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/pagination"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
@@ -48,34 +48,34 @@ type UserMgo struct {
}
func (u *UserMgo) Create(ctx context.Context, users []*relation.UserModel) error {
- return mgoutil.InsertMany(ctx, u.coll, users)
+ return mongoutil.InsertMany(ctx, u.coll, users)
}
func (u *UserMgo) UpdateByMap(ctx context.Context, userID string, args map[string]any) (err error) {
if len(args) == 0 {
return nil
}
- return mgoutil.UpdateOne(ctx, u.coll, bson.M{"user_id": userID}, bson.M{"$set": args}, true)
+ return mongoutil.UpdateOne(ctx, u.coll, bson.M{"user_id": userID}, bson.M{"$set": args}, true)
}
func (u *UserMgo) Find(ctx context.Context, userIDs []string) (users []*relation.UserModel, err error) {
- return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
+ return mongoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"user_id": bson.M{"$in": userIDs}})
}
func (u *UserMgo) Take(ctx context.Context, userID string) (user *relation.UserModel, err error) {
- return mgoutil.FindOne[*relation.UserModel](ctx, u.coll, bson.M{"user_id": userID})
+ return mongoutil.FindOne[*relation.UserModel](ctx, u.coll, bson.M{"user_id": userID})
}
func (u *UserMgo) TakeNotification(ctx context.Context, level int64) (user []*relation.UserModel, err error) {
- return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"app_manger_level": level})
+ return mongoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"app_manger_level": level})
}
func (u *UserMgo) TakeByNickname(ctx context.Context, nickname string) (user []*relation.UserModel, err error) {
- return mgoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"nickname": nickname})
+ return mongoutil.Find[*relation.UserModel](ctx, u.coll, bson.M{"nickname": nickname})
}
func (u *UserMgo) Page(ctx context.Context, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) {
- return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{}, pagination)
+ return mongoutil.FindPage[*relation.UserModel](ctx, u.coll, bson.M{}, pagination)
}
func (u *UserMgo) PageFindUser(ctx context.Context, level1 int64, level2 int64, pagination pagination.Pagination) (count int64, users []*relation.UserModel, err error) {
@@ -86,7 +86,7 @@ func (u *UserMgo) PageFindUser(ctx context.Context, level1 int64, level2 int64,
},
}
- return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
+ return mongoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
}
func (u *UserMgo) PageFindUserWithKeyword(
@@ -121,26 +121,26 @@ func (u *UserMgo) PageFindUserWithKeyword(
}
// Perform the paginated search
- return mgoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
+ return mongoutil.FindPage[*relation.UserModel](ctx, u.coll, query, pagination)
}
func (u *UserMgo) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (int64, []string, error) {
- return mgoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
+ return mongoutil.FindPage[string](ctx, u.coll, bson.M{}, pagination, options.Find().SetProjection(bson.M{"_id": 0, "user_id": 1}))
}
func (u *UserMgo) Exist(ctx context.Context, userID string) (exist bool, err error) {
- return mgoutil.Exist(ctx, u.coll, bson.M{"user_id": userID})
+ return mongoutil.Exist(ctx, u.coll, bson.M{"user_id": userID})
}
func (u *UserMgo) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) {
- return mgoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"_id": 0, "global_recv_msg_opt": 1}))
+ return mongoutil.FindOne[int](ctx, u.coll, bson.M{"user_id": userID}, options.FindOne().SetProjection(bson.M{"_id": 0, "global_recv_msg_opt": 1}))
}
func (u *UserMgo) CountTotal(ctx context.Context, before *time.Time) (count int64, err error) {
if before == nil {
- return mgoutil.Count(ctx, u.coll, bson.M{})
+ return mongoutil.Count(ctx, u.coll, bson.M{})
}
- return mgoutil.Count(ctx, u.coll, bson.M{"create_time": bson.M{"$lt": before}})
+ return mongoutil.Count(ctx, u.coll, bson.M{"create_time": bson.M{"$lt": before}})
}
func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error {
@@ -308,7 +308,7 @@ func (u *UserMgo) CountRangeEverydayTotal(ctx context.Context, start time.Time,
Date string `bson:"_id"`
Count int64 `bson:"count"`
}
- items, err := mgoutil.Aggregate[Item](ctx, u.coll, pipeline)
+ items, err := mongoutil.Aggregate[Item](ctx, u.coll, pipeline)
if err != nil {
return nil, err
}
diff --git a/pkg/common/db/s3/cont/consts.go b/pkg/common/db/s3/cont/consts.go
deleted file mode 100644
index b51878e496..0000000000
--- a/pkg/common/db/s3/cont/consts.go
+++ /dev/null
@@ -1,38 +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 cont
-
-const (
- // hashPath defines the storage path for hash data within the 'openim' directory.
- hashPath = "openim/data/hash/"
-
- // tempPath specifies the directory for temporary files in the 'openim' structure.
- tempPath = "openim/temp/"
-
- // DirectPath indicates the directory for direct uploads or access within the 'openim' structure.
- DirectPath = "openim/direct"
-
- // UploadTypeMultipart represents the identifier for multipart uploads,
- // allowing large files to be uploaded in chunks.
- UploadTypeMultipart = 1
-
- // UploadTypePresigned signifies the use of presigned URLs for uploads,
- // facilitating secure, authorized file transfers without requiring direct access to the storage credentials.
- UploadTypePresigned = 2
-
- // partSeparator is used as a delimiter in multipart upload processes,
- // separating individual file parts.
- partSeparator = ","
-)
diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go
deleted file mode 100644
index 915109aa51..0000000000
--- a/pkg/common/db/s3/cont/controller.go
+++ /dev/null
@@ -1,282 +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 cont
-
-import (
- "context"
- "crypto/md5"
- "encoding/hex"
- "errors"
- "fmt"
- "path"
- "strings"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/google/uuid"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
-)
-
-func New(cache cache.S3Cache, impl s3.Interface) *Controller {
- return &Controller{
- cache: cache,
- impl: impl,
- }
-}
-
-type Controller struct {
- cache cache.S3Cache
- impl s3.Interface
-}
-
-func (c *Controller) Engine() string {
- return c.impl.Engine()
-}
-
-func (c *Controller) HashPath(md5 string) string {
- return path.Join(hashPath, md5)
-}
-
-func (c *Controller) NowPath() string {
- now := time.Now()
- return path.Join(
- fmt.Sprintf("%04d", now.Year()),
- fmt.Sprintf("%02d", now.Month()),
- fmt.Sprintf("%02d", now.Day()),
- fmt.Sprintf("%02d", now.Hour()),
- fmt.Sprintf("%02d", now.Minute()),
- fmt.Sprintf("%02d", now.Second()),
- )
-}
-
-func (c *Controller) UUID() string {
- id := uuid.New()
- return hex.EncodeToString(id[:])
-}
-
-func (c *Controller) PartSize(ctx context.Context, size int64) (int64, error) {
- return c.impl.PartSize(ctx, size)
-}
-
-func (c *Controller) PartLimit() *s3.PartLimit {
- return c.impl.PartLimit()
-}
-
-func (c *Controller) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
- return c.cache.GetKey(ctx, c.impl.Engine(), name)
-}
-
-func (c *Controller) GetHashObject(ctx context.Context, hash string) (*s3.ObjectInfo, error) {
- return c.StatObject(ctx, c.HashPath(hash))
-}
-
-func (c *Controller) InitiateUpload(ctx context.Context, hash string, size int64, expire time.Duration, maxParts int) (*InitiateUploadResult, error) {
- defer log.ZDebug(ctx, "return")
- if size < 0 {
- return nil, errors.New("invalid size")
- }
- if hashBytes, err := hex.DecodeString(hash); err != nil {
- return nil, err
- } else if len(hashBytes) != md5.Size {
- return nil, errors.New("invalid md5")
- }
- partSize, err := c.impl.PartSize(ctx, size)
- if err != nil {
- return nil, err
- }
- partNumber := int(size / partSize)
- if size%partSize > 0 {
- partNumber++
- }
- if maxParts > 0 && partNumber > 0 && partNumber < maxParts {
- return nil, fmt.Errorf("too many parts: %d", partNumber)
- }
- if info, err := c.StatObject(ctx, c.HashPath(hash)); err == nil {
- return nil, &HashAlreadyExistsError{Object: info}
- } else if !c.impl.IsNotFound(err) {
- return nil, err
- }
- if size <= partSize {
- // Pre-signed upload
- key := path.Join(tempPath, c.NowPath(), fmt.Sprintf("%s_%d_%s.presigned", hash, size, c.UUID()))
- rawURL, err := c.impl.PresignedPutObject(ctx, key, expire)
- if err != nil {
- return nil, err
- }
- return &InitiateUploadResult{
- UploadID: newMultipartUploadID(multipartUploadID{
- Type: UploadTypePresigned,
- ID: "",
- Key: key,
- Size: size,
- Hash: hash,
- }),
- PartSize: partSize,
- Sign: &s3.AuthSignResult{
- Parts: []s3.SignPart{
- {
- PartNumber: 1,
- URL: rawURL,
- },
- },
- },
- }, nil
- } else {
- // Fragment upload
- upload, err := c.impl.InitiateMultipartUpload(ctx, c.HashPath(hash))
- if err != nil {
- return nil, err
- }
- if maxParts < 0 {
- maxParts = partNumber
- }
- var authSign *s3.AuthSignResult
- if maxParts > 0 {
- partNumbers := make([]int, maxParts)
- for i := 0; i < maxParts; i++ {
- partNumbers[i] = i + 1
- }
- authSign, err = c.impl.AuthSign(ctx, upload.UploadID, upload.Key, time.Hour*24, partNumbers)
- if err != nil {
- return nil, err
- }
- }
- return &InitiateUploadResult{
- UploadID: newMultipartUploadID(multipartUploadID{
- Type: UploadTypeMultipart,
- ID: upload.UploadID,
- Key: upload.Key,
- Size: size,
- Hash: hash,
- }),
- PartSize: partSize,
- Sign: authSign,
- }, nil
- }
-}
-
-func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHashs []string) (*UploadResult, error) {
- defer log.ZDebug(ctx, "return")
- upload, err := parseMultipartUploadID(uploadID)
- if err != nil {
- return nil, err
- }
- if md5Sum := md5.Sum([]byte(strings.Join(partHashs, partSeparator))); hex.EncodeToString(md5Sum[:]) != upload.Hash {
- return nil, errors.New("md5 mismatching")
- }
- if info, err := c.StatObject(ctx, c.HashPath(upload.Hash)); err == nil {
- return &UploadResult{
- Key: info.Key,
- Size: info.Size,
- Hash: info.ETag,
- }, nil
- } else if !c.IsNotFound(err) {
- return nil, err
- }
- cleanObject := make(map[string]struct{})
- defer func() {
- for key := range cleanObject {
- _ = c.impl.DeleteObject(ctx, key)
- }
- }()
- var targetKey string
- switch upload.Type {
- case UploadTypeMultipart:
- parts := make([]s3.Part, len(partHashs))
- for i, part := range partHashs {
- parts[i] = s3.Part{
- PartNumber: i + 1,
- ETag: part,
- }
- }
- // todo: Validation size
- result, err := c.impl.CompleteMultipartUpload(ctx, upload.ID, upload.Key, parts)
- if err != nil {
- return nil, err
- }
- targetKey = result.Key
- case UploadTypePresigned:
- uploadInfo, err := c.StatObject(ctx, upload.Key)
- if err != nil {
- return nil, err
- }
- cleanObject[uploadInfo.Key] = struct{}{}
- if uploadInfo.Size != upload.Size {
- return nil, errors.New("upload size mismatching")
- }
- md5Sum := md5.Sum([]byte(strings.Join([]string{uploadInfo.ETag}, partSeparator)))
- if md5val := hex.EncodeToString(md5Sum[:]); md5val != upload.Hash {
- return nil, errs.ErrArgs.Wrap(fmt.Sprintf("md5 mismatching %s != %s", md5val, upload.Hash))
- }
- // Prevents concurrent operations at this time that cause files to be overwritten
- copyInfo, err := c.impl.CopyObject(ctx, uploadInfo.Key, upload.Key+"."+c.UUID())
- if err != nil {
- return nil, err
- }
- cleanObject[copyInfo.Key] = struct{}{}
- if copyInfo.ETag != uploadInfo.ETag {
- return nil, errors.New("[concurrency]copy md5 mismatching")
- }
- hashCopyInfo, err := c.impl.CopyObject(ctx, copyInfo.Key, c.HashPath(upload.Hash))
- if err != nil {
- return nil, err
- }
- log.ZInfo(ctx, "hashCopyInfo", "value", fmt.Sprintf("%+v", hashCopyInfo))
- targetKey = hashCopyInfo.Key
- default:
- return nil, errors.New("invalid upload id type")
- }
- if err := c.cache.DelS3Key(c.impl.Engine(), targetKey).ExecDel(ctx); err != nil {
- return nil, err
- }
- return &UploadResult{
- Key: targetKey,
- Size: upload.Size,
- Hash: upload.Hash,
- }, nil
-}
-
-func (c *Controller) AuthSign(ctx context.Context, uploadID string, partNumbers []int) (*s3.AuthSignResult, error) {
- upload, err := parseMultipartUploadID(uploadID)
- if err != nil {
- return nil, err
- }
- switch upload.Type {
- case UploadTypeMultipart:
- return c.impl.AuthSign(ctx, upload.ID, upload.Key, time.Hour*24, partNumbers)
- case UploadTypePresigned:
- return nil, errors.New("presigned id not support auth sign")
- default:
- return nil, errors.New("invalid upload id type")
- }
-}
-
-func (c *Controller) IsNotFound(err error) bool {
- return c.impl.IsNotFound(err) || errs.ErrRecordNotFound.Is(err)
-}
-
-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)
-}
-
-func (c *Controller) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
- return c.impl.FormData(ctx, name, size, contentType, duration)
-}
diff --git a/pkg/common/db/s3/cont/error.go b/pkg/common/db/s3/cont/error.go
deleted file mode 100644
index 8225274d35..0000000000
--- a/pkg/common/db/s3/cont/error.go
+++ /dev/null
@@ -1,29 +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 cont
-
-import (
- "fmt"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
-)
-
-type HashAlreadyExistsError struct {
- Object *s3.ObjectInfo
-}
-
-func (e *HashAlreadyExistsError) Error() string {
- return fmt.Sprintf("hash already exists: %s", e.Object.Key)
-}
diff --git a/pkg/common/db/s3/cont/id.go b/pkg/common/db/s3/cont/id.go
deleted file mode 100644
index 47f37d4aa6..0000000000
--- a/pkg/common/db/s3/cont/id.go
+++ /dev/null
@@ -1,49 +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 cont
-
-import (
- "encoding/base64"
- "encoding/json"
- "fmt"
-)
-
-type multipartUploadID struct {
- Type int `json:"a,omitempty"`
- ID string `json:"b,omitempty"`
- Key string `json:"c,omitempty"`
- Size int64 `json:"d,omitempty"`
- Hash string `json:"e,omitempty"`
-}
-
-func newMultipartUploadID(id multipartUploadID) string {
- data, err := json.Marshal(id)
- if err != nil {
- panic(err)
- }
- return base64.StdEncoding.EncodeToString(data)
-}
-
-func parseMultipartUploadID(id string) (*multipartUploadID, error) {
- data, err := base64.StdEncoding.DecodeString(id)
- if err != nil {
- return nil, fmt.Errorf("invalid multipart upload id: %w", err)
- }
- var upload multipartUploadID
- if err := json.Unmarshal(data, &upload); err != nil {
- return nil, fmt.Errorf("invalid multipart upload id: %w", err)
- }
- return &upload, nil
-}
diff --git a/pkg/common/db/s3/cont/structs.go b/pkg/common/db/s3/cont/structs.go
deleted file mode 100644
index de484cc5f2..0000000000
--- a/pkg/common/db/s3/cont/structs.go
+++ /dev/null
@@ -1,34 +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 cont
-
-import "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
-
-type InitiateUploadResult struct {
- // UploadID uniquely identifies the upload session for tracking and management purposes.
- UploadID string `json:"uploadID"`
-
- // PartSize specifies the size of each part in a multipart upload. This is relevant for breaking down large uploads into manageable pieces.
- PartSize int64 `json:"partSize"`
-
- // Sign contains the authentication and signature information necessary for securely uploading each part. This could include signed URLs or tokens.
- Sign *s3.AuthSignResult `json:"sign"`
-}
-
-type UploadResult struct {
- Hash string `json:"hash"`
- Size int64 `json:"size"`
- Key string `json:"key"`
-}
diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go
deleted file mode 100644
index 9852d2a981..0000000000
--- a/pkg/common/db/s3/cos/cos.go
+++ /dev/null
@@ -1,400 +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 cos
-
-import (
- "context"
- "crypto/hmac"
- "crypto/sha1"
- "encoding/base64"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "net/http"
- "net/url"
- "strconv"
- "strings"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
- "github.com/tencentyun/cos-go-sdk-v5"
-)
-
-const (
- minPartSize int64 = 1024 * 1024 * 1 // 1MB
- maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB
- maxNumSize int64 = 1000
-)
-
-const (
- imagePng = "png"
- imageJpg = "jpg"
- imageJpeg = "jpeg"
- imageGif = "gif"
- imageWebp = "webp"
-)
-
-const successCode = http.StatusOK
-
-type Config struct {
- BucketURL string
- SecretID string
- SecretKey string
- SessionToken string
- PublicRead bool
-}
-
-func NewCos(conf Config) (s3.Interface, error) {
- u, err := url.Parse(conf.BucketURL)
- if err != nil {
- panic(err)
- }
- client := cos.NewClient(&cos.BaseURL{BucketURL: u}, &http.Client{
- Transport: &cos.AuthorizationTransport{
- SecretID: conf.SecretID,
- SecretKey: conf.SecretKey,
- SessionToken: conf.SessionToken,
- },
- })
- return &Cos{
- publicRead: conf.PublicRead,
- copyURL: u.Host + "/",
- client: client,
- credential: client.GetCredential(),
- }, nil
-}
-
-type Cos struct {
- publicRead bool
- copyURL string
- client *cos.Client
- credential *cos.Credential
-}
-
-func (c *Cos) Engine() string {
- return "tencent-cos"
-}
-
-func (c *Cos) PartLimit() *s3.PartLimit {
- return &s3.PartLimit{
- MinPartSize: minPartSize,
- MaxPartSize: maxPartSize,
- MaxNumSize: maxNumSize,
- }
-}
-
-func (c *Cos) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) {
- result, _, err := c.client.Object.InitiateMultipartUpload(ctx, name, nil)
- if err != nil {
- return nil, err
- }
- return &s3.InitiateMultipartUploadResult{
- UploadID: result.UploadID,
- Bucket: result.Bucket,
- Key: result.Key,
- }, nil
-}
-
-func (c *Cos) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) {
- opts := &cos.CompleteMultipartUploadOptions{
- Parts: make([]cos.Object, len(parts)),
- }
- for i, part := range parts {
- opts.Parts[i] = cos.Object{
- PartNumber: part.PartNumber,
- ETag: strings.ReplaceAll(part.ETag, `"`, ``),
- }
- }
- result, _, err := c.client.Object.CompleteMultipartUpload(ctx, name, uploadID, opts)
- if err != nil {
- return nil, err
- }
- return &s3.CompleteMultipartUploadResult{
- Location: result.Location,
- Bucket: result.Bucket,
- Key: result.Key,
- ETag: result.ETag,
- }, nil
-}
-
-func (c *Cos) PartSize(ctx context.Context, size int64) (int64, error) {
- if size <= 0 {
- return 0, errors.New("size must be greater than 0")
- }
- if size > maxPartSize*maxNumSize {
- return 0, fmt.Errorf("COS size must be less than the maximum allowed limit")
- }
- if size <= minPartSize*maxNumSize {
- return minPartSize, nil
- }
- partSize := size / maxNumSize
- if size%maxNumSize != 0 {
- partSize++
- }
- return partSize, nil
-}
-
-func (c *Cos) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) {
- result := s3.AuthSignResult{
- URL: c.client.BaseURL.BucketURL.String() + "/" + cos.EncodeURIComponent(name),
- Query: url.Values{"uploadId": {uploadID}},
- Header: make(http.Header),
- Parts: make([]s3.SignPart, len(partNumbers)),
- }
- req, err := http.NewRequestWithContext(ctx, http.MethodPut, result.URL, nil)
- if err != nil {
- return nil, err
- }
- cos.AddAuthorizationHeader(c.credential.SecretID, c.credential.SecretKey, c.credential.SessionToken, req, cos.NewAuthTime(expire))
- result.Header = req.Header
- for i, partNumber := range partNumbers {
- result.Parts[i] = s3.SignPart{
- PartNumber: partNumber,
- Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}},
- }
- }
- return &result, nil
-}
-
-func (c *Cos) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) {
- rawURL, err := c.client.Object.GetPresignedURL(ctx, http.MethodPut, name, c.credential.SecretID, c.credential.SecretKey, expire, nil)
- if err != nil {
- return "", err
- }
- return rawURL.String(), nil
-}
-
-func (c *Cos) DeleteObject(ctx context.Context, name string) error {
- _, err := c.client.Object.Delete(ctx, name)
- return err
-}
-
-func (c *Cos) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
- if name != "" && name[0] == '/' {
- name = name[1:]
- }
- info, err := c.client.Object.Head(ctx, name, nil)
- if err != nil {
- return nil, err
- }
- res := &s3.ObjectInfo{Key: name}
- if res.ETag = strings.ToLower(strings.ReplaceAll(info.Header.Get("ETag"), `"`, "")); res.ETag == "" {
- return nil, errors.New("StatObject etag not found")
- }
- if contentLengthStr := info.Header.Get("Content-Length"); contentLengthStr == "" {
- return nil, errors.New("StatObject content-length not found")
- } else {
- res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64)
- if err != nil {
- return nil, fmt.Errorf("StatObject content-length parse error: %w", err)
- }
- if res.Size < 0 {
- return nil, errors.New("StatObject content-length must be greater than 0")
- }
- }
- if lastModified := info.Header.Get("Last-Modified"); lastModified == "" {
- return nil, errors.New("StatObject last-modified not found")
- } else {
- res.LastModified, err = time.Parse(http.TimeFormat, lastModified)
- if err != nil {
- return nil, fmt.Errorf("StatObject last-modified parse error: %w", err)
- }
- }
- return res, nil
-}
-
-func (c *Cos) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) {
- sourceURL := c.copyURL + src
- result, _, err := c.client.Object.Copy(ctx, dst, sourceURL, nil)
- if err != nil {
- return nil, err
- }
- return &s3.CopyObjectInfo{
- Key: dst,
- ETag: strings.ReplaceAll(result.ETag, `"`, ``),
- }, nil
-}
-
-func (c *Cos) IsNotFound(err error) bool {
- switch e := errs.Unwrap(err).(type) {
- case *cos.ErrorResponse:
- return e.Response.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
- default:
- return false
- }
-}
-
-func (c *Cos) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error {
- _, err := c.client.Object.AbortMultipartUpload(ctx, name, uploadID)
- return err
-}
-
-func (c *Cos) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) {
- result, _, err := c.client.Object.ListParts(ctx, name, uploadID, &cos.ObjectListPartsOptions{
- MaxParts: strconv.Itoa(maxParts),
- PartNumberMarker: strconv.Itoa(partNumberMarker),
- })
- if err != nil {
- return nil, err
- }
- res := &s3.ListUploadedPartsResult{
- Key: result.Key,
- UploadID: result.UploadID,
- UploadedParts: make([]s3.UploadedPart, len(result.Parts)),
- }
- res.MaxParts, _ = strconv.Atoi(result.MaxParts)
- res.NextPartNumberMarker, _ = strconv.Atoi(result.NextPartNumberMarker)
- for i, part := range result.Parts {
- lastModified, _ := time.Parse(http.TimeFormat, part.LastModified)
- res.UploadedParts[i] = s3.UploadedPart{
- PartNumber: part.PartNumber,
- LastModified: lastModified,
- ETag: part.ETag,
- Size: part.Size,
- }
- }
- return res, nil
-}
-
-func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
- var imageMogr string
- var option cos.PresignedURLOptions
- if opt != nil {
- query := make(url.Values)
- if opt.Image != nil {
- // https://cloud.tencent.com/document/product/436/44880
- style := make([]string, 0, 2)
- wh := make([]string, 2)
- if opt.Image.Width > 0 {
- wh[0] = strconv.Itoa(opt.Image.Width)
- }
- if opt.Image.Height > 0 {
- wh[1] = strconv.Itoa(opt.Image.Height)
- }
- if opt.Image.Width > 0 || opt.Image.Height > 0 {
- style = append(style, strings.Join(wh, "x"))
- }
- switch opt.Image.Format {
- case
- imagePng,
- imageJpg,
- imageJpeg,
- imageGif,
- imageWebp:
- style = append(style, "format/"+opt.Image.Format)
- }
- if len(style) > 0 {
- imageMogr = "imageMogr2/thumbnail/" + strings.Join(style, "/") + "/ignore-error/1"
- }
- }
- if opt.ContentType != "" {
- query.Set("response-content-type", opt.ContentType)
- }
- if opt.Filename != "" {
- query.Set("response-content-disposition", `attachment; filename=`+strconv.Quote(opt.Filename))
- }
- if len(query) > 0 {
- option.Query = &query
- }
- }
- if expire <= 0 {
- expire = time.Hour * 24 * 365 * 99 // 99 years
- } else if expire < time.Second {
- expire = time.Second
- }
- rawURL, err := c.getPresignedURL(ctx, name, expire, &option)
- if err != nil {
- return "", err
- }
- if imageMogr != "" {
- if rawURL.RawQuery == "" {
- rawURL.RawQuery = imageMogr
- } else {
- rawURL.RawQuery = rawURL.RawQuery + "&" + imageMogr
- }
- }
- return rawURL.String(), nil
-}
-
-func (c *Cos) getPresignedURL(ctx context.Context, name string, expire time.Duration, opt *cos.PresignedURLOptions) (*url.URL, error) {
- if !c.publicRead {
- return c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, c.credential.SecretID, c.credential.SecretKey, expire, opt)
- }
- return c.client.Object.GetObjectURL(name), nil
-}
-
-func (c *Cos) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
- // https://cloud.tencent.com/document/product/436/14690
- now := time.Now()
- expiration := now.Add(duration)
- keyTime := fmt.Sprintf("%d;%d", now.Unix(), expiration.Unix())
- conditions := []any{
- map[string]string{"q-sign-algorithm": "sha1"},
- map[string]string{"q-ak": c.credential.SecretID},
- map[string]string{"q-sign-time": keyTime},
- map[string]string{"key": name},
- }
- if contentType != "" {
- conditions = append(conditions, map[string]string{"Content-Type": contentType})
- }
- policy := map[string]any{
- "expiration": expiration.Format("2006-01-02T15:04:05.000Z"),
- "conditions": conditions,
- }
- policyJson, err := json.Marshal(policy)
- if err != nil {
- return nil, err
- }
- signKey := hmacSha1val(c.credential.SecretKey, keyTime)
- strToSign := sha1val(string(policyJson))
- signature := hmacSha1val(signKey, strToSign)
-
- fd := &s3.FormData{
- URL: c.client.BaseURL.BucketURL.String(),
- File: "file",
- Expires: expiration,
- FormData: map[string]string{
- "policy": base64.StdEncoding.EncodeToString(policyJson),
- "q-sign-algorithm": "sha1",
- "q-ak": c.credential.SecretID,
- "q-key-time": keyTime,
- "q-signature": signature,
- "key": name,
- "success_action_status": strconv.Itoa(successCode),
- },
- SuccessCodes: []int{successCode},
- }
- if contentType != "" {
- fd.FormData["Content-Type"] = contentType
- }
- if c.credential.SessionToken != "" {
- fd.FormData["x-cos-security-token"] = c.credential.SessionToken
- }
- return fd, nil
-}
-
-func hmacSha1val(key, msg string) string {
- v := hmac.New(sha1.New, []byte(key))
- v.Write([]byte(msg))
- return hex.EncodeToString(v.Sum(nil))
-}
-
-func sha1val(msg string) string {
- sha1Hash := sha1.New()
- sha1Hash.Write([]byte(msg))
- return hex.EncodeToString(sha1Hash.Sum(nil))
-}
diff --git a/pkg/common/db/s3/cos/internal.go b/pkg/common/db/s3/cos/internal.go
deleted file mode 100644
index 0645469536..0000000000
--- a/pkg/common/db/s3/cos/internal.go
+++ /dev/null
@@ -1,27 +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 cos
-
-import (
- "context"
- "net/http"
- "net/url"
- _ "unsafe"
-
- "github.com/tencentyun/cos-go-sdk-v5"
-)
-
-//go:linkname newRequest github.com/tencentyun/cos-go-sdk-v5.(*Client).newRequest
-func newRequest(c *cos.Client, ctx context.Context, baseURL *url.URL, uri, method string, body any, optQuery any, optHeader any) (req *http.Request, err error)
diff --git a/pkg/common/db/s3/minio/image.go b/pkg/common/db/s3/minio/image.go
deleted file mode 100644
index 3223993f43..0000000000
--- a/pkg/common/db/s3/minio/image.go
+++ /dev/null
@@ -1,120 +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 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
-
- // Calculating scaling
- scaleWidth := float64(maxWidth) / float64(imgWidth)
- scaleHeight := float64(maxHeight) / float64(imgHeight)
-
- // If both are 0, then no scaling is done and the original image is returned
- if maxWidth == 0 && maxHeight == 0 {
- return img
- }
-
- // If both width and height are greater than 0, select a smaller zoom ratio to maintain the aspect ratio
- if maxWidth > 0 && maxHeight > 0 {
- scale := scaleWidth
- if scaleHeight < scaleWidth {
- scale = scaleHeight
- }
-
- // Calculate Thumbnail Size
- thumbnailWidth := int(float64(imgWidth) * scale)
- thumbnailHeight := int(float64(imgHeight) * scale)
-
- // Thumbnails are generated using the Resample method of the "image" library.
- 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 only width or height is specified, thumbnails are generated based on the maximum not to exceed rule
- if maxWidth > 0 {
- thumbnailWidth := maxWidth
- thumbnailHeight := int(float64(imgHeight) * scaleWidth)
-
- // Thumbnails are generated using the Resample method of the "image" library.
- 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
-
- // Thumbnails are generated using the Resample method of the "image" library.
- 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
- }
-
- // By default, the original image is returned
- return img
-}
diff --git a/pkg/common/db/s3/minio/internal.go b/pkg/common/db/s3/minio/internal.go
deleted file mode 100644
index 7e9dcd9e49..0000000000
--- a/pkg/common/db/s3/minio/internal.go
+++ /dev/null
@@ -1,25 +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 minio
-
-import (
- "net/url"
- _ "unsafe"
-
- "github.com/minio/minio-go/v7"
-)
-
-//go:linkname makeTargetURL github.com/minio/minio-go/v7.(*Client).makeTargetURL
-func makeTargetURL(client *minio.Client, bucketName, objectName, bucketLocation string, isVirtualHostStyle bool, queryValues url.Values) (*url.URL, error)
diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go
deleted file mode 100644
index 10526998fb..0000000000
--- a/pkg/common/db/s3/minio/minio.go
+++ /dev/null
@@ -1,499 +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 minio
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "path"
- "reflect"
- "strconv"
- "strings"
- "sync"
- "time"
- "unsafe"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/minio/minio-go/v7"
- "github.com/minio/minio-go/v7/pkg/credentials"
- "github.com/minio/minio-go/v7/pkg/signer"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
-)
-
-const (
- unsignedPayload = "UNSIGNED-PAYLOAD"
-)
-
-const (
- minPartSize int64 = 1024 * 1024 * 5 // 5MB
- maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB
- maxNumSize int64 = 10000
-)
-
-const (
- maxImageWidth = 1024
- maxImageHeight = 1024
- maxImageSize = 1024 * 1024 * 50
- imageThumbnailPath = "openim/thumbnail"
-)
-
-const successCode = http.StatusOK
-
-type Config struct {
- Bucket string
- Endpoint string
- AccessKeyID string
- SecretAccessKey string
- SessionToken string
- SignEndpoint string
- PublicRead bool
-}
-
-func NewMinio(cache cache.MinioCache, conf Config) (s3.Interface, error) {
- u, err := url.Parse(conf.Endpoint)
- if err != nil {
- return nil, err
- }
- opts := &minio.Options{
- Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, conf.SessionToken),
- Secure: u.Scheme == "https",
- }
- client, err := minio.New(u.Host, opts)
- if err != nil {
- return nil, err
- }
- m := &Minio{
- conf: conf,
- bucket: conf.Bucket,
- core: &minio.Core{Client: client},
- lock: &sync.Mutex{},
- init: false,
- cache: cache,
- }
- if conf.SignEndpoint == "" || conf.SignEndpoint == conf.Endpoint {
- m.opts = opts
- m.sign = m.core.Client
- m.prefix = u.Path
- u.Path = ""
- conf.Endpoint = u.String()
- m.signEndpoint = conf.Endpoint
- } 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
- }
- m.prefix = su.Path
- su.Path = ""
- conf.SignEndpoint = su.String()
- m.signEndpoint = conf.SignEndpoint
- }
- ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
- defer cancel()
- if err := m.initMinio(ctx); err != nil {
- fmt.Println("init minio error:", err)
- }
- return m, nil
-}
-
-type Minio struct {
- conf Config
- bucket string
- signEndpoint string
- location string
- opts *minio.Options
- core *minio.Core
- sign *minio.Client
- lock sync.Locker
- init bool
- prefix string
- cache cache.MinioCache
-}
-
-func (m *Minio) initMinio(ctx context.Context) error {
- if m.init {
- return nil
- }
- m.lock.Lock()
- defer m.lock.Unlock()
- if m.init {
- return nil
- }
- exists, err := m.core.Client.BucketExists(ctx, m.conf.Bucket)
- if err != nil {
- return fmt.Errorf("check bucket exists error: %w", err)
- }
- if !exists {
- if err = m.core.Client.MakeBucket(ctx, m.conf.Bucket, minio.MakeBucketOptions{}); err != nil {
- return fmt.Errorf("make bucket error: %w", err)
- }
- }
- if m.conf.PublicRead {
- policy := fmt.Sprintf(
- `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject","s3:PutObject"],"Effect": "Allow","Principal": {"AWS": ["*"]},"Resource": ["arn:aws:s3:::%s/*"],"Sid": ""}]}`,
- m.conf.Bucket,
- )
- if err = m.core.Client.SetBucketPolicy(ctx, m.conf.Bucket, policy); err != nil {
- return err
- }
- }
- m.location, err = m.core.Client.GetBucketLocation(ctx, m.conf.Bucket)
- if err != nil {
- return err
- }
- func() {
- if m.conf.SignEndpoint == "" || m.conf.SignEndpoint == m.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(m.conf.Bucket, m.location)
- }()
- m.init = true
- return nil
-}
-
-func (m *Minio) Engine() string {
- return "minio"
-}
-
-func (m *Minio) PartLimit() *s3.PartLimit {
- return &s3.PartLimit{
- MinPartSize: minPartSize,
- MaxPartSize: maxPartSize,
- MaxNumSize: maxNumSize,
- }
-}
-
-func (m *Minio) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- uploadID, err := m.core.NewMultipartUpload(ctx, m.bucket, name, minio.PutObjectOptions{})
- if err != nil {
- return nil, err
- }
- return &s3.InitiateMultipartUploadResult{
- Bucket: m.bucket,
- Key: name,
- UploadID: uploadID,
- }, nil
-}
-
-func (m *Minio) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- minioParts := make([]minio.CompletePart, len(parts))
- for i, part := range parts {
- minioParts[i] = minio.CompletePart{
- PartNumber: part.PartNumber,
- ETag: strings.ToLower(part.ETag),
- }
- }
- upload, err := m.core.CompleteMultipartUpload(ctx, m.bucket, name, uploadID, minioParts, minio.PutObjectOptions{})
- if err != nil {
- return nil, err
- }
- m.delObjectImageInfoKey(ctx, name, upload.Size)
- return &s3.CompleteMultipartUploadResult{
- Location: upload.Location,
- Bucket: upload.Bucket,
- Key: upload.Key,
- ETag: strings.ToLower(upload.ETag),
- }, nil
-}
-
-func (m *Minio) PartSize(ctx context.Context, size int64) (int64, error) {
- if size <= 0 {
- return 0, errors.New("size must be greater than 0")
- }
- if size > maxPartSize*maxNumSize {
- return 0, fmt.Errorf("MINIO size must be less than the maximum allowed limit")
- }
- if size <= minPartSize*maxNumSize {
- return minPartSize, nil
- }
- partSize := size / maxNumSize
- if size%maxNumSize != 0 {
- partSize++
- }
- return partSize, nil
-}
-
-func (m *Minio) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- creds, err := m.opts.Creds.Get()
- if err != nil {
- return nil, err
- }
- result := s3.AuthSignResult{
- URL: m.signEndpoint + "/" + m.bucket + "/" + name,
- Query: url.Values{"uploadId": {uploadID}},
- Parts: make([]s3.SignPart, len(partNumbers)),
- }
- for i, partNumber := range partNumbers {
- rawURL := result.URL + "?partNumber=" + strconv.Itoa(partNumber) + "&uploadId=" + uploadID
- request, err := http.NewRequestWithContext(ctx, http.MethodPut, rawURL, nil)
- if err != nil {
- return nil, err
- }
- request.Header.Set("X-Amz-Content-Sha256", unsignedPayload)
- request = signer.SignV4Trailer(*request, creds.AccessKeyID, creds.SecretAccessKey, creds.SessionToken, m.location, nil)
- result.Parts[i] = s3.SignPart{
- PartNumber: partNumber,
- Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}},
- Header: request.Header,
- }
- }
- if m.prefix != "" {
- result.URL = m.signEndpoint + m.prefix + "/" + m.bucket + "/" + name
- }
- return &result, nil
-}
-
-func (m *Minio) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) {
- if err := m.initMinio(ctx); err != nil {
- return "", err
- }
- rawURL, err := m.sign.PresignedPutObject(ctx, m.bucket, name, expire)
- if err != nil {
- return "", err
- }
- if m.prefix != "" {
- rawURL.Path = path.Join(m.prefix, rawURL.Path)
- }
- return rawURL.String(), nil
-}
-
-func (m *Minio) DeleteObject(ctx context.Context, name string) error {
- if err := m.initMinio(ctx); err != nil {
- return err
- }
- return m.core.Client.RemoveObject(ctx, m.bucket, name, minio.RemoveObjectOptions{})
-}
-
-func (m *Minio) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- info, err := m.core.Client.StatObject(ctx, m.bucket, name, minio.StatObjectOptions{})
- if err != nil {
- return nil, err
- }
- return &s3.ObjectInfo{
- ETag: strings.ToLower(info.ETag),
- Key: info.Key,
- Size: info.Size,
- LastModified: info.LastModified,
- }, nil
-}
-
-func (m *Minio) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- result, err := m.core.Client.CopyObject(ctx, minio.CopyDestOptions{
- Bucket: m.bucket,
- Object: dst,
- }, minio.CopySrcOptions{
- Bucket: m.bucket,
- Object: src,
- })
- if err != nil {
- return nil, err
- }
- return &s3.CopyObjectInfo{
- Key: dst,
- ETag: strings.ToLower(result.ETag),
- }, nil
-}
-
-func (m *Minio) IsNotFound(err error) bool {
- switch e := errs.Unwrap(err).(type) {
- case minio.ErrorResponse:
- return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
- case *minio.ErrorResponse:
- return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
- default:
- return false
- }
-}
-
-func (m *Minio) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error {
- if err := m.initMinio(ctx); err != nil {
- return err
- }
- return m.core.AbortMultipartUpload(ctx, m.bucket, name, uploadID)
-}
-
-func (m *Minio) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- result, err := m.core.ListObjectParts(ctx, m.bucket, name, uploadID, partNumberMarker, maxParts)
- if err != nil {
- return nil, err
- }
- res := &s3.ListUploadedPartsResult{
- Key: result.Key,
- UploadID: result.UploadID,
- MaxParts: result.MaxParts,
- NextPartNumberMarker: result.NextPartNumberMarker,
- UploadedParts: make([]s3.UploadedPart, len(result.ObjectParts)),
- }
- for i, part := range result.ObjectParts {
- res.UploadedParts[i] = s3.UploadedPart{
- PartNumber: part.PartNumber,
- LastModified: part.LastModified,
- ETag: part.ETag,
- Size: part.Size,
- }
- }
- 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
- }
- var (
- rawURL *url.URL
- err error
- )
- if m.conf.PublicRead {
- rawURL, err = makeTargetURL(m.sign, m.bucket, name, m.location, false, query)
- } else {
- rawURL, err = m.sign.PresignedGetObject(ctx, m.bucket, name, expire, query)
- }
- if err != nil {
- return "", err
- }
- if m.prefix != "" {
- rawURL.Path = path.Join(m.prefix, rawURL.Path)
- }
- return rawURL.String(), nil
-}
-
-func (m *Minio) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
- if err := m.initMinio(ctx); err != nil {
- return "", err
- }
- reqParams := make(url.Values)
- if opt != nil {
- if opt.ContentType != "" {
- reqParams.Set("response-content-type", opt.ContentType)
- }
- if opt.Filename != "" {
- reqParams.Set("response-content-disposition", `attachment; filename=`+strconv.Quote(opt.Filename))
- }
- }
- if opt.Image == nil || (opt.Image.Width < 0 && opt.Image.Height < 0 && opt.Image.Format == "") || (opt.Image.Width > maxImageWidth || opt.Image.Height > maxImageHeight) {
- return m.PresignedGetObject(ctx, name, expire, reqParams)
- }
- return m.getImageThumbnailURL(ctx, name, expire, opt.Image)
-}
-
-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, limit))
-}
-
-func (m *Minio) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
- if err := m.initMinio(ctx); err != nil {
- return nil, err
- }
- policy := minio.NewPostPolicy()
- if err := policy.SetKey(name); err != nil {
- return nil, err
- }
- expires := time.Now().Add(duration)
- if err := policy.SetExpires(expires); err != nil {
- return nil, err
- }
- if size > 0 {
- if err := policy.SetContentLengthRange(0, size); err != nil {
- return nil, err
- }
- }
- if err := policy.SetSuccessStatusAction(strconv.Itoa(successCode)); err != nil {
- return nil, err
- }
- if contentType != "" {
- if err := policy.SetContentType(contentType); err != nil {
- return nil, err
- }
- }
- if err := policy.SetBucket(m.bucket); err != nil {
- return nil, err
- }
- u, fd, err := m.core.PresignedPostPolicy(ctx, policy)
- if err != nil {
- return nil, err
- }
- sign, err := url.Parse(m.signEndpoint)
- if err != nil {
- return nil, err
- }
- u.Scheme = sign.Scheme
- u.Host = sign.Host
- return &s3.FormData{
- URL: u.String(),
- File: "file",
- Header: nil,
- FormData: fd,
- Expires: expires,
- SuccessCodes: []int{successCode},
- }, nil
-}
diff --git a/pkg/common/db/s3/minio/thumbnail.go b/pkg/common/db/s3/minio/thumbnail.go
deleted file mode 100644
index 1bf96c27c9..0000000000
--- a/pkg/common/db/s3/minio/thumbnail.go
+++ /dev/null
@@ -1,150 +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 minio
-
-import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "image"
- "image/gif"
- "image/jpeg"
- "image/png"
- "net/url"
- "path/filepath"
- "strings"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/minio/minio-go/v7"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
-)
-
-func (m *Minio) getImageThumbnailURL(ctx context.Context, name string, expire time.Duration, opt *s3.Image) (string, error) {
- var img image.Image
- info, err := m.cache.GetImageObjectKeyInfo(ctx, name, func(ctx context.Context) (info *cache.MinioImageInfo, err error) {
- info, img, err = m.getObjectImageInfo(ctx, name)
- return
- })
- if err != nil {
- return "", err
- }
- if !info.IsImg {
- return "", errs.ErrData.Wrap("object not image")
- }
- if opt.Width > info.Width || opt.Width <= 0 {
- opt.Width = info.Width
- }
- if opt.Height > info.Height || opt.Height <= 0 {
- opt.Height = info.Height
- }
- opt.Format = strings.ToLower(opt.Format)
- if opt.Format == formatJpg {
- opt.Format = formatJpeg
- }
- switch opt.Format {
- case formatPng, formatJpeg, formatGif:
- default:
- opt.Format = ""
- }
- reqParams := make(url.Values)
- if opt.Width == info.Width && opt.Height == info.Height && (opt.Format == info.Format || opt.Format == "") {
- reqParams.Set("response-content-type", "image/"+info.Format)
- return m.PresignedGetObject(ctx, name, expire, reqParams)
- }
- if opt.Format == "" {
- switch opt.Format {
- case formatGif:
- opt.Format = formatGif
- case formatJpeg:
- opt.Format = formatJpeg
- case formatPng:
- opt.Format = formatPng
- default:
- opt.Format = formatPng
- }
- }
- key, err := m.cache.GetThumbnailKey(ctx, name, opt.Format, opt.Width, opt.Height, func(ctx context.Context) (string, error) {
- if img == nil {
- var reader *minio.Object
- 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.Width, opt.Height)
- buf := bytes.NewBuffer(nil)
- switch opt.Format {
- case formatPng:
- err = png.Encode(buf, thumbnail)
- case formatJpeg:
- err = jpeg.Encode(buf, thumbnail, nil)
- case formatGif:
- err = gif.Encode(buf, thumbnail, nil)
- }
- cacheKey := filepath.Join(imageThumbnailPath, info.Etag, fmt.Sprintf("image_w%d_h%d.%s", opt.Width, opt.Height, opt.Format))
- if _, err = m.core.Client.PutObject(ctx, m.bucket, cacheKey, buf, int64(buf.Len()), minio.PutObjectOptions{}); err != nil {
- return "", err
- }
- return cacheKey, nil
- })
- if err != nil {
- return "", err
- }
- reqParams.Set("response-content-type", "image/"+opt.Format)
- return m.PresignedGetObject(ctx, key, expire, reqParams)
-}
-
-func (m *Minio) getObjectImageInfo(ctx context.Context, name string) (*cache.MinioImageInfo, image.Image, error) {
- fileInfo, err := m.StatObject(ctx, name)
- if err != nil {
- return nil, nil, err
- }
- if fileInfo.Size > maxImageSize {
- return nil, nil, errors.New("file size too large")
- }
- imageData, err := m.getObjectData(ctx, name, fileInfo.Size)
- if err != nil {
- return nil, nil, err
- }
- var info cache.MinioImageInfo
- imageInfo, format, err := ImageStat(bytes.NewReader(imageData))
- if err == nil {
- info.IsImg = true
- info.Format = format
- info.Width, info.Height = ImageWidthHeight(imageInfo)
- } else {
- info.IsImg = false
- }
- info.Etag = fileInfo.ETag
- return &info, imageInfo, nil
-}
-
-func (m *Minio) delObjectImageInfoKey(ctx context.Context, key string, size int64) {
- if size > 0 && size > maxImageSize {
- return
- }
- if err := m.cache.DelObjectImageInfoKey(key).ExecDel(ctx); err != nil {
- log.ZError(ctx, "DelObjectImageInfoKey failed", err, "key", key)
- }
-}
diff --git a/pkg/common/db/s3/oss/internal.go b/pkg/common/db/s3/oss/internal.go
deleted file mode 100644
index 155708ffd7..0000000000
--- a/pkg/common/db/s3/oss/internal.go
+++ /dev/null
@@ -1,39 +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 (
- "net/http"
- "net/url"
- _ "unsafe"
-
- "github.com/aliyun/aliyun-oss-go-sdk/oss"
-)
-
-//go:linkname signHeader github.com/aliyun/aliyun-oss-go-sdk/oss.Conn.signHeader
-func signHeader(c oss.Conn, req *http.Request, canonicalizedResource string)
-
-//go:linkname getURLParams github.com/aliyun/aliyun-oss-go-sdk/oss.Conn.getURLParams
-func getURLParams(c oss.Conn, params map[string]any) string
-
-//go:linkname getURL github.com/aliyun/aliyun-oss-go-sdk/oss.urlMaker.getURL
-func getURL(um urlMaker, bucket, object, params string) *url.URL
-
-type urlMaker struct {
- Scheme string
- NetLoc string
- Type int
- IsProxy bool
-}
diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go
deleted file mode 100644
index e485db2774..0000000000
--- a/pkg/common/db/s3/oss/oss.go
+++ /dev/null
@@ -1,382 +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 (
- "context"
- "crypto/hmac"
- "crypto/sha1"
- "encoding/base64"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "net/http"
- "net/url"
- "reflect"
- "strconv"
- "strings"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/aliyun/aliyun-oss-go-sdk/oss"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3"
-)
-
-const (
- minPartSize int64 = 1024 * 1024 * 1 // 1MB
- maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB
- maxNumSize int64 = 10000
-)
-
-const (
- imagePng = "png"
- imageJpg = "jpg"
- imageJpeg = "jpeg"
- imageGif = "gif"
- imageWebp = "webp"
-)
-
-const successCode = http.StatusOK
-
-type Config struct {
- Endpoint string
- Bucket string
- BucketURL string
- AccessKeyID string
- AccessKeySecret string
- SessionToken string
- PublicRead bool
-}
-
-func NewOSS(conf Config) (s3.Interface, error) {
- if conf.BucketURL == "" {
- return nil, errs.Wrap(errors.New("bucket url is empty"))
- }
- client, err := oss.New(conf.Endpoint, conf.AccessKeyID, conf.AccessKeySecret)
- if err != nil {
- return nil, err
- }
- bucket, err := client.Bucket(conf.Bucket)
- if err != nil {
- return nil, errs.Wrap(err, "ali-oss bucket error")
- }
- if conf.BucketURL[len(conf.BucketURL)-1] != '/' {
- conf.BucketURL += "/"
- }
- return &OSS{
- bucketURL: conf.BucketURL,
- bucket: bucket,
- credentials: client.Config.GetCredentials(),
- um: *(*urlMaker)(reflect.ValueOf(bucket.Client.Conn).Elem().FieldByName("url").UnsafePointer()),
- publicRead: conf.PublicRead,
- }, nil
-}
-
-type OSS struct {
- bucketURL string
- bucket *oss.Bucket
- credentials oss.Credentials
- um urlMaker
- publicRead bool
-}
-
-func (o *OSS) Engine() string {
- return "ali-oss"
-}
-
-func (o *OSS) PartLimit() *s3.PartLimit {
- return &s3.PartLimit{
- MinPartSize: minPartSize,
- MaxPartSize: maxPartSize,
- MaxNumSize: maxNumSize,
- }
-}
-
-func (o *OSS) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) {
- result, err := o.bucket.InitiateMultipartUpload(name)
- if err != nil {
- return nil, err
- }
- return &s3.InitiateMultipartUploadResult{
- UploadID: result.UploadID,
- Bucket: result.Bucket,
- Key: result.Key,
- }, nil
-}
-
-func (o *OSS) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) {
- ossParts := make([]oss.UploadPart, len(parts))
- for i, part := range parts {
- ossParts[i] = oss.UploadPart{
- PartNumber: part.PartNumber,
- ETag: strings.ToUpper(part.ETag),
- }
- }
- result, err := o.bucket.CompleteMultipartUpload(oss.InitiateMultipartUploadResult{
- UploadID: uploadID,
- Bucket: o.bucket.BucketName,
- Key: name,
- }, ossParts)
- if err != nil {
- return nil, err
- }
- return &s3.CompleteMultipartUploadResult{
- Location: result.Location,
- Bucket: result.Bucket,
- Key: result.Key,
- ETag: strings.ToLower(strings.ReplaceAll(result.ETag, `"`, ``)),
- }, nil
-}
-
-func (o *OSS) PartSize(ctx context.Context, size int64) (int64, error) {
- if size <= 0 {
- return 0, errs.Wrap(errors.New("size must be greater than 0"))
- }
- if size > maxPartSize*maxNumSize {
- return 0, errs.Wrap(errors.New("size must be less than the maximum allowed limit"))
- }
- if size <= minPartSize*maxNumSize {
- return minPartSize, nil
- }
- partSize := size / maxNumSize
- if size%maxNumSize != 0 {
- partSize++
- }
- return partSize, nil
-}
-
-func (o *OSS) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) {
- result := s3.AuthSignResult{
- URL: o.bucketURL + name,
- Query: url.Values{"uploadId": {uploadID}},
- Header: make(http.Header),
- Parts: make([]s3.SignPart, len(partNumbers)),
- }
- for i, partNumber := range partNumbers {
- rawURL := fmt.Sprintf(`%s%s?partNumber=%d&uploadId=%s`, o.bucketURL, name, partNumber, uploadID)
- request, err := http.NewRequest(http.MethodPut, rawURL, nil)
- if err != nil {
- return nil, err
- }
- if o.credentials.GetSecurityToken() != "" {
- request.Header.Set(oss.HTTPHeaderOssSecurityToken, o.credentials.GetSecurityToken())
- }
- now := time.Now().UTC().Format(http.TimeFormat)
- request.Header.Set(oss.HTTPHeaderHost, request.Host)
- request.Header.Set(oss.HTTPHeaderDate, now)
- request.Header.Set(oss.HttpHeaderOssDate, now)
- signHeader(*o.bucket.Client.Conn, request, fmt.Sprintf(`/%s/%s?partNumber=%d&uploadId=%s`, o.bucket.BucketName, name, partNumber, uploadID))
- delete(request.Header, oss.HTTPHeaderDate)
- result.Parts[i] = s3.SignPart{
- PartNumber: partNumber,
- Query: url.Values{"partNumber": {strconv.Itoa(partNumber)}},
- URL: request.URL.String(),
- Header: request.Header,
- }
- }
- return &result, nil
-}
-
-func (o *OSS) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) {
- return o.bucket.SignURL(name, http.MethodPut, int64(expire/time.Second))
-}
-
-func (o *OSS) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) {
- header, err := o.bucket.GetObjectMeta(name)
- if err != nil {
- return nil, err
- }
- res := &s3.ObjectInfo{Key: name}
- if res.ETag = strings.ToLower(strings.ReplaceAll(header.Get("ETag"), `"`, ``)); res.ETag == "" {
- return nil, errs.Wrap(errors.New("StatObject etag not found"))
- }
- if contentLengthStr := header.Get("Content-Length"); contentLengthStr == "" {
- return nil, errors.New("StatObject content-length not found")
- } else {
- res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64)
- if err != nil {
- return nil, errs.Wrap(err, "StatObject content-length parse error")
- }
- if res.Size < 0 {
- return nil, errs.Wrap(errors.New("StatObject content-length must be greater than 0"))
- }
- }
- if lastModified := header.Get("Last-Modified"); lastModified == "" {
- return nil, errs.Wrap(errors.New("StatObject last-modified not found"))
- } else {
- res.LastModified, err = time.Parse(http.TimeFormat, lastModified)
- if err != nil {
- return nil, errs.Wrap(err, "StatObject last-modified parse error")
- }
- }
- return res, nil
-}
-
-func (o *OSS) DeleteObject(ctx context.Context, name string) error {
- return o.bucket.DeleteObject(name)
-}
-
-func (o *OSS) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) {
- result, err := o.bucket.CopyObject(src, dst)
- if err != nil {
- return nil, errs.Wrap(err, "CopyObject error")
- }
- return &s3.CopyObjectInfo{
- Key: dst,
- ETag: strings.ToLower(strings.ReplaceAll(result.ETag, `"`, ``)),
- }, nil
-}
-
-func (o *OSS) IsNotFound(err error) bool {
- switch e := errs.Unwrap(err).(type) {
- case oss.ServiceError:
- return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
- case *oss.ServiceError:
- return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey"
- default:
- return false
- }
-}
-
-func (o *OSS) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error {
- return o.bucket.AbortMultipartUpload(oss.InitiateMultipartUploadResult{
- UploadID: uploadID,
- Key: name,
- Bucket: o.bucket.BucketName,
- })
-}
-
-func (o *OSS) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) {
- result, err := o.bucket.ListUploadedParts(oss.InitiateMultipartUploadResult{
- UploadID: uploadID,
- Key: name,
- Bucket: o.bucket.BucketName,
- }, oss.MaxUploads(100), oss.MaxParts(maxParts), oss.PartNumberMarker(partNumberMarker))
- if err != nil {
- return nil, errs.Wrap(err, "ListUploadedParts error")
- }
- res := &s3.ListUploadedPartsResult{
- Key: result.Key,
- UploadID: result.UploadID,
- MaxParts: result.MaxParts,
- UploadedParts: make([]s3.UploadedPart, len(result.UploadedParts)),
- }
- res.NextPartNumberMarker, _ = strconv.Atoi(result.NextPartNumberMarker)
- for i, part := range result.UploadedParts {
- res.UploadedParts[i] = s3.UploadedPart{
- PartNumber: part.PartNumber,
- LastModified: part.LastModified,
- ETag: part.ETag,
- Size: int64(part.Size),
- }
- }
- return res, nil
-}
-
-func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) {
- var opts []oss.Option
- if opt != nil {
- if opt.Image != nil {
- // Docs Address: 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 !o.publicRead {
- if opt.ContentType != "" {
- opts = append(opts, oss.ResponseContentType(opt.ContentType))
- }
- if opt.Filename != "" {
- opts = append(opts, oss.ResponseContentDisposition(`attachment; filename=`+strconv.Quote(opt.Filename)))
- }
- }
- }
- if expire <= 0 {
- expire = time.Hour * 24 * 365 * 99 // 99 years
- } else if expire < time.Second {
- expire = time.Second
- }
- if !o.publicRead {
- return o.bucket.SignURL(name, http.MethodGet, int64(expire/time.Second), opts...)
- }
- rawParams, err := oss.GetRawParams(opts)
- if err != nil {
- return "", errs.Wrap(err, "AccessURL error")
- }
- params := getURLParams(*o.bucket.Client.Conn, rawParams)
- return getURL(o.um, o.bucket.BucketName, name, params).String(), nil
-}
-
-func (o *OSS) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) {
- // https://help.aliyun.com/zh/oss/developer-reference/postobject?spm=a2c4g.11186623.0.0.1cb83cebkP55nn
- expires := time.Now().Add(duration)
- conditions := []any{
- map[string]string{"bucket": o.bucket.BucketName},
- map[string]string{"key": name},
- }
- if size > 0 {
- conditions = append(conditions, []any{"content-length-range", 0, size})
- }
- policy := map[string]any{
- "expiration": expires.Format("2006-01-02T15:04:05.000Z"),
- "conditions": conditions,
- }
- policyJson, err := json.Marshal(policy)
- if err != nil {
- return nil, errs.Wrap(err, "Marshal json error")
- }
- policyStr := base64.StdEncoding.EncodeToString(policyJson)
- h := hmac.New(sha1.New, []byte(o.credentials.GetAccessKeySecret()))
- if _, err := io.WriteString(h, policyStr); err != nil {
- return nil, errs.Wrap(err, "WriteString error")
- }
- fd := &s3.FormData{
- URL: o.bucketURL,
- File: "file",
- Expires: expires,
- FormData: map[string]string{
- "key": name,
- "policy": policyStr,
- "OSSAccessKeyId": o.credentials.GetAccessKeyID(),
- "success_action_status": strconv.Itoa(successCode),
- "signature": base64.StdEncoding.EncodeToString(h.Sum(nil)),
- },
- SuccessCodes: []int{successCode},
- }
- if contentType != "" {
- fd.FormData["x-oss-content-type"] = contentType
- }
- return fd, nil
-}
diff --git a/pkg/common/db/s3/s3.go b/pkg/common/db/s3/s3.go
deleted file mode 100644
index d3dd90ae98..0000000000
--- a/pkg/common/db/s3/s3.go
+++ /dev/null
@@ -1,166 +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 s3
-
-import (
- "context"
- "net/http"
- "net/url"
- "time"
-)
-
-type PartLimit struct {
- MinPartSize int64 `json:"minPartSize"`
- MaxPartSize int64 `json:"maxPartSize"`
- MaxNumSize int64 `json:"maxNumSize"`
-}
-
-type InitiateMultipartUploadResult struct {
- Bucket string `json:"bucket"`
- Key string `json:"key"`
- UploadID string `json:"uploadID"`
-}
-
-type MultipartUploadRequest struct {
- UploadID string `json:"uploadId"`
- Bucket string `json:"bucket"`
- Key string `json:"key"`
- Method string `json:"method"`
- URL string `json:"url"`
- Query url.Values `json:"query"`
- Header http.Header `json:"header"`
- PartKey string `json:"partKey"`
- PartSize int64 `json:"partSize"`
- FirstPart int `json:"firstPart"`
-}
-
-type Part struct {
- PartNumber int `json:"partNumber"`
- ETag string `json:"etag"`
-}
-
-type CompleteMultipartUploadResult struct {
- Location string `json:"location"`
- Bucket string `json:"bucket"`
- Key string `json:"key"`
- ETag string `json:"etag"`
-}
-
-type SignResult struct {
- Parts []SignPart `json:"parts"`
-}
-
-type ObjectInfo struct {
- ETag string `json:"etag"`
- Key string `json:"name"`
- Size int64 `json:"size"`
- LastModified time.Time `json:"lastModified"`
-}
-
-type CopyObjectInfo struct {
- Key string `json:"name"`
- ETag string `json:"etag"`
-}
-
-type FormData struct {
- URL string `json:"url"`
- File string `json:"file"`
- Header http.Header `json:"header"`
- FormData map[string]string `json:"form"`
- Expires time.Time `json:"expires"`
- SuccessCodes []int `json:"successActionStatus"`
-}
-
-type SignPart struct {
- PartNumber int `json:"partNumber"`
- URL string `json:"url"`
- Query url.Values `json:"query"`
- Header http.Header `json:"header"`
-}
-
-type AuthSignResult struct {
- URL string `json:"url"`
- Query url.Values `json:"query"`
- Header http.Header `json:"header"`
- Parts []SignPart `json:"parts"`
-}
-
-type InitiateUpload struct {
- UploadID string `json:"uploadId"`
- Bucket string `json:"bucket"`
- Key string `json:"key"`
- Method string `json:"method"`
- URL string `json:"url"`
- Query url.Values `json:"query"`
- Header http.Header `json:"header"`
- PartKey string `json:"partKey"`
- PartSize int64 `json:"partSize"`
- FirstPart int `json:"firstPart"`
-}
-
-type UploadedPart struct {
- PartNumber int `json:"partNumber"`
- LastModified time.Time `json:"lastModified"`
- ETag string `json:"etag"`
- Size int64 `json:"size"`
-}
-
-type ListUploadedPartsResult struct {
- Key string `xml:"Key"`
- UploadID string `xml:"UploadId"`
- NextPartNumberMarker int `xml:"NextPartNumberMarker"`
- MaxParts int `xml:"MaxParts"`
- UploadedParts []UploadedPart `xml:"Part"`
-}
-
-type Image struct {
- Format string `json:"format"`
- Width int `json:"width"`
- Height int `json:"height"`
-}
-
-type AccessURLOption struct {
- ContentType string `json:"contentType"`
- Filename string `json:"filename"`
- Image *Image `json:"image"`
-}
-
-type Interface interface {
- Engine() string
- PartLimit() *PartLimit
-
- InitiateMultipartUpload(ctx context.Context, name string) (*InitiateMultipartUploadResult, error)
- CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []Part) (*CompleteMultipartUploadResult, error)
-
- PartSize(ctx context.Context, size int64) (int64, error)
- AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*AuthSignResult, error)
-
- PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error)
-
- DeleteObject(ctx context.Context, name string) error
-
- CopyObject(ctx context.Context, src string, dst string) (*CopyObjectInfo, error)
-
- StatObject(ctx context.Context, name string) (*ObjectInfo, error)
-
- IsNotFound(err error) bool
-
- AbortMultipartUpload(ctx context.Context, uploadID string, name string) error
- ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*ListUploadedPartsResult, error)
-
- AccessURL(ctx context.Context, name string, expire time.Duration, opt *AccessURLOption) (string, error)
-
- FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*FormData, error)
-}
diff --git a/pkg/common/db/table/relation/black.go b/pkg/common/db/table/relation/black.go
index 50499054cc..f5d1cb2369 100644
--- a/pkg/common/db/table/relation/black.go
+++ b/pkg/common/db/table/relation/black.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type BlackModel struct {
@@ -33,8 +33,8 @@ type BlackModel struct {
type BlackModelInterface interface {
Create(ctx context.Context, blacks []*BlackModel) (err error)
Delete(ctx context.Context, blacks []*BlackModel) (err error)
- //UpdateByMap(ctx context.Context, ownerUserID, blockUserID string, args map[string]any) (err error)
- //Update(ctx context.Context, blacks []*BlackModel) (err error)
+ // UpdateByMap(ctx context.Context, ownerUserID, blockUserID string, args map[string]any) (err error)
+ // Update(ctx context.Context, blacks []*BlackModel) (err error)
Find(ctx context.Context, blacks []*BlackModel) (blackList []*BlackModel, err error)
Take(ctx context.Context, ownerUserID, blockUserID string) (black *BlackModel, err error)
FindOwnerBlacks(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, blacks []*BlackModel, err error)
diff --git a/pkg/common/db/table/relation/conversation.go b/pkg/common/db/table/relation/conversation.go
index 583e41c0f2..4990c96c60 100644
--- a/pkg/common/db/table/relation/conversation.go
+++ b/pkg/common/db/table/relation/conversation.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type ConversationModel struct {
diff --git a/pkg/common/db/unrelation/doc.go b/pkg/common/db/table/relation/doc.go
similarity index 79%
rename from pkg/common/db/unrelation/doc.go
rename to pkg/common/db/table/relation/doc.go
index 1a15e42c4b..c711dec705 100644
--- a/pkg/common/db/unrelation/doc.go
+++ b/pkg/common/db/table/relation/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package unrelation // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation"
+package relation // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation"
diff --git a/pkg/common/db/table/relation/friend.go b/pkg/common/db/table/relation/friend.go
index 73f7454df1..4c84e773dd 100644
--- a/pkg/common/db/table/relation/friend.go
+++ b/pkg/common/db/table/relation/friend.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
// FriendModel represents the data structure for a friend relationship in MongoDB.
diff --git a/pkg/common/db/table/relation/friend_request.go b/pkg/common/db/table/relation/friend_request.go
index 8dceb0778c..d59e3bb0b5 100644
--- a/pkg/common/db/table/relation/friend_request.go
+++ b/pkg/common/db/table/relation/friend_request.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type FriendRequestModel struct {
diff --git a/pkg/common/db/table/relation/group.go b/pkg/common/db/table/relation/group.go
index 1f969cd4fb..f479a4745b 100644
--- a/pkg/common/db/table/relation/group.go
+++ b/pkg/common/db/table/relation/group.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type GroupModel struct {
diff --git a/pkg/common/db/table/relation/group_member.go b/pkg/common/db/table/relation/group_member.go
index 88ab87739d..37f1cfc03e 100644
--- a/pkg/common/db/table/relation/group_member.go
+++ b/pkg/common/db/table/relation/group_member.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type GroupMemberModel struct {
@@ -36,10 +36,10 @@ type GroupMemberModel struct {
}
type GroupMemberModelInterface interface {
- //NewTx(tx any) GroupMemberModelInterface
+ // NewTx(tx any) GroupMemberModelInterface
Create(ctx context.Context, groupMembers []*GroupMemberModel) (err error)
Delete(ctx context.Context, groupID string, userIDs []string) (err error)
- //DeleteGroup(ctx context.Context, groupIDs []string) (err error)
+ // DeleteGroup(ctx context.Context, groupIDs []string) (err error)
Update(ctx context.Context, groupID string, userID string, data map[string]any) (err error)
UpdateRoleLevel(ctx context.Context, groupID string, userID string, roleLevel int32) error
FindMemberUserID(ctx context.Context, groupID string) (userIDs []string, err error)
@@ -47,11 +47,11 @@ type GroupMemberModelInterface interface {
TakeOwner(ctx context.Context, groupID string) (groupMember *GroupMemberModel, err error)
SearchMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (total int64, groupList []*GroupMemberModel, err error)
FindRoleLevelUserIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error)
- //MapGroupMemberNum(ctx context.Context, groupIDs []string) (count map[string]uint32, err error)
- //FindJoinUserID(ctx context.Context, groupIDs []string) (groupUsers map[string][]string, err error)
+ // MapGroupMemberNum(ctx context.Context, groupIDs []string) (count map[string]uint32, err error)
+ // FindJoinUserID(ctx context.Context, groupIDs []string) (groupUsers map[string][]string, err error)
FindUserJoinedGroupID(ctx context.Context, userID string) (groupIDs []string, err error)
TakeGroupMemberNum(ctx context.Context, groupID string) (count int64, err error)
- //FindUsersJoinedGroupID(ctx context.Context, userIDs []string) (map[string][]string, error)
+ // FindUsersJoinedGroupID(ctx context.Context, userIDs []string) (map[string][]string, error)
FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error)
IsUpdateRoleLevel(data map[string]any) bool
}
diff --git a/pkg/common/db/table/relation/group_request.go b/pkg/common/db/table/relation/group_request.go
index 39999d799f..7e9b258de0 100644
--- a/pkg/common/db/table/relation/group_request.go
+++ b/pkg/common/db/table/relation/group_request.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type GroupRequestModel struct {
diff --git a/pkg/common/db/table/relation/log.go b/pkg/common/db/table/relation/log.go
index ba63c0c2bc..afc32c68e5 100644
--- a/pkg/common/db/table/relation/log.go
+++ b/pkg/common/db/table/relation/log.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/tools/db/pagination"
)
type LogModel struct {
diff --git a/pkg/common/db/table/unrelation/msg.go b/pkg/common/db/table/relation/msg.go
similarity index 84%
rename from pkg/common/db/table/unrelation/msg.go
rename to pkg/common/db/table/relation/msg.go
index 60dd7c260c..41a6ede974 100644
--- a/pkg/common/db/table/unrelation/msg.go
+++ b/pkg/common/db/table/relation/msg.go
@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package unrelation
+package relation
import (
"context"
"strconv"
"time"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/mongo"
)
@@ -32,6 +33,8 @@ const (
NewestList = -1
)
+var ErrMsgListNotExist = errs.New("user not have msg in mongoDB")
+
type MsgDocModel struct {
DocID string `bson:"doc_id"`
Msg []*MsgInfoModel `bson:"msgs"`
@@ -110,24 +113,15 @@ type MsgDocModelInterface interface {
DeleteMsgsInOneDocByIndex(ctx context.Context, docID string, indexes []int) error
MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error
SearchMessage(ctx context.Context, req *msg.SearchMessageReq) (int32, []*MsgInfoModel, error)
- RangeUserSendCount(
- ctx context.Context,
- start time.Time,
- end time.Time,
- group bool,
- ase bool,
- pageNumber int32,
- showNumber int32,
- ) (msgCount int64, userCount int64, users []*UserCount, dateCount map[string]int64, err error)
- RangeGroupSendCount(
- ctx context.Context,
- start time.Time,
- end time.Time,
- ase bool,
- pageNumber int32,
- showNumber int32,
- ) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error)
+ RangeUserSendCount(ctx context.Context, start time.Time, end time.Time, group bool, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, users []*UserCount, dateCount map[string]int64, err error)
+ RangeGroupSendCount(ctx context.Context, start time.Time, end time.Time, ase bool, pageNumber int32, showNumber int32) (msgCount int64, userCount int64, groups []*GroupCount, dateCount map[string]int64, err error)
ConvertMsgsDocLen(ctx context.Context, conversationIDs []string)
+
+ DeleteDoc(ctx context.Context, docID string) error
+ DeleteMsgByIndex(ctx context.Context, docID string, index []int) error
+ GetBeforeMsg(ctx context.Context, ts int64, limit int) ([]*MsgDocModel, error)
+
+ //ClearMsg(ctx context.Context, t time.Time) (int64, error)
}
func (MsgDocModel) TableName() string {
@@ -165,11 +159,11 @@ func (m MsgDocModel) GetDocIDSeqsMap(conversationID string, seqs []int64) map[st
return t
}
-func (m MsgDocModel) GetMsgIndex(seq int64) int64 {
+func (MsgDocModel) GetMsgIndex(seq int64) int64 {
return (seq - 1) % singleGocMsgNum
}
-func (m MsgDocModel) indexGen(conversationID string, seqSuffix int64) string {
+func (MsgDocModel) indexGen(conversationID string, seqSuffix int64) string {
return conversationID + ":" + strconv.FormatInt(seqSuffix, 10)
}
diff --git a/pkg/common/db/table/unrelation/user.go b/pkg/common/db/table/relation/subscribe.go
similarity index 86%
rename from pkg/common/db/table/unrelation/user.go
rename to pkg/common/db/table/relation/subscribe.go
index 1505829e56..4e184cf384 100644
--- a/pkg/common/db/table/unrelation/user.go
+++ b/pkg/common/db/table/relation/subscribe.go
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package unrelation
+package relation
import "context"
@@ -21,18 +21,18 @@ const (
SubscribeUser = "subscribe_user"
)
-// UserModel collection structure.
-type UserModel struct {
+// SubscribeUserModel collection structure.
+type SubscribeUserModel struct {
UserID string `bson:"user_id" json:"userID"`
UserIDList []string `bson:"user_id_list" json:"userIDList"`
}
-func (UserModel) TableName() string {
+func (SubscribeUserModel) TableName() string {
return SubscribeUser
}
-// UserModelInterface Operation interface of user mongodb.
-type UserModelInterface interface {
+// SubscribeUserModelInterface Operation interface of user mongodb.
+type SubscribeUserModelInterface interface {
// AddSubscriptionList Subscriber's handling of thresholds.
AddSubscriptionList(ctx context.Context, userID string, userIDList []string) error
// UnsubscriptionList Handling of unsubscribe.
diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go
index 375930ddf2..938a8a77d7 100644
--- a/pkg/common/db/table/relation/user.go
+++ b/pkg/common/db/table/relation/user.go
@@ -18,8 +18,8 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/pagination"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/db/pagination"
)
type UserModel struct {
@@ -65,7 +65,7 @@ type UserModelInterface interface {
CountTotal(ctx context.Context, before *time.Time) (count int64, err error)
// Get user total quantity every day
CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error)
- //CRUD user command
+ // CRUD user command
AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error
DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error
UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error
diff --git a/pkg/common/db/table/relation/utils.go b/pkg/common/db/table/relation/utils.go
index 380f2410ea..006da48083 100644
--- a/pkg/common/db/table/relation/utils.go
+++ b/pkg/common/db/table/relation/utils.go
@@ -15,7 +15,7 @@
package relation
import (
- "github.com/OpenIMSDK/tools/utils"
+ "github.com/openimsdk/tools/errs"
"go.mongodb.org/mongo-driver/mongo"
)
@@ -31,5 +31,5 @@ type GroupSimpleUserID struct {
}
func IsNotFound(err error) bool {
- return utils.Unwrap(err) == mongo.ErrNoDocuments
+ return errs.Unwrap(err) == mongo.ErrNoDocuments
}
diff --git a/pkg/common/db/table/unrelation/common.go b/pkg/common/db/table/unrelation/common.go
deleted file mode 100644
index bd46ccc2ac..0000000000
--- a/pkg/common/db/table/unrelation/common.go
+++ /dev/null
@@ -1,20 +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 unrelation
-
-type CommonUserModel struct {
- UserID string `bson:"user_id"`
- UserName string `bson:"user_name"`
-}
diff --git a/pkg/common/db/table/unrelation/super_group.go b/pkg/common/db/table/unrelation/super_group.go
deleted file mode 100644
index 1fd80c67a7..0000000000
--- a/pkg/common/db/table/unrelation/super_group.go
+++ /dev/null
@@ -1,53 +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 unrelation
-
-//import (
-// "context"
-//)
-//
-//const (
-// CSuperGroup = "super_group"
-// CUserToSuperGroup = "user_to_super_group"
-//)
-//
-//type SuperGroupModel struct {
-// GroupID string `bson:"group_id" json:"groupID"`
-// MemberIDs []string `bson:"member_id_list" json:"memberIDList"`
-//}
-//
-//func (SuperGroupModel) TableName() string {
-// return CSuperGroup
-//}
-//
-//type UserToSuperGroupModel struct {
-// UserID string `bson:"user_id" json:"userID"`
-// GroupIDs []string `bson:"group_id_list" json:"groupIDList"`
-//}
-//
-//func (UserToSuperGroupModel) TableName() string {
-// return CUserToSuperGroup
-//}
-//
-//type SuperGroupModelInterface interface {
-// CreateSuperGroup(ctx context.Context, groupID string, initMemberIDs []string) error
-// TakeSuperGroup(ctx context.Context, groupID string) (group *SuperGroupModel, err error)
-// FindSuperGroup(ctx context.Context, groupIDs []string) (groups []*SuperGroupModel, err error)
-// AddUserToSuperGroup(ctx context.Context, groupID string, userIDs []string) error
-// RemoverUserFromSuperGroup(ctx context.Context, groupID string, userIDs []string) error
-// GetSuperGroupByUserID(ctx context.Context, userID string) (*UserToSuperGroupModel, error)
-// DeleteSuperGroup(ctx context.Context, groupID string) error
-// RemoveGroupFromUser(ctx context.Context, groupID string, userIDs []string) error
-//}
diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go
deleted file mode 100644
index 834e812375..0000000000
--- a/pkg/common/db/unrelation/mongo.go
+++ /dev/null
@@ -1,168 +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 unrelation
-
-import (
- "context"
- "fmt"
- "os"
- "strings"
- "time"
-
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mw/specialerror"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
- "go.mongodb.org/mongo-driver/bson"
- "go.mongodb.org/mongo-driver/mongo"
- "go.mongodb.org/mongo-driver/mongo/options"
-)
-
-const (
- maxRetry = 10 // number of retries
- mongoConnTimeout = 10 * time.Second
-)
-
-type Mongo struct {
- db *mongo.Client
- config *config.GlobalConfig
-}
-
-// NewMongo Initialize MongoDB connection.
-func NewMongo(config *config.GlobalConfig) (*Mongo, error) {
- specialerror.AddReplace(mongo.ErrNoDocuments, errs.ErrRecordNotFound)
- uri := buildMongoURI(config)
-
- var mongoClient *mongo.Client
- var err error
-
- // Retry connecting to MongoDB
- for i := 0; i <= maxRetry; i++ {
- ctx, cancel := context.WithTimeout(context.Background(), mongoConnTimeout)
- defer cancel()
- mongoClient, err = mongo.Connect(ctx, options.Client().ApplyURI(uri))
- if err == nil {
- if err = mongoClient.Ping(ctx, nil); err != nil {
- return nil, errs.Wrap(err, uri)
- }
- return &Mongo{db: mongoClient, config: config}, nil
- }
- if shouldRetry(err) {
- time.Sleep(time.Second) // exponential backoff could be implemented here
- continue
- }
- }
- return nil, errs.Wrap(err, uri)
-}
-
-func buildMongoURI(config *config.GlobalConfig) string {
- uri := os.Getenv("MONGO_URI")
- if uri != "" {
- return uri
- }
-
- if config.Mongo.Uri != "" {
- return config.Mongo.Uri
- }
-
- username := os.Getenv("MONGO_OPENIM_USERNAME")
- password := os.Getenv("MONGO_OPENIM_PASSWORD")
- address := os.Getenv("MONGO_ADDRESS")
- port := os.Getenv("MONGO_PORT")
- database := os.Getenv("MONGO_DATABASE")
- maxPoolSize := os.Getenv("MONGO_MAX_POOL_SIZE")
-
- if username == "" {
- username = config.Mongo.Username
- }
- if password == "" {
- password = config.Mongo.Password
- }
- if address == "" {
- address = strings.Join(config.Mongo.Address, ",")
- } else if port != "" {
- address = fmt.Sprintf("%s:%s", address, port)
- }
- if database == "" {
- database = config.Mongo.Database
- }
- if maxPoolSize == "" {
- maxPoolSize = fmt.Sprint(config.Mongo.MaxPoolSize)
- }
-
- if username != "" && password != "" {
-
- return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%s", username, password, address, database, maxPoolSize)
- }
- return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%s", address, database, maxPoolSize)
-}
-
-func shouldRetry(err error) bool {
- if cmdErr, ok := err.(mongo.CommandError); ok {
- return cmdErr.Code != 13 && cmdErr.Code != 18
- }
- return true
-}
-
-// GetClient returns the MongoDB client.
-func (m *Mongo) GetClient() *mongo.Client {
- return m.db
-}
-
-// GetDatabase returns the specific database from MongoDB.
-func (m *Mongo) GetDatabase(database string) *mongo.Database {
- return m.db.Database(database)
-}
-
-// CreateMsgIndex creates an index for messages in MongoDB.
-func (m *Mongo) CreateMsgIndex() error {
- return m.createMongoIndex(unrelation.Msg, true, "doc_id")
-}
-
-// createMongoIndex creates an index in a MongoDB collection.
-func (m *Mongo) createMongoIndex(collection string, isUnique bool, keys ...string) error {
- db := m.GetDatabase(m.config.Mongo.Database).Collection(collection)
- opts := options.CreateIndexes().SetMaxTime(10 * time.Second)
- indexView := db.Indexes()
-
- keysDoc := buildIndexKeys(keys)
-
- index := mongo.IndexModel{
- Keys: keysDoc,
- }
- if isUnique {
- index.Options = options.Index().SetUnique(true)
- }
-
- _, err := indexView.CreateOne(context.Background(), index, opts)
- if err != nil {
- return errs.Wrap(err, "CreateIndex")
- }
- return nil
-}
-
-// buildIndexKeys builds the BSON document for index keys.
-func buildIndexKeys(keys []string) bson.D {
- keysDoc := bson.D{}
- for _, key := range keys {
- direction := 1 // default direction is ascending
- if strings.HasPrefix(key, "-") {
- direction = -1 // descending order for prefixed with "-"
- key = strings.TrimLeft(key, "-")
- }
- keysDoc = append(keysDoc, bson.E{Key: key, Value: direction})
- }
- return keysDoc
-}
diff --git a/pkg/common/db/unrelation/msg_convert.go b/pkg/common/db/unrelation/msg_convert.go
deleted file mode 100644
index 30c74e927b..0000000000
--- a/pkg/common/db/unrelation/msg_convert.go
+++ /dev/null
@@ -1,81 +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 unrelation
-
-import (
- "context"
- "fmt"
-
- "github.com/OpenIMSDK/tools/log"
- table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
- "go.mongodb.org/mongo-driver/bson"
- "go.mongodb.org/mongo-driver/bson/primitive"
-)
-
-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 []any
- 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))
- }
- }
- }
-}
diff --git a/pkg/common/db/unrelation/super_group.go b/pkg/common/db/unrelation/super_group.go
deleted file mode 100644
index 6c2bb6aafb..0000000000
--- a/pkg/common/db/unrelation/super_group.go
+++ /dev/null
@@ -1,163 +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 unrelation
-
-//
-//import (
-// "context"
-//
-// "go.mongodb.org/mongo-driver/bson"
-// "go.mongodb.org/mongo-driver/mongo"
-// "go.mongodb.org/mongo-driver/mongo/options"
-//
-// "github.com/OpenIMSDK/tools/utils"
-//
-// "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
-//)
-//
-//func NewSuperGroupMongoDriver(database *mongo.Database) unrelation.SuperGroupModelInterface {
-// return &SuperGroupMongoDriver{
-// superGroupCollection: database.Collection(unrelation.CSuperGroup),
-// userToSuperGroupCollection: database.Collection(unrelation.CUserToSuperGroup),
-// }
-//}
-//
-//type SuperGroupMongoDriver struct {
-// superGroupCollection *mongo.Collection
-// userToSuperGroupCollection *mongo.Collection
-//}
-//
-//func (s *SuperGroupMongoDriver) CreateSuperGroup(ctx context.Context, groupID string, initMemberIDs []string) error {
-// _, err := s.superGroupCollection.InsertOne(ctx, &unrelation.SuperGroupModel{
-// GroupID: groupID,
-// MemberIDs: initMemberIDs,
-// })
-// if err != nil {
-// return err
-// }
-// for _, userID := range initMemberIDs {
-// _, err = s.userToSuperGroupCollection.UpdateOne(
-// ctx,
-// bson.M{"user_id": userID},
-// bson.M{"$addToSet": bson.M{"group_id_list": groupID}},
-// &options.UpdateOptions{
-// Upsert: utils.ToPtr(true),
-// },
-// )
-// if err != nil {
-// return err
-// }
-// }
-// return nil
-//}
-//
-//func (s *SuperGroupMongoDriver) TakeSuperGroup(
-// ctx context.Context,
-// groupID string,
-//) (group *unrelation.SuperGroupModel, err error) {
-// if err := s.superGroupCollection.FindOne(ctx, bson.M{"group_id": groupID}).Decode(&group); err != nil {
-// return nil, utils.Wrap(err, "")
-// }
-// return group, nil
-//}
-//
-//func (s *SuperGroupMongoDriver) FindSuperGroup(
-// ctx context.Context,
-// groupIDs []string,
-//) (groups []*unrelation.SuperGroupModel, err error) {
-// cursor, err := s.superGroupCollection.Find(ctx, bson.M{"group_id": bson.M{
-// "$in": groupIDs,
-// }})
-// if err != nil {
-// return nil, err
-// }
-// defer cursor.Close(ctx)
-// if err := cursor.All(ctx, &groups); err != nil {
-// return nil, utils.Wrap(err, "")
-// }
-// return groups, nil
-//}
-//
-//func (s *SuperGroupMongoDriver) AddUserToSuperGroup(ctx context.Context, groupID string, userIDs []string) error {
-// _, err := s.superGroupCollection.UpdateOne(
-// ctx,
-// bson.M{"group_id": groupID},
-// bson.M{"$addToSet": bson.M{"member_id_list": bson.M{"$each": userIDs}}},
-// )
-// if err != nil {
-// return err
-// }
-// upsert := true
-// opts := &options.UpdateOptions{
-// Upsert: &upsert,
-// }
-// for _, userID := range userIDs {
-// _, err = s.userToSuperGroupCollection.UpdateOne(
-// ctx,
-// bson.M{"user_id": userID},
-// bson.M{"$addToSet": bson.M{"group_id_list": groupID}},
-// opts,
-// )
-// if err != nil {
-// return utils.Wrap(err, "transaction failed")
-// }
-// }
-// return nil
-//}
-//
-//func (s *SuperGroupMongoDriver) RemoverUserFromSuperGroup(ctx context.Context, groupID string, userIDs []string) error {
-// _, err := s.superGroupCollection.UpdateOne(
-// ctx,
-// bson.M{"group_id": groupID},
-// bson.M{"$pull": bson.M{"member_id_list": bson.M{"$in": userIDs}}},
-// )
-// if err != nil {
-// return err
-// }
-// err = s.RemoveGroupFromUser(ctx, groupID, userIDs)
-// if err != nil {
-// return err
-// }
-// return nil
-//}
-//
-//func (s *SuperGroupMongoDriver) GetSuperGroupByUserID(
-// ctx context.Context,
-// userID string,
-//) (*unrelation.UserToSuperGroupModel, error) {
-// var user unrelation.UserToSuperGroupModel
-// err := s.userToSuperGroupCollection.FindOne(ctx, bson.M{"user_id": userID}).Decode(&user)
-// return &user, utils.Wrap(err, "")
-//}
-//
-//func (s *SuperGroupMongoDriver) DeleteSuperGroup(ctx context.Context, groupID string) error {
-// group, err := s.TakeSuperGroup(ctx, groupID)
-// if err != nil {
-// return err
-// }
-// if _, err := s.superGroupCollection.DeleteOne(ctx, bson.M{"group_id": groupID}); err != nil {
-// return utils.Wrap(err, "")
-// }
-// return s.RemoveGroupFromUser(ctx, groupID, group.MemberIDs)
-//}
-//
-//func (s *SuperGroupMongoDriver) RemoveGroupFromUser(ctx context.Context, groupID string, userIDs []string) error {
-// _, err := s.userToSuperGroupCollection.UpdateOne(
-// ctx,
-// bson.M{"user_id": bson.M{"$in": userIDs}},
-// bson.M{"$pull": bson.M{"group_id_list": groupID}},
-// )
-// return utils.Wrap(err, "")
-//}
diff --git a/pkg/common/discoveryregister/direct/direct_resolver.go b/pkg/common/discoveryregister/direct/direct_resolver.go
index a706ce5e4d..8213782772 100644
--- a/pkg/common/discoveryregister/direct/direct_resolver.go
+++ b/pkg/common/discoveryregister/direct/direct_resolver.go
@@ -19,7 +19,7 @@ import (
"math/rand"
"strings"
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/log"
"google.golang.org/grpc/resolver"
)
diff --git a/pkg/common/discoveryregister/direct/directconn.go b/pkg/common/discoveryregister/direct/directconn.go
index df03825e51..1cbe56dd52 100644
--- a/pkg/common/discoveryregister/direct/directconn.go
+++ b/pkg/common/discoveryregister/direct/directconn.go
@@ -14,159 +14,161 @@
package direct
-import (
- "context"
- "errors"
- "fmt"
-
- "github.com/OpenIMSDK/tools/errs"
- config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "google.golang.org/grpc"
- "google.golang.org/grpc/credentials/insecure"
-)
-
-type ServiceAddresses map[string][]int
-
-func getServiceAddresses(config *config2.GlobalConfig) ServiceAddresses {
- return ServiceAddresses{
- config.RpcRegisterName.OpenImUserName: config.RpcPort.OpenImUserPort,
- config.RpcRegisterName.OpenImFriendName: config.RpcPort.OpenImFriendPort,
- config.RpcRegisterName.OpenImMsgName: config.RpcPort.OpenImMessagePort,
- config.RpcRegisterName.OpenImMessageGatewayName: config.LongConnSvr.OpenImMessageGatewayPort,
- config.RpcRegisterName.OpenImGroupName: config.RpcPort.OpenImGroupPort,
- config.RpcRegisterName.OpenImAuthName: config.RpcPort.OpenImAuthPort,
- config.RpcRegisterName.OpenImPushName: config.RpcPort.OpenImPushPort,
- config.RpcRegisterName.OpenImConversationName: config.RpcPort.OpenImConversationPort,
- config.RpcRegisterName.OpenImThirdName: config.RpcPort.OpenImThirdPort,
- }
-}
-
-type ConnDirect struct {
- additionalOpts []grpc.DialOption
- currentServiceAddress string
- conns map[string][]*grpc.ClientConn
- resolverDirect *ResolverDirect
- config *config2.GlobalConfig
-}
-
-func (cd *ConnDirect) GetClientLocalConns() map[string][]*grpc.ClientConn {
- return nil
-}
-
-func (cd *ConnDirect) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
- return "", nil
-}
-
-func (cd *ConnDirect) Register(serviceName, host string, port int, opts ...grpc.DialOption) error {
- return nil
-}
-
-func (cd *ConnDirect) UnRegister() error {
- return nil
-}
-
-func (cd *ConnDirect) CreateRpcRootNodes(serviceNames []string) error {
- return nil
-}
-
-func (cd *ConnDirect) RegisterConf2Registry(key string, conf []byte) error {
- return nil
-}
-
-func (cd *ConnDirect) GetConfFromRegistry(key string) ([]byte, error) {
- return nil, nil
-}
-
-func (cd *ConnDirect) Close() {
-
-}
-
-func NewConnDirect(config *config2.GlobalConfig) (*ConnDirect, error) {
- return &ConnDirect{
- conns: make(map[string][]*grpc.ClientConn),
- resolverDirect: NewResolverDirect(),
- config: config,
- }, nil
-}
-
-func (cd *ConnDirect) GetConns(ctx context.Context,
- serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
-
- if conns, exists := cd.conns[serviceName]; exists {
- return conns, nil
- }
- ports := getServiceAddresses(cd.config)[serviceName]
- var connections []*grpc.ClientConn
- for _, port := range ports {
- conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...)
- if err != nil {
- return nil, errs.Wrap(fmt.Errorf("connect to port %d failed,serviceName %s, IP %s", port, serviceName, cd.config.Rpc.ListenIP))
- }
- connections = append(connections, conn)
- }
-
- if len(connections) == 0 {
- return nil, errs.Wrap(errors.New("no connections found for service"), "serviceName", serviceName)
- }
- return connections, nil
-}
-
-func (cd *ConnDirect) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
- // Get service addresses
- addresses := getServiceAddresses(cd.config)
- address, ok := addresses[serviceName]
- if !ok {
- return nil, errs.Wrap(errors.New("unknown service name"), "serviceName", serviceName)
- }
- var result string
- for _, addr := range address {
- if result != "" {
- result = result + "," + fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr)
- } else {
- result = fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr)
- }
- }
- // Try to dial a new connection
- conn, err := cd.dialService(ctx, result, append(cd.additionalOpts, opts...)...)
- if err != nil {
- return nil, errs.Wrap(err, "address", result)
- }
-
- // Store the new connection
- cd.conns[serviceName] = append(cd.conns[serviceName], conn)
- return conn, nil
-}
-
-func (cd *ConnDirect) GetSelfConnTarget() string {
- return cd.currentServiceAddress
-}
-
-func (cd *ConnDirect) AddOption(opts ...grpc.DialOption) {
- cd.additionalOpts = append(cd.additionalOpts, opts...)
-}
-
-func (cd *ConnDirect) CloseConn(conn *grpc.ClientConn) {
- if conn != nil {
- conn.Close()
- }
-}
-
-func (cd *ConnDirect) dialService(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
- options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
- conn, err := grpc.DialContext(ctx, cd.resolverDirect.Scheme()+":///"+address, options...)
-
- if err != nil {
- return nil, errs.Wrap(err, "address", address)
- }
- return conn, nil
-}
-
-func (cd *ConnDirect) dialServiceWithoutResolver(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
- options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
- conn, err := grpc.DialContext(ctx, address, options...)
-
- if err != nil {
- return nil, errs.Wrap(err)
- }
- return conn, nil
-}
+//import (
+// "context"
+// "fmt"
+//
+// config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+// "github.com/openimsdk/tools/errs"
+// "google.golang.org/grpc"
+// "google.golang.org/grpc/credentials/insecure"
+//)
+//
+//type ServiceAddresses map[string][]int
+//
+//func getServiceAddresses(rpcRegisterName *config2.RpcRegisterName,
+// rpcPort *config2.RpcPort, longConnSvrPort []int) ServiceAddresses {
+// return ServiceAddresses{
+// rpcRegisterName.OpenImUserName: rpcPort.OpenImUserPort,
+// rpcRegisterName.OpenImFriendName: rpcPort.OpenImFriendPort,
+// rpcRegisterName.OpenImMsgName: rpcPort.OpenImMessagePort,
+// rpcRegisterName.OpenImMessageGatewayName: longConnSvrPort,
+// rpcRegisterName.OpenImGroupName: rpcPort.OpenImGroupPort,
+// rpcRegisterName.OpenImAuthName: rpcPort.OpenImAuthPort,
+// rpcRegisterName.OpenImPushName: rpcPort.OpenImPushPort,
+// rpcRegisterName.OpenImConversationName: rpcPort.OpenImConversationPort,
+// rpcRegisterName.OpenImThirdName: rpcPort.OpenImThirdPort,
+// }
+//}
+//
+//type ConnDirect struct {
+// additionalOpts []grpc.DialOption
+// currentServiceAddress string
+// conns map[string][]*grpc.ClientConn
+// resolverDirect *ResolverDirect
+// config *config2.GlobalConfig
+//}
+//
+//func (cd *ConnDirect) GetClientLocalConns() map[string][]*grpc.ClientConn {
+// return nil
+//}
+//
+//func (cd *ConnDirect) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
+// return "", nil
+//}
+//
+//func (cd *ConnDirect) Register(serviceName, host string, port int, opts ...grpc.DialOption) error {
+// return nil
+//}
+//
+//func (cd *ConnDirect) UnRegister() error {
+// return nil
+//}
+//
+//func (cd *ConnDirect) CreateRpcRootNodes(serviceNames []string) error {
+// return nil
+//}
+//
+//func (cd *ConnDirect) RegisterConf2Registry(key string, conf []byte) error {
+// return nil
+//}
+//
+//func (cd *ConnDirect) GetConfFromRegistry(key string) ([]byte, error) {
+// return nil, nil
+//}
+//
+//func (cd *ConnDirect) Close() {
+//
+//}
+//
+//func NewConnDirect(config *config2.GlobalConfig) (*ConnDirect, error) {
+// return &ConnDirect{
+// conns: make(map[string][]*grpc.ClientConn),
+// resolverDirect: NewResolverDirect(),
+// config: config,
+// }, nil
+//}
+//
+//func (cd *ConnDirect) GetConns(ctx context.Context,
+// serviceName string, opts ...grpc.DialOption) ([]*grpc.ClientConn, error) {
+//
+// if conns, exists := cd.conns[serviceName]; exists {
+// return conns, nil
+// }
+// ports := getServiceAddresses(&cd.config.RpcRegisterName,
+// &cd.config.RpcPort, cd.config.LongConnSvr.OpenImMessageGatewayPort)[serviceName]
+// var connections []*grpc.ClientConn
+// for _, port := range ports {
+// conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...)
+// if err != nil {
+// return nil, errs.Wrap(fmt.Errorf("connect to port %d failed,serviceName %s, IP %s", port, serviceName, cd.config.Rpc.ListenIP))
+// }
+// connections = append(connections, conn)
+// }
+//
+// if len(connections) == 0 {
+// return nil, errs.New("no connections found for service", "serviceName", serviceName).Wrap()
+// }
+// return connections, nil
+//}
+//
+//func (cd *ConnDirect) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
+// // Get service addresses
+// addresses := getServiceAddresses(&cd.config.RpcRegisterName,
+// &cd.config.RpcPort, cd.config.LongConnSvr.OpenImMessageGatewayPort)
+// address, ok := addresses[serviceName]
+// if !ok {
+// return nil, errs.New("unknown service name", "serviceName", serviceName).Wrap()
+// }
+// var result string
+// for _, addr := range address {
+// if result != "" {
+// result = result + "," + fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr)
+// } else {
+// result = fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr)
+// }
+// }
+// // Try to dial a new connection
+// conn, err := cd.dialService(ctx, result, append(cd.additionalOpts, opts...)...)
+// if err != nil {
+// return nil, errs.WrapMsg(err, "address", result)
+// }
+//
+// // Store the new connection
+// cd.conns[serviceName] = append(cd.conns[serviceName], conn)
+// return conn, nil
+//}
+//
+//func (cd *ConnDirect) GetSelfConnTarget() string {
+// return cd.currentServiceAddress
+//}
+//
+//func (cd *ConnDirect) AddOption(opts ...grpc.DialOption) {
+// cd.additionalOpts = append(cd.additionalOpts, opts...)
+//}
+//
+//func (cd *ConnDirect) CloseConn(conn *grpc.ClientConn) {
+// if conn != nil {
+// conn.Close()
+// }
+//}
+//
+//func (cd *ConnDirect) dialService(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
+// options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
+// conn, err := grpc.DialContext(ctx, cd.resolverDirect.Scheme()+":///"+address, options...)
+//
+// if err != nil {
+// return nil, errs.WrapMsg(err, "address", address)
+// }
+// return conn, nil
+//}
+//
+//func (cd *ConnDirect) dialServiceWithoutResolver(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
+// options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials()))
+// conn, err := grpc.DialContext(ctx, address, options...)
+//
+// if err != nil {
+// return nil, errs.Wrap(err)
+// }
+// return conn, nil
+//}
diff --git a/pkg/common/db/table/unrelation/doc.go b/pkg/common/discoveryregister/direct/doc.go
similarity index 78%
rename from pkg/common/db/table/unrelation/doc.go
rename to pkg/common/discoveryregister/direct/doc.go
index 7596206a08..b3cd0f804b 100644
--- a/pkg/common/db/table/unrelation/doc.go
+++ b/pkg/common/discoveryregister/direct/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package unrelation // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation"
+package direct // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct"
diff --git a/pkg/common/discoveryregister/discoveryregister.go b/pkg/common/discoveryregister/discoveryregister.go
index d3acf85f69..38d7382fa6 100644
--- a/pkg/common/discoveryregister/discoveryregister.go
+++ b/pkg/common/discoveryregister/discoveryregister.go
@@ -15,32 +15,39 @@
package discoveryregister
import (
- "errors"
- "os"
-
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct"
"github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes"
- "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/discovery/zookeeper"
+ "github.com/openimsdk/tools/errs"
+ "time"
)
-// NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type.
-func NewDiscoveryRegister(config *config.GlobalConfig) (discoveryregistry.SvcDiscoveryRegistry, error) {
+const (
+ zookeeperConst = "zookeeper"
+ kubenetesConst = "k8s"
+ directConst = "direct"
+)
- if os.Getenv("ENVS_DISCOVERY") != "" {
- config.Envs.Discovery = os.Getenv("ENVS_DISCOVERY")
- }
+// NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type.
+func NewDiscoveryRegister(zookeeperConfig *config.ZooKeeper, share *config.Share) (discovery.SvcDiscoveryRegistry, error) {
+ switch share.Env {
+ case zookeeperConst:
- switch config.Envs.Discovery {
- case "zookeeper":
- return zookeeper.NewZookeeperDiscoveryRegister(config)
- case "k8s":
- return kubernetes.NewK8sDiscoveryRegister(config.RpcRegisterName.OpenImMessageGatewayName)
- case "direct":
- return direct.NewConnDirect(config)
+ return zookeeper.NewZkClient(
+ zookeeperConfig.Address,
+ zookeeperConfig.Schema,
+ zookeeper.WithFreq(time.Hour),
+ zookeeper.WithUserNameAndPassword(zookeeperConfig.Username, zookeeperConfig.Password),
+ zookeeper.WithRoundRobin(),
+ zookeeper.WithTimeout(10),
+ )
+ case kubenetesConst:
+ return kubernetes.NewK8sDiscoveryRegister(share.RpcRegisterName.MessageGateway)
+ case directConst:
+ //return direct.NewConnDirect(config)
default:
- return nil, errs.Wrap(errors.New("envType not correct"))
+ return nil, errs.New("unsupported discovery type", "type", share.Env).Wrap()
}
+ return nil, nil
}
diff --git a/pkg/common/discoveryregister/discoveryregister_test.go b/pkg/common/discoveryregister/discoveryregister_test.go
index 08aa5d5d5e..4172266451 100644
--- a/pkg/common/discoveryregister/discoveryregister_test.go
+++ b/pkg/common/discoveryregister/discoveryregister_test.go
@@ -16,12 +16,6 @@ package discoveryregister
import (
"os"
- "testing"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/stretchr/testify/assert"
)
func setupTestEnvironment() {
@@ -32,35 +26,35 @@ func setupTestEnvironment() {
os.Setenv("ZOOKEEPER_PASSWORD", "")
}
-func TestNewDiscoveryRegister(t *testing.T) {
- setupTestEnvironment()
- conf := config.NewGlobalConfig()
- tests := []struct {
- envType string
- gatewayName string
- expectedError bool
- expectedResult bool
- }{
- {"zookeeper", "MessageGateway", false, true},
- {"k8s", "MessageGateway", false, true},
- {"direct", "MessageGateway", false, true},
- {"invalid", "MessageGateway", true, false},
- }
-
- for _, test := range tests {
- conf.Envs.Discovery = test.envType
- conf.RpcRegisterName.OpenImMessageGatewayName = test.gatewayName
- client, err := NewDiscoveryRegister(conf)
-
- if test.expectedError {
- assert.Error(t, err)
- } else {
- assert.NoError(t, err)
- if test.expectedResult {
- assert.Implements(t, (*discoveryregistry.SvcDiscoveryRegistry)(nil), client)
- } else {
- assert.Nil(t, client)
- }
- }
- }
-}
+//func TestNewDiscoveryRegister(t *testing.T) {
+// setupTestEnvironment()
+// conf := config.NewGlobalConfig()
+// tests := []struct {
+// envType string
+// gatewayName string
+// expectedError bool
+// expectedResult bool
+// }{
+// {"zookeeper", "MessageGateway", false, true},
+// {"k8s", "MessageGateway", false, true},
+// {"direct", "MessageGateway", false, true},
+// {"invalid", "MessageGateway", true, false},
+// }
+//
+// for _, test := range tests {
+// conf.Envs.Discovery = test.envType
+// conf.RpcRegisterName.OpenImMessageGatewayName = test.gatewayName
+// client, err := NewDiscoveryRegister(conf)
+//
+// if test.expectedError {
+// assert.Error(t, err)
+// } else {
+// assert.NoError(t, err)
+// if test.expectedResult {
+// assert.Implements(t, (*discovery.SvcDiscoveryRegistry)(nil), client)
+// } else {
+// assert.Nil(t, client)
+// }
+// }
+// }
+//}
diff --git a/pkg/common/discoveryregister/doc.go b/pkg/common/discoveryregister/doc.go
new file mode 100644
index 0000000000..46bbe70012
--- /dev/null
+++ b/pkg/common/discoveryregister/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package discoveryregister // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
diff --git a/pkg/common/discoveryregister/kubernetes/doc.go b/pkg/common/discoveryregister/kubernetes/doc.go
new file mode 100644
index 0000000000..8615caa6b6
--- /dev/null
+++ b/pkg/common/discoveryregister/kubernetes/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package kubernetes // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes"
diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go
index 83af53db07..f1ce0bbdce 100644
--- a/pkg/common/discoveryregister/kubernetes/kubernetes.go
+++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go
@@ -22,8 +22,8 @@ import (
"strconv"
"strings"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/log"
"github.com/stathat/consistent"
"google.golang.org/grpc"
)
@@ -36,7 +36,7 @@ type K8sDR struct {
gatewayName string
}
-func NewK8sDiscoveryRegister(gatewayName string) (discoveryregistry.SvcDiscoveryRegistry, error) {
+func NewK8sDiscoveryRegister(gatewayName string) (discovery.SvcDiscoveryRegistry, error) {
gatewayConsistent := consistent.New()
gatewayHosts := getMsgGatewayHost(context.Background(), gatewayName)
for _, v := range gatewayHosts {
@@ -74,6 +74,7 @@ func (cli *K8sDR) GetConfFromRegistry(key string) ([]byte, error) {
return nil, nil
}
+
func (cli *K8sDR) GetUserIdHashGatewayHost(ctx context.Context, userId string) (string, error) {
host, err := cli.gatewayHostConsistent.Get(userId)
if err != nil {
@@ -81,6 +82,7 @@ func (cli *K8sDR) GetUserIdHashGatewayHost(ctx context.Context, userId string) (
}
return host, err
}
+
func getSelfHost(ctx context.Context, gatewayName string) string {
port := 88
instance := "openimserver"
@@ -102,6 +104,7 @@ func getSelfHost(ctx context.Context, gatewayName string) string {
}
// like openimserver-openim-msggateway-0.openimserver-openim-msggateway-headless.openim-lin.svc.cluster.local:88.
+// Replica set in kubernetes environment
func getMsgGatewayHost(ctx context.Context, gatewayName string) []string {
port := 88
instance := "openimserver"
@@ -122,7 +125,7 @@ func getMsgGatewayHost(ctx context.Context, gatewayName string) []string {
host := fmt.Sprintf("%s-openim-msggateway-%d.%s-openim-msggateway-headless.%s.svc.cluster.local:%d", instance, i, instance, ns, port)
ret = append(ret, host)
}
- log.ZInfo(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret)
+ log.ZDebug(ctx, "getMsgGatewayHost", "instance", instance, "selfPodName", selfPodName, "replicas", replicas, "ns", ns, "ret", ret)
return ret
}
@@ -187,9 +190,10 @@ func (cli *K8sDR) CloseConn(conn *grpc.ClientConn) {
// do not use this method for call rpc.
func (cli *K8sDR) GetClientLocalConns() map[string][]*grpc.ClientConn {
- fmt.Println("should not call this function!!!!!!!!!!!!!!!!!!!!!!!!!")
+ log.ZError(context.Background(), "should not call this function!", nil)
return nil
}
+
func (cli *K8sDR) Close() {
}
diff --git a/pkg/common/discoveryregister/zookeeper/doc.go b/pkg/common/discoveryregister/zookeeper/doc.go
new file mode 100644
index 0000000000..1c24d77ac9
--- /dev/null
+++ b/pkg/common/discoveryregister/zookeeper/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package zookeeper // import "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper"
diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go
index 0aa40a907c..1d11414b67 100644
--- a/pkg/common/discoveryregister/zookeeper/zookeeper.go
+++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go
@@ -15,46 +15,10 @@
package zookeeper
import (
- "fmt"
"os"
"strings"
- "time"
-
- "github.com/OpenIMSDK/tools/discoveryregistry"
- openkeeper "github.com/OpenIMSDK/tools/discoveryregistry/zookeeper"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
)
-// NewZookeeperDiscoveryRegister creates a new instance of ZookeeperDR for Zookeeper service discovery and registration.
-func NewZookeeperDiscoveryRegister(config *config.GlobalConfig) (discoveryregistry.SvcDiscoveryRegistry, error) {
- schema := getEnv("ZOOKEEPER_SCHEMA", config.Zookeeper.Schema)
- zkAddr := getZkAddrFromEnv(config.Zookeeper.ZkAddr)
- username := getEnv("ZOOKEEPER_USERNAME", config.Zookeeper.Username)
- password := getEnv("ZOOKEEPER_PASSWORD", config.Zookeeper.Password)
-
- zk, err := openkeeper.NewClient(
- zkAddr,
- schema,
- openkeeper.WithFreq(time.Hour),
- openkeeper.WithUserNameAndPassword(username, password),
- openkeeper.WithRoundRobin(),
- openkeeper.WithTimeout(10),
- openkeeper.WithLogger(log.NewZkLogger()),
- )
- if err != nil {
- uriFormat := "address:%s, username:%s, password:%s, schema:%s."
- errInfo := fmt.Sprintf(uriFormat,
- config.Zookeeper.ZkAddr,
- config.Zookeeper.Username,
- config.Zookeeper.Password,
- config.Zookeeper.Schema)
- return nil, errs.Wrap(err, errInfo)
- }
- return zk, nil
-}
-
// getEnv returns the value of an environment variable if it exists, otherwise it returns the fallback value.
func getEnv(key, fallback string) string {
if value, exists := os.LookupEnv(key); exists {
diff --git a/pkg/common/ginprometheus/doc.go b/pkg/common/ginprometheus/doc.go
new file mode 100644
index 0000000000..7d81b7a8af
--- /dev/null
+++ b/pkg/common/ginprometheus/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ginprometheus // import "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus"
diff --git a/pkg/common/http/http_client.go b/pkg/common/http/http_client.go
deleted file mode 100644
index 83908b8d33..0000000000
--- a/pkg/common/http/http_client.go
+++ /dev/null
@@ -1,139 +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 http
-
-import (
- "bytes"
- "context"
- "encoding/json"
- "io"
- "net/http"
- "time"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-var (
- // define http client.
- client = &http.Client{
- Timeout: 15 * time.Second, // max timeout is 15s
- }
-)
-
-func init() {
- // reset http default transport
- http.DefaultTransport.(*http.Transport).MaxConnsPerHost = 100 // default: 2
-}
-
-func Get(url string) (response []byte, err error) {
- hclient := http.Client{Timeout: 5 * time.Second}
- resp, err := hclient.Get(url)
- if err != nil {
- return nil, err
- }
-
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
- return body, nil
-}
-
-func Post(ctx context.Context, url string, header map[string]string, data any, timeout int) (content []byte, err error) {
- if timeout > 0 {
- var cancel func()
- ctx, cancel = context.WithTimeout(ctx, time.Second*time.Duration(timeout))
- defer cancel()
- }
-
- jsonStr, err := json.Marshal(data)
- if err != nil {
- return nil, errs.Wrap(err, "Post: JSON marshal failed")
- }
-
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonStr))
- if err != nil {
- return nil, errs.Wrap(err, "Post: NewRequestWithContext failed")
- }
-
- if operationID, _ := ctx.Value(constant.OperationID).(string); operationID != "" {
- req.Header.Set(constant.OperationID, operationID)
- }
- for k, v := range header {
- req.Header.Set(k, v)
- }
- req.Header.Add("content-type", "application/json; charset=utf-8")
-
- resp, err := client.Do(req)
- if err != nil {
- return nil, errs.Wrap(err, "Post: client.Do failed")
- }
- defer resp.Body.Close()
-
- result, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil, errs.Wrap(err, "Post: ReadAll failed")
- }
-
- return result, nil
-}
-
-func PostReturn(ctx context.Context, url string, header map[string]string, input, output any, timeOutSecond int) error {
- b, err := Post(ctx, url, header, input, timeOutSecond)
- if err != nil {
- return err
- }
- err = json.Unmarshal(b, output)
- if err != nil {
- return errs.Wrap(err, "PostReturn: JSON unmarshal failed")
- }
- return nil
-}
-
-func callBackPostReturn(ctx context.Context, url, command string, input interface{}, output callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error {
- url = url + "/" + command
- log.ZInfo(ctx, "callback", "url", url, "input", input, "config", callbackConfig)
- b, err := Post(ctx, url, nil, input, callbackConfig.CallbackTimeOut)
- if err != nil {
- if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
- log.ZInfo(ctx, "callback failed but continue", err, "url", url)
- return nil
- }
- log.ZWarn(ctx, "callback network failed", err, "url", url, "input", input)
- return errs.ErrNetwork.Wrap(err.Error())
- }
- if err = json.Unmarshal(b, output); err != nil {
- if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue {
- log.ZWarn(ctx, "callback failed but continue", err, "url", url)
- return nil
- }
- log.ZWarn(ctx, "callback json unmarshal failed", err, "url", url, "input", input, "response", string(b))
- return errs.ErrData.WithDetail(err.Error() + "response format error")
- }
- if err := output.Parse(); err != nil {
- log.ZWarn(ctx, "callback parse failed", err, "url", url, "input", input, "response", string(b))
- }
- log.ZInfo(ctx, "callback success", "url", url, "input", input, "response", string(b))
- return nil
-}
-
-func CallBackPostReturn(ctx context.Context, url string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error {
- return callBackPostReturn(ctx, url, req.GetCallbackCommand(), req, resp, callbackConfig)
-}
diff --git a/pkg/common/http/http_client_test.go b/pkg/common/http/http_client_test.go
deleted file mode 100644
index 5d25886736..0000000000
--- a/pkg/common/http/http_client_test.go
+++ /dev/null
@@ -1,154 +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 http
-
-import (
- "context"
- "reflect"
- "testing"
-
- "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-func TestGet(t *testing.T) {
- type args struct {
- url string
- }
- tests := []struct {
- name string
- args args
- wantResponse []byte
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- gotResponse, err := Get(tt.args.url)
- if (err != nil) != tt.wantErr {
- t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(gotResponse, tt.wantResponse) {
- t.Errorf("Get() = %v, want %v", gotResponse, tt.wantResponse)
- }
- })
- }
-}
-
-func TestPost(t *testing.T) {
- type args struct {
- ctx context.Context
- url string
- header map[string]string
- data any
- timeout int
- }
- tests := []struct {
- name string
- args args
- wantContent []byte
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- gotContent, err := Post(tt.args.ctx, tt.args.url, tt.args.header, tt.args.data, tt.args.timeout)
- if (err != nil) != tt.wantErr {
- t.Errorf("Post() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(gotContent, tt.wantContent) {
- t.Errorf("Post() = %v, want %v", gotContent, tt.wantContent)
- }
- })
- }
-}
-
-func TestPostReturn(t *testing.T) {
- type args struct {
- ctx context.Context
- url string
- header map[string]string
- input any
- output any
- timeOutSecond int
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := PostReturn(tt.args.ctx, tt.args.url, tt.args.header, tt.args.input, tt.args.output, tt.args.timeOutSecond); (err != nil) != tt.wantErr {
- t.Errorf("PostReturn() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
-
-func Test_callBackPostReturn(t *testing.T) {
- type args struct {
- ctx context.Context
- url string
- command string
- input any
- output callbackstruct.CallbackResp
- callbackConfig config.CallBackConfig
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := callBackPostReturn(tt.args.ctx, tt.args.url, tt.args.command, tt.args.input, tt.args.output, tt.args.callbackConfig); (err != nil) != tt.wantErr {
- t.Errorf("callBackPostReturn() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
-
-func TestCallBackPostReturn(t *testing.T) {
- type args struct {
- ctx context.Context
- url string
- req callbackstruct.CallbackReq
- resp callbackstruct.CallbackResp
- callbackConfig config.CallBackConfig
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- // TODO: Add test cases.
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := CallBackPostReturn(tt.args.ctx, tt.args.url, tt.args.req, tt.args.resp, tt.args.callbackConfig); (err != nil) != tt.wantErr {
- t.Errorf("CallBackPostReturn() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
diff --git a/pkg/common/kafka/consumer.go b/pkg/common/kafka/consumer.go
deleted file mode 100644
index 6627c21aea..0000000000
--- a/pkg/common/kafka/consumer.go
+++ /dev/null
@@ -1,71 +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 kafka
-
-import (
- "sync"
-
- "github.com/IBM/sarama"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-type Consumer struct {
- addr []string
- WG sync.WaitGroup
- Topic string
- PartitionList []int32
- Consumer sarama.Consumer
-}
-
-func NewKafkaConsumer(addr []string, topic string, config *config.GlobalConfig) (*Consumer, error) {
- p := Consumer{}
- p.Topic = topic
- p.addr = addr
- consumerConfig := sarama.NewConfig()
- if config.Kafka.Username != "" && config.Kafka.Password != "" {
- consumerConfig.Net.SASL.Enable = true
- consumerConfig.Net.SASL.User = config.Kafka.Username
- consumerConfig.Net.SASL.Password = config.Kafka.Password
- }
- var tlsConfig *TLSConfig
- if config.Kafka.TLS != nil {
- tlsConfig = &TLSConfig{
- CACrt: config.Kafka.TLS.CACrt,
- ClientCrt: config.Kafka.TLS.ClientCrt,
- ClientKey: config.Kafka.TLS.ClientKey,
- ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
- InsecureSkipVerify: false,
- }
- }
- err := SetupTLSConfig(consumerConfig, tlsConfig)
- if err != nil {
- return nil, err
- }
- consumer, err := sarama.NewConsumer(p.addr, consumerConfig)
- if err != nil {
- return nil, errs.Wrap(err, "NewKafkaConsumer: creating consumer failed")
- }
- p.Consumer = consumer
-
- partitionList, err := consumer.Partitions(p.Topic)
- if err != nil {
- return nil, errs.Wrap(err, "NewKafkaConsumer: getting partitions failed")
- }
- p.PartitionList = partitionList
-
- return &p, nil
-
-}
diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go
deleted file mode 100644
index 95794aacb7..0000000000
--- a/pkg/common/kafka/consumer_group.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright © 2023 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package kafka
-
-import (
- "context"
- "errors"
- "strings"
-
- "github.com/IBM/sarama"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
-)
-
-type MConsumerGroup struct {
- sarama.ConsumerGroup
- groupID string
- topics []string
-}
-
-type MConsumerGroupConfig struct {
- KafkaVersion sarama.KafkaVersion
- OffsetsInitial int64
- IsReturnErr bool
- UserName string
- Password string
-}
-
-func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string, tlsConfig *TLSConfig) (*MConsumerGroup, error) {
- consumerGroupConfig := sarama.NewConfig()
- consumerGroupConfig.Version = consumerConfig.KafkaVersion
- consumerGroupConfig.Consumer.Offsets.Initial = consumerConfig.OffsetsInitial
- consumerGroupConfig.Consumer.Return.Errors = consumerConfig.IsReturnErr
- if consumerConfig.UserName != "" && consumerConfig.Password != "" {
- consumerGroupConfig.Net.SASL.Enable = true
- consumerGroupConfig.Net.SASL.User = consumerConfig.UserName
- consumerGroupConfig.Net.SASL.Password = consumerConfig.Password
- }
-
- SetupTLSConfig(consumerGroupConfig, tlsConfig)
- consumerGroup, err := sarama.NewConsumerGroup(addrs, groupID, consumerGroupConfig)
- if err != nil {
- return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, consumerConfig.UserName, consumerConfig.Password)
- }
-
- return &MConsumerGroup{
- consumerGroup,
- groupID,
- topics,
- }, nil
-}
-
-func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) context.Context {
- return GetContextWithMQHeader(cMsg.Headers)
-}
-
-func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) {
- log.ZDebug(ctx, "register consumer group", "groupID", mc.groupID)
- for {
- err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler)
- if errors.Is(err, sarama.ErrClosedConsumerGroup) {
- return
- }
- if errors.Is(err, context.Canceled) {
- return
- }
- if err != nil {
- log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID)
- }
- }
-}
-
-func (mc *MConsumerGroup) Close() error {
- return mc.ConsumerGroup.Close()
-}
diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go
deleted file mode 100644
index 8ee938b515..0000000000
--- a/pkg/common/kafka/producer.go
+++ /dev/null
@@ -1,197 +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 kafka
-
-import (
- "bytes"
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/IBM/sarama"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/mcontext"
- "google.golang.org/protobuf/proto"
-)
-
-const maxRetry = 10 // number of retries
-
-var errEmptyMsg = errors.New("kafka binary msg is empty")
-
-// Producer represents a Kafka producer.
-type Producer struct {
- addr []string
- topic string
- config *sarama.Config
- producer sarama.SyncProducer
-}
-
-type ProducerConfig struct {
- ProducerAck string
- CompressType string
- Username string
- Password string
-}
-
-// NewKafkaProducer initializes a new Kafka producer.
-func NewKafkaProducer(addr []string, topic string, producerConfig *ProducerConfig, tlsConfig *TLSConfig) (*Producer, error) {
- p := Producer{
- addr: addr,
- topic: topic,
- config: sarama.NewConfig(),
- }
-
- // Set producer return flags
- p.config.Producer.Return.Successes = true
- p.config.Producer.Return.Errors = true
-
- // Set partitioner strategy
- p.config.Producer.Partitioner = sarama.NewHashPartitioner
-
- // Configure producer acknowledgement level
- configureProducerAck(&p, producerConfig.ProducerAck)
-
- // Configure message compression
- configureCompression(&p, producerConfig.CompressType)
-
- // Get Kafka configuration from environment variables or fallback to config file
- kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", producerConfig.Username)
- kafkaPassword := getEnvOrConfig("KAFKA_PASSWORD", producerConfig.Password)
- kafkaAddr := getKafkaAddrFromEnv(addr) // Updated to use the new function
-
- // Configure SASL authentication if credentials are provided
- if kafkaUsername != "" && kafkaPassword != "" {
- p.config.Net.SASL.Enable = true
- p.config.Net.SASL.User = kafkaUsername
- p.config.Net.SASL.Password = kafkaPassword
- }
-
- // Set the Kafka address
- p.addr = kafkaAddr
-
- // Set up TLS configuration (if required)
- SetupTLSConfig(p.config, tlsConfig)
-
- // Create the producer with retries
- var err error
- for i := 0; i <= maxRetry; i++ {
- p.producer, err = sarama.NewSyncProducer(p.addr, p.config)
- if err == nil {
- return &p, errs.Wrap(err)
- }
- time.Sleep(1 * time.Second) // Wait before retrying
- }
- // Panic if unable to create producer after retries
- if err != nil {
- return nil, errs.Wrap(errors.New("failed to create Kafka producer: " + err.Error()))
- }
-
- return &p, nil
-}
-
-// configureProducerAck configures the producer's acknowledgement level.
-func configureProducerAck(p *Producer, ackConfig string) {
- switch strings.ToLower(ackConfig) {
- case "no_response":
- p.config.Producer.RequiredAcks = sarama.NoResponse
- case "wait_for_local":
- p.config.Producer.RequiredAcks = sarama.WaitForLocal
- case "wait_for_all":
- p.config.Producer.RequiredAcks = sarama.WaitForAll
- default:
- p.config.Producer.RequiredAcks = sarama.WaitForAll
- }
-}
-
-// configureCompression configures the message compression type for the producer.
-func configureCompression(p *Producer, compressType string) {
- var compress = sarama.CompressionNone
- err := compress.UnmarshalText(bytes.ToLower([]byte(compressType)))
- if err != nil {
- fmt.Printf("Failed to configure compression: %v\n", err)
- return
- }
- p.config.Producer.Compression = compress
-}
-
-// GetMQHeaderWithContext extracts message queue headers from the context.
-func GetMQHeaderWithContext(ctx context.Context) ([]sarama.RecordHeader, error) {
- operationID, opUserID, platform, connID, err := mcontext.GetCtxInfos(ctx)
- if err != nil {
- return nil, err
- }
- return []sarama.RecordHeader{
- {Key: []byte(constant.OperationID), Value: []byte(operationID)},
- {Key: []byte(constant.OpUserID), Value: []byte(opUserID)},
- {Key: []byte(constant.OpUserPlatform), Value: []byte(platform)},
- {Key: []byte(constant.ConnID), Value: []byte(connID)},
- }, nil
-}
-
-// GetContextWithMQHeader creates a context from message queue headers.
-func GetContextWithMQHeader(header []*sarama.RecordHeader) context.Context {
- var values []string
- for _, recordHeader := range header {
- values = append(values, string(recordHeader.Value))
- }
- return mcontext.WithMustInfoCtx(values) // Attach extracted values to context
-}
-
-// SendMessage sends a message to the Kafka topic configured in the Producer.
-func (p *Producer) SendMessage(ctx context.Context, key string, msg proto.Message) (int32, int64, error) {
- log.ZDebug(ctx, "SendMessage", "msg", msg, "topic", p.topic, "key", key)
-
- // Marshal the protobuf message
- bMsg, err := proto.Marshal(msg)
- if err != nil {
- return 0, 0, errs.Wrap(err, "kafka proto Marshal err")
- }
- if len(bMsg) == 0 {
- return 0, 0, errs.Wrap(errEmptyMsg, "")
- }
-
- // Prepare Kafka message
- kMsg := &sarama.ProducerMessage{
- Topic: p.topic,
- Key: sarama.StringEncoder(key),
- Value: sarama.ByteEncoder(bMsg),
- }
-
- // Validate message key and value
- if kMsg.Key.Length() == 0 || kMsg.Value.Length() == 0 {
- return 0, 0, errs.Wrap(errEmptyMsg)
- }
-
- // Attach context metadata as headers
- header, err := GetMQHeaderWithContext(ctx)
- if err != nil {
- return 0, 0, err
- }
- kMsg.Headers = header
-
- // Send the message
- partition, offset, err := p.producer.SendMessage(kMsg)
- if err != nil {
- log.ZWarn(ctx, "p.producer.SendMessage error", err)
- return 0, 0, errs.Wrap(err)
- }
-
- log.ZDebug(ctx, "ByteEncoder SendMessage end", "key", kMsg.Key, "key length", kMsg.Value.Length())
- return partition, offset, nil
-}
diff --git a/pkg/common/kafka/util.go b/pkg/common/kafka/util.go
deleted file mode 100644
index 4e2a027143..0000000000
--- a/pkg/common/kafka/util.go
+++ /dev/null
@@ -1,77 +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 kafka
-
-import (
- "fmt"
- "os"
- "strings"
-
- "github.com/IBM/sarama"
- "github.com/openimsdk/open-im-server/v3/pkg/common/tls"
-)
-
-type TLSConfig struct {
- CACrt string
- ClientCrt string
- ClientKey string
- ClientKeyPwd string
- InsecureSkipVerify bool
-}
-
-// SetupTLSConfig set up the TLS config from config file.
-func SetupTLSConfig(cfg *sarama.Config, tlsConfig *TLSConfig) error {
- if tlsConfig != nil {
- cfg.Net.TLS.Enable = true
- tlsConfig, err := tls.NewTLSConfig(
- tlsConfig.ClientCrt,
- tlsConfig.ClientKey,
- tlsConfig.CACrt,
- []byte(tlsConfig.ClientKeyPwd),
- tlsConfig.InsecureSkipVerify,
- )
- if err != nil {
- return err
- }
- cfg.Net.TLS.Config = tlsConfig
- }
- return nil
-}
-
-// getEnvOrConfig returns the value of the environment variable if it exists,
-// otherwise, it returns the value from the configuration file.
-func getEnvOrConfig(envName string, configValue string) string {
- if value, exists := os.LookupEnv(envName); exists {
- return value
- }
- return configValue
-}
-
-// getKafkaAddrFromEnv returns the Kafka addresses combined from the KAFKA_ADDRESS and KAFKA_PORT environment variables.
-// If the environment variables are not set, it returns the fallback value.
-func getKafkaAddrFromEnv(fallback []string) []string {
- envAddr := os.Getenv("KAFKA_ADDRESS")
- envPort := os.Getenv("KAFKA_PORT")
-
- if envAddr != "" && envPort != "" {
- addresses := strings.Split(envAddr, ",")
- for i, addr := range addresses {
- addresses[i] = fmt.Sprintf("%s:%s", addr, envPort)
- }
- return addresses
- }
-
- return fallback
-}
diff --git a/pkg/common/prommetrics/doc.go b/pkg/common/prommetrics/doc.go
new file mode 100644
index 0000000000..c5108b4cba
--- /dev/null
+++ b/pkg/common/prommetrics/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package prommetrics // import "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
diff --git a/pkg/common/prommetrics/prommetrics.go b/pkg/common/prommetrics/prommetrics.go
index 9089e7b5fe..47e5d02b88 100644
--- a/pkg/common/prommetrics/prommetrics.go
+++ b/pkg/common/prommetrics/prommetrics.go
@@ -31,17 +31,17 @@ func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *g
return reg, grpcMetrics, nil
}
-func GetGrpcCusMetrics(registerName string, config *config2.GlobalConfig) []prometheus.Collector {
+func GetGrpcCusMetrics(registerName string, share *config2.Share) []prometheus.Collector {
switch registerName {
- case config.RpcRegisterName.OpenImMessageGatewayName:
+ case share.RpcRegisterName.MessageGateway:
return []prometheus.Collector{OnlineUserGauge}
- case config.RpcRegisterName.OpenImMsgName:
+ case share.RpcRegisterName.Msg:
return []prometheus.Collector{SingleChatMsgProcessSuccessCounter, SingleChatMsgProcessFailedCounter, GroupChatMsgProcessSuccessCounter, GroupChatMsgProcessFailedCounter}
case "Transfer":
return []prometheus.Collector{MsgInsertRedisSuccessCounter, MsgInsertRedisFailedCounter, MsgInsertMongoSuccessCounter, MsgInsertMongoFailedCounter, SeqSetFailedCounter}
- case config.RpcRegisterName.OpenImPushName:
+ case share.RpcRegisterName.Push:
return []prometheus.Collector{MsgOfflinePushFailedCounter}
- case config.RpcRegisterName.OpenImAuthName:
+ case share.RpcRegisterName.Auth:
return []prometheus.Collector{UserLoginCounter}
default:
return nil
diff --git a/pkg/common/prommetrics/prommetrics_test.go b/pkg/common/prommetrics/prommetrics_test.go
index eb6f3c7716..65b05652f7 100644
--- a/pkg/common/prommetrics/prommetrics_test.go
+++ b/pkg/common/prommetrics/prommetrics_test.go
@@ -19,8 +19,6 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
-
- config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
)
func TestNewGrpcPromObj(t *testing.T) {
@@ -57,22 +55,22 @@ func TestNewGrpcPromObj(t *testing.T) {
assert.True(t, found, "Custom metric not found in registry")
}
-func TestGetGrpcCusMetrics(t *testing.T) {
- conf := config2.NewGlobalConfig()
-
- config2.InitConfig(conf, "../../config")
- // Test various cases based on the switch statement in the GetGrpcCusMetrics function.
- testCases := []struct {
- name string
- expected int // The expected number of metrics for each case.
- }{
- {conf.RpcRegisterName.OpenImMessageGatewayName, 1},
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- metrics := GetGrpcCusMetrics(tc.name, conf)
- assert.Len(t, metrics, tc.expected)
- })
- }
-}
+//func TestGetGrpcCusMetrics(t *testing.T) {
+// conf := config2.NewGlobalConfig()
+//
+// config2.InitConfig(conf, "../../config")
+// // Test various cases based on the switch statement in the GetGrpcCusMetrics function.
+// testCases := []struct {
+// name string
+// expected int // The expected number of metrics for each case.
+// }{
+// {conf.RpcRegisterName.OpenImMessageGatewayName, 1},
+// }
+//
+// for _, tc := range testCases {
+// t.Run(tc.name, func(t *testing.T) {
+// metrics := GetGrpcCusMetrics(tc.name, &conf.RpcRegisterName)
+// assert.Len(t, metrics, tc.expected)
+// })
+// }
+//}
diff --git a/pkg/common/redispubsub/doc.go b/pkg/common/redispubsub/doc.go
new file mode 100644
index 0000000000..19b2e38f2e
--- /dev/null
+++ b/pkg/common/redispubsub/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package redispubsub // import "github.com/openimsdk/open-im-server/v3/pkg/common/redispubsub"
diff --git a/pkg/common/servererrs/code.go b/pkg/common/servererrs/code.go
new file mode 100644
index 0000000000..3d0aa4a717
--- /dev/null
+++ b/pkg/common/servererrs/code.go
@@ -0,0 +1,95 @@
+// 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 servererrs
+
+// UnknownCode represents the error code when code is not parsed or parsed code equals 0.
+const UnknownCode = 1000
+
+// Error codes for various error scenarios.
+const (
+ FormattingError = 10001 // Error in formatting
+ HasRegistered = 10002 // user has already registered
+ NotRegistered = 10003 // user is not registered
+ PasswordErr = 10004 // Password error
+ GetIMTokenErr = 10005 // Error in getting IM token
+ RepeatSendCode = 10006 // Repeat sending code
+ MailSendCodeErr = 10007 // Error in sending code via email
+ SmsSendCodeErr = 10008 // Error in sending code via SMS
+ CodeInvalidOrExpired = 10009 // Code is invalid or expired
+ RegisterFailed = 10010 // Registration failed
+ ResetPasswordFailed = 10011 // Resetting password failed
+ RegisterLimit = 10012 // Registration limit exceeded
+ LoginLimit = 10013 // Login limit exceeded
+ InvitationError = 10014 // Error in invitation
+)
+
+// General error codes.
+const (
+ NoError = 0 // No error
+ DatabaseError = 90002 // Database error (redis/mysql, etc.)
+ NetworkError = 90004 // Network error
+ DataError = 90007 // Data error
+
+ CallbackError = 80000
+
+ // General error codes.
+ ServerInternalError = 500 // Server internal error
+ ArgsError = 1001 // Input parameter error
+ NoPermissionError = 1002 // Insufficient permission
+ DuplicateKeyError = 1003
+ RecordNotFoundError = 1004 // Record does not exist
+
+ // Account error codes.
+ UserIDNotFoundError = 1101 // UserID does not exist or is not registered
+ RegisteredAlreadyError = 1102 // user is already registered
+
+ // Group error codes.
+ GroupIDNotFoundError = 1201 // GroupID does not exist
+ GroupIDExisted = 1202 // GroupID already exists
+ NotInGroupYetError = 1203 // Not in the group yet
+ DismissedAlreadyError = 1204 // Group has already been dismissed
+ GroupTypeNotSupport = 1205
+ GroupRequestHandled = 1206
+
+ // Relationship error codes.
+ CanNotAddYourselfError = 1301 // Cannot add yourself as a friend
+ BlockedByPeer = 1302 // Blocked by the peer
+ NotPeersFriend = 1303 // Not the peer's friend
+ RelationshipAlreadyError = 1304 // Already in a friend relationship
+
+ // Message error codes.
+ MessageHasReadDisable = 1401
+ MutedInGroup = 1402 // Member muted in the group
+ MutedGroup = 1403 // Group is muted
+ MsgAlreadyRevoke = 1404 // Message already revoked
+
+ // Token error codes.
+ TokenExpiredError = 1501
+ TokenInvalidError = 1502
+ TokenMalformedError = 1503
+ TokenNotValidYetError = 1504
+ TokenUnknownError = 1505
+ TokenKickedError = 1506
+ TokenNotExistError = 1507
+
+ // Long connection gateway error codes.
+ ConnOverMaxNumLimit = 1601
+ ConnArgsErr = 1602
+ PushMsgErr = 1603
+ IOSBackgroundPushErr = 1604
+
+ // S3 error codes.
+ FileUploadedExpiredError = 1701 // Upload expired
+)
diff --git a/pkg/common/servererrs/doc.go b/pkg/common/servererrs/doc.go
new file mode 100644
index 0000000000..d408a75146
--- /dev/null
+++ b/pkg/common/servererrs/doc.go
@@ -0,0 +1 @@
+package servererrs // import "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
diff --git a/pkg/common/servererrs/predefine.go b/pkg/common/servererrs/predefine.go
new file mode 100644
index 0000000000..ab09aa5123
--- /dev/null
+++ b/pkg/common/servererrs/predefine.go
@@ -0,0 +1,68 @@
+// 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 servererrs
+
+import "github.com/openimsdk/tools/errs"
+
+var (
+ ErrDatabase = errs.NewCodeError(DatabaseError, "DatabaseError")
+ ErrNetwork = errs.NewCodeError(NetworkError, "NetworkError")
+ ErrCallback = errs.NewCodeError(CallbackError, "CallbackError")
+ ErrCallbackContinue = errs.NewCodeError(CallbackError, "ErrCallbackContinue")
+
+ ErrInternalServer = errs.NewCodeError(ServerInternalError, "ServerInternalError")
+ ErrArgs = errs.NewCodeError(ArgsError, "ArgsError")
+ ErrNoPermission = errs.NewCodeError(NoPermissionError, "NoPermissionError")
+ ErrDuplicateKey = errs.NewCodeError(DuplicateKeyError, "DuplicateKeyError")
+ ErrRecordNotFound = errs.NewCodeError(RecordNotFoundError, "RecordNotFoundError")
+
+ ErrUserIDNotFound = errs.NewCodeError(UserIDNotFoundError, "UserIDNotFoundError")
+ ErrGroupIDNotFound = errs.NewCodeError(GroupIDNotFoundError, "GroupIDNotFoundError")
+ ErrGroupIDExisted = errs.NewCodeError(GroupIDExisted, "GroupIDExisted")
+
+ ErrNotInGroupYet = errs.NewCodeError(NotInGroupYetError, "NotInGroupYetError")
+ ErrDismissedAlready = errs.NewCodeError(DismissedAlreadyError, "DismissedAlreadyError")
+ ErrRegisteredAlready = errs.NewCodeError(RegisteredAlreadyError, "RegisteredAlreadyError")
+ ErrGroupTypeNotSupport = errs.NewCodeError(GroupTypeNotSupport, "")
+ ErrGroupRequestHandled = errs.NewCodeError(GroupRequestHandled, "GroupRequestHandled")
+
+ ErrData = errs.NewCodeError(DataError, "DataError")
+ ErrTokenExpired = errs.NewCodeError(TokenExpiredError, "TokenExpiredError")
+ ErrTokenInvalid = errs.NewCodeError(TokenInvalidError, "TokenInvalidError") //
+ ErrTokenMalformed = errs.NewCodeError(TokenMalformedError, "TokenMalformedError") //
+ ErrTokenNotValidYet = errs.NewCodeError(TokenNotValidYetError, "TokenNotValidYetError") //
+ ErrTokenUnknown = errs.NewCodeError(TokenUnknownError, "TokenUnknownError") //
+ ErrTokenKicked = errs.NewCodeError(TokenKickedError, "TokenKickedError")
+ ErrTokenNotExist = errs.NewCodeError(TokenNotExistError, "TokenNotExistError") //
+
+ ErrMessageHasReadDisable = errs.NewCodeError(MessageHasReadDisable, "MessageHasReadDisable")
+
+ ErrCanNotAddYourself = errs.NewCodeError(CanNotAddYourselfError, "CanNotAddYourselfError")
+ ErrBlockedByPeer = errs.NewCodeError(BlockedByPeer, "BlockedByPeer")
+ ErrNotPeersFriend = errs.NewCodeError(NotPeersFriend, "NotPeersFriend")
+ ErrRelationshipAlready = errs.NewCodeError(RelationshipAlreadyError, "RelationshipAlreadyError")
+
+ ErrMutedInGroup = errs.NewCodeError(MutedInGroup, "MutedInGroup")
+ ErrMutedGroup = errs.NewCodeError(MutedGroup, "MutedGroup")
+ ErrMsgAlreadyRevoke = errs.NewCodeError(MsgAlreadyRevoke, "MsgAlreadyRevoke")
+
+ ErrConnOverMaxNumLimit = errs.NewCodeError(ConnOverMaxNumLimit, "ConnOverMaxNumLimit")
+
+ ErrConnArgsErr = errs.NewCodeError(ConnArgsErr, "args err, need token, sendID, platformID")
+ ErrPushMsgErr = errs.NewCodeError(PushMsgErr, "push msg err")
+ ErrIOSBackgroundPushErr = errs.NewCodeError(IOSBackgroundPushErr, "ios background push err")
+
+ ErrFileUploadedExpired = errs.NewCodeError(FileUploadedExpiredError, "FileUploadedExpiredError")
+)
diff --git a/pkg/common/servererrs/relation.go b/pkg/common/servererrs/relation.go
new file mode 100644
index 0000000000..62b0561471
--- /dev/null
+++ b/pkg/common/servererrs/relation.go
@@ -0,0 +1,58 @@
+// 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 servererrs
+
+import "github.com/openimsdk/tools/errs"
+
+var Relation = &relation{m: make(map[int]map[int]struct{})}
+
+func init() {
+ Relation.Add(errs.RecordNotFoundError, UserIDNotFoundError)
+ Relation.Add(errs.RecordNotFoundError, GroupIDNotFoundError)
+ Relation.Add(errs.DuplicateKeyError, GroupIDExisted)
+}
+
+type relation struct {
+ m map[int]map[int]struct{}
+}
+
+func (r *relation) Add(codes ...int) {
+ if len(codes) < 2 {
+ panic("codes length must be greater than 2")
+ }
+ for i := 1; i < len(codes); i++ {
+ parent := codes[i-1]
+ s, ok := r.m[parent]
+ if !ok {
+ s = make(map[int]struct{})
+ r.m[parent] = s
+ }
+ for _, code := range codes[i:] {
+ s[code] = struct{}{}
+ }
+ }
+}
+
+func (r *relation) Is(parent, child int) bool {
+ if parent == child {
+ return true
+ }
+ s, ok := r.m[parent]
+ if !ok {
+ return false
+ }
+ _, ok = s[child]
+ return ok
+}
diff --git a/pkg/common/startrpc/doc.go b/pkg/common/startrpc/doc.go
index 206e7900f7..fce7309f42 100644
--- a/pkg/common/startrpc/doc.go
+++ b/pkg/common/startrpc/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,5 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// Package startrpc start rpc server.
-package startrpc
+package startrpc // import "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc"
diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go
index bf95509d11..ebcd5aa7cc 100644
--- a/pkg/common/startrpc/start.go
+++ b/pkg/common/startrpc/start.go
@@ -16,8 +16,10 @@ package startrpc
import (
"context"
- "errors"
"fmt"
+ config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/utils/datautil"
+ "github.com/prometheus/client_golang/prometheus"
"net"
"net/http"
"os"
@@ -27,59 +29,61 @@ import (
"syscall"
"time"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/mw"
- "github.com/OpenIMSDK/tools/network"
grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config"
kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister"
"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
- "github.com/prometheus/client_golang/prometheus"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mw"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/openimsdk/tools/utils/network"
"github.com/prometheus/client_golang/prometheus/promhttp"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
// Start rpc server.
-func Start(
- rpcPort int,
- rpcRegisterName string,
- prometheusPort int,
- config *config2.GlobalConfig,
- rpcFn func(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error,
- options ...grpc.ServerOption,
-) error {
- fmt.Printf("start %s server, port: %d, prometheusPort: %d, OpenIM version: %s\n",
- rpcRegisterName, rpcPort, prometheusPort, config2.Version)
- rpcTcpAddr := net.JoinHostPort(network.GetListenIP(config.Rpc.ListenIP), strconv.Itoa(rpcPort))
+func Start[T any](ctx context.Context, zookeeperConfig *config2.ZooKeeper, prometheusConfig *config2.Prometheus, listenIP,
+ registerIP string, rpcPorts []int, index int, rpcRegisterName string, share *config2.Share, config T, rpcFn func(ctx context.Context,
+ config T, client discovery.SvcDiscoveryRegistry, server *grpc.Server) error, options ...grpc.ServerOption) error {
+
+ rpcPort, err := datautil.GetElemByIndex(rpcPorts, index)
+ if err != nil {
+ return err
+ }
+ prometheusPort, err := datautil.GetElemByIndex(prometheusConfig.Ports, index)
+ if err != nil {
+ return err
+ }
+ log.CInfo(ctx, "RPC server is initializing", "rpcRegisterName", rpcRegisterName, "rpcPort", rpcPort,
+ "prometheusPort", prometheusPort)
+ rpcTcpAddr := net.JoinHostPort(network.GetListenIP(listenIP), strconv.Itoa(rpcPort))
listener, err := net.Listen(
"tcp",
rpcTcpAddr,
)
if err != nil {
- return errs.Wrap(err, "listen err", rpcTcpAddr)
+ return errs.WrapMsg(err, "listen err", "rpcTcpAddr", rpcTcpAddr)
}
defer listener.Close()
- client, err := kdisc.NewDiscoveryRegister(config)
+ client, err := kdisc.NewDiscoveryRegister(zookeeperConfig, share)
if err != nil {
return err
}
defer client.Close()
client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")))
- registerIP, err := network.GetRpcRegisterIP(config.Rpc.RegisterIP)
+ registerIP, err = network.GetRpcRegisterIP(registerIP)
if err != nil {
- return errs.Wrap(err)
+ return err
}
var reg *prometheus.Registry
var metric *grpcprometheus.ServerMetrics
- if config.Prometheus.Enable {
- cusMetrics := prommetrics.GetGrpcCusMetrics(rpcRegisterName, config)
+ if prometheusConfig.Enable {
+ cusMetrics := prommetrics.GetGrpcCusMetrics(rpcRegisterName, share)
reg, metric, _ = prommetrics.NewGrpcPromObj(cusMetrics)
options = append(options, mw.GrpcServer(), grpc.StreamInterceptor(metric.StreamServerInterceptor()),
grpc.UnaryInterceptor(metric.UnaryServerInterceptor()))
@@ -93,10 +97,11 @@ func Start(
once.Do(srv.GracefulStop)
}()
- err = rpcFn(config, client, srv)
+ err = rpcFn(ctx, config, client, srv)
if err != nil {
return err
}
+
err = client.Register(
rpcRegisterName,
registerIP,
@@ -104,7 +109,7 @@ func Start(
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
- return errs.Wrap(err)
+ return err
}
var (
@@ -112,13 +117,14 @@ func Start(
netErr error
httpServer *http.Server
)
+
go func() {
- if config.Prometheus.Enable && prometheusPort != 0 {
+ if prometheusConfig.Enable && prometheusPort != 0 {
metric.InitializeMetrics(srv)
// Create a HTTP server for prometheus.
httpServer = &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)}
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
- netErr = errs.Wrap(err, "prometheus start err", httpServer.Addr)
+ netErr = errs.WrapMsg(err, "prometheus start err", httpServer.Addr)
netDone <- struct{}{}
}
}
@@ -127,7 +133,7 @@ func Start(
go func() {
err := srv.Serve(listener)
if err != nil {
- netErr = errs.Wrap(err, "rpc start err: ", rpcTcpAddr)
+ netErr = errs.WrapMsg(err, "rpc start err: ", rpcTcpAddr)
netDone <- struct{}{}
}
}()
@@ -136,7 +142,7 @@ func Start(
signal.Notify(sigs, syscall.SIGTERM)
select {
case <-sigs:
- util.SIGTERMExit()
+ program.SIGTERMExit()
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()
if err := gracefulStopWithCtx(ctx, srv.GracefulStop); err != nil {
@@ -146,7 +152,7 @@ func Start(
defer cancel()
err := httpServer.Shutdown(ctx)
if err != nil {
- return errs.Wrap(err, "shutdown err")
+ return errs.WrapMsg(err, "shutdown err")
}
return nil
case <-netDone:
@@ -163,7 +169,7 @@ func gracefulStopWithCtx(ctx context.Context, f func()) error {
}()
select {
case <-ctx.Done():
- return errs.Wrap(errors.New("timeout, ctx graceful stop"))
+ return errs.New("timeout, ctx graceful stop")
case <-done:
return nil
}
diff --git a/pkg/common/startrpc/start_test.go b/pkg/common/startrpc/start_test.go
deleted file mode 100644
index 754fc9c502..0000000000
--- a/pkg/common/startrpc/start_test.go
+++ /dev/null
@@ -1,69 +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 startrpc
-
-import (
- "fmt"
- "net"
- "testing"
- "time"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "google.golang.org/grpc"
-)
-
-// mockRpcFn is a mock gRPC function for testing.
-func mockRpcFn(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error {
- // Implement a mock gRPC service registration logic if needed
- return nil
-}
-
-// TestStart tests the Start function for starting the RPC server.
-func TestStart(t *testing.T) {
- // Use an available port for testing purposes.
- testRpcPort := 12345
- testPrometheusPort := 12346
- testRpcRegisterName := "testService"
-
- doneChan := make(chan error, 1)
-
- go func() {
- err := Start(testRpcPort, testRpcRegisterName, testPrometheusPort,
- config.NewGlobalConfig(), mockRpcFn)
- doneChan <- err
- }()
-
- // Give some time for the server to start.
- time.Sleep(2 * time.Second)
-
- // Test if the server is listening on the RPC port.
- conn, err := net.Dial("tcp", fmt.Sprintf(":%d", testRpcPort))
- if err != nil {
- // t.Fatalf("Failed to dial the RPC server: %v", err)
- // TODO: Fix this test
- t.Skip("Failed to dial the RPC server")
- }
- conn.Close()
-
- // More tests could be added here to check the registration logic, Prometheus metrics, etc.
-
- // Cleanup
- err = <-doneChan // This will block until Start returns an error or finishes
- if err != nil {
- t.Fatalf("Start returned an error: %v", err)
- }
-}
diff --git a/pkg/common/tls/tls.go b/pkg/common/tls/tls.go
deleted file mode 100755
index 9666ed9c87..0000000000
--- a/pkg/common/tls/tls.go
+++ /dev/null
@@ -1,88 +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 tls
-
-import (
- "crypto/tls"
- "crypto/x509"
- "encoding/pem"
- "errors"
- "os"
-
- "github.com/OpenIMSDK/tools/errs"
-)
-
-// decryptPEM decrypts a PEM block using a password.
-func decryptPEM(data []byte, passphrase []byte) ([]byte, error) {
- if len(passphrase) == 0 {
- return data, nil
- }
- b, _ := pem.Decode(data)
- d, err := x509.DecryptPEMBlock(b, passphrase)
- if err != nil {
- return nil, err
- }
- return pem.EncodeToMemory(&pem.Block{
- Type: b.Type,
- Bytes: d,
- }), nil
-}
-
-func readEncryptablePEMBlock(path string, pwd []byte) ([]byte, error) {
- data, err := os.ReadFile(path)
- if err != nil {
- return nil, err
- }
- return decryptPEM(data, pwd)
-}
-
-// NewTLSConfig setup the TLS config from general config file.
-func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte, insecureSkipVerify bool) (*tls.Config, error) {
- tlsConfig := tls.Config{}
-
- if clientCertFile != "" && clientKeyFile != "" {
- certPEMBlock, err := os.ReadFile(clientCertFile)
- if err != nil {
- return nil, errs.Wrap(err, "NewTLSConfig: failed to read client cert file")
- }
- keyPEMBlock, err := readEncryptablePEMBlock(clientKeyFile, keyPwd)
- if err != nil {
- return nil, err
- }
-
- cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock)
- if err != nil {
- return nil, errs.Wrap(err, "NewTLSConfig: failed to create X509 key pair")
- }
- tlsConfig.Certificates = []tls.Certificate{cert}
- }
-
- if caCertFile != "" {
- caCert, err := os.ReadFile(caCertFile)
- if err != nil {
- return nil, errs.Wrap(err, "NewTLSConfig: failed to read CA cert file")
- }
-
- caCertPool := x509.NewCertPool()
- if ok := caCertPool.AppendCertsFromPEM(caCert); !ok {
- return nil, errors.New("NewTLSConfig: not a valid CA cert")
- }
- tlsConfig.RootCAs = caCertPool
- }
-
- tlsConfig.InsecureSkipVerify = insecureSkipVerify
-
- return &tlsConfig, nil
-}
diff --git a/pkg/common/version/base.go b/pkg/common/version/base.go
deleted file mode 100644
index 9a656e03a4..0000000000
--- a/pkg/common/version/base.go
+++ /dev/null
@@ -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 version
-
-// Base version information.
-//
-// This is the fallback data used when version information from git is not
-// provided via go ldflags. It provides an approximation of the Kubernetes
-// version for ad-hoc builds (e.g. `go build`) that cannot get the version
-// information from git.
-//
-// If you are looking at these fields in the git tree, they look
-// strange. They are modified on the fly by the build process. The
-// in-tree values are dummy values used for "git archive", which also
-// works for GitHub tar downloads.
-//
-// When releasing a new Kubernetes version, this file is updated by
-// build/mark_new_version.sh to reflect the new version, and then a
-// git annotated tag (using format vX.Y where X == Major version and Y
-// == Minor version) is created to point to the commit that updates.
-var (
- // TODO: Deprecate gitMajor and gitMinor, use only gitVersion
- // instead. First step in deprecation, keep the fields but make
- // them irrelevant. (Next we'll take it out, which may muck with
- // scripts consuming the kubectl version output - but most of
- // these should be looking at gitVersion already anyways.)
- gitMajor string = "" // major version, always numeric
- gitMinor string = "" // minor version, numeric possibly followed by "+"
-
- // semantic version, derived by build scripts (see
- // https://github.com/kubernetes/sig-release/blob/master/release-engineering/versioning.md#kubernetes-release-versioning
- // https://kubernetes.io/releases/version-skew-policy/
- // for a detailed discussion of this field)
- //
- // TODO: This field is still called "gitVersion" for legacy
- // reasons. For prerelease versions, the build metadata on the
- // semantic version is a git hash, but the version itself is no
- // longer the direct output of "git describe", but a slight
- // translation to be semver compliant.
-
- // NOTE: The $Format strings are replaced during 'git archive' thanks to the
- // companion .gitattributes file containing 'export-subst' in this same
- // directory. See also https://git-scm.com/docs/gitattributes
- gitVersion string = "latest"
- gitCommit string = "" // sha1 from git, output of $(git rev-parse HEAD)
- gitTreeState string = "" // state of git tree, either "clean" or "dirty"
-
- buildDate string = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ')
-)
diff --git a/pkg/common/version/types.go b/pkg/common/version/types.go
deleted file mode 100644
index da9c1ed901..0000000000
--- a/pkg/common/version/types.go
+++ /dev/null
@@ -1,44 +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 version
-
-// Info contains versioning information.
-// TODO: Add []string of api versions supported? It's still unclear
-// how we'll want to distribute that information.
-type Info struct {
- Major string `json:"major,omitempty"`
- Minor string `json:"minor,omitempty"`
- GitVersion string `json:"gitVersion"`
- GitTreeState string `json:"gitTreeState,omitempty"`
- GitCommit string `json:"gitCommit,omitempty"`
- BuildDate string `json:"buildDate"`
- GoVersion string `json:"goVersion"`
- Compiler string `json:"compiler"`
- Platform string `json:"platform"`
-}
-
-type Output struct {
- OpenIMServerVersion Info `json:"OpenIMServerVersion,omitempty" yaml:"OpenIMServerVersion,omitempty"`
- OpenIMClientVersion *OpenIMClientVersion `json:"OpenIMClientVersion,omitempty" yaml:"OpenIMClientVersion,omitempty"`
-}
-
-type OpenIMClientVersion struct {
- ClientVersion string `json:"clientVersion,omitempty" yaml:"clientVersion,omitempty"` //sdk core version
-}
-
-// String returns info as a human-friendly version string.
-func (info Info) String() string {
- return info.GitVersion
-}
diff --git a/pkg/common/version/version.go b/pkg/common/version/version.go
deleted file mode 100644
index 3b271b3f60..0000000000
--- a/pkg/common/version/version.go
+++ /dev/null
@@ -1,72 +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 version
-
-import (
- "fmt"
- "runtime"
-
- "gopkg.in/src-d/go-git.v4"
-)
-
-// Get returns the overall codebase version. It's for detecting
-// what code a binary was built from.
-func Get() Info {
- // These variables typically come from -ldflags settings and in
- // their absence fallback to the settings in ./base.go
- return Info{
- Major: gitMajor,
- Minor: gitMinor,
- GitVersion: gitVersion,
- GitTreeState: gitTreeState,
- GitCommit: gitCommit,
- BuildDate: buildDate,
- GoVersion: runtime.Version(),
- Compiler: runtime.Compiler,
- Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH),
- }
-}
-
-// GetClientVersion returns the git version of the OpenIM client repository.
-func GetClientVersion() (*OpenIMClientVersion, error) {
- clientVersion, err := getClientVersion()
- if err != nil {
- return nil, err
- }
- return &OpenIMClientVersion{
- ClientVersion: clientVersion,
- }, nil
-}
-
-func getClientVersion() (string, error) {
- repo, err := git.PlainClone("/tmp/openim-sdk-core", false, &git.CloneOptions{
- URL: "https://github.com/OpenIMSDK/openim-sdk-core",
- })
- if err != nil {
- return "", fmt.Errorf("error cloning repository: %w", err)
- }
-
- ref, err := repo.Head()
- if err != nil {
- return "", fmt.Errorf("error getting head reference: %w", err)
- }
-
- return ref.Hash().String(), nil
-}
-
-// GetSingleVersion returns single version of sealer.
-func GetSingleVersion() string {
- return gitVersion
-}
diff --git a/pkg/common/webhook/condition.go b/pkg/common/webhook/condition.go
new file mode 100644
index 0000000000..2c038f5674
--- /dev/null
+++ b/pkg/common/webhook/condition.go
@@ -0,0 +1,13 @@
+package webhook
+
+import (
+ "context"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+)
+
+func WithCondition(ctx context.Context, before *config.BeforeConfig, callback func(context.Context) error) error {
+ if !before.Enable {
+ return nil
+ }
+ return callback(ctx)
+}
diff --git a/pkg/common/db/s3/cont/doc.go b/pkg/common/webhook/doc.go
similarity index 80%
rename from pkg/common/db/s3/cont/doc.go
rename to pkg/common/webhook/doc.go
index 45737bc7c9..3a8e3e622c 100644
--- a/pkg/common/db/s3/cont/doc.go
+++ b/pkg/common/webhook/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package cont // import "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont"
+package webhook // import "github.com/openimsdk/open-im-server/v3/pkg/common/webhook"
diff --git a/pkg/common/webhook/http_client.go b/pkg/common/webhook/http_client.go
new file mode 100644
index 0000000000..e46f08806d
--- /dev/null
+++ b/pkg/common/webhook/http_client.go
@@ -0,0 +1,86 @@
+// 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 webhook
+
+import (
+ "context"
+ "encoding/json"
+ "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/mq/memamq"
+ "github.com/openimsdk/tools/utils/httputil"
+ "net/http"
+)
+
+type Client struct {
+ client *httputil.HTTPClient
+ url string
+ queue *memamq.MemoryQueue
+}
+
+const (
+ webhookWorkerCount = 2
+ webhookBufferSize = 100
+)
+
+func NewWebhookClient(url string, options ...*memamq.MemoryQueue) *Client {
+ var queue *memamq.MemoryQueue
+ if len(options) > 0 && options[0] != nil {
+ queue = options[0]
+ } else {
+ queue = memamq.NewMemoryQueue(webhookWorkerCount, webhookBufferSize)
+ }
+
+ http.DefaultTransport.(*http.Transport).MaxConnsPerHost = 100 // Enhance the default number of max connections per host
+
+ return &Client{
+ client: httputil.NewHTTPClient(httputil.NewClientConfig()),
+ url: url,
+ queue: queue,
+ }
+}
+
+func (c *Client) SyncPost(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, before *config.BeforeConfig) error {
+ return c.post(ctx, command, req, resp, before.Timeout)
+}
+
+func (c *Client) AsyncPost(ctx context.Context, command string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, after *config.AfterConfig) {
+ if after.Enable {
+ c.queue.Push(func() { c.post(ctx, command, req, resp, after.Timeout) })
+ }
+}
+
+func (c *Client) post(ctx context.Context, command string, input interface{}, output callbackstruct.CallbackResp, timeout int) error {
+ ctx = mcontext.WithMustInfoCtx([]string{mcontext.GetOperationID(ctx), mcontext.GetOpUserID(ctx), mcontext.GetOpUserPlatform(ctx), mcontext.GetConnID(ctx)})
+ fullURL := c.url + "/" + command
+ log.ZInfo(ctx, "webhook", "url", fullURL, "input", input, "config", timeout)
+ operationID, _ := ctx.Value(constant.OperationID).(string)
+ b, err := c.client.Post(ctx, fullURL, map[string]string{constant.OperationID: operationID}, input, timeout)
+ if err != nil {
+ return servererrs.ErrNetwork.WrapMsg(err.Error(), "post url", fullURL)
+ }
+ if err = json.Unmarshal(b, output); err != nil {
+ return servererrs.ErrData.WithDetail(err.Error() + " response format error")
+ }
+ if err := output.Parse(); err != nil {
+ return err
+ }
+ log.ZInfo(ctx, "webhook success", "url", fullURL, "input", input, "response", string(b))
+ return nil
+}
diff --git a/internal/rpc/statistics/statistics.go b/pkg/common/webhook/http_client_test.go
similarity index 96%
rename from internal/rpc/statistics/statistics.go
rename to pkg/common/webhook/http_client_test.go
index 2f81301a15..3c3aeb809d 100644
--- a/internal/rpc/statistics/statistics.go
+++ b/pkg/common/webhook/http_client_test.go
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package statistics
+package webhook
diff --git a/pkg/localcache/cache.go b/pkg/localcache/cache.go
index 91c490922f..0e040ad389 100644
--- a/pkg/localcache/cache.go
+++ b/pkg/localcache/cache.go
@@ -19,8 +19,8 @@ import (
"hash/fnv"
"unsafe"
- "github.com/openimsdk/localcache/link"
- "github.com/openimsdk/localcache/lru"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache/link"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache/lru"
)
type Cache[V any] interface {
diff --git a/pkg/localcache/doc.go b/pkg/localcache/doc.go
new file mode 100644
index 0000000000..24d44b090d
--- /dev/null
+++ b/pkg/localcache/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package localcache // import "github.com/openimsdk/open-im-server/v3/pkg/localcache"
diff --git a/pkg/localcache/go.mod b/pkg/localcache/go.mod
deleted file mode 100644
index 5f0793042e..0000000000
--- a/pkg/localcache/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module github.com/openimsdk/localcache
-
-go 1.19
-
-require github.com/hashicorp/golang-lru/v2 v2.0.7
diff --git a/pkg/localcache/link/doc.go b/pkg/localcache/link/doc.go
new file mode 100644
index 0000000000..e01e7d23dd
--- /dev/null
+++ b/pkg/localcache/link/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package link // import "github.com/openimsdk/open-im-server/v3/pkg/localcache/link"
diff --git a/pkg/common/kafka/doc.go b/pkg/localcache/lru/doc.go
similarity index 80%
rename from pkg/common/kafka/doc.go
rename to pkg/localcache/lru/doc.go
index 0f0c69a76b..50edcc11b1 100644
--- a/pkg/common/kafka/doc.go
+++ b/pkg/localcache/lru/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package kafka // import "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
+package lru // import "github.com/openimsdk/open-im-server/v3/pkg/localcache/lru"
diff --git a/pkg/localcache/lru/lru_lazy_test.go b/pkg/localcache/lru/lru_lazy_test.go
index 167dd2135b..ab0fa50a07 100644
--- a/pkg/localcache/lru/lru_lazy_test.go
+++ b/pkg/localcache/lru/lru_lazy_test.go
@@ -92,12 +92,7 @@ func TestName(t *testing.T) {
defer wg.Done()
//t.Log(key)
fn(key, 10000, func() (string, error) {
- //time.Sleep(time.Second * 3)
- //t.Log(time.Now(), "key", key, "fetch")
- //if rand.Uint32()%5 == 0 {
- // return "value_" + key, nil
- //}
- //return "", errors.New("rand error")
+
return "value_" + key, nil
})
}()
diff --git a/pkg/localcache/option.go b/pkg/localcache/option.go
index e60d5aa07b..00bb9d0441 100644
--- a/pkg/localcache/option.go
+++ b/pkg/localcache/option.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/openimsdk/localcache/lru"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache/lru"
)
func defaultOption() *option {
diff --git a/pkg/msgprocessor/conversation.go b/pkg/msgprocessor/conversation.go
index e3ea89fadd..b369269cc8 100644
--- a/pkg/msgprocessor/conversation.go
+++ b/pkg/msgprocessor/conversation.go
@@ -18,9 +18,9 @@ import (
"sort"
"strings"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
"google.golang.org/protobuf/proto"
)
@@ -30,9 +30,9 @@ func GetNotificationConversationIDByMsg(msg *sdkws.MsgData) string {
l := []string{msg.SendID, msg.RecvID}
sort.Strings(l)
return "n_" + strings.Join(l, "_")
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
return "n_" + msg.GroupID
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "n_" + msg.GroupID
case constant.NotificationChatType:
return "n_" + msg.SendID + "_" + msg.RecvID
@@ -46,9 +46,9 @@ func GetChatConversationIDByMsg(msg *sdkws.MsgData) string {
l := []string{msg.SendID, msg.RecvID}
sort.Strings(l)
return "si_" + strings.Join(l, "_")
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
return "g_" + msg.GroupID
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "sg_" + msg.GroupID
case constant.NotificationChatType:
return "sn_" + msg.SendID + "_" + msg.RecvID
@@ -63,7 +63,7 @@ func GenConversationUniqueKey(msg *sdkws.MsgData) string {
l := []string{msg.SendID, msg.RecvID}
sort.Strings(l)
return strings.Join(l, "_")
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return msg.GroupID
}
return ""
@@ -79,12 +79,12 @@ func GetConversationIDByMsg(msg *sdkws.MsgData) string {
return "n_" + strings.Join(l, "_")
}
return "si_" + strings.Join(l, "_") // single chat
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
if !options.IsNotNotification() {
return "n_" + msg.GroupID // group chat
}
return "g_" + msg.GroupID // group chat
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
if !options.IsNotNotification() {
return "n_" + msg.GroupID // super group chat
}
@@ -106,9 +106,9 @@ func GetConversationIDBySessionType(sessionType int, ids ...string) string {
switch sessionType {
case constant.SingleChatType:
return "si_" + strings.Join(ids, "_") // single chat
- case constant.GroupChatType:
+ case constant.WriteGroupChatType:
return "g_" + ids[0] // group chat
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "sg_" + ids[0] // super group chat
case constant.NotificationChatType:
return "sn_" + ids[0] // server notification chat
@@ -134,7 +134,7 @@ func GetNotificationConversationID(sessionType int, ids ...string) string {
switch sessionType {
case constant.SingleChatType:
return "n_" + strings.Join(ids, "_") // single chat
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
return "n_" + ids[0] // super group chat
}
return ""
@@ -158,7 +158,7 @@ func ParseConversationID(msg *sdkws.MsgData) (isNotification bool, conversationI
return true, "n_" + strings.Join(l, "_")
}
return false, "si_" + strings.Join(l, "_") // single chat
- case constant.SuperGroupChatType:
+ case constant.ReadGroupChatType:
if !options.IsNotNotification() {
return true, "n_" + msg.GroupID // super group chat
}
diff --git a/pkg/msgprocessor/conversation_test.go b/pkg/msgprocessor/conversation_test.go
index c3fdb459d8..32601baec4 100644
--- a/pkg/msgprocessor/conversation_test.go
+++ b/pkg/msgprocessor/conversation_test.go
@@ -17,7 +17,7 @@ package msgprocessor
import (
"testing"
- "github.com/OpenIMSDK/protocol/sdkws"
+ "github.com/openimsdk/protocol/sdkws"
"google.golang.org/protobuf/proto"
)
diff --git a/pkg/msgprocessor/doc.go b/pkg/msgprocessor/doc.go
index d1e24ce305..e8c96befb2 100644
--- a/pkg/msgprocessor/doc.go
+++ b/pkg/msgprocessor/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/msgprocessor/options.go b/pkg/msgprocessor/options.go
index c6e209b987..406145ef46 100644
--- a/pkg/msgprocessor/options.go
+++ b/pkg/msgprocessor/options.go
@@ -14,7 +14,7 @@
package msgprocessor
-import "github.com/OpenIMSDK/protocol/constant"
+import "github.com/openimsdk/protocol/constant"
type (
Options map[string]bool
diff --git a/pkg/rpccache/conversation.go b/pkg/rpccache/conversation.go
index 027ef42fa6..55897a8dae 100644
--- a/pkg/rpccache/conversation.go
+++ b/pkg/rpccache/conversation.go
@@ -17,18 +17,18 @@ package rpccache
import (
"context"
- pbconversation "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/openimsdk/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ pbconversation "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
-func NewConversationLocalCache(client rpcclient.ConversationRpcClient, cli redis.UniversalClient) *ConversationLocalCache {
- lc := config.Config.LocalCache.Conversation
+func NewConversationLocalCache(client rpcclient.ConversationRpcClient, localCache *config.LocalCache, cli redis.UniversalClient) *ConversationLocalCache {
+ lc := localCache.Conversation
log.ZDebug(context.Background(), "ConversationLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable())
x := &ConversationLocalCache{
client: client,
diff --git a/pkg/common/http/doc.go b/pkg/rpccache/doc.go
similarity index 81%
rename from pkg/common/http/doc.go
rename to pkg/rpccache/doc.go
index e6fd3ec1ee..4244bb239a 100644
--- a/pkg/common/http/doc.go
+++ b/pkg/rpccache/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -12,4 +12,4 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-package http // import "github.com/openimsdk/open-im-server/v3/pkg/common/http"
+package rpccache // import "github.com/openimsdk/open-im-server/v3/pkg/rpccache"
diff --git a/pkg/rpccache/friend.go b/pkg/rpccache/friend.go
index 35122fff5a..3e9e7863ab 100644
--- a/pkg/rpccache/friend.go
+++ b/pkg/rpccache/friend.go
@@ -17,16 +17,16 @@ package rpccache
import (
"context"
- "github.com/OpenIMSDK/tools/log"
- "github.com/openimsdk/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
-func NewFriendLocalCache(client rpcclient.FriendRpcClient, cli redis.UniversalClient) *FriendLocalCache {
- lc := config.Config.LocalCache.Friend
+func NewFriendLocalCache(client rpcclient.FriendRpcClient, localCache *config.LocalCache, cli redis.UniversalClient) *FriendLocalCache {
+ lc := localCache.Friend
log.ZDebug(context.Background(), "FriendLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable())
x := &FriendLocalCache{
client: client,
diff --git a/pkg/rpccache/group.go b/pkg/rpccache/group.go
index 402f63185e..7ba22beb82 100644
--- a/pkg/rpccache/group.go
+++ b/pkg/rpccache/group.go
@@ -17,18 +17,18 @@ package rpccache
import (
"context"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/openimsdk/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
-func NewGroupLocalCache(client rpcclient.GroupRpcClient, cli redis.UniversalClient) *GroupLocalCache {
- lc := config.Config.LocalCache.Group
+func NewGroupLocalCache(client rpcclient.GroupRpcClient, localCache *config.LocalCache, cli redis.UniversalClient) *GroupLocalCache {
+ lc := localCache.Group
log.ZDebug(context.Background(), "GroupLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable())
x := &GroupLocalCache{
client: client,
diff --git a/pkg/rpccache/subscriber.go b/pkg/rpccache/subscriber.go
index cd65094e2b..3046f84b11 100644
--- a/pkg/rpccache/subscriber.go
+++ b/pkg/rpccache/subscriber.go
@@ -18,7 +18,7 @@ import (
"context"
"encoding/json"
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
diff --git a/pkg/rpccache/user.go b/pkg/rpccache/user.go
index c48b058214..0a7a4e4b84 100644
--- a/pkg/rpccache/user.go
+++ b/pkg/rpccache/user.go
@@ -17,18 +17,18 @@ package rpccache
import (
"context"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/openimsdk/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/common/cachekey"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/open-im-server/v3/pkg/localcache"
"github.com/openimsdk/open-im-server/v3/pkg/rpcclient"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/log"
"github.com/redis/go-redis/v9"
)
-func NewUserLocalCache(client rpcclient.UserRpcClient, cli redis.UniversalClient) *UserLocalCache {
- lc := config.Config.LocalCache.User
+func NewUserLocalCache(client rpcclient.UserRpcClient, localCache *config.LocalCache, cli redis.UniversalClient) *UserLocalCache {
+ lc := localCache.User
log.ZDebug(context.Background(), "UserLocalCache", "topic", lc.Topic, "slotNum", lc.SlotNum, "slotSize", lc.SlotSize, "enable", lc.Enable())
x := &UserLocalCache{
client: client,
diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go
index 24597120fd..6665936bdd 100644
--- a/pkg/rpcclient/auth.go
+++ b/pkg/rpcclient/auth.go
@@ -16,26 +16,48 @@ package rpcclient
import (
"context"
-
- "github.com/OpenIMSDK/protocol/auth"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/protocol/auth"
+ pbAuth "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
-func NewAuth(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Auth {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImAuthName)
+func NewAuth(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Auth {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := auth.NewAuthClient(conn)
- return &Auth{discov: discov, conn: conn, Client: client, Config: config}
+ return &Auth{discov: discov, conn: conn, Client: client}
}
type Auth struct {
conn grpc.ClientConnInterface
Client auth.AuthClient
- discov discoveryregistry.SvcDiscoveryRegistry
- Config *config.GlobalConfig
+ discov discovery.SvcDiscoveryRegistry
+}
+
+func (a *Auth) ParseToken(ctx context.Context, token string) (*pbAuth.ParseTokenResp, error) {
+ req := pbAuth.ParseTokenReq{
+ Token: token,
+ }
+ resp, err := a.Client.ParseToken(ctx, &req)
+ if err != nil {
+ return nil, err
+ }
+ return resp, err
+}
+
+func (a *Auth) InvalidateToken(ctx context.Context, preservedToken, userID string, platformID int) (*pbAuth.InvalidateTokenResp, error) {
+ req := pbAuth.InvalidateTokenReq{
+ PreservedToken: preservedToken,
+ UserID: userID,
+ PlatformID: int32(platformID),
+ }
+ resp, err := a.Client.InvalidateToken(ctx, &req)
+ if err != nil {
+ return nil, err
+ }
+ return resp, err
}
diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go
index 127e029e16..e078f432b3 100644
--- a/pkg/rpcclient/conversation.go
+++ b/pkg/rpcclient/conversation.go
@@ -18,34 +18,32 @@ import (
"context"
"fmt"
- pbconversation "github.com/OpenIMSDK/protocol/conversation"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ pbconversation "github.com/openimsdk/protocol/conversation"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/errs"
+ "github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Conversation struct {
Client pbconversation.ConversationClient
conn grpc.ClientConnInterface
- discov discoveryregistry.SvcDiscoveryRegistry
- Config *config.GlobalConfig
+ discov discovery.SvcDiscoveryRegistry
}
-func NewConversation(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Conversation {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImConversationName)
+func NewConversation(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Conversation {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := pbconversation.NewConversationClient(conn)
- return &Conversation{discov: discov, conn: conn, Client: client, Config: config}
+ return &Conversation{discov: discov, conn: conn, Client: client}
}
type ConversationRpcClient Conversation
-func NewConversationRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) ConversationRpcClient {
- return ConversationRpcClient(*NewConversation(discov, config))
+func NewConversationRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) ConversationRpcClient {
+ return ConversationRpcClient(*NewConversation(discov, rpcRegisterName))
}
func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt(ctx context.Context, userID, conversationID string) (int32, error) {
@@ -84,6 +82,11 @@ func (c *ConversationRpcClient) SetConversations(ctx context.Context, userIDs []
return err
}
+func (c *ConversationRpcClient) UpdateConversations(ctx context.Context, conversation *pbconversation.UpdateConversationReq) error {
+ _, err := c.Client.UpdateConversation(ctx, conversation)
+ return err
+}
+
func (c *ConversationRpcClient) GetConversationIDs(ctx context.Context, ownerUserID string) ([]string, error) {
resp, err := c.Client.GetConversationIDs(ctx, &pbconversation.GetConversationIDsReq{UserID: ownerUserID})
if err != nil {
@@ -109,7 +112,7 @@ func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Con
return nil, err
}
if len(resp.Conversations) == 0 {
- return nil, errs.ErrRecordNotFound.Wrap(fmt.Sprintf("conversationIDs: %v not found", conversationIDs))
+ return nil, errs.ErrRecordNotFound.WrapMsg(fmt.Sprintf("conversationIDs: %v not found", conversationIDs))
}
return resp.Conversations, nil
}
diff --git a/pkg/rpcclient/doc.go b/pkg/rpcclient/doc.go
index 0fdfacc60a..66b0aee91d 100644
--- a/pkg/rpcclient/doc.go
+++ b/pkg/rpcclient/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go
index 5a5f38698d..5543afe4f7 100644
--- a/pkg/rpcclient/friend.go
+++ b/pkg/rpcclient/friend.go
@@ -17,34 +17,32 @@ package rpcclient
import (
"context"
- "github.com/OpenIMSDK/protocol/friend"
- sdkws "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/protocol/friend"
+ sdkws "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Friend struct {
conn grpc.ClientConnInterface
Client friend.FriendClient
- discov discoveryregistry.SvcDiscoveryRegistry
- Config *config.GlobalConfig
+ discov discovery.SvcDiscoveryRegistry
}
-func NewFriend(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Friend {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImFriendName)
+func NewFriend(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Friend {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := friend.NewFriendClient(conn)
- return &Friend{discov: discov, conn: conn, Client: client, Config: config}
+ return &Friend{discov: discov, conn: conn, Client: client}
}
type FriendRpcClient Friend
-func NewFriendRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) FriendRpcClient {
- return FriendRpcClient(*NewFriend(discov, config))
+func NewFriendRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) FriendRpcClient {
+ return FriendRpcClient(*NewFriend(discov, rpcRegisterName))
}
func (f *FriendRpcClient) GetFriendsInfo(
diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go
index 3e9c93e7a4..30d0b3288f 100644
--- a/pkg/rpcclient/group.go
+++ b/pkg/rpcclient/group.go
@@ -18,35 +18,33 @@ import (
"context"
"strings"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/utils"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/openimsdk/tools/utils/datautil"
)
type Group struct {
Client group.GroupClient
- discov discoveryregistry.SvcDiscoveryRegistry
- Config *config.GlobalConfig
+ discov discovery.SvcDiscoveryRegistry
}
-func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Group {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImGroupName)
+func NewGroup(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Group {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := group.NewGroupClient(conn)
- return &Group{discov: discov, Client: client, Config: config}
+ return &Group{discov: discov, Client: client}
}
type GroupRpcClient Group
-func NewGroupRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) GroupRpcClient {
- return GroupRpcClient(*NewGroup(discov, config))
+func NewGroupRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) GroupRpcClient {
+ return GroupRpcClient(*NewGroup(discov, rpcRegisterName))
}
func (g *GroupRpcClient) GetGroupInfos(ctx context.Context, groupIDs []string, complete bool) ([]*sdkws.GroupInfo, error) {
@@ -57,10 +55,10 @@ func (g *GroupRpcClient) GetGroupInfos(ctx context.Context, groupIDs []string, c
return nil, err
}
if complete {
- if ids := utils.Single(groupIDs, utils.Slice(resp.GroupInfos, func(e *sdkws.GroupInfo) string {
+ if ids := datautil.Single(groupIDs, datautil.Slice(resp.GroupInfos, func(e *sdkws.GroupInfo) string {
return e.GroupID
})); len(ids) > 0 {
- return nil, errs.ErrGroupIDNotFound.Wrap(strings.Join(ids, ","))
+ return nil, servererrs.ErrGroupIDNotFound.WrapMsg(strings.Join(ids, ","))
}
}
return resp.GroupInfos, nil
@@ -83,7 +81,7 @@ func (g *GroupRpcClient) GetGroupInfoMap(
if err != nil {
return nil, err
}
- return utils.SliceToMap(groups, func(e *sdkws.GroupInfo) string {
+ return datautil.SliceToMap(groups, func(e *sdkws.GroupInfo) string {
return e.GroupID
}), nil
}
@@ -102,10 +100,10 @@ func (g *GroupRpcClient) GetGroupMemberInfos(
return nil, err
}
if complete {
- if ids := utils.Single(userIDs, utils.Slice(resp.Members, func(e *sdkws.GroupMemberFullInfo) string {
+ if ids := datautil.Single(userIDs, datautil.Slice(resp.Members, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
})); len(ids) > 0 {
- return nil, errs.ErrNotInGroupYet.Wrap(strings.Join(ids, ","))
+ return nil, servererrs.ErrNotInGroupYet.WrapMsg(strings.Join(ids, ","))
}
}
return resp.Members, nil
@@ -133,7 +131,7 @@ func (g *GroupRpcClient) GetGroupMemberInfoMap(
if err != nil {
return nil, err
}
- return utils.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
+ return datautil.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
}), nil
}
diff --git a/pkg/rpcclient/grouphash/doc.go b/pkg/rpcclient/grouphash/doc.go
new file mode 100644
index 0000000000..c780701d9a
--- /dev/null
+++ b/pkg/rpcclient/grouphash/doc.go
@@ -0,0 +1,15 @@
+// Copyright © 2024 OpenIM. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package grouphash // import "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/grouphash"
diff --git a/pkg/rpcclient/grouphash/grouphash.go b/pkg/rpcclient/grouphash/grouphash.go
index dee47ad44e..5015b88c8f 100644
--- a/pkg/rpcclient/grouphash/grouphash.go
+++ b/pkg/rpcclient/grouphash/grouphash.go
@@ -20,9 +20,9 @@ import (
"encoding/binary"
"encoding/json"
- "github.com/OpenIMSDK/protocol/group"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/utils"
+ "github.com/openimsdk/protocol/group"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/utils/datautil"
)
func NewGroupHashFromGroupClient(x group.GroupClient) *GroupHash {
@@ -79,9 +79,9 @@ func (gh *GroupHash) GetGroupHash(ctx context.Context, groupID string) (uint64,
if err != nil {
return 0, err
}
- utils.Sort(userIDs, true)
+ datautil.Sort(userIDs, true)
}
- memberMap := utils.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
+ memberMap := datautil.SliceToMap(members, func(e *sdkws.GroupMemberFullInfo) string {
return e.UserID
})
res := make([]*sdkws.GroupMemberFullInfo, 0, len(members))
diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go
index 54a4671a3d..f660c74dd7 100644
--- a/pkg/rpcclient/msg.go
+++ b/pkg/rpcclient/msg.go
@@ -17,62 +17,64 @@ package rpcclient
import (
"context"
"encoding/json"
- "fmt"
-
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/msg"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/log"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/msg"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/log"
+ "github.com/openimsdk/tools/mcontext"
+ "github.com/openimsdk/tools/mq/memamq"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/openimsdk/tools/utils/idutil"
+ "github.com/openimsdk/tools/utils/jsonutil"
+ "github.com/openimsdk/tools/utils/timeutil"
"google.golang.org/grpc"
"google.golang.org/protobuf/proto"
+ "time"
)
-func newContentTypeConf(conf *config.GlobalConfig) map[int32]config.NotificationConf {
- return map[int32]config.NotificationConf{
+func newContentTypeConf(conf *config.Notification) map[int32]config.NotificationConfig {
+ return map[int32]config.NotificationConfig{
// group
- constant.GroupCreatedNotification: conf.Notification.GroupCreated,
- constant.GroupInfoSetNotification: conf.Notification.GroupInfoSet,
- constant.JoinGroupApplicationNotification: conf.Notification.JoinGroupApplication,
- constant.MemberQuitNotification: conf.Notification.MemberQuit,
- constant.GroupApplicationAcceptedNotification: conf.Notification.GroupApplicationAccepted,
- constant.GroupApplicationRejectedNotification: conf.Notification.GroupApplicationRejected,
- constant.GroupOwnerTransferredNotification: conf.Notification.GroupOwnerTransferred,
- constant.MemberKickedNotification: conf.Notification.MemberKicked,
- constant.MemberInvitedNotification: conf.Notification.MemberInvited,
- constant.MemberEnterNotification: conf.Notification.MemberEnter,
- constant.GroupDismissedNotification: conf.Notification.GroupDismissed,
- constant.GroupMutedNotification: conf.Notification.GroupMuted,
- constant.GroupCancelMutedNotification: conf.Notification.GroupCancelMuted,
- constant.GroupMemberMutedNotification: conf.Notification.GroupMemberMuted,
- constant.GroupMemberCancelMutedNotification: conf.Notification.GroupMemberCancelMuted,
- constant.GroupMemberInfoSetNotification: conf.Notification.GroupMemberInfoSet,
- constant.GroupMemberSetToAdminNotification: conf.Notification.GroupMemberSetToAdmin,
- constant.GroupMemberSetToOrdinaryUserNotification: conf.Notification.GroupMemberSetToOrdinary,
- constant.GroupInfoSetAnnouncementNotification: conf.Notification.GroupInfoSetAnnouncement,
- constant.GroupInfoSetNameNotification: conf.Notification.GroupInfoSetName,
+ constant.GroupCreatedNotification: conf.GroupCreated,
+ constant.GroupInfoSetNotification: conf.GroupInfoSet,
+ constant.JoinGroupApplicationNotification: conf.JoinGroupApplication,
+ constant.MemberQuitNotification: conf.MemberQuit,
+ constant.GroupApplicationAcceptedNotification: conf.GroupApplicationAccepted,
+ constant.GroupApplicationRejectedNotification: conf.GroupApplicationRejected,
+ constant.GroupOwnerTransferredNotification: conf.GroupOwnerTransferred,
+ constant.MemberKickedNotification: conf.MemberKicked,
+ constant.MemberInvitedNotification: conf.MemberInvited,
+ constant.MemberEnterNotification: conf.MemberEnter,
+ constant.GroupDismissedNotification: conf.GroupDismissed,
+ constant.GroupMutedNotification: conf.GroupMuted,
+ constant.GroupCancelMutedNotification: conf.GroupCancelMuted,
+ constant.GroupMemberMutedNotification: conf.GroupMemberMuted,
+ constant.GroupMemberCancelMutedNotification: conf.GroupMemberCancelMuted,
+ constant.GroupMemberInfoSetNotification: conf.GroupMemberInfoSet,
+ constant.GroupMemberSetToAdminNotification: conf.GroupMemberSetToAdmin,
+ constant.GroupMemberSetToOrdinaryUserNotification: conf.GroupMemberSetToOrdinary,
+ constant.GroupInfoSetAnnouncementNotification: conf.GroupInfoSetAnnouncement,
+ constant.GroupInfoSetNameNotification: conf.GroupInfoSetName,
// user
- constant.UserInfoUpdatedNotification: conf.Notification.UserInfoUpdated,
- constant.UserStatusChangeNotification: conf.Notification.UserStatusChanged,
+ constant.UserInfoUpdatedNotification: conf.UserInfoUpdated,
+ constant.UserStatusChangeNotification: conf.UserStatusChanged,
// friend
- constant.FriendApplicationNotification: conf.Notification.FriendApplicationAdded,
- constant.FriendApplicationApprovedNotification: conf.Notification.FriendApplicationApproved,
- constant.FriendApplicationRejectedNotification: conf.Notification.FriendApplicationRejected,
- constant.FriendAddedNotification: conf.Notification.FriendAdded,
- constant.FriendDeletedNotification: conf.Notification.FriendDeleted,
- constant.FriendRemarkSetNotification: conf.Notification.FriendRemarkSet,
- constant.BlackAddedNotification: conf.Notification.BlackAdded,
- constant.BlackDeletedNotification: conf.Notification.BlackDeleted,
- constant.FriendInfoUpdatedNotification: conf.Notification.FriendInfoUpdated,
- constant.FriendsInfoUpdateNotification: conf.Notification.FriendInfoUpdated, //use the same FriendInfoUpdated
+ constant.FriendApplicationNotification: conf.FriendApplicationAdded,
+ constant.FriendApplicationApprovedNotification: conf.FriendApplicationApproved,
+ constant.FriendApplicationRejectedNotification: conf.FriendApplicationRejected,
+ constant.FriendAddedNotification: conf.FriendAdded,
+ constant.FriendDeletedNotification: conf.FriendDeleted,
+ constant.FriendRemarkSetNotification: conf.FriendRemarkSet,
+ constant.BlackAddedNotification: conf.BlackAdded,
+ constant.BlackDeletedNotification: conf.BlackDeleted,
+ constant.FriendInfoUpdatedNotification: conf.FriendInfoUpdated,
+ constant.FriendsInfoUpdateNotification: conf.FriendInfoUpdated, // use the same FriendInfoUpdated
// conversation
- constant.ConversationChangeNotification: conf.Notification.ConversationChanged,
- constant.ConversationUnreadNotification: conf.Notification.ConversationChanged,
- constant.ConversationPrivateChatNotification: conf.Notification.ConversationSetPrivate,
+ constant.ConversationChangeNotification: conf.ConversationChanged,
+ constant.ConversationUnreadNotification: conf.ConversationChanged,
+ constant.ConversationPrivateChatNotification: conf.ConversationSetPrivate,
// msg
constant.MsgRevokeNotification: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg},
constant.HasReadReceipt: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg},
@@ -83,26 +85,26 @@ func newContentTypeConf(conf *config.GlobalConfig) map[int32]config.Notification
func newSessionTypeConf() map[int32]int32 {
return map[int32]int32{
// group
- constant.GroupCreatedNotification: constant.SuperGroupChatType,
- constant.GroupInfoSetNotification: constant.SuperGroupChatType,
+ constant.GroupCreatedNotification: constant.ReadGroupChatType,
+ constant.GroupInfoSetNotification: constant.ReadGroupChatType,
constant.JoinGroupApplicationNotification: constant.SingleChatType,
- constant.MemberQuitNotification: constant.SuperGroupChatType,
+ constant.MemberQuitNotification: constant.ReadGroupChatType,
constant.GroupApplicationAcceptedNotification: constant.SingleChatType,
constant.GroupApplicationRejectedNotification: constant.SingleChatType,
- constant.GroupOwnerTransferredNotification: constant.SuperGroupChatType,
- constant.MemberKickedNotification: constant.SuperGroupChatType,
- constant.MemberInvitedNotification: constant.SuperGroupChatType,
- constant.MemberEnterNotification: constant.SuperGroupChatType,
- constant.GroupDismissedNotification: constant.SuperGroupChatType,
- constant.GroupMutedNotification: constant.SuperGroupChatType,
- constant.GroupCancelMutedNotification: constant.SuperGroupChatType,
- constant.GroupMemberMutedNotification: constant.SuperGroupChatType,
- constant.GroupMemberCancelMutedNotification: constant.SuperGroupChatType,
- constant.GroupMemberInfoSetNotification: constant.SuperGroupChatType,
- constant.GroupMemberSetToAdminNotification: constant.SuperGroupChatType,
- constant.GroupMemberSetToOrdinaryUserNotification: constant.SuperGroupChatType,
- constant.GroupInfoSetAnnouncementNotification: constant.SuperGroupChatType,
- constant.GroupInfoSetNameNotification: constant.SuperGroupChatType,
+ constant.GroupOwnerTransferredNotification: constant.ReadGroupChatType,
+ constant.MemberKickedNotification: constant.ReadGroupChatType,
+ constant.MemberInvitedNotification: constant.ReadGroupChatType,
+ constant.MemberEnterNotification: constant.ReadGroupChatType,
+ constant.GroupDismissedNotification: constant.ReadGroupChatType,
+ constant.GroupMutedNotification: constant.ReadGroupChatType,
+ constant.GroupCancelMutedNotification: constant.ReadGroupChatType,
+ constant.GroupMemberMutedNotification: constant.ReadGroupChatType,
+ constant.GroupMemberCancelMutedNotification: constant.ReadGroupChatType,
+ constant.GroupMemberInfoSetNotification: constant.ReadGroupChatType,
+ constant.GroupMemberSetToAdminNotification: constant.ReadGroupChatType,
+ constant.GroupMemberSetToOrdinaryUserNotification: constant.ReadGroupChatType,
+ constant.GroupInfoSetAnnouncementNotification: constant.ReadGroupChatType,
+ constant.GroupInfoSetNameNotification: constant.ReadGroupChatType,
// user
constant.UserInfoUpdatedNotification: constant.SingleChatType,
constant.UserStatusChangeNotification: constant.SingleChatType,
@@ -129,23 +131,22 @@ func newSessionTypeConf() map[int32]int32 {
type Message struct {
conn grpc.ClientConnInterface
Client msg.MsgClient
- discov discoveryregistry.SvcDiscoveryRegistry
- Config *config.GlobalConfig
+ discov discovery.SvcDiscoveryRegistry
}
-func NewMessage(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Message {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImMsgName)
+func NewMessage(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Message {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := msg.NewMsgClient(conn)
- return &Message{discov: discov, conn: conn, Client: client, Config: config}
+ return &Message{discov: discov, conn: conn, Client: client}
}
type MessageRpcClient Message
-func NewMessageRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) MessageRpcClient {
- return MessageRpcClient(*NewMessage(discov, config))
+func NewMessageRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) MessageRpcClient {
+ return MessageRpcClient(*NewMessage(discov, rpcRegisterName))
}
// SendMsg sends a message through the gRPC client and returns the response.
@@ -211,11 +212,23 @@ func (m *MessageRpcClient) GetConversationMaxSeq(ctx context.Context, conversati
return resp.MaxSeq, nil
}
+func (m *MessageRpcClient) ClearMsg(ctx context.Context, ts int64) error {
+ _, err := m.Client.ClearMsg(ctx, &msg.ClearMsgReq{Timestamp: ts})
+ return err
+}
+
type NotificationSender struct {
- contentTypeConf map[int32]config.NotificationConf
+ contentTypeConf map[int32]config.NotificationConfig
sessionTypeConf map[int32]int32
sendMsg func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error)
getUserInfo func(ctx context.Context, userID string) (*sdkws.UserInfo, error)
+ queue *memamq.MemoryQueue
+}
+
+func WithQueue(queue *memamq.MemoryQueue) NotificationSenderOptions {
+ return func(s *NotificationSender) {
+ s.queue = queue
+ }
}
type NotificationSenderOptions func(*NotificationSender)
@@ -238,11 +251,19 @@ func WithUserRpcClient(userRpcClient *UserRpcClient) NotificationSenderOptions {
}
}
-func NewNotificationSender(config *config.GlobalConfig, opts ...NotificationSenderOptions) *NotificationSender {
- notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(config), sessionTypeConf: newSessionTypeConf()}
+const (
+ notificationWorkerCount = 2
+ notificationBufferSize = 200
+)
+
+func NewNotificationSender(conf *config.Notification, opts ...NotificationSenderOptions) *NotificationSender {
+ notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(conf), sessionTypeConf: newSessionTypeConf()}
for _, opt := range opts {
opt(notificationSender)
}
+ if notificationSender.queue == nil {
+ notificationSender.queue = memamq.NewMemoryQueue(notificationWorkerCount, notificationBufferSize)
+ }
return notificationSender
}
@@ -258,12 +279,15 @@ func WithRpcGetUserName() NotificationOptions {
}
}
-func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, sendID, recvID string, contentType, sesstionType int32, m proto.Message, opts ...NotificationOptions) (err error) {
- n := sdkws.NotificationElem{Detail: utils.StructToJsonString(m)}
+func (s *NotificationSender) send(ctx context.Context, sendID, recvID string, contentType, sessionType int32, m proto.Message, opts ...NotificationOptions) {
+ ctx = mcontext.WithMustInfoCtx([]string{mcontext.GetOperationID(ctx), mcontext.GetOpUserID(ctx), mcontext.GetOpUserPlatform(ctx), mcontext.GetConnID(ctx)})
+ ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(5))
+ defer cancel()
+ n := sdkws.NotificationElem{Detail: jsonutil.StructToJsonString(m)}
content, err := json.Marshal(&n)
if err != nil {
- errInfo := fmt.Sprintf("MsgClient Notification json.Marshal failed, sendID:%s, recvID:%s, contentType:%d, msg:%s", sendID, recvID, contentType, m)
- return errs.Wrap(err, errInfo)
+ log.ZWarn(ctx, "json.Marshal failed", err, "sendID", sendID, "recvID", recvID, "contentType", contentType, "msg", jsonutil.StructToJsonString(m))
+ return
}
notificationOpt := ¬ificationOpt{}
for _, opt := range opts {
@@ -275,26 +299,24 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s
if notificationOpt.WithRpcGetUsername && s.getUserInfo != nil {
userInfo, err = s.getUserInfo(ctx, sendID)
if err != nil {
- errInfo := fmt.Sprintf("getUserInfo failed, sendID:%s", sendID)
- return errs.Wrap(err, errInfo)
- } else {
- msg.SenderNickname = userInfo.Nickname
- msg.SenderFaceURL = userInfo.FaceURL
+ log.ZWarn(ctx, "getUserInfo failed", err, "sendID", sendID)
+ return
}
+ msg.SenderNickname = userInfo.Nickname
+ msg.SenderFaceURL = userInfo.FaceURL
}
var offlineInfo sdkws.OfflinePushInfo
- var title, desc, ex string
msg.SendID = sendID
msg.RecvID = recvID
msg.Content = content
msg.MsgFrom = constant.SysMsgType
msg.ContentType = contentType
- msg.SessionType = sesstionType
- if msg.SessionType == constant.SuperGroupChatType {
+ msg.SessionType = sessionType
+ if msg.SessionType == constant.ReadGroupChatType {
msg.GroupID = recvID
}
- msg.CreateTime = utils.GetCurrentTimestampByMill()
- msg.ClientMsgID = utils.GetMsgID(sendID)
+ msg.CreateTime = timeutil.GetCurrentTimestampByMill()
+ msg.ClientMsgID = idutil.GetMsgIDByMD5(sendID)
optionsConfig := s.contentTypeConf[contentType]
if sendID == recvID && contentType == constant.HasReadReceipt {
optionsConfig.ReliabilityLevel = constant.UnreliableNotification
@@ -302,21 +324,20 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s
options := config.GetOptionsByNotification(optionsConfig)
s.SetOptionsByContentType(ctx, options, contentType)
msg.Options = options
- offlineInfo.Title = title
- offlineInfo.Desc = desc
- offlineInfo.Ex = ex
msg.OfflinePushInfo = &offlineInfo
req.MsgData = &msg
_, err = s.sendMsg(ctx, &req)
if err != nil {
- errInfo := fmt.Sprintf("MsgClient Notification SendMsg failed, req:%s", &req)
- return errs.Wrap(err, errInfo)
+ log.ZWarn(ctx, "SendMsg failed", err, "req", req.String())
}
- return err
}
-func (s *NotificationSender) Notification(ctx context.Context, sendID, recvID string, contentType int32, m proto.Message, opts ...NotificationOptions) error {
- return s.NotificationWithSesstionType(ctx, sendID, recvID, contentType, s.sessionTypeConf[contentType], m, opts...)
+func (s *NotificationSender) NotificationWithSessionType(ctx context.Context, sendID, recvID string, contentType, sessionType int32, m proto.Message, opts ...NotificationOptions) {
+ s.queue.Push(func() { s.send(ctx, sendID, recvID, contentType, sessionType, m, opts...) })
+}
+
+func (s *NotificationSender) Notification(ctx context.Context, sendID, recvID string, contentType int32, m proto.Message, opts ...NotificationOptions) {
+ s.NotificationWithSessionType(ctx, sendID, recvID, contentType, s.sessionTypeConf[contentType], m, opts...)
}
func (s *NotificationSender) SetOptionsByContentType(_ context.Context, options map[string]bool, contentType int32) {
diff --git a/pkg/rpcclient/notification/doc.go b/pkg/rpcclient/notification/doc.go
index 2d409bdc94..8ce57ca4e8 100644
--- a/pkg/rpcclient/notification/doc.go
+++ b/pkg/rpcclient/notification/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go
index c0aa9efa4c..c549e454a7 100644
--- a/pkg/rpcclient/push.go
+++ b/pkg/rpcclient/push.go
@@ -17,23 +17,22 @@ package rpcclient
import (
"context"
- "github.com/OpenIMSDK/protocol/push"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/protocol/push"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Push struct {
conn grpc.ClientConnInterface
Client push.PushMsgServiceClient
- discov discoveryregistry.SvcDiscoveryRegistry
+ discov discovery.SvcDiscoveryRegistry
}
-func NewPush(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Push {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImPushName)
+func NewPush(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) *Push {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
return &Push{
discov: discov,
@@ -44,8 +43,8 @@ func NewPush(discov discoveryregistry.SvcDiscoveryRegistry, config *config.Globa
type PushRpcClient Push
-func NewPushRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) PushRpcClient {
- return PushRpcClient(*NewPush(discov, config))
+func NewPushRpcClient(discov discovery.SvcDiscoveryRegistry, rpcRegisterName string) PushRpcClient {
+ return PushRpcClient(*NewPush(discov, rpcRegisterName))
}
func (p *PushRpcClient) DelUserPushToken(ctx context.Context, req *push.DelUserPushTokenReq) (*push.DelUserPushTokenResp, error) {
diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go
index a1f8ae180a..4c71dff6ad 100644
--- a/pkg/rpcclient/third.go
+++ b/pkg/rpcclient/third.go
@@ -16,58 +16,28 @@ package rpcclient
import (
"context"
- "net/url"
- "github.com/OpenIMSDK/protocol/third"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/minio/minio-go/v7"
- "github.com/minio/minio-go/v7/pkg/credentials"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/system/program"
"google.golang.org/grpc"
)
type Third struct {
- conn grpc.ClientConnInterface
- Client third.ThirdClient
- discov discoveryregistry.SvcDiscoveryRegistry
- MinioClient *minio.Client
- Config *config.GlobalConfig
+ conn grpc.ClientConnInterface
+ Client third.ThirdClient
+ discov discovery.SvcDiscoveryRegistry
+ GrafanaUrl string
}
-func NewThird(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Third {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImThirdName)
+func NewThird(discov discovery.SvcDiscoveryRegistry, rpcRegisterName, grafanaUrl string) *Third {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := third.NewThirdClient(conn)
- minioClient, err := minioInit(config)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
- return &Third{discov: discov, Client: client, conn: conn, MinioClient: minioClient, Config: config}
-}
-
-func minioInit(config *config.GlobalConfig) (*minio.Client, error) {
- minioClient := &minio.Client{}
- initUrl := config.Object.Minio.Endpoint
- minioUrl, err := url.Parse(initUrl)
- if err != nil {
- return nil, errs.Wrap(err, "minioInit: failed to parse MinIO endpoint URL")
- }
- opts := &minio.Options{
- Creds: credentials.NewStaticV4(config.Object.Minio.AccessKeyID, config.Object.Minio.SecretAccessKey, ""),
- // Region: config.Credential.Minio.Location,
- }
- if minioUrl.Scheme == "http" {
- opts.Secure = false
- } else if minioUrl.Scheme == "https" {
- opts.Secure = true
- }
- minioClient, err = minio.New(minioUrl.Host, opts)
- if err != nil {
- return nil, errs.Wrap(err, "minioInit: failed to create MinIO client")
- }
- return minioClient, nil
+ return &Third{discov: discov, Client: client, conn: conn, GrafanaUrl: grafanaUrl}
}
diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go
index 08ad41dc1a..aab96603eb 100644
--- a/pkg/rpcclient/user.go
+++ b/pkg/rpcclient/user.go
@@ -18,33 +18,37 @@ import (
"context"
"strings"
- "github.com/OpenIMSDK/protocol/sdkws"
- "github.com/OpenIMSDK/protocol/user"
- "github.com/OpenIMSDK/tools/discoveryregistry"
- "github.com/OpenIMSDK/tools/errs"
- "github.com/OpenIMSDK/tools/utils"
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
- util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/servererrs"
+ "github.com/openimsdk/protocol/sdkws"
+ "github.com/openimsdk/protocol/user"
+ "github.com/openimsdk/tools/discovery"
+ "github.com/openimsdk/tools/system/program"
+ "github.com/openimsdk/tools/utils/datautil"
"google.golang.org/grpc"
)
// User represents a structure holding connection details for the User RPC client.
type User struct {
- conn grpc.ClientConnInterface
- Client user.UserClient
- Discov discoveryregistry.SvcDiscoveryRegistry
- Config *config.GlobalConfig
+ conn grpc.ClientConnInterface
+ Client user.UserClient
+ Discov discovery.SvcDiscoveryRegistry
+ MessageGateWayRpcName string
+ imAdminUserID []string
}
// NewUser initializes and returns a User instance based on the provided service discovery registry.
-func NewUser(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *User {
- conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImUserName)
+func NewUser(discov discovery.SvcDiscoveryRegistry, rpcRegisterName, messageGateWayRpcName string,
+ imAdminUserID []string) *User {
+ conn, err := discov.GetConn(context.Background(), rpcRegisterName)
if err != nil {
- util.ExitWithError(err)
+ program.ExitWithError(err)
}
client := user.NewUserClient(conn)
- return &User{Discov: discov, Client: client, conn: conn, Config: config}
+ return &User{Discov: discov, Client: client,
+ conn: conn,
+ MessageGateWayRpcName: messageGateWayRpcName,
+ imAdminUserID: imAdminUserID}
}
// UserRpcClient represents the structure for a User RPC client.
@@ -57,8 +61,9 @@ func NewUserRpcClientByUser(user *User) *UserRpcClient {
}
// NewUserRpcClient initializes a UserRpcClient based on the provided service discovery registry.
-func NewUserRpcClient(client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) UserRpcClient {
- return UserRpcClient(*NewUser(client, config))
+func NewUserRpcClient(client discovery.SvcDiscoveryRegistry, rpcRegisterName string,
+ imAdminUserID []string) UserRpcClient {
+ return UserRpcClient(*NewUser(client, rpcRegisterName, "", imAdminUserID))
}
// GetUsersInfo retrieves information for multiple users based on their user IDs.
@@ -72,10 +77,10 @@ func (u *UserRpcClient) GetUsersInfo(ctx context.Context, userIDs []string) ([]*
if err != nil {
return nil, err
}
- if ids := utils.Single(userIDs, utils.Slice(resp.UsersInfo, func(e *sdkws.UserInfo) string {
+ if ids := datautil.Single(userIDs, datautil.Slice(resp.UsersInfo, func(e *sdkws.UserInfo) string {
return e.UserID
})); len(ids) > 0 {
- return nil, errs.ErrUserIDNotFound.Wrap(strings.Join(ids, ","))
+ return nil, servererrs.ErrUserIDNotFound.WrapMsg(strings.Join(ids, ","))
}
return resp.UsersInfo, nil
}
@@ -95,7 +100,7 @@ func (u *UserRpcClient) GetUsersInfoMap(ctx context.Context, userIDs []string) (
if err != nil {
return nil, err
}
- return utils.SliceToMap(users, func(e *sdkws.UserInfo) string {
+ return datautil.SliceToMap(users, func(e *sdkws.UserInfo) string {
return e.UserID
}), nil
}
@@ -110,7 +115,7 @@ func (u *UserRpcClient) GetPublicUserInfos(
if err != nil {
return nil, err
}
- return utils.Slice(users, func(e *sdkws.UserInfo) *sdkws.PublicUserInfo {
+ return datautil.Slice(users, func(e *sdkws.UserInfo) *sdkws.PublicUserInfo {
return &sdkws.PublicUserInfo{
UserID: e.UserID,
Nickname: e.Nickname,
@@ -139,7 +144,7 @@ func (u *UserRpcClient) GetPublicUserInfoMap(
if err != nil {
return nil, err
}
- return utils.SliceToMap(users, func(e *sdkws.PublicUserInfo) string {
+ return datautil.SliceToMap(users, func(e *sdkws.PublicUserInfo) string {
return e.UserID
}), nil
}
@@ -161,7 +166,7 @@ func (u *UserRpcClient) Access(ctx context.Context, ownerUserID string) error {
if err != nil {
return err
}
- return authverify.CheckAccessV3(ctx, ownerUserID, u.Config)
+ return authverify.CheckAccessV3(ctx, ownerUserID, u.imAdminUserID)
}
// GetAllUserIDs retrieves all user IDs with pagination options.
diff --git a/pkg/statistics/doc.go b/pkg/statistics/doc.go
index 8051c4c719..7b02631dc0 100644
--- a/pkg/statistics/doc.go
+++ b/pkg/statistics/doc.go
@@ -1,4 +1,4 @@
-// Copyright © 2023 OpenIM. All rights reserved.
+// Copyright © 2024 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
diff --git a/pkg/statistics/statistics.go b/pkg/statistics/statistics.go
index 6dfc8155c2..222fd5cbdd 100644
--- a/pkg/statistics/statistics.go
+++ b/pkg/statistics/statistics.go
@@ -18,7 +18,7 @@ import (
"context"
"time"
- "github.com/OpenIMSDK/tools/log"
+ "github.com/openimsdk/tools/log"
)
type Statistics struct {
diff --git a/pkg/util/conversationutil/conversationutil.go b/pkg/util/conversationutil/conversationutil.go
new file mode 100644
index 0000000000..5683d8df89
--- /dev/null
+++ b/pkg/util/conversationutil/conversationutil.go
@@ -0,0 +1,46 @@
+package conversationutil
+
+import (
+ "sort"
+ "strings"
+)
+
+func GenConversationIDForSingle(sendID, recvID string) string {
+ l := []string{sendID, recvID}
+ sort.Strings(l)
+ return "si_" + strings.Join(l, "_")
+}
+
+func GenConversationUniqueKeyForGroup(groupID string) string {
+ return groupID
+}
+
+func GenGroupConversationID(groupID string) string {
+ return "sg_" + groupID
+}
+
+func GenConversationUniqueKeyForSingle(sendID, recvID string) string {
+ l := []string{sendID, recvID}
+ sort.Strings(l)
+ return strings.Join(l, "_")
+}
+
+func GetNotificationConversationIDByConversationID(conversationID string) string {
+ l := strings.Split(conversationID, "_")
+ if len(l) > 1 {
+ l[0] = "n"
+ return strings.Join(l, "_")
+ }
+ return ""
+}
+
+func GetSelfNotificationConversationID(userID string) string {
+ return "n_" + userID + "_" + userID
+}
+
+func GetSeqsBeginEnd(seqs []int64) (int64, int64) {
+ if len(seqs) == 0 {
+ return 0, 0
+ }
+ return seqs[0], seqs[len(seqs)-1]
+}
diff --git a/pkg/util/conversationutil/doc.go b/pkg/util/conversationutil/doc.go
new file mode 100644
index 0000000000..90325ccb00
--- /dev/null
+++ b/pkg/util/conversationutil/doc.go
@@ -0,0 +1 @@
+package conversationutil // import "github.com/openimsdk/open-im-server/v3/pkg/util/conversationutil"
diff --git a/pkg/util/flag/flag.go b/pkg/util/flag/flag.go
deleted file mode 100644
index 0a8e527ab7..0000000000
--- a/pkg/util/flag/flag.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright © 2024 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package flag
-
-import (
- "flag"
- "log"
- "strings"
-
- "github.com/spf13/pflag"
-)
-
-// WordSepNormalizeFunc changes all flags that contain "_" separators.
-func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
- if strings.Contains(name, "_") {
- return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
- }
- return pflag.NormalizedName(name)
-}
-
-// WarnWordSepNormalizeFunc changes and warns for flags that contain "_" separators.
-func WarnWordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
- if strings.Contains(name, "_") {
- normalizedName := strings.ReplaceAll(name, "_", "-")
- log.Printf("WARNING: flag %s has been deprecated and will be removed in a future version. Use %s instead.", name, normalizedName)
- return pflag.NormalizedName(normalizedName)
- }
- return pflag.NormalizedName(name)
-}
-
-// InitFlags normalizes, parses, then logs the command line flags.
-func InitFlags() {
- pflag.CommandLine.SetNormalizeFunc(WordSepNormalizeFunc)
- pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
-}
-
-// PrintFlags logs the flags in the flagset.
-func PrintFlags(flags *pflag.FlagSet) {
- flags.VisitAll(func(flag *pflag.Flag) {
- log.Printf("FLAG: --%s=%q", flag.Name, flag.Value)
- })
-}
diff --git a/pkg/util/genutil/genutil.go b/pkg/util/genutil/genutil.go
deleted file mode 100644
index 95735485d5..0000000000
--- a/pkg/util/genutil/genutil.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// Copyright © 2024 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package genutil
-
-import (
- "fmt"
- "os"
- "path/filepath"
-
- "github.com/OpenIMSDK/tools/errs"
-)
-
-// OutDir creates the absolute path name from path and checks path exists.
-// Returns absolute path including trailing '/' or error if path does not exist.
-func OutDir(path string) (string, error) {
- outDir, err := filepath.Abs(path)
- if err != nil {
- return "", errs.Wrap(err, "output directory %s does not exist", path)
- }
-
- stat, err := os.Stat(outDir)
- if err != nil {
- return "", errs.Wrap(err, "output directory %s does not exist", outDir)
- }
-
- if !stat.IsDir() {
- return "", errs.Wrap(err, "output directory %s is not a directory", outDir)
- }
- outDir += "/"
- return outDir, nil
-}
-
-func ExitWithError(err error) {
- progName := filepath.Base(os.Args[0])
- fmt.Fprintf(os.Stderr, "%s exit -1: %+v\n", progName, err)
- os.Exit(-1)
-}
-
-func SIGTERMExit() {
- progName := filepath.Base(os.Args[0])
- fmt.Fprintf(os.Stderr, "Warning %s receive process terminal SIGTERM exit 0\n", progName)
-}
diff --git a/pkg/util/genutil/genutil_test.go b/pkg/util/genutil/genutil_test.go
deleted file mode 100644
index 050d140407..0000000000
--- a/pkg/util/genutil/genutil_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright © 2024 OpenIM. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package genutil
-
-import (
- "testing"
-)
-
-func TestValidDir(t *testing.T) {
- _, err := OutDir("./")
- if err != nil {
- t.Fatal(err)
- }
-}
-
-func TestInvalidDir(t *testing.T) {
- _, err := OutDir("./nondir")
- if err == nil {
- t.Fatal("expected an error")
- }
-}
-
-func TestNotDir(t *testing.T) {
- _, err := OutDir("./genutils_test.go")
- if err == nil {
- t.Fatal("expected an error")
- }
-}
diff --git a/scripts/check-all.sh b/scripts/check-all.sh
index d410233845..54296b22d5 100755
--- a/scripts/check-all.sh
+++ b/scripts/check-all.sh
@@ -20,9 +20,6 @@
# READ: https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh
-
-
-
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
source "${OPENIM_ROOT}/scripts/install/common.sh"
@@ -33,7 +30,7 @@ fi
OPENIM_VERBOSE=4
-openim::log::info "\n# Begin to check all openim service"
+openim::log::info "\n# Begin to check all OpenIM service"
openim::log::status "Check all dependent service ports"
# Elegant printing function
@@ -65,16 +62,16 @@ print_services_and_ports "${OPENIM_SERVER_NAME_TARGETS[@]}" "${OPENIM_SERVER_POR
print_services_and_ports "${OPENIM_DEPENDENCY_TARGETS[@]}" "${OPENIM_DEPENDENCY_PORT_TARGETS[@]}"
# OpenIM check
-echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}"
-openim::log::info "\n## Check all dependent service ports"
-echo "++ The port being checked: ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}"
+#echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}"
+openim::log::info "\n## Check all dependent components service ports"
+#echo "++ The port being checked: ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}"
# Later, after discarding Docker, the Docker keyword is unreliable, and Kubepods is used
if grep -qE 'docker|kubepods' /proc/1/cgroup || [ -f /.dockerenv ]; then
openim::color::echo ${COLOR_CYAN} "Environment in the interior of the container"
else
- openim::color::echo ${COLOR_CYAN} "The environment is outside the container"
+ openim::color::echo ${COLOR_CYAN}"The environment is outside the container"
openim::util::check_ports ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}
fi
@@ -82,30 +79,35 @@ if [[ $? -ne 0 ]]; then
openim::log::error_exit "The service does not start properly, please check the port, query variable definition!"
echo "+++ https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh +++"
else
- openim::log::success "All components depended on by openim are running normally! "
+ openim::log::success "All components depended on by OpenIM are running normally! "
fi
-openim::log::info "\n## Check openim service name:\n${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer"
+openim::log::status "Check OpenIM service:"
+openim::log::colorless "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer"
result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check)
if [[ $? -ne 0 ]]; then
- echo "+++ cat openim log file >>> ${LOG_FILE}"
- openim::log::error "check process failed.\n $result"
+ #echo "+++ cat openim log file >>> ${LOG_FILE}"
+
+ openim::log::error "The service is not running properly, please check the logs $result"
fi
-echo "Check openim service name:"
+openim::log::status "Check OpenIM service:"
for item in "${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}"; do
- echo "$item"
+ openim::log::colorless "$item"
done
+
result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]})
if [[ $? -ne 0 ]]; then
- echo "+++ cat openim log file >>> ${LOG_FILE}"
- openim::log::error "check process failed.\n "
+ #echo "+++ cat OpenIM log file >>> ${LOG_FILE}"
+ openim::log::error "The service is not running properly, please check the logs "
echo "$result"
exit 1
else
- openim::log::success "All openim services are running normally! "
+ openim::log::status "List the ports listened to by the OpenIM service:"
+ openim::util::find_ports_for_all_services ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}
+ openim::util::find_ports_for_all_services ${OPENIM_MSGTRANSFER_BINARY[@]}
+ openim::log::success "All OpenIM services are running normally! "
fi
-
diff --git a/scripts/cherry-pick.sh b/scripts/cherry-pick.sh
index ff303269d8..b95289cd4f 100755
--- a/scripts/cherry-pick.sh
+++ b/scripts/cherry-pick.sh
@@ -35,8 +35,8 @@ DRY_RUN=${DRY_RUN:-""}
REGENERATE_DOCS=${REGENERATE_DOCS:-""}
UPSTREAM_REMOTE=${UPSTREAM_REMOTE:-upstream}
FORK_REMOTE=${FORK_REMOTE:-origin}
-MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')}
-MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/http[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')}
+MAIN_REPO_ORG=${MAIN_REPO_ORG:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/webhook[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $3}')}
+MAIN_REPO_NAME=${MAIN_REPO_NAME:-$(git remote get-url "$UPSTREAM_REMOTE" | awk '{gsub(/webhook[s]:\/\/|git@/,"")}1' | awk -F'[@:./]' 'NR==1{print $4}')}
if [[ -z ${GITHUB_USER:-} ]]; then
openim::log::error_exit "Please export GITHUB_USER= (or GH organization, if that's where your fork lives)"
diff --git a/scripts/create-topic.sh b/scripts/create-topic.sh
index 5c291ff085..206075fb83 100755
--- a/scripts/create-topic.sh
+++ b/scripts/create-topic.sh
@@ -14,14 +14,42 @@
# limitations under the License.
# Wait for Kafka to be ready
-until /opt/bitnami/kafka/bin/kafka-topics.sh --list --bootstrap-server localhost:9092; do
- echo "Waiting for Kafka to be ready..."
- sleep 2
+
+KAFKA_SERVER=localhost:9092
+
+MAX_ATTEMPTS=300
+attempt_num=1
+
+echo "Waiting for Kafka to be ready..."
+
+until /opt/bitnami/kafka/bin/kafka-topics.sh --list --bootstrap-server $KAFKA_SERVER; do
+ echo "Attempt $attempt_num of $MAX_ATTEMPTS: Kafka not ready yet..."
+ if [ $attempt_num -eq $MAX_ATTEMPTS ]; then
+ echo "Kafka not ready after $MAX_ATTEMPTS attempts, exiting"
+ exit 1
+ fi
+ attempt_num=$((attempt_num+1))
+ sleep 1
done
-# Create topics
-/opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 8 --topic latestMsgToRedis
-/opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 8 --topic msgToPush
-/opt/bitnami/kafka/bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 8 --topic offlineMsgToMongoMysql
+echo "Kafka is ready. Creating topics..."
+
+
+topics=("toRedis" "toMongo" "toPush")
+partitions=8
+replicationFactor=1
+
+for topic in "${topics[@]}"; do
+ if /opt/bitnami/kafka/bin/kafka-topics.sh --create \
+ --bootstrap-server $KAFKA_SERVER \
+ --replication-factor $replicationFactor \
+ --partitions $partitions \
+ --topic $topic
+ then
+ echo "Topic $topic created."
+ else
+ echo "Failed to create topic $topic."
+ fi
+done
-echo "Topics created."
\ No newline at end of file
+echo "All topics created."
diff --git a/scripts/demo.sh b/scripts/demo.sh
deleted file mode 100755
index 4b877b9ed3..0000000000
--- a/scripts/demo.sh
+++ /dev/null
@@ -1,156 +0,0 @@
-#!/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.
-
-if ! command -v pv &> /dev/null
-then
- echo "pv not found, installing..."
- if [ -e /etc/debian_version ]; then
- sudo apt-get update
- sudo apt-get install -y pv
- elif [ -e /etc/redhat-release ]; then
- sudo yum install -y pv
- else
- echo "Unsupported OS, please install pv manually."
- exit 1
- fi
-fi
-
-readonly t_reset=$(tput sgr0)
-readonly green=$(tput bold; tput setaf 2)
-readonly yellow=$(tput bold; tput setaf 3)
-readonly blue=$(tput bold; tput setaf 6)
-readonly timeout=$(if [ "$(uname)" == "Darwin" ]; then echo "1"; else echo "0.1"; fi)
-readonly ipv6regex='(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))'
-
-clear
-. $(dirname ${BASH_SOURCE})/lib/util.sh
-
-openim::util::ensure-bash-version
-
-trap 'openim::util::onCtrlC' INT
-
-function openim::util::onCtrlC() {
- echo -e "\n${t_reset}Ctrl+C Press it. It's exiting openim make init..."
- exit 0
-}
-
-openim::util::desc "========> Welcome to the OpenIM Demo"
-openim::util::desc "========> We'll help you get started with OpenIM quickly"
-openim::util::desc "========> Press Enter to continue...."
-openim::util::run "make advertise"
-clear
-
-openim::util::desc "========> Initialize the project and generate configuration files"
-openim::util::run "make init"
-clear
-
-# openim::util::desc "========> You can look git diff"
-# openim::util::run "git diff"
-# clear
-
-openim::util::desc "You can learn a lot about automation using make help"
-openim::util::run "make help"
-clear
-
-openim::util::desc "You can learn a lot about automation using make help-all"
-openim::util::run "make help-all"
-clear
-
-openim::util::desc "First, let's verify and install some necessary tools"
-openim::util::run "make tools"
-clear
-
-openim::util::desc "========> Start the basic openim docker components"
-openim::util::desc "========> You can use docker-compose ps to check the status of the container"
-openim::util::run "docker compose up -d"
-clear
-
-openim::util::desc "========> Use make init-githooks Initialize git hooks "
-openim::util::run "make init-githooks"
-clear
-
-openim::util::desc "The specification is pretty high, you need to be bound on your branch name, as well as commit messages"
-openim::util::run "git commit -a -s -m 'feta: commit demo against specification'"
-openim::util::run "# git commit -a -s -m 'feat: commit demo against specification' --amend"
-clear
-
-openim::util::desc "How did we teach you how to build OpenIM"
-openim::util::desc "A full build startup check"
-openim::util::run "# make all"
-openim::util::desc "Build one OpenIM binary"
-openim::util::desc "BINS: openim-api openim-cmdutils openim-crontask openim-msggateway openim-msgtransfer openim-push openim-rpc changelog infra ncpu yamlfmt"
-openim::util::run "make build BINS=openim-api"
-openim::util::run "make build"
-
-openim::util::desc "Build binaries for all platforms"
-openim::util::run "make multiarch -j BINS=openim-crontask PLATFORMS='linux_arm64 linux_amd64' "
-
-openim::util::desc "If you wish to use dlv for debugging, either binary or process"
-openim::util::desc "You need to enable debug mode"
-openim::util::run "make build BINS=openim-cmdutils DEBUG=1"
-clear
-
-openim::util::desc "Next, let's learn how to start the OpenIM service. For starting, we have two ways"
-openim::util::desc "The first is Background startup"
-openim::util::run "make start"
-openim::util::desc "The second way is through the Linux system way"
-openim::util::run "./scripts/install/install.sh --help"
-clear
-
-openim::util::desc "Next, let's learn how to check the OpenIM service. For checking, we have two ways"
-openim::util::run "make check"
-clear
-
-openim::util::desc "Next, let's learn how to stop the OpenIM service. For stopping, we have two ways"
-openim::util::run "make stop"
-clear
-
-openim::util::desc "Run tidy to format and fix imports"
-openim::util::run "make tidy"
-clear
-
-openim::util::desc "Vendor go.mod dependencies"
-openim::util::run "# make vendor"
-clear
-
-openim::util::desc "Run unit tests"
-openim::util::run "# make test"
-clear
-
-openim::util::desc "Run unit tests and get test coverage"
-openim::util::run "# make cover"
-clear
-
-openim::util::desc "Check for updates to go.mod dependencies"
-openim::util::run "# make updates"
-clear
-
-openim::util::desc "You can learn a lot about automation using make clean, remove all files that are created by building"
-openim::util::run "make clean"
-clear
-
-openim::util::desc "Generate all necessary files"
-openim::util::run "make gen"
-clear
-
-openim::util::desc "Verify the license headers for all files"
-openim::util::run "make verify-copyright"
-clear
-
-openim::util::desc "Add copyright"
-openim::util::run "make add-copyright"
-clear
-
-exit 0
diff --git a/scripts/docker-check-service.sh b/scripts/docker-check-service.sh
deleted file mode 100755
index 30ca89b5a9..0000000000
--- a/scripts/docker-check-service.sh
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/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.
-
-OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
-source "${OPENIM_ROOT}/scripts/install/common.sh"
-
-cd "$OPENIM_ROOT"
-
-openim::util::check_docker_and_compose_versions
-
-progress() {
- local _main_pid="$1"
- local _length=20
- local _ratio=1
- local _colors=("31" "32" "33" "34" "35" "36" "37")
- local _wave=("▁" "▂" "▃" "▄" "▅" "▆" "▇" "█" "▇" "▆" "▅" "▄" "▃" "▂")
-
- while pgrep -P "$_main_pid" &> /dev/null; do
- local _mark='>'
- local _progress_bar=
- for ((i = 1; i <= _length; i++)); do
- if ((i > _ratio)); then
- _mark='-'
- fi
- _progress_bar="${_progress_bar}${_mark}"
- done
-
- local _color_idx=$((_ratio % ${#_colors[@]}))
- local _color_prefix="\033[${_colors[_color_idx]}m"
- local _reset_suffix="\033[0m"
-
- local _wave_idx=$((_ratio % ${#_wave[@]}))
- local _wave_progress=${_wave[_wave_idx]}
-
- printf "Progress: ${_color_prefix}${_progress_bar}${_reset_suffix} ${_wave_progress} Countdown: %2ds \r" "$_countdown"
- ((_ratio++))
- ((_ratio > _length)) && _ratio=1
- sleep 0.1
- done
-}
-
-countdown() {
- local _duration="$1"
-
- for ((i = _duration; i >= 1; i--)); do
- printf "\rCountdown: %2ds \r" "$i"
- sleep 1
- done
- printf "\rCountdown: %2ds \r" "$_duration"
-}
-
-do_sth() {
- echo "++++++++++++++++++++++++"
- progress $$ &
- local _progress_pid=$!
- local _countdown=30
-
- countdown "$_countdown" &
- local _countdown_pid=$!
-
- sleep 30
-
- kill "$_progress_pid" "$_countdown_pid"
-
- "${SCRIPTS_ROOT}/check-all.sh"
- echo -e "${PURPLE_PREFIX}=========> Check docker-compose status ${COLOR_SUFFIX} \n"
-}
-
-set -e
-
-do_sth &
-do_sth_pid=$(jobs -p | tail -1)
-
-progress "${do_sth_pid}" &
-progress_pid=$(jobs -p | tail -1)
-
-wait "${do_sth_pid}"
-printf "Progress: done \n"
diff --git a/scripts/docker-start-all.sh b/scripts/docker-start-all.sh
index 4f8747a2a2..116038b536 100755
--- a/scripts/docker-start-all.sh
+++ b/scripts/docker-start-all.sh
@@ -21,9 +21,6 @@
#fixme This scripts is the total startup scripts
#fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array
-OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
-source "${OPENIM_ROOT}/scripts/install/common.sh"
-openim::log::info "\n# Use Docker to start all openim service"
-trap 'openim::util::onCtrlC' INT
-"${OPENIM_ROOT}"/scripts/start-all.sh 2>&1 &
-tail -f ${DOCKER_LOG_FILE}
+cd /openim/openim-server
+mage start
+tail -f /dev/null
diff --git a/scripts/gendoc.sh b/scripts/gendoc.sh
index ece090190e..43b69c3edd 100755
--- a/scripts/gendoc.sh
+++ b/scripts/gendoc.sh
@@ -13,66 +13,72 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-DEFAULT_DIRS=(
- "pkg"
- "internal/pkg"
-)
-BASE_URL="github.com/openimsdk/open-im-server"
+#!/bin/bash
+
+DEFAULT_DIRS=("pkg")
+BASE_URL="github.com/openimsdk/open-im-server/v3"
+REMOVE_DOC=false
usage() {
echo "Usage: $0 [OPTIONS]"
echo
- echo "This script iterates over directories and generates doc.go if necessary."
- echo "By default, it processes 'pkg' and 'internal/pkg' directories."
+ echo "This script iterates over directories. By default, it generates doc.go files for 'pkg' and 'internal/pkg'."
echo
echo "Options:"
- echo " -d DIRS, --dirs DIRS Specify the directories to be processed, separated by commas. E.g., 'pkg,internal/pkg'."
- echo " -u URL, --url URL Set the base URL for the import path. Default is '$BASE_URL'."
- echo " -h, --help Show this help message."
+ echo " -d DIRS, --dirs DIRS Specify directories to process, separated by commas (e.g., 'pkg,internal/pkg')."
+ echo " -u URL, --url URL Set the base URL for the import path. Default is '$BASE_URL'."
+ echo " -r, --remove Remove all doc.go files in the specified directories."
+ echo " -h, --help Show this help message."
echo
}
process_dir() {
- local dir=$1
- local base_url=$2
-
- for d in $(find $dir -type d); do
- if [ ! -f $d/doc.go ]; then
- if ls $d/*.go > /dev/null 2>&1; then
- echo $d/doc.go
- echo "package $(basename $d) // import \"$base_url/$d\"" > $d/doc.go
+ local dir="$1"
+ local base_url="$2"
+ local remove_doc="$3"
+
+ find "$dir" -type d | while read -r d; do
+ if [ "$remove_doc" = true ]; then
+ if [ -f "$d/doc.go" ]; then
+ echo "Removing $d/doc.go"
+ rm -f "$d/doc.go"
+ fi
+ else
+ if [ ! -f "$d/doc.go" ] && ls "$d/"*.go &>/dev/null; then
+ echo "Creating $d/doc.go"
+ echo "package $(basename "$d") // import \"$base_url/$(echo "$d" | sed "s|^\./||")\"" >"$d/doc.go"
fi
fi
done
}
while [[ $# -gt 0 ]]; do
- key="$1"
-
- case $key in
+ case "$1" in
-d|--dirs)
- IFS=',' read -ra DIRS <<< "$2"
- shift # shift past argument
- shift # shift past value
- ;;
- -u|--url)
- BASE_URL="$2"
- shift # shift past argument
- shift # shift past value
- ;;
- -h|--help)
- usage
- exit 0
- ;;
- *)
- usage
- exit 1
- ;;
- esac
+ IFS=',' read -ra DIRS <<< "$2"
+ shift 2
+ ;;
+ -u|--url)
+ BASE_URL="$2"
+ shift 2
+ ;;
+ -r|--remove)
+ REMOVE_DOC=true
+ shift
+ ;;
+ -h|--help)
+ usage
+ exit 0
+ ;;
+ *)
+ usage
+ exit 1
+ ;;
+ esac
done
-DIRS=${DIRS:-${DEFAULT_DIRS[@]}}
+DIRS=(${DIRS:-"${DEFAULT_DIRS[@]}"})
for dir in "${DIRS[@]}"; do
- process_dir $dir $BASE_URL
+ process_dir "$dir" "$BASE_URL" "$REMOVE_DOC"
done
diff --git a/scripts/init-config.sh b/scripts/init-config.sh
index c5b21d061b..ef2cd0a401 100755
--- a/scripts/init-config.sh
+++ b/scripts/init-config.sh
@@ -65,6 +65,8 @@ GENERATE_EXAMPLES=false
CLEAN_CONFIG=false
CLEAN_EXAMPLES=false
+FILES_PROCESSED=false
+
# Function to display help information
show_help() {
echo "Usage: $(basename "$0") [options]"
@@ -126,7 +128,7 @@ process_file() {
if [[ "${FORCE_OVERWRITE}" == true ]]; then
openim::log::info "Force overwriting ${output_file}."
elif [[ "${SKIP_EXISTING}" == true ]]; then
- openim::log::info "Skipping generation of ${output_file} as it already exists."
+ #openim::log::info "Skipping generation of ${output_file} as it already exists."
return
else
echo -n "File ${output_file} already exists. Overwrite? (Y/N): "
@@ -173,8 +175,7 @@ process_file() {
exit 1
}
fi
-
- sleep 0.5
+ FILES_PROCESSED=true
}
clean_config_files() {
@@ -259,4 +260,6 @@ if [[ "${GENERATE_EXAMPLES}" == true ]] && [[ "${CLEAN_EXAMPLES}" == false ]]; t
generate_example_files
fi
-openim::log::success "Configuration and example files operation complete!"
+if [[ "${FILES_PROCESSED}" == true ]]; then
+ openim::log::success "Configuration and example files operation complete!"
+fi
diff --git a/scripts/install/openim-api.sh b/scripts/install/openim-api.sh
index cd3d5eb080..8403382e6a 100755
--- a/scripts/install/openim-api.sh
+++ b/scripts/install/openim-api.sh
@@ -89,7 +89,7 @@ function openim::api::start_service() {
echo "Starting service with command: $cmd"
#nohup $cmd >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) &
- nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 &
if [ $? -ne 0 ]; then
openim::log::error_exit "Failed to start ${binary_name} on port ${service_port}."
return 1
diff --git a/scripts/install/openim-crontask.sh b/scripts/install/openim-crontask.sh
index 1917010823..d785b37a4e 100755
--- a/scripts/install/openim-crontask.sh
+++ b/scripts/install/openim-crontask.sh
@@ -53,7 +53,7 @@ function openim::crontask::start() {
openim::log::status "start cron_task process, path: ${OPENIM_CRONTASK_BINARY}"
#nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) &
cmd="${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG}"
- nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 &
return 0
}
diff --git a/scripts/install/openim-msggateway.sh b/scripts/install/openim-msggateway.sh
index 25051aa6e0..ed79108055 100755
--- a/scripts/install/openim-msggateway.sh
+++ b/scripts/install/openim-msggateway.sh
@@ -62,7 +62,7 @@ function openim::msggateway::start() {
PROMETHEUS_PORT_OPTION="--prometheus_port ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}"
fi
cmd="${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG}"
- nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 &
# nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) &
done
diff --git a/scripts/install/openim-msgtransfer.sh b/scripts/install/openim-msgtransfer.sh
index 23ce79c36f..8403d1cfcf 100755
--- a/scripts/install/openim-msgtransfer.sh
+++ b/scripts/install/openim-msgtransfer.sh
@@ -58,7 +58,8 @@ function openim::msgtransfer::start() {
PROMETHEUS_PORT_OPTION="--prometheus_port ${PROMETHEUS_MSG_TRANSFER_PORT}"
fi
cmd="${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i}"
- nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 &
#nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) &
done
return 0
@@ -82,7 +83,7 @@ function openim::msgtransfer::check() {
fi
done
else
- openim::log::error "Expected $OPENIM_MSGGATEWAY_NUM openim msgtransfer processes, but found $NUM_PROCESSES msgtransfer processes."
+ openim::log::error "Expected $OPENIM_MSGGATEWAY_NUM OpenIM msgtransfer processes, but found $NUM_PROCESSES msgtransfer processes running"
return 1
fi
return 0
diff --git a/scripts/install/openim-push.sh b/scripts/install/openim-push.sh
index 8dea4b1f15..aafb24c7b0 100755
--- a/scripts/install/openim-push.sh
+++ b/scripts/install/openim-push.sh
@@ -72,7 +72,8 @@ function openim::push::start() {
for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do
openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}"
cmd="${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]}"
- nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 &
#nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) &
done
return 0
diff --git a/scripts/install/openim-rpc.sh b/scripts/install/openim-rpc.sh
index c3ac12449d..1265a11c67 100755
--- a/scripts/install/openim-rpc.sh
+++ b/scripts/install/openim-rpc.sh
@@ -166,7 +166,8 @@ function openim::rpc::start_service() {
cmd="${cmd} --prometheus_port ${prometheus_port}"
fi
#nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) &
- nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null &
+ nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >> "${LOG_FILE}" 2>&1 &
return 0
}
diff --git a/scripts/install/openim-tools.sh b/scripts/install/openim-tools.sh
index 7f6e1f52b5..57de9772d3 100755
--- a/scripts/install/openim-tools.sh
+++ b/scripts/install/openim-tools.sh
@@ -83,67 +83,65 @@ function openim::tools::start_service() {
local prometheus_port="$4"
local cmd="${OPENIM_OUTPUT_HOSTBIN_TOOLS}/${binary_name}"
- openim::log::info "Starting PATH: ${OPENIM_OUTPUT_HOSTBIN_TOOLS}/${binary_name}..."
+ #openim::log::info "Starting PATH: ${OPENIM_OUTPUT_HOSTBIN_TOOLS}/${binary_name}..."
if [ -n "${config}" ]; then
- printf "Specifying config: %s\n" "${config}"
+ # printf "Specifying config: %s\n" "${config}"
cmd="${cmd} -c ${config}/config.yaml"
fi
if [ -n "${service_port}" ]; then
- printf "Specifying service port: %s\n" "${service_port}"
+ #printf "Specifying service port: %s\n" "${service_port}"
cmd="${cmd} --port ${service_port}"
fi
if [ -n "${prometheus_port}" ]; then
- printf "Specifying prometheus port: %s\n" "${prometheus_port}"
+ #printf "Specifying prometheus port: %s\n" "${prometheus_port}"
cmd="${cmd} --prometheus_port ${prometheus_port}"
fi
- openim::log::status "Starting binary ${binary_name}..."
+ #openim::log::status "Starting binary ${binary_name}..."
-
- ${cmd}
-
+ # openim::log::info "cmd: $cmd"
+ ${cmd}
local status=$?
-
- if [ $status -eq 0 ]; then
- openim::log::info "Service ${binary_name} started successfully."
- return 0
- else
- openim::log::error "Failed to start service ${binary_name}."
- return 1
- fi
+ if [ $status -eq 0 ]; then
+ openim::log::colorless "Service ${binary_name} started successfully."
+ return 0
+ else
+ openim::log::error "Failed to start service ${binary_name}."
+ return 1
+ fi
}
function openim::tools::start() {
openim::log::info "Starting OpenIM Tools..."
for tool in "${OPENIM_TOOLS_NAME_LISTARIES[@]}"; do
- openim::log::info "Starting tool ${tool}..."
+ openim::log::colorless "Starting tool ${tool}..."
# openim::tools::start_service ${tool}
- sleep 0.2
+ #sleep 0.2
done
}
function openim::tools::pre-start() {
- openim::log::info "Preparing to start OpenIM Tools..."
+ #openim::log::info "Preparing to start OpenIM Tools..."
for tool in "${OPENIM_TOOLS_PRE_START_NAME_LISTARIES[@]}"; do
- openim::log::info "Starting tool ${tool}..."
+ openim::log::colorless "Starting tool: ${tool}"
if ! openim::tools::start_service ${tool} ${OPNEIM_CONFIG}; then
openim::log::error "Failed to start ${tool}, aborting..."
return 1
fi
done
- openim::log::info "All tools started successfully."
+ #openim::log::info "All tools started successfully."
}
function openim::tools::post-start() {
- openim::log::info "Post-start actions for OpenIM Tools..."
+ #openim::log::info "Post-start actions for OpenIM Tools..."
for tool in "${OPENIM_TOOLS_POST_START_NAME_LISTARIES[@]}"; do
- openim::log::info "Starting tool ${tool}..."
+ openim::log::colorless "Starting tool: ${tool}"
openim::tools::start_service ${tool}
done
}
diff --git a/scripts/lib/golang.sh b/scripts/lib/golang.sh
index f9648cbdba..51db58d891 100755
--- a/scripts/lib/golang.sh
+++ b/scripts/lib/golang.sh
@@ -149,7 +149,7 @@ openim::golang::start_script_list() {
openim-msgtransfer.sh
openim-msggateway.sh
openim-crontask.sh
- openim-tools.sh
+ #openim-tools.sh
)
local result=()
for target in "${targets[@]}"; do
@@ -181,7 +181,7 @@ openim::golang::check_openim_binaries() {
done
return 1
else
- echo "All binaries have been installed in ${OPENIM_OUTPUT_HOSTBIN}。"
+ echo -e "All binaries have been installed in: \n${OPENIM_OUTPUT_HOSTBIN}\n${OPENIM_OUTPUT_HOSTBIN_TOOLS}"
return 0
fi
}
@@ -317,8 +317,6 @@ readonly OPENIM_CLIENT_TARGETS=(
imctl
infra
ncpu
- openim-web
- up35
versionchecker
yamlfmt
)
diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh
index fec71b19d9..0d15463eb0 100755
--- a/scripts/lib/logging.sh
+++ b/scripts/lib/logging.sh
@@ -210,16 +210,26 @@ openim::log::status() {
if [[ ${OPENIM_VERBOSE} < ${V} ]]; then
return
fi
-
- timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]")
- echo_log "${timestamp} ${1}"
+
+ local COLOR_BLUE="\033[0;34m"
+ local COLOR_RESET="\033[0m"
+
+ local timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]")
+
+ echo_log() {
+ echo -e "$@"
+ }
+
+
+ echo_log "${COLOR_BLUE}${timestamp} ${1}${COLOR_RESET}"
shift
for message; do
- echo_log " ${message}"
+ echo_log "${COLOR_BLUE}${message}${COLOR_RESET}"
done
}
+
openim::log::success() {
local V="${V:-0}"
if [[ ${OPENIM_VERBOSE} < ${V} ]]; then
@@ -232,7 +242,6 @@ openim::log::success() {
-
function openim::log::test_log() {
echo_log "test log"
openim::log::info "openim::log::info"
@@ -249,3 +258,14 @@ function openim::log::print_blue() {
echo -e "\033[0;36m$1\033[0m"
}
+
+openim::log::colorless() {
+ local V="${V:-0}"
+ if [[ ${OPENIM_VERBOSE} < ${V} ]]; then
+ return
+ fi
+ timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]")
+ echo_log -e "${timestamp} ${1} "
+}
+
+
diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh
index 05e92377d5..3e6cde103d 100755
--- a/scripts/lib/release.sh
+++ b/scripts/lib/release.sh
@@ -243,7 +243,7 @@ function openim::release::package_client_tarballs() {
local client_bins=("${OPENIM_CLIENT_BINARIES[@]}")
- # client_bins: changelog component imctl infra ncpu openim-web up35 versionchecker yamlfmt
+ # client_bins: changelog component imctl infra ncpu versionchecker yamlfmt
# Copy client binclient_bins:aries
openim::log::info " Copy client binaries: ${client_bins[@]/#/${LOCAL_OUTPUT_BINTOOLSPATH}/${platform}/}"
openim::log::info " Copy client binaries to: ${release_stage}/client/bin"
diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh
index dcce56c12f..13bd263caf 100755
--- a/scripts/lib/util.sh
+++ b/scripts/lib/util.sh
@@ -360,8 +360,8 @@ openim::util::check_ports() {
# If any of the processes is not running, return a status of 1.
if [[ ${#not_started[@]} -ne 0 ]]; then
- openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}"
- openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
+ #openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}"
+ #openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}'
return 1
else
@@ -446,8 +446,8 @@ openim::util::check_process_names() {
# Return status
if [[ ${#not_started[@]} -ne 0 ]]; then
- openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}"
- openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
+ #openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}"
+ #openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}"
cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}'
return 1
else
@@ -599,33 +599,36 @@ openim::util::stop_services_with_name() {
# An array to collect information about processes that were stopped.
local stopped=()
- echo "Stopping services with names: $*"
# Iterate over each given service name.
for server_name in "$@"; do
# Use the `pgrep` command to find process IDs related to the given service name.
local pids=$(pgrep -f "$server_name")
-
# If no process was found with the name, add it to the not_stopped list
if [[ -z $pids ]]; then
not_stopped+=("$server_name")
continue
fi
local stopped_this_time=false
- for pid in $pids; do
+ for pid in $pids; do
- # Exclude the PID of the current script
- if [[ "$pid" == "$$" ]]; then
- continue
- fi
+ # Exclude the PID of the current script
+ if [[ "$pid" == "$$" ]]; then
+ continue
+ fi
+
+ # If there's a Process ID, it means the service with the name is running.
+ if [[ -n $pid ]]; then
+ # Print the binary path for the PID
+ binary_path=$(readlink -f /proc/$pid/exe)
+ openim::log::colorless "stop PID $pid full path: $binary_path"
+
+ # Try to stop the service by killing its process.
+ if kill -15 $pid 2>/dev/null; then
+ stopped_this_time=true
+ fi
+ fi
+ done
- # If there's a Process ID, it means the service with the name is running.
- if [[ -n $pid ]]; then
- # Try to stop the service by killing its process.
- if kill -15 $pid 2>/dev/null; then
- stopped_this_time=true
- fi
- fi
- done
if $stopped_this_time; then
stopped+=("$server_name")
@@ -2848,6 +2851,46 @@ function openim::util::check_process_names_for_stop() {
+function openim::util::find_process_ports() {
+ local process_path="$1"
+ if [[ -z "$process_path" ]]; then
+ echo "Usage: find_process_ports /path/to/process"
+ return 1
+ fi
+
+ local protocol_ports=""
+ while read -r line; do
+ local port_protocol=($line)
+ local port=${port_protocol[0]##*:}
+ local protocol=${port_protocol[1]}
+ protocol_ports="${protocol_ports}${protocol} ${port}, "
+
+ done < <(lsof -nP -iTCP -iUDP | grep LISTEN | grep "$(pgrep -f "$process_path")" | awk '{print $9, $8}')
+
+ protocol_ports=${protocol_ports%, }
+
+ if [[ -z "$protocol_ports" ]]; then
+ openim::log::colorless "$process_path is not listening on any ports"
+ else
+ openim::log::colorless "$process_path is listening on protocol & port: $protocol_ports"
+ fi
+}
+
+
+
+
+
+function openim::util::find_ports_for_all_services() {
+ local services=("$@")
+ for service in "${services[@]}"; do
+ openim::util::find_process_ports "$service"
+ done
+}
+
+
+
+
+
if [[ "$*" =~ openim::util:: ]];then
eval $*
fi
diff --git a/scripts/list-feature-tests.sh b/scripts/list-feature-tests.sh
index d6eaa48731..f48a7a7e3c 100755
--- a/scripts/list-feature-tests.sh
+++ b/scripts/list-feature-tests.sh
@@ -19,8 +19,5 @@
# Usage: `scripts/list-feature-tests.sh`.
-
-
-
OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
grep "\[Feature:\w+\]" "${OPENIM_ROOT}"/test/e2e/**/*.go -Eoh | LC_ALL=C sort -u
\ No newline at end of file
diff --git a/scripts/make-rules/gen.mk b/scripts/make-rules/gen.mk
index 1671fafffe..fba0132ff5 100644
--- a/scripts/make-rules/gen.mk
+++ b/scripts/make-rules/gen.mk
@@ -88,11 +88,6 @@ gen.docgo.check: gen.docgo.doc
gen.docgo.add:
@git ls-files --others '*/doc.go' | $(XARGS) -- git add
-## gen.docgo: Generate missing doc.go for go packages ✨
-.PHONY: gen.defaultconfigs
-gen.defaultconfigs:
- @${ROOT_DIR}/scripts/gen_default_config.sh
-
## gen.docgo: Generate missing doc.go for go packages ✨
.PHONY: gen.clean
gen.clean:
@@ -101,5 +96,5 @@ gen.clean:
## gen.help: show help for gen
.PHONY: gen.help
-gen.help: scripts/make-rules/gen.mk
+gen.help: scripts/make-rules/gen.m
$(call smallhelp)
\ No newline at end of file
diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk
index d6de8e3465..cdc5a5fc95 100644
--- a/scripts/make-rules/golang.mk
+++ b/scripts/make-rules/golang.mk
@@ -104,25 +104,25 @@ go.build: go.build.verify $(addprefix go.build., $(addprefix $(PLATFORM)., $(BIN
## go.start: Start openim
.PHONY: go.start
go.start:
- @echo "===========> Starting openim"
+ @echo "=========================> Starting OpenIM <========================="
@$(ROOT_DIR)/scripts/start-all.sh
## go.stop: Stop openim
.PHONY: go.stop
go.stop:
- @echo "===========> Stopping openim"
+ @echo "=========================> Stopping OpenIM <========================="
@$(ROOT_DIR)/scripts/stop-all.sh
## go.check: Check openim
.PHONY: go.check
go.check:
- @echo "===========> Checking openim"
+ @echo "=========================> Checking OpenIM <========================="
@$(ROOT_DIR)/scripts/check-all.sh
## go.check-component: Check openim component
.PHONY: go.check-component
go.check-component:
- @echo "===========> Checking openim component"
+ @echo "=========================> Checking OpenIM component <========================="
@$(ROOT_DIR)/scripts/install/openim-tools.sh openim::tools::pre-start
## go.versionchecker: Design, detect some environment variables and versions
diff --git a/scripts/mongo-init.sh b/scripts/mongo-init.sh
index 41d9ca0aae..01199c4806 100755
--- a/scripts/mongo-init.sh
+++ b/scripts/mongo-init.sh
@@ -12,19 +12,37 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-set -e
-
mongosh <> ${DOCKER_LOG_FILE} 2>&1
fi
-
-
openim::golang::check_openim_binaries
if [[ $? -ne 0 ]]; then
openim::log::error "OpenIM binaries are not found. Please run 'make build' to build binaries."
"${OPENIM_ROOT}"/scripts/build-all-service.sh
fi
-
"${OPENIM_ROOT}"/scripts/init-config.sh --skip
#openim::log::print_blue "Execute the following script in sequence: ${OPENIM_SERVER_SCRIPTARIES[@]}"
-
# TODO Prelaunch tools, simple for now, can abstract functions later
TOOLS_START_SCRIPTS_PATH=${START_SCRIPTS_PATH}/openim-tools.sh
-openim::log::print_blue "\n## Pre Starting OpenIM services"
-
-
-
-if ! ${TOOLS_START_SCRIPTS_PATH} openim::tools::pre-start; then
- openim::log::error "Pre Starting OpenIM services failed, aborting..."
- exit 1
-fi
+openim::log::status "Start the pre-start tools:"
+# if ! ${TOOLS_START_SCRIPTS_PATH} openim::tools::pre-start; then
+# openim::log::error "Start the pre-start tools, aborting!"
+# exit 1
+# fi
-openim::log::print_blue "Pre Starting OpenIM services processed successfully"
+openim::log::colorless "pre-start has been successfully completed!"
result=$("${OPENIM_ROOT}"/scripts/stop-all.sh)
if [[ $? -ne 0 ]]; then
@@ -102,28 +82,28 @@ if [[ $? -ne 0 ]]; then
exit 1
fi
-
-
-openim::log::status "\n## Starting openim scripts: "
+openim::log::status "Start the OpenIM startup scripts: "
execute_start_scripts
+openim::log::status "OpenIM startup scripts have been successfully completed!"
sleep 2
result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check)
if [[ $? -ne 0 ]]; then
- openim::log::error "The program may fail to start.\n $result"
+ openim::log::error "The OpenIM services may fail to start.\n $result"
exit 1
fi
-
result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]})
if [[ $? -ne 0 ]]; then
- openim::log::error "The program may fail to start.\n $result"
+ openim::log::error "The OpenIM services may fail to start.\n $result"
exit 1
fi
+openim::log::status "Start the post-start tools:"
+# ${TOOLS_START_SCRIPTS_PATH} openim::tools::post-start
+openim::log::status "post-start has been successfully completed!"
+openim::util::find_ports_for_all_services ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}
+openim::util::find_ports_for_all_services ${OPENIM_MSGTRANSFER_BINARY[@]}
-openim::log::info "\n## Post Starting openim services"
-${TOOLS_START_SCRIPTS_PATH} openim::tools::post-start
-
-openim::log::success "All openim services have been successfully started!"
\ No newline at end of file
+openim::log::success "All OpenIM services have been successfully started!"
\ No newline at end of file
diff --git a/start-config.yml b/start-config.yml
new file mode 100644
index 0000000000..a9c412b33f
--- /dev/null
+++ b/start-config.yml
@@ -0,0 +1,17 @@
+serviceBinaries:
+ openim-api: 1
+ openim-crontask: 1
+ openim-rpc-user: 1
+ openim-msggateway: 1
+ openim-push: 1
+ openim-msgtransfer: 4
+ openim-rpc-conversation: 1
+ openim-rpc-auth: 1
+ openim-rpc-group: 1
+ openim-rpc-friend: 1
+ openim-rpc-msg: 1
+ openim-rpc-third: 1
+toolBinaries:
+ - check-free-memory
+ - check-component
+maxFileDescriptors: 10000
diff --git a/test/e2e/framework/helpers/chat/chat.go b/test/e2e/framework/helpers/chat/chat.go
index aa37c34b55..0613ff5699 100644
--- a/test/e2e/framework/helpers/chat/chat.go
+++ b/test/e2e/framework/helpers/chat/chat.go
@@ -38,7 +38,6 @@ func main() {
// }
latestVersion := defaultTemplateVersion
- // getLatestVersion
// getLatestVersion
// Construct the download URL
@@ -101,9 +100,8 @@ func main() {
select {}
}
-// getLatestVersion fetches the latest version number from a given URL.
/* func getLatestVersion(url string) (string, error) {
- resp, err := http.Get(url)
+ resp, err := webhook.Get(url)
if err != nil {
return "", err
}
diff --git a/tools/changelog/go.mod b/tools/changelog/go.mod
deleted file mode 100644
index b5a2272a41..0000000000
--- a/tools/changelog/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/changelog
-
-go 1.19
diff --git a/tools/changelog/changelog.go b/tools/changelog/main.go
similarity index 100%
rename from tools/changelog/changelog.go
rename to tools/changelog/main.go
diff --git a/tools/check-component/main.go b/tools/check-component/main.go
new file mode 100644
index 0000000000..d3225abb8e
--- /dev/null
+++ b/tools/check-component/main.go
@@ -0,0 +1,165 @@
+// 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 main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/cmd"
+ "github.com/openimsdk/open-im-server/v3/pkg/common/config"
+ "github.com/openimsdk/tools/db/mongoutil"
+ "github.com/openimsdk/tools/db/redisutil"
+ "github.com/openimsdk/tools/discovery/zookeeper"
+ "github.com/openimsdk/tools/mq/kafka"
+ "github.com/openimsdk/tools/s3/minio"
+ "github.com/openimsdk/tools/system/program"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "time"
+)
+
+const maxRetry = 180
+
+func CheckZookeeper(ctx context.Context, config *config.ZooKeeper) error {
+ // Temporary disable logging
+ originalLogger := log.Default().Writer()
+ log.SetOutput(ioutil.Discard)
+ defer log.SetOutput(originalLogger) // Ensure logging is restored
+ return zookeeper.Check(ctx, config.Address, config.Schema, zookeeper.WithUserNameAndPassword(config.Username, config.Password))
+}
+
+func CheckMongo(ctx context.Context, config *config.Mongo) error {
+ return mongoutil.Check(ctx, config.Build())
+}
+
+func CheckRedis(ctx context.Context, config *config.Redis) error {
+ return redisutil.Check(ctx, config.Build())
+}
+
+func CheckMinIO(ctx context.Context, config *config.Minio) error {
+ return minio.Check(ctx, config.Build())
+}
+
+func CheckKafka(ctx context.Context, conf *config.Kafka) error {
+ return kafka.Check(ctx, conf.Build(), []string{conf.ToMongoTopic, conf.ToRedisTopic, conf.ToPushTopic})
+}
+
+func initConfig(configDir string) (*config.Mongo, *config.Redis, *config.Kafka, *config.Minio, *config.ZooKeeper, error) {
+ var (
+ mongoConfig = &config.Mongo{}
+ redisConfig = &config.Redis{}
+ kafkaConfig = &config.Kafka{}
+ minioConfig = &config.Minio{}
+ zookeeperConfig = &config.ZooKeeper{}
+ )
+ err := config.LoadConfig(filepath.Join(configDir, cmd.MongodbConfigFileName), cmd.ConfigEnvPrefixMap[cmd.MongodbConfigFileName], mongoConfig)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ err = config.LoadConfig(filepath.Join(configDir, cmd.RedisConfigFileName), cmd.ConfigEnvPrefixMap[cmd.RedisConfigFileName], redisConfig)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ err = config.LoadConfig(filepath.Join(configDir, cmd.KafkaConfigFileName), cmd.ConfigEnvPrefixMap[cmd.KafkaConfigFileName], kafkaConfig)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ err = config.LoadConfig(filepath.Join(configDir, cmd.MinioConfigFileName), cmd.ConfigEnvPrefixMap[cmd.MinioConfigFileName], minioConfig)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+
+ err = config.LoadConfig(filepath.Join(configDir, cmd.ZookeeperConfigFileName), cmd.ConfigEnvPrefixMap[cmd.ZookeeperConfigFileName], zookeeperConfig)
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+ return mongoConfig, redisConfig, kafkaConfig, minioConfig, zookeeperConfig, nil
+}
+
+func main() {
+ var index int
+ var configDir string
+ flag.IntVar(&index, "i", 0, "Index number")
+ defaultConfigDir := filepath.Join("..", "..", "..", "..", "..", "config")
+ flag.StringVar(&configDir, "c", defaultConfigDir, "Configuration dir")
+ flag.Parse()
+
+ fmt.Printf("%s Index: %d, Config Path: %s\n", filepath.Base(os.Args[0]), index, configDir)
+
+ mongoConfig, redisConfig, kafkaConfig, minioConfig, zookeeperConfig, err := initConfig(configDir)
+ if err != nil {
+ program.ExitWithError(err)
+ }
+
+ ctx := context.Background()
+ err = performChecks(ctx, mongoConfig, redisConfig, kafkaConfig, minioConfig, zookeeperConfig, maxRetry)
+ if err != nil {
+ // Assume program.ExitWithError logs the error and exits.
+ // Replace with your error handling logic as necessary.
+ program.ExitWithError(err)
+ }
+}
+
+func performChecks(ctx context.Context, mongoConfig *config.Mongo, redisConfig *config.Redis, kafkaConfig *config.Kafka, minioConfig *config.Minio, zookeeperConfig *config.ZooKeeper, maxRetry int) error {
+ checksDone := make(map[string]bool)
+
+ checks := map[string]func() error{
+ "Zookeeper": func() error {
+ return CheckZookeeper(ctx, zookeeperConfig)
+ },
+ "Mongo": func() error {
+ return CheckMongo(ctx, mongoConfig)
+ },
+ "Redis": func() error {
+ return CheckRedis(ctx, redisConfig)
+ },
+ "MinIO": func() error {
+ return CheckMinIO(ctx, minioConfig)
+ },
+ "Kafka": func() error {
+ return CheckKafka(ctx, kafkaConfig)
+ },
+ }
+
+ for i := 0; i < maxRetry; i++ {
+ allSuccess := true
+ for name, check := range checks {
+ if !checksDone[name] {
+ if err := check(); err != nil {
+ fmt.Printf("%s check failed: %v\n", name, err)
+ allSuccess = false
+ } else {
+ fmt.Printf("%s check succeeded.\n", name)
+ checksDone[name] = true
+ }
+ }
+ }
+
+ if allSuccess {
+ fmt.Println("All components checks passed successfully.")
+ return nil
+ }
+
+ time.Sleep(1 * time.Second)
+ }
+
+ return fmt.Errorf("not all components checks passed successfully after %d attempts", maxRetry)
+}
diff --git a/tools/check-free-memory/main.go b/tools/check-free-memory/main.go
new file mode 100644
index 0000000000..e182a15443
--- /dev/null
+++ b/tools/check-free-memory/main.go
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "fmt"
+ "os"
+
+ "github.com/shirou/gopsutil/mem"
+)
+
+func main() {
+ vMem, err := mem.VirtualMemory()
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "Failed to get virtual memory info: %v\n", err)
+ os.Exit(1)
+ }
+
+ // Use the Available field to get the available memory
+ availableMemoryGB := float64(vMem.Available) / float64(1024*1024*1024)
+
+ if availableMemoryGB < 1.0 {
+ fmt.Fprintf(os.Stderr, "System available memory is less than 1GB: %.2fGB\n", availableMemoryGB)
+ os.Exit(1)
+ } else {
+ fmt.Printf("System available memory is sufficient: %.2fGB\n", availableMemoryGB)
+ }
+}
diff --git a/tools/component/component.go b/tools/component/component.go
deleted file mode 100644
index e1f86e1207..0000000000
--- a/tools/component/component.go
+++ /dev/null
@@ -1,400 +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 main
-
-import (
- "errors"
- "flag"
- "fmt"
- "os"
- "strconv"
- "strings"
- "time"
-
- "github.com/IBM/sarama"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/kafka"
-
- "github.com/OpenIMSDK/tools/component"
- "github.com/OpenIMSDK/tools/errs"
-
- "gopkg.in/yaml.v3"
-
- "github.com/openimsdk/open-im-server/v3/pkg/common/config"
-)
-
-const (
- // defaultCfgPath is the default path of the configuration file.
- defaultCfgPath = "../../../../../config/config.yaml"
- maxRetry = 100
-)
-
-var (
- cfgPath = flag.String("c", defaultCfgPath, "Path to the configuration file")
-)
-
-func initCfg() (*config.GlobalConfig, error) {
- data, err := os.ReadFile(*cfgPath)
- if err != nil {
- return nil, errs.Wrap(err, "ReadFile unmarshal failed")
- }
-
- conf := config.NewGlobalConfig()
- err = yaml.Unmarshal(data, &conf)
- if err != nil {
- return nil, errs.Wrap(err, "InitConfig unmarshal failed")
- }
- return conf, nil
-}
-
-type checkFunc struct {
- name string
- function func(*config.GlobalConfig) error
- flag bool
- config *config.GlobalConfig
-}
-
-// colorErrPrint prints formatted string in red to stderr
-func colorErrPrint(msg string) {
- // ANSI escape code for red text
- const redColor = "\033[31m"
- // ANSI escape code to reset color
- const resetColor = "\033[0m"
- msg = redColor + msg + resetColor
- // Print to stderr in red
- fmt.Fprintf(os.Stderr, "%s\n", msg)
-}
-
-func colorSuccessPrint(format string, a ...interface{}) {
- // ANSI escape code for green text is \033[32m
- // \033[0m resets the color
- fmt.Printf("\033[32m"+format+"\033[0m", a...)
-}
-
-func main() {
- flag.Parse()
-
- conf, err := initCfg()
- if err != nil {
- fmt.Printf("Read config failed: %v\n", err)
- return
- }
-
- err = configGetEnv(conf)
- if err != nil {
- fmt.Printf("configGetEnv failed, err:%v", err)
- return
- }
-
- checks := []checkFunc{
- {name: "Mongo", function: checkMongo, config: conf},
- {name: "Redis", function: checkRedis, config: conf},
- {name: "Zookeeper", function: checkZookeeper, config: conf},
- {name: "Kafka", function: checkKafka, config: conf},
- }
- if conf.Object.Enable == "minio" {
- checks = append(checks, checkFunc{name: "Minio", function: checkMinio, config: conf})
- }
-
- for i := 0; i < maxRetry; i++ {
- if i != 0 {
- time.Sleep(1 * time.Second)
- }
- fmt.Printf("Checking components round %v...\n", i+1)
-
- var err error
- allSuccess := true
- for index, check := range checks {
- if !check.flag {
- err = check.function(check.config)
- if err != nil {
- allSuccess = false
- colorErrPrint(fmt.Sprintf("Check component: %s, failed: %v", check.name, err.Error()))
-
- if check.name == "Minio" {
- if errors.Is(err, errMinioNotEnabled) ||
- errors.Is(err, errSignEndPoint) ||
- errors.Is(err, errApiURL) {
- checks[index].flag = true
- continue
- }
- break
- }
- } else {
- checks[index].flag = true
- component.SuccessPrint(fmt.Sprintf("%s connected successfully", check.name))
- }
- }
-
- }
- if allSuccess {
- component.SuccessPrint("All components started successfully!")
- return
- }
- }
- component.ErrorPrint("Some components checked failed!")
- os.Exit(-1)
-}
-
-var errMinioNotEnabled = errors.New("minio.Enable is not configured to use MinIO")
-
-var errSignEndPoint = errors.New("minio.signEndPoint contains 127.0.0.1, causing issues with image sending")
-var errApiURL = errors.New("object.apiURL contains 127.0.0.1, causing issues with image sending")
-
-// checkMongo checks the MongoDB connection without retries
-func checkMongo(config *config.GlobalConfig) error {
- mongoStu := &component.Mongo{
- URL: config.Mongo.Uri,
- Address: config.Mongo.Address,
- Database: config.Mongo.Database,
- Username: config.Mongo.Username,
- Password: config.Mongo.Password,
- MaxPoolSize: config.Mongo.MaxPoolSize,
- }
- err := component.CheckMongo(mongoStu)
-
- return err
-}
-
-// checkRedis checks the Redis connection
-func checkRedis(config *config.GlobalConfig) error {
- redisStu := &component.Redis{
- Address: config.Redis.Address,
- Username: config.Redis.Username,
- Password: config.Redis.Password,
- }
- err := component.CheckRedis(redisStu)
- return err
-}
-
-// checkMinio checks the MinIO connection
-func checkMinio(config *config.GlobalConfig) error {
- if strings.Contains(config.Object.ApiURL, "127.0.0.1") {
- return errs.Wrap(errApiURL)
- }
- if config.Object.Enable != "minio" {
- return errs.Wrap(errMinioNotEnabled)
- }
- if strings.Contains(config.Object.Minio.Endpoint, "127.0.0.1") {
- return errs.Wrap(errSignEndPoint)
- }
-
- minio := &component.Minio{
- ApiURL: config.Object.ApiURL,
- Endpoint: config.Object.Minio.Endpoint,
- AccessKeyID: config.Object.Minio.AccessKeyID,
- SecretAccessKey: config.Object.Minio.SecretAccessKey,
- SignEndpoint: config.Object.Minio.SignEndpoint,
- UseSSL: getEnv("MINIO_USE_SSL", "false"),
- }
- err := component.CheckMinio(minio)
- return err
-}
-
-// checkZookeeper checks the Zookeeper connection
-func checkZookeeper(config *config.GlobalConfig) error {
- zkStu := &component.Zookeeper{
- Schema: config.Zookeeper.Schema,
- ZkAddr: config.Zookeeper.ZkAddr,
- Username: config.Zookeeper.Username,
- Password: config.Zookeeper.Password,
- }
- err := component.CheckZookeeper(zkStu)
- return err
-}
-
-// checkKafka checks the Kafka connection
-func checkKafka(config *config.GlobalConfig) error {
- // Prioritize environment variables
- kafkaStu := &component.Kafka{
- Username: config.Kafka.Username,
- Password: config.Kafka.Password,
- Addr: config.Kafka.Addr,
- }
-
- kafkaClient, err := component.CheckKafka(kafkaStu)
- if err != nil {
- return err
- }
- defer kafkaClient.Close()
-
- // Verify if necessary topics exist
- topics, err := kafkaClient.Topics()
- if err != nil {
- return errs.Wrap(err)
- }
-
- requiredTopics := []string{
- config.Kafka.MsgToMongo.Topic,
- config.Kafka.MsgToPush.Topic,
- config.Kafka.LatestMsgToRedis.Topic,
- }
-
- for _, requiredTopic := range requiredTopics {
- if !isTopicPresent(requiredTopic, topics) {
- return errs.Wrap(err, fmt.Sprintf("Kafka doesn't contain topic: %v", requiredTopic))
- }
- }
-
- var tlsConfig *kafka.TLSConfig
- if config.Kafka.TLS != nil {
- tlsConfig = &kafka.TLSConfig{
- CACrt: config.Kafka.TLS.CACrt,
- ClientCrt: config.Kafka.TLS.ClientCrt,
- ClientKey: config.Kafka.TLS.ClientKey,
- ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd,
- InsecureSkipVerify: config.Kafka.TLS.InsecureSkipVerify,
- }
- }
-
- _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
- KafkaVersion: sarama.V2_0_0_0,
- OffsetsInitial: sarama.OffsetNewest,
- IsReturnErr: false,
- UserName: config.Kafka.Username,
- Password: config.Kafka.Password,
- }, []string{config.Kafka.LatestMsgToRedis.Topic},
- config.Kafka.Addr, config.Kafka.ConsumerGroupID.MsgToRedis, tlsConfig)
- if err != nil {
- return err
- }
-
- _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
- KafkaVersion: sarama.V2_0_0_0,
- OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
- }, []string{config.Kafka.MsgToMongo.Topic},
- config.Kafka.Addr, config.Kafka.ConsumerGroupID.MsgToMongo, tlsConfig)
- if err != nil {
- return err
- }
-
- _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{
- KafkaVersion: sarama.V2_0_0_0,
- OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false,
- }, []string{config.Kafka.MsgToPush.Topic}, config.Kafka.Addr,
- config.Kafka.ConsumerGroupID.MsgToPush, tlsConfig)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-// isTopicPresent checks if a topic is present in the list of topics
-func isTopicPresent(topic string, topics []string) bool {
- for _, t := range topics {
- if t == topic {
- return true
- }
- }
- return false
-}
-
-func configGetEnv(config *config.GlobalConfig) error {
- config.Mongo.Uri = getEnv("MONGO_URI", config.Mongo.Uri)
- config.Mongo.Username = getEnv("MONGO_OPENIM_USERNAME", config.Mongo.Username)
- config.Mongo.Password = getEnv("MONGO_OPENIM_PASSWORD", config.Mongo.Password)
- config.Mongo.Address = getArrEnv("MONGO_ADDRESS", "MONGO_PORT", config.Mongo.Address)
- config.Mongo.Database = getEnv("MONGO_DATABASE", config.Mongo.Database)
- maxPoolSize, err := getEnvInt("MONGO_MAX_POOL_SIZE", config.Mongo.MaxPoolSize)
- if err != nil {
- return errs.Wrap(err, "MONGO_MAX_POOL_SIZE")
- }
- config.Mongo.MaxPoolSize = maxPoolSize
-
- config.Redis.Username = getEnv("REDIS_USERNAME", config.Redis.Username)
- config.Redis.Password = getEnv("REDIS_PASSWORD", config.Redis.Password)
- config.Redis.Address = getArrEnv("REDIS_ADDRESS", "REDIS_PORT", config.Redis.Address)
-
- config.Object.ApiURL = getEnv("OBJECT_APIURL", config.Object.ApiURL)
- config.Object.Minio.Endpoint = getEnv("MINIO_ENDPOINT", config.Object.Minio.Endpoint)
- config.Object.Minio.AccessKeyID = getEnv("MINIO_ACCESS_KEY_ID", config.Object.Minio.AccessKeyID)
- config.Object.Minio.SecretAccessKey = getEnv("MINIO_SECRET_ACCESS_KEY", config.Object.Minio.SecretAccessKey)
- config.Object.Minio.SignEndpoint = getEnv("MINIO_SIGN_ENDPOINT", config.Object.Minio.SignEndpoint)
-
- config.Zookeeper.Schema = getEnv("ZOOKEEPER_SCHEMA", config.Zookeeper.Schema)
- config.Zookeeper.ZkAddr = getArrEnv("ZOOKEEPER_ADDRESS", "ZOOKEEPER_PORT", config.Zookeeper.ZkAddr)
- config.Zookeeper.Username = getEnv("ZOOKEEPER_USERNAME", config.Zookeeper.Username)
- config.Zookeeper.Password = getEnv("ZOOKEEPER_PASSWORD", config.Zookeeper.Password)
-
- config.Kafka.Username = getEnv("KAFKA_USERNAME", config.Kafka.Username)
- config.Kafka.Password = getEnv("KAFKA_PASSWORD", config.Kafka.Password)
- config.Kafka.Addr = getArrEnv("KAFKA_ADDRESS", "KAFKA_PORT", config.Kafka.Addr)
- config.Object.Minio.Endpoint = getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Object.Minio.Endpoint)
- return nil
-}
-
-func getMinioAddr(key1, key2, key3, fallback string) string {
- // Prioritize environment variables
- endpoint := getEnv(key1, fallback)
- address, addressExist := os.LookupEnv(key2)
- port, portExist := os.LookupEnv(key3)
- if portExist && addressExist {
- endpoint = "http://" + address + ":" + port
- return endpoint
- }
- return endpoint
-}
-
-// Helper function to get environment variable or default value
-func getEnv(key, fallback string) string {
- if value, exists := os.LookupEnv(key); exists {
- return value
- }
- return fallback
-}
-
-// Helper function to get environment variable or default value
-func getEnvInt(key string, fallback int) (int, error) {
- if value, exists := os.LookupEnv(key); exists {
- val, err := strconv.Atoi(value)
- if err != nil {
- return 0, errs.Wrap(err, "string to int failed")
- }
- return val, nil
- }
- return fallback, nil
-}
-
-func getArrEnv(key1, key2 string, fallback []string) []string {
- address, addrExists := os.LookupEnv(key1)
- port, portExists := os.LookupEnv(key2)
-
- if addrExists && portExists {
- addresses := strings.Split(address, ",")
- for i, addr := range addresses {
- addresses[i] = addr + ":" + port
- }
- return addresses
- }
-
- if addrExists && !portExists {
- addresses := strings.Split(address, ",")
- for i, addr := range addresses {
- addresses[i] = addr + ":" + "0"
- }
- return addresses
- }
-
- if !addrExists && portExists {
- result := make([]string, len(fallback))
- for i, addr := range fallback {
- add := strings.Split(addr, ":")
- result[i] = add[0] + ":" + port
- }
- return result
- }
- return fallback
-}
diff --git a/tools/component/component_test.go b/tools/component/component_test.go
deleted file mode 100644
index c56361b2c0..0000000000
--- a/tools/component/component_test.go
+++ /dev/null
@@ -1,65 +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 main
-
-import (
- "context"
- "strconv"
- "testing"
- "time"
-
- "github.com/redis/go-redis/v9"
-)
-
-func TestRedis(t *testing.T) {
- conf, err := initCfg()
- conf.Redis.Address = []string{
- "172.16.8.142:7000",
- //"172.16.8.142:7000", "172.16.8.142:7001", "172.16.8.142:7002", "172.16.8.142:7003", "172.16.8.142:7004", "172.16.8.142:7005",
- }
-
- var redisClient redis.UniversalClient
- defer func() {
- if redisClient != nil {
- redisClient.Close()
- }
- }()
- if len(conf.Redis.Address) > 1 {
- redisClient = redis.NewClusterClient(&redis.ClusterOptions{
- Addrs: conf.Redis.Address,
- Username: conf.Redis.Username,
- Password: conf.Redis.Password,
- })
- } else {
- redisClient = redis.NewClient(&redis.Options{
- Addr: conf.Redis.Address[0],
- Username: conf.Redis.Username,
- Password: conf.Redis.Password,
- })
- }
- _, err = redisClient.Ping(context.Background()).Result()
- if err != nil {
- t.Fatal(err)
- }
-
- for i := 0; i < 1000000; i++ {
- val, err := redisClient.Set(context.Background(), "b_"+strconv.Itoa(i), "test", time.Second*10).Result()
- t.Log("index", i, "resp", val, "err", err)
- if err != nil {
- return
- }
- }
-
-}
diff --git a/tools/component/go.mod b/tools/component/go.mod
deleted file mode 100644
index 05a27bcad6..0000000000
--- a/tools/component/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/component
-
-go 1.19
diff --git a/tools/imctl/go.mod b/tools/imctl/go.mod
deleted file mode 100644
index e3a720b27f..0000000000
--- a/tools/imctl/go.mod
+++ /dev/null
@@ -1,18 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/imctl
-
-go 1.19
-
-require (
- github.com/MakeNowJust/heredoc/v2 v2.0.1
- github.com/mitchellh/go-wordwrap v1.0.1
- github.com/moby/term v0.5.0
- github.com/russross/blackfriday v1.6.0
- github.com/spf13/cobra v1.7.0
- github.com/spf13/pflag v1.0.5
-)
-
-require (
- github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
- github.com/inconshreveable/mousetrap v1.1.0 // indirect
- golang.org/x/sys v0.1.0 // indirect
-)
diff --git a/tools/imctl/go.sum b/tools/imctl/go.sum
deleted file mode 100644
index 3d4c613430..0000000000
--- a/tools/imctl/go.sum
+++ /dev/null
@@ -1,24 +0,0 @@
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
-github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
-github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A=
-github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
-github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
-github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
-github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
-github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
-github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
-github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tools/imctl/imctl.go b/tools/imctl/main.go
similarity index 100%
rename from tools/imctl/imctl.go
rename to tools/imctl/main.go
diff --git a/tools/infra/go.mod b/tools/infra/go.mod
deleted file mode 100644
index 8c66e2654d..0000000000
--- a/tools/infra/go.mod
+++ /dev/null
@@ -1,11 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/infra
-
-go 1.19
-
-require github.com/fatih/color v1.15.0
-
-require (
- github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.17 // indirect
- golang.org/x/sys v0.6.0 // indirect
-)
diff --git a/tools/infra/go.sum b/tools/infra/go.sum
deleted file mode 100644
index 2624c9db06..0000000000
--- a/tools/infra/go.sum
+++ /dev/null
@@ -1,10 +0,0 @@
-github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
-github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/tools/infra/infra.go b/tools/infra/main.go
similarity index 90%
rename from tools/infra/infra.go
rename to tools/infra/main.go
index bc01a00eb6..f6225a352e 100644
--- a/tools/infra/infra.go
+++ b/tools/infra/main.go
@@ -25,13 +25,14 @@ func printLinks() {
blue := color.New(color.FgBlue).SprintFunc()
fmt.Printf("OpenIM Github: %s\n", blue("https://github.com/OpenIMSDK/Open-IM-Server"))
fmt.Printf("Slack Invitation: %s\n", blue("https://openimsdk.slack.com"))
+ fmt.Printf("Follow Twitter: %s\n", blue("https://twitter.com/founder_im63606"))
}
func main() {
yellow := color.New(color.FgYellow)
blue := color.New(color.FgBlue, color.Bold)
- yellow.Println("Current module is still under development.")
+ yellow.Println("Please use the release branch or tag for production environments!")
message := `
____ _____ __ __
diff --git a/tools/ncpu/go.mod b/tools/ncpu/go.mod
deleted file mode 100644
index 66697e3501..0000000000
--- a/tools/ncpu/go.mod
+++ /dev/null
@@ -1,5 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/ncpu
-
-go 1.19
-
-require go.uber.org/automaxprocs v1.5.3
diff --git a/tools/ncpu/go.sum b/tools/ncpu/go.sum
deleted file mode 100644
index 804f593b54..0000000000
--- a/tools/ncpu/go.sum
+++ /dev/null
@@ -1,7 +0,0 @@
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
-go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
-go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/tools/ncpu/ncpu.go b/tools/ncpu/main.go
similarity index 100%
rename from tools/ncpu/ncpu.go
rename to tools/ncpu/main.go
diff --git a/tools/ncpu/ncpu_test.go b/tools/ncpu/main_test.go
similarity index 100%
rename from tools/ncpu/ncpu_test.go
rename to tools/ncpu/main_test.go
diff --git a/tools/openim-web/Dockerfile b/tools/openim-web/Dockerfile
deleted file mode 100644
index 2e0cd9e8e0..0000000000
--- a/tools/openim-web/Dockerfile
+++ /dev/null
@@ -1,47 +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.
-
-# 使用官方Go镜像作为基础镜像
-FROM golang:1.21 AS build-env
-ENV CGO_ENABLED=0
-# 设置工作目录
-WORKDIR /app
-
-# 安装curl和unzip工具
-#RUN apt-get update && apt-get install -y curl unzip
-
-# 从GitHub下载并解压dist.zip
-#RUN curl -LO https://github.com/OpenIMSDK/dist.zip \
- # && unzip dist.zip -d ./ \
- # && rm dist.zip
-
-# 复制Go代码到容器
-COPY . .
-
-# 编译Go代码
-RUN go build -o openim-web
-
-# 使用轻量级的基础镜像
-FROM debian:buster-slim
-
-# 将编译好的二进制文件和dist资源复制到新的容器
-WORKDIR /app
-COPY --from=build-env /app/openim-web /app/openim-web
-COPY --from=build-env /app/dist /app/dist
-
-# 开放容器的20001端口
-EXPOSE 20001
-
-# 指定容器启动命令
-ENTRYPOINT ["/app/openim-web"]
\ No newline at end of file
diff --git a/tools/openim-web/README.md b/tools/openim-web/README.md
deleted file mode 100644
index 5794a946de..0000000000
--- a/tools/openim-web/README.md
+++ /dev/null
@@ -1,78 +0,0 @@
-# OpenIM Web Service
-
-- [OpenIM Web Service](#openim-web-service)
- - [Overview](#overview)
- - [User](#user)
- - [Docker Deployment](#docker-deployment)
- - [Build the Docker Image](#build-the-docker-image)
- - [Run the Docker Container](#run-the-docker-container)
- - [Configuration](#configuration)
- - [Contributions](#contributions)
-
-
-OpenIM Web Service is a lightweight containerized service built with Go. The service serves static files and allows customization via environment variables.
-
-## Overview
-
-- Built using Go.
-- Deployed as a Docker container.
-- Serves static files from a directory which can be set via an environment variable.
-- The default port for the service is `20001`, but it can be customized using an environment variable.
-
-## User
-
-example:
-
-```bash
-$ ./openim-web -h
-Usage of ./openim-web:
- -distPath string
- Path to the distribution (default "/app/dist")
- -port string
- Port to run the server on (default "20001")
-```
-
-Variables can be set as above, Environment variables can also be set
-
-example:
-
-```bash
-$ export OPENIM_WEB_PPRT="11001"
-```
-
-Initialize the env configuration file:
-
-```bash
-$ make init
-```
-
-## Docker Deployment
-
-### Build the Docker Image
-
-Even though we've implemented automation, it's to make the developer experience easier:
-
-To build the Docker image for OpenIM Web Service:
-
-```bash
-$ docker build -t openim-web .
-```
-
-### Run the Docker Container
-
-To run the service:
-
-```bash
-$ docker run -e DIST_PATH=/app/dist -e PORT=20001 -p 20001:20001 openim-web
-```
-
-## Configuration
-
-You can configure the OpenIM Web Service using the following environment variables:
-
-- **DIST_PATH**: The path to the directory containing the static files. Default: `/app/dist`.
-- **PORT**: The port on which the service listens. Default: `11001`.
-
-## Contributions
-
-We welcome contributions from the community. If you find any bugs or have feature suggestions, please create an issue or send a pull request.
\ No newline at end of file
diff --git a/tools/openim-web/go.mod b/tools/openim-web/go.mod
deleted file mode 100644
index deb24f4e5d..0000000000
--- a/tools/openim-web/go.mod
+++ /dev/null
@@ -1,7 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/openim-web
-
-go 1.19
-
-require gopkg.in/yaml.v2 v2.4.0
-
-require github.com/NYTimes/gziphandler v1.1.1 // indirect
diff --git a/tools/openim-web/go.sum b/tools/openim-web/go.sum
deleted file mode 100644
index 54ca3deb19..0000000000
--- a/tools/openim-web/go.sum
+++ /dev/null
@@ -1,10 +0,0 @@
-github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
-github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/tools/openim-web/openim-web.go b/tools/openim-web/openim-web.go
deleted file mode 100644
index c913e35e76..0000000000
--- a/tools/openim-web/openim-web.go
+++ /dev/null
@@ -1,63 +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 main
-
-import (
- "flag"
- "log"
- "net/http"
- "os"
-
- "github.com/NYTimes/gziphandler"
-)
-
-var (
- distPathFlag string
- portFlag string
-)
-
-func init() {
- flag.StringVar(&distPathFlag, "distPath", "/app/dist", "Path to the distribution")
- flag.StringVar(&portFlag, "port", "11001", "Port to run the server on")
-}
-
-func main() {
- flag.Parse()
-
- distPath := getConfigValue("DIST_PATH", distPathFlag, "/app/dist")
- fs := http.FileServer(http.Dir(distPath))
-
- withGzip := gziphandler.GzipHandler(fs)
-
- http.Handle("/", withGzip)
-
- port := getConfigValue("PORT", portFlag, "11001")
- log.Printf("Server listening on port %s in %s...", port, distPath)
- err := http.ListenAndServe(":"+port, nil)
- if err != nil {
- log.Fatal(err)
- }
-}
-
-func getConfigValue(envKey, flagValue, fallback string) string {
- envVal := os.Getenv(envKey)
- if envVal != "" {
- return envVal
- }
- if flagValue != "" {
- return flagValue
- }
- return fallback
-}
diff --git a/tools/openim-web/openim-web_test.go b/tools/openim-web/openim-web_test.go
deleted file mode 100644
index dd1c933167..0000000000
--- a/tools/openim-web/openim-web_test.go
+++ /dev/null
@@ -1,71 +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 main
-
-import (
- "os"
- "testing"
-)
-
-func TestGetConfigValue(t *testing.T) {
- tests := []struct {
- name string
- envKey string
- envValue string
- flagValue string
- fallback string
- wantResult string
- }{
- {
- name: "environment variable set",
- envKey: "TEST_KEY",
- envValue: "envValue",
- flagValue: "",
- fallback: "default",
- wantResult: "envValue",
- },
- {
- name: "flag set and environment variable not set",
- envKey: "TEST_KEY",
- envValue: "",
- flagValue: "flagValue",
- fallback: "default",
- wantResult: "flagValue",
- },
- {
- name: "nothing set, use fallback",
- envKey: "TEST_KEY",
- envValue: "",
- flagValue: "",
- fallback: "default",
- wantResult: "default",
- },
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if tt.envValue != "" {
- os.Setenv(tt.envKey, tt.envValue)
- defer os.Unsetenv(tt.envKey)
- }
-
- got := getConfigValue(tt.envKey, tt.flagValue, tt.fallback)
-
- if got != tt.wantResult {
- t.Errorf("getConfigValue(%s, %s, %s) = %s; want %s", tt.envKey, tt.flagValue, tt.fallback, got, tt.wantResult)
- }
- })
- }
-}
diff --git a/tools/url2im/go.mod b/tools/url2im/go.mod
deleted file mode 100644
index b6011909db..0000000000
--- a/tools/url2im/go.mod
+++ /dev/null
@@ -1,20 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/url2im
-
-go 1.20
-
-require (
- github.com/OpenIMSDK/protocol v0.0.21
- github.com/kelindar/bitmap v1.5.1
-)
-
-require (
- github.com/golang/protobuf v1.5.3 // indirect
- github.com/kelindar/simd v1.1.2 // indirect
- github.com/klauspost/cpuid/v2 v2.2.4 // indirect
- golang.org/x/net v0.17.0 // indirect
- golang.org/x/sys v0.13.0 // indirect
- golang.org/x/text v0.13.0 // indirect
- google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
- google.golang.org/grpc v1.56.3 // indirect
- google.golang.org/protobuf v1.31.0 // indirect
-)
diff --git a/tools/url2im/go.sum b/tools/url2im/go.sum
deleted file mode 100644
index 1970dce2c7..0000000000
--- a/tools/url2im/go.sum
+++ /dev/null
@@ -1,33 +0,0 @@
-github.com/OpenIMSDK/protocol v0.0.21 h1:5H6H+hJ9d/VgRqttvxD/zfK9Asd+4M8Eknk5swSbUVY=
-github.com/OpenIMSDK/protocol v0.0.21/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/kelindar/bitmap v1.5.1 h1:+ZmZdwHbJ+CGE+q/aAJ74KJSnp0vOlGD7KY5x51mVzk=
-github.com/kelindar/bitmap v1.5.1/go.mod h1:j3qZjxH9s4OtvsnFTP2bmPkjqil9Y2xQlxPYHexasEA=
-github.com/kelindar/simd v1.1.2 h1:KduKb+M9cMY2HIH8S/cdJyD+5n5EGgq+Aeeleos55To=
-github.com/kelindar/simd v1.1.2/go.mod h1:inq4DFudC7W8L5fhxoeZflLRNpWSs0GNx6MlWFvuvr0=
-github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
-github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
-golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
-google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
-google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc=
-google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/tools/url2im/pkg/api.go b/tools/url2im/pkg/api.go
index 7575b078aa..5bf48c4ea5 100644
--- a/tools/url2im/pkg/api.go
+++ b/tools/url2im/pkg/api.go
@@ -18,14 +18,14 @@ import (
"bytes"
"context"
"encoding/json"
- "errors"
"fmt"
"io"
"net/http"
- "github.com/OpenIMSDK/protocol/auth"
- "github.com/OpenIMSDK/protocol/constant"
- "github.com/OpenIMSDK/protocol/third"
+ "github.com/openimsdk/protocol/auth"
+ "github.com/openimsdk/protocol/constant"
+ "github.com/openimsdk/protocol/third"
+ "github.com/openimsdk/tools/errs"
)
type Api struct {
@@ -39,7 +39,7 @@ type Api struct {
func (a *Api) apiPost(ctx context.Context, path string, req any, resp any) error {
operationID, _ := ctx.Value("operationID").(string)
if operationID == "" {
- return errors.New("call api operationID is empty")
+ return errs.New("call api operationID is empty")
}
reqBody, err := json.Marshal(req)
if err != nil {
diff --git a/tools/url2im/pkg/manage.go b/tools/url2im/pkg/manage.go
index 70c6713fcc..5e1626da9f 100644
--- a/tools/url2im/pkg/manage.go
+++ b/tools/url2im/pkg/manage.go
@@ -20,8 +20,8 @@ import (
"crypto/md5"
"encoding/hex"
"encoding/json"
- "errors"
"fmt"
+ "github.com/openimsdk/tools/errs"
"io"
"log"
"net/http"
@@ -34,7 +34,7 @@ import (
"sync/atomic"
"time"
- "github.com/OpenIMSDK/protocol/third"
+ "github.com/openimsdk/protocol/third"
)
type Upload struct {
@@ -256,10 +256,10 @@ func (m *Manage) RunTask(ctx context.Context, task Task) (string, error) {
func (m *Manage) partSize(size int64) (int64, error) {
if size <= 0 {
- return 0, errors.New("size must be greater than 0")
+ return 0, errs.New("size must be greater than 0")
}
if size > m.partLimit.MaxPartSize*int64(m.partLimit.MaxNumSize) {
- return 0, fmt.Errorf("size must be less than %db", m.partLimit.MaxPartSize*int64(m.partLimit.MaxNumSize))
+ return 0, errs.New("size must be less than", "size", m.partLimit.MaxPartSize*int64(m.partLimit.MaxNumSize))
}
if size <= m.partLimit.MinPartSize*int64(m.partLimit.MaxNumSize) {
return m.partLimit.MinPartSize, nil
@@ -392,7 +392,7 @@ func (m *Manage) HttpGet(ctx context.Context, url string) (*http.Response, error
}
if response.StatusCode != http.StatusOK {
_ = response.Body.Close()
- return nil, fmt.Errorf("http get %s status %s", url, response.Status)
+ return nil, fmt.Errorf("webhook get %s status %s", url, response.Status)
}
return response, nil
}
diff --git a/tools/versionchecker/go.mod b/tools/versionchecker/go.mod
deleted file mode 100644
index 7c274fcf9b..0000000000
--- a/tools/versionchecker/go.mod
+++ /dev/null
@@ -1,11 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/versionchecker
-
-go 1.19
-
-require github.com/fatih/color v1.15.0
-
-require (
- github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.17 // indirect
- golang.org/x/sys v0.6.0 // indirect
-)
diff --git a/tools/versionchecker/go.sum b/tools/versionchecker/go.sum
deleted file mode 100644
index 2624c9db06..0000000000
--- a/tools/versionchecker/go.sum
+++ /dev/null
@@ -1,10 +0,0 @@
-github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
-github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/tools/versionchecker/versionchecker.go b/tools/versionchecker/main.go
similarity index 82%
rename from tools/versionchecker/versionchecker.go
rename to tools/versionchecker/main.go
index 12254b58e8..bec7daa799 100644
--- a/tools/versionchecker/versionchecker.go
+++ b/tools/versionchecker/main.go
@@ -19,10 +19,9 @@ import (
"fmt"
"os/exec"
"runtime"
- "time"
"github.com/fatih/color"
- //"github.com/openimsdk/open-im-server/v3/pkg/common/version"
+ "github.com/openimsdk/tools/utils/timeutil"
)
func ExecuteCommand(cmdName string, args ...string) (string, error) {
@@ -40,8 +39,7 @@ func ExecuteCommand(cmdName string, args ...string) (string, error) {
}
func printTime() string {
- currentTime := time.Now()
- formattedTime := currentTime.Format("2006-01-02 15:04:05")
+ formattedTime := timeutil.GetCurrentTimeFormatted()
return fmt.Sprintf("Current Date & Time: %s", formattedTime)
}
@@ -60,14 +58,6 @@ func getDockerVersion() string {
return version
}
-func getDockerComposeVersion() string {
- version, err := ExecuteCommand("docker-compose", "--version")
- if err != nil {
- return "Docker Compose is not installed. Please install it to get the version."
- }
- return version
-}
-
func getKubernetesVersion() string {
version, err := ExecuteCommand("kubectl", "version", "--client", "--short")
if err != nil {
@@ -101,20 +91,15 @@ func getGitVersion() string {
func main() {
// red := color.New(color.FgRed).SprintFunc()
- green := color.New(color.FgGreen).SprintFunc()
+ // green := color.New(color.FgGreen).SprintFunc()
blue := color.New(color.FgBlue).SprintFunc()
- yellow := color.New(color.FgYellow).SprintFunc()
-
- fmt.Println(green(printTime()))
- fmt.Println(yellow("# Diagnostic Tool Result\n"))
+ // yellow := color.New(color.FgYellow).SprintFunc()
fmt.Println(blue("## Go Version"))
fmt.Println(getGoVersion())
fmt.Println(blue("## Branch Type"))
fmt.Println(getGitVersion())
fmt.Println(blue("## Docker Version"))
fmt.Println(getDockerVersion())
- fmt.Println(blue("## Docker Compose Version"))
- fmt.Println(getDockerComposeVersion())
fmt.Println(blue("## Kubernetes Version"))
fmt.Println(getKubernetesVersion())
// fmt.Println(blue("## OpenIM Versions"))
diff --git a/tools/yamlfmt/go.mod b/tools/yamlfmt/go.mod
deleted file mode 100644
index 7c496a5308..0000000000
--- a/tools/yamlfmt/go.mod
+++ /dev/null
@@ -1,8 +0,0 @@
-module github.com/openimsdk/open-im-server/v3/tools/yamlfmt
-
-go 1.19
-
-require (
- github.com/likexian/gokit v0.25.13
- gopkg.in/yaml.v3 v3.0.1
-)
diff --git a/tools/yamlfmt/go.sum b/tools/yamlfmt/go.sum
deleted file mode 100644
index 0cf090cc81..0000000000
--- a/tools/yamlfmt/go.sum
+++ /dev/null
@@ -1,6 +0,0 @@
-github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
-github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/tools/yamlfmt/yamlfmt.go b/tools/yamlfmt/main.go
similarity index 100%
rename from tools/yamlfmt/yamlfmt.go
rename to tools/yamlfmt/main.go
diff --git a/tools/yamlfmt/yamlfmt_test.go b/tools/yamlfmt/main_test.go
similarity index 100%
rename from tools/yamlfmt/yamlfmt_test.go
rename to tools/yamlfmt/main_test.go