mirror of
				https://github.com/openimsdk/open-im-server.git
				synced 2025-10-29 15:30:03 +08:00 
			
		
		
		
	Compare commits
	
		
			162 Commits
		
	
	
		
			main
			...
			v3.8.3-pat
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 4f865f83c1 | ||
|  | 2a0df3a22e | ||
|  | 1954b70023 | ||
|  | 17a3105f41 | ||
|  | 0fa714b831 | ||
|  | 746da2554d | ||
|  | ed1c22127e | ||
|  | 34c35099d1 | ||
|  | fd479c15b4 | ||
|  | 95d383d656 | ||
|  | 36810b99a4 | ||
|  | 0bd60efeab | ||
|  | 909f3a4a24 | ||
|  | 15d7129c46 | ||
|  | 310d70b64e | ||
|  | dc7ce098cd | ||
|  | 4aea6bb4ff | ||
|  | 2aab7034b0 | ||
|  | 7a9c336a96 | ||
|  | 02025278b3 | ||
|  | 90596b1a02 | ||
|  | 482284a0fb | ||
|  | a893141ae6 | ||
|  | 9eccfee997 | ||
|  | f950dbc5e7 | ||
|  | 55113e5277 | ||
|  | 7fdc438500 | ||
|  | 2804d90020 | ||
|  | 4ca3f2dc0c | ||
|  | b92c5ab821 | ||
|  | 9c4b6e50ef | ||
|  | 54e189d80f | ||
|  | 1b6d70e4a7 | ||
|  | 4c9cf55867 | ||
|  | 9be25ab041 | ||
|  | 8a5f8a10ba | ||
|  | 1f3b41e379 | ||
|  | 1684c82458 | ||
|  | bc326704f1 | ||
|  | e254bbf1fc | ||
|  | 54309b83a2 | ||
|  | d55cb0fa8b | ||
|  | 2e58a5cc82 | ||
|  | cc957886b1 | ||
|  | 78c82e08b8 | ||
|  | 09cb8336ad | ||
|  | 9a436087cf | ||
|  | 6c7c268b1f | ||
|  | 04b7e6b75e | ||
|  | 916d074cf9 | ||
|  | 61b968fbf2 | ||
|  | efed369c0c | ||
|  | aaf898567f | ||
|  | 62583b32a7 | ||
|  | 4890487429 | ||
|  | 744f481b9c | ||
|  | f242066f30 | ||
|  | 57710bb9e3 | ||
|  | 0006bce0a2 | ||
|  | 0bb6f610ef | ||
|  | 82d133588e | ||
|  | d70dc7b16b | ||
|  | 9f796e78c1 | ||
|  | efcd76318e | ||
|  | d7af353e42 | ||
|  | ab1691865f | ||
|  | cb4ac19968 | ||
|  | 3fec3b4321 | ||
|  | 0f8d4d0943 | ||
|  | e20e52b779 | ||
|  | 28f00a8ffb | ||
|  | 376c1b87ee | ||
|  | c0a0b4da62 | ||
|  | 89503aa529 | ||
|  | 1e68f99d11 | ||
|  | 7912ac0623 | ||
|  | ade108d656 | ||
|  | 222a2f0029 | ||
|  | 8c6d734f88 | ||
|  | 1339121e29 | ||
|  | 382e5947ac | ||
|  | 12800c1421 | ||
|  | d0cd40aae6 | ||
|  | 36c18ce7ea | ||
|  | 4b20286a96 | ||
|  | 3b3ce0d8f5 | ||
|  | 34c6fe5838 | ||
|  | 7415dff32c | ||
|  | 66edc76c54 | ||
|  | 0b78948f62 | ||
|  | a395c82e0d | ||
|  | 3d064d66f2 | ||
|  | 5f333426a3 | ||
|  | 9a122d2eb3 | ||
|  | b805113870 | ||
|  | 2676295a4c | ||
|  | d3b2587743 | ||
|  | f9e6d07581 | ||
|  | 52052a9165 | ||
|  | 3e872d6c5a | ||
|  | 9ac35c9059 | ||
|  | de42eb1f11 | ||
|  | b26b0a422c | ||
|  | 248cb5c107 | ||
|  | 035baff1b5 | ||
|  | de94014b1b | ||
|  | aa35155ccb | ||
|  | 1542a0c98d | ||
|  | 716e06f3ad | ||
|  | 3fe2053d4f | ||
|  | 5430bc4569 | ||
|  | 01c0d9ca89 | ||
|  | 3c6fbabded | ||
|  | 7f471c44bf | ||
|  | f3a78260a8 | ||
|  | be4061da85 | ||
|  | c62945ed05 | ||
|  | 7d2fd64429 | ||
|  | 1f91c756a4 | ||
|  | 573b400af6 | ||
|  | 22820fa189 | ||
|  | 645a5925bd | ||
|  | c328d39cae | ||
|  | 7d517970ec | ||
|  | d8afbb82fc | ||
|  | 3e220a3519 | ||
|  | 3a30479b73 | ||
|  | 687b2ebc07 | ||
|  | 23966f3155 | ||
|  | 674b288654 | ||
|  | a4287309ae | ||
|  | ce140beddc | ||
|  | 0e07ad70c3 | ||
|  | c9e2f7d375 | ||
|  | 1e749b6217 | ||
|  | 4698446050 | ||
|  | 624ae99a12 | ||
|  | 453c426ab5 | ||
|  | 0266dc830d | ||
|  | 9490d8f8ee | ||
|  | 0d84190ed6 | ||
|  | 5089568004 | ||
|  | 9e4cad1815 | ||
|  | d4d626606b | ||
|  | 058eeaefd0 | ||
|  | 0ac6668a50 | ||
|  | 404a9048e2 | ||
|  | 7881c8c89a | ||
|  | eb598ec0e6 | ||
|  | 625fa77e89 | ||
|  | f707069089 | ||
|  | 2bbd1bcfe9 | ||
|  | e53ae33e39 | ||
|  | 3914dc1435 | ||
|  | 047fa33704 | ||
|  | caf5d5c2f3 | ||
|  | 7fa2d08636 | ||
|  | 7b5c18b549 | ||
|  | 0a565070b8 | ||
|  | 43bc87ce99 | ||
|  | c4fe659c69 | ||
|  | 59c4c7575d | 
							
								
								
									
										21
									
								
								.env
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								.env
									
									
									
									
									
								
							| @ -1,19 +1,26 @@ | ||||
| MONGO_IMAGE=mongo:6.0.2 | ||||
| MONGO_IMAGE=mongo:7.0 | ||||
| REDIS_IMAGE=redis:7.0.0 | ||||
| ZOOKEEPER_IMAGE=bitnami/zookeeper:3.8 | ||||
| KAFKA_IMAGE=bitnami/kafka:3.5.1 | ||||
| KAFKA_IMAGE=bitnamilegacy/kafka:3.5.1 | ||||
| MINIO_IMAGE=minio/minio:RELEASE.2024-01-11T07-46-16Z | ||||
| ETCD_IMAGE=quay.io/coreos/etcd:v3.5.13 | ||||
| PROMETHEUS_IMAGE=prom/prometheus:v2.45.6 | ||||
| ALERTMANAGER_IMAGE=prom/alertmanager:v0.27.0 | ||||
| GRAFANA_IMAGE=grafana/grafana:11.0.1 | ||||
| NODE_EXPORTER_IMAGE=prom/node-exporter:v1.7.0 | ||||
| 
 | ||||
| OPENIM_WEB_FRONT_IMAGE=openim/openim-web-front:release-v3.8.1 | ||||
| OPENIM_ADMIN_FRONT_IMAGE=openim/openim-admin-front:release-v1.8.2 | ||||
| OPENIM_WEB_FRONT_IMAGE=openim/openim-web-front:release-v3.8.3 | ||||
| OPENIM_ADMIN_FRONT_IMAGE=openim/openim-admin-front:release-v1.8.4 | ||||
| 
 | ||||
| #FRONT_IMAGE: use aliyun images | ||||
| #OPENIM_WEB_FRONT_IMAGE=registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-web-front:release-v3.8.1 | ||||
| #OPENIM_ADMIN_FRONT_IMAGE=registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-admin-front:release-v1.8.2 | ||||
| #OPENIM_WEB_FRONT_IMAGE=registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-web-front:release-v3.8.3 | ||||
| #OPENIM_ADMIN_FRONT_IMAGE=registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-admin-front:release-v1.8.4 | ||||
| 
 | ||||
| DATA_DIR=./ | ||||
| 
 | ||||
| MONGO_BACKUP_DIR=${DATA_DIR}components/backup/mongo/ | ||||
| 
 | ||||
| PROMETHEUS_PORT=19091 | ||||
| ALERTMANAGER_PORT=19093 | ||||
| GRAFANA_PORT=13000 | ||||
| 
 | ||||
| NODE_EXPORTER_PORT=19100 | ||||
							
								
								
									
										78
									
								
								.github/workflows/changelog.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								.github/workflows/changelog.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| name: Release Changelog | ||||
| 
 | ||||
| on: | ||||
|   release: | ||||
|     types: [released] | ||||
| 
 | ||||
| permissions: | ||||
|   contents: write | ||||
|   pull-requests: write | ||||
| 
 | ||||
| jobs: | ||||
|   update-changelog: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - name: Checkout code | ||||
|       uses: actions/checkout@v4 | ||||
| 
 | ||||
|     - name: Run Go Changelog Generator | ||||
|       run: | | ||||
|         # Run the Go changelog generator, passing the release tag if available | ||||
|         if [ "${{ github.event.release.tag_name }}" = "latest" ]; then | ||||
|           go run tools/changelog/changelog.go > "${{ github.event.release.tag_name }}-changelog.md" | ||||
|         else | ||||
|           go run tools/changelog/changelog.go "${{ github.event.release.tag_name }}" > "${{ github.event.release.tag_name }}-changelog.md" | ||||
|         fi | ||||
| 
 | ||||
|     - name: Handle changelog files | ||||
|       run: | | ||||
|         # Ensure that the CHANGELOG directory exists | ||||
|         mkdir -p CHANGELOG | ||||
| 
 | ||||
|         # Extract Major.Minor version by removing the 'v' prefix from the tag name | ||||
|         TAG_NAME=${{ github.event.release.tag_name }} | ||||
|         CHANGELOG_VERSION_NUMBER=$(echo "$TAG_NAME" | sed 's/^v//' | grep -oP '^\d+\.\d+') | ||||
| 
 | ||||
|         # Define the new changelog file path | ||||
|         CHANGELOG_FILENAME="CHANGELOG-$CHANGELOG_VERSION_NUMBER.md" | ||||
|         CHANGELOG_PATH="CHANGELOG/$CHANGELOG_FILENAME" | ||||
| 
 | ||||
|         # Check if the changelog file for the current release already exists | ||||
|         if [ -f "$CHANGELOG_PATH" ]; then | ||||
|           # If the file exists, append the new changelog to the existing one | ||||
|           cat "$CHANGELOG_PATH" >> "${TAG_NAME}-changelog.md" | ||||
|           # Overwrite the existing changelog with the updated content | ||||
|           mv "${TAG_NAME}-changelog.md" "$CHANGELOG_PATH" | ||||
|         else | ||||
|           # If the changelog file doesn't exist, rename the temp changelog file to the new changelog file | ||||
|           mv "${TAG_NAME}-changelog.md" "$CHANGELOG_PATH" | ||||
| 
 | ||||
|           # Ensure that README.md exists | ||||
|           if [ ! -f "CHANGELOG/README.md" ]; then | ||||
|             echo -e "# CHANGELOGs\n\n" > CHANGELOG/README.md | ||||
|           fi | ||||
|            | ||||
|             # Add the new changelog entry at the top of the README.md | ||||
|             if ! grep -q "\[$CHANGELOG_FILENAME\]" CHANGELOG/README.md; then | ||||
|             sed -i "3i- [$CHANGELOG_FILENAME](./$CHANGELOG_FILENAME)" CHANGELOG/README.md | ||||
|             # Remove the extra newline character added by sed | ||||
|             # sed -i '4d' CHANGELOG/README.md | ||||
|             fi | ||||
|           fi | ||||
| 
 | ||||
|     - name: Clean up | ||||
|       run: | | ||||
|         # Remove any temporary files that were created during the process | ||||
|         rm -f "${{ github.event.release.tag_name }}-changelog.md" | ||||
| 
 | ||||
|     - name: Create Pull Request | ||||
|       uses: peter-evans/create-pull-request@v7.0.5 | ||||
|       with: | ||||
|         token: ${{ secrets.GITHUB_TOKEN }} | ||||
|         commit-message: "Update CHANGELOG for release ${{ github.event.release.tag_name }}" | ||||
|         title: "Update CHANGELOG for release ${{ github.event.release.tag_name }}" | ||||
|         body: "This PR updates the CHANGELOG files for release ${{ github.event.release.tag_name }}" | ||||
|         branch: changelog-${{ github.event.release.tag_name }}  | ||||
|         base: main  | ||||
|         delete-branch: true | ||||
|         labels: changelog | ||||
							
								
								
									
										65
									
								
								.github/workflows/cleanup-after-milestone-prs-merged.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								.github/workflows/cleanup-after-milestone-prs-merged.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,65 @@ | ||||
| name: Cleanup After Milestone PRs Merged | ||||
| 
 | ||||
| on: | ||||
|   pull_request: | ||||
|     types: | ||||
|       - closed | ||||
| 
 | ||||
| jobs: | ||||
|   handle_pr: | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|     - name: Checkout repository | ||||
|       uses: actions/checkout@v4.2.0 | ||||
| 
 | ||||
|     - name: Get the PR title and extract PR numbers | ||||
|       id: extract_pr_numbers | ||||
|       run: | | ||||
|         # Get the PR title | ||||
|         PR_TITLE="${{ github.event.pull_request.title }}" | ||||
| 
 | ||||
|         echo "PR Title: $PR_TITLE" | ||||
| 
 | ||||
|         # Extract PR numbers from the title | ||||
|         PR_NUMBERS=$(echo "$PR_TITLE" | grep -oE "#[0-9]+" | tr -d '#' | tr '\n' ' ') | ||||
|         echo "Extracted PR Numbers: $PR_NUMBERS" | ||||
| 
 | ||||
|         # Save PR numbers to a file | ||||
|         echo "$PR_NUMBERS" > pr_numbers.txt | ||||
|         echo "Saved PR Numbers to pr_numbers.txt" | ||||
| 
 | ||||
|         # Check if the title matches a specific pattern | ||||
|         if echo "$PR_TITLE" | grep -qE "^deps: Merge( #[0-9]+)+ PRs into .+"; then | ||||
|           echo "proceed=true" >> $GITHUB_OUTPUT | ||||
|         else | ||||
|           echo "proceed=false" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
| 
 | ||||
|     - name: Use extracted PR numbers and label PRs | ||||
|       if: (steps.extract_pr_numbers.outputs.proceed == 'true' || contains(github.event.pull_request.labels.*.name, 'milestone-merge')) && github.event.pull_request.merged == true | ||||
|       run: | | ||||
|         # Read the previously saved PR numbers | ||||
|         PR_NUMBERS=$(cat pr_numbers.txt) | ||||
|         echo "Using extracted PR Numbers: $PR_NUMBERS" | ||||
| 
 | ||||
|         # Loop through each PR number and add label | ||||
|         for PR_NUMBER in $PR_NUMBERS; do | ||||
|           echo "Adding 'cherry-picked' label to PR #$PR_NUMBER" | ||||
|           curl -X POST \ | ||||
|             -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | ||||
|             -H "Accept: application/vnd.github+json" \ | ||||
|             https://api.github.com/repos/${{ github.repository }}/issues/$PR_NUMBER/labels \ | ||||
|             -d '{"labels":["cherry-picked"]}' | ||||
|         done | ||||
|       env: | ||||
|         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
| 
 | ||||
|     - name: Delete branch after PR close | ||||
|       if: steps.extract_pr_numbers.outputs.proceed == 'true' || contains(github.event.pull_request.labels.*.name, 'milestone-merge') | ||||
|       run: | | ||||
|         BRANCH_NAME="${{ github.event.pull_request.head.ref }}" | ||||
|         echo "Branch to delete: $BRANCH_NAME" | ||||
|         git push origin --delete "$BRANCH_NAME" | ||||
|       env: | ||||
|         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
							
								
								
									
										91
									
								
								.github/workflows/docker-build-and-release-services-images.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								.github/workflows/docker-build-and-release-services-images.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | ||||
| name: Build and release services Images | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - release-* | ||||
|   release: | ||||
|     types: [published] | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       tag: | ||||
|         description: "Tag version to be used for Docker image" | ||||
|         required: true | ||||
|         default: "v3.8.3" | ||||
| 
 | ||||
| jobs: | ||||
|   build-and-push: | ||||
|     runs-on: ubuntu-latest | ||||
| 
 | ||||
|     steps: | ||||
|       - name: Checkout repository | ||||
|         uses: actions/checkout@v3 | ||||
| 
 | ||||
|       - name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v2 | ||||
| 
 | ||||
|       - name: Log in to Docker Hub | ||||
|         uses: docker/login-action@v2 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKER_USERNAME }} | ||||
|           password: ${{ secrets.DOCKER_PASSWORD }} | ||||
| 
 | ||||
|       - name: Log in to GitHub Container Registry | ||||
|         uses: docker/login-action@v2 | ||||
|         with: | ||||
|           registry: ghcr.io | ||||
|           username: ${{ github.repository_owner }} | ||||
|           password: ${{ secrets.GITHUB_TOKEN }} | ||||
| 
 | ||||
|       - name: Log in to Aliyun Container Registry | ||||
|         uses: docker/login-action@v2 | ||||
|         with: | ||||
|           registry: registry.cn-hangzhou.aliyuncs.com | ||||
|           username: ${{ secrets.ALIREGISTRY_USERNAME }} | ||||
|           password: ${{ secrets.ALIREGISTRY_TOKEN }} | ||||
| 
 | ||||
|       - name: Extract metadata for Docker (tags, labels) | ||||
|         id: meta | ||||
|         uses: docker/metadata-action@v5 | ||||
|         with: | ||||
|           tags: | | ||||
|             type=ref,event=tag | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=semver,pattern={{version}} | ||||
|             type=semver,pattern=v{{version}} | ||||
|             # type=semver,pattern={{major}}.{{minor}} | ||||
|             type=semver,pattern=release-{{raw}} | ||||
|             type=sha | ||||
|             type=raw,value=${{ github.event.inputs.tag }} | ||||
| 
 | ||||
|       - name: Build and push Docker images | ||||
|         run: | | ||||
|           ROOT_DIR="build/images" | ||||
|           for dir in "$ROOT_DIR"/*/; do | ||||
|               # Find Dockerfile or *.dockerfile in a case-insensitive manner | ||||
|               dockerfile=$(find "$dir" -maxdepth 1 -type f \( -iname 'dockerfile' -o -iname '*.dockerfile' \) | head -n 1) | ||||
|                | ||||
|               if [ -n "$dockerfile" ] && [ -f "$dockerfile" ]; then | ||||
|                   IMAGE_NAME=$(basename "$dir") | ||||
|                   echo "Building Docker image for $IMAGE_NAME with tags:" | ||||
|                    | ||||
|                   # Initialize tag arguments | ||||
|                   tag_args=() | ||||
| 
 | ||||
|                   # Read each tag and append --tag arguments | ||||
|                   while IFS= read -r tag; do | ||||
|                       tag_args+=(--tag "${{ secrets.DOCKER_USERNAME }}/$IMAGE_NAME:$tag") | ||||
|                       tag_args+=(--tag "ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME:$tag") | ||||
|                       tag_args+=(--tag "registry.cn-hangzhou.aliyuncs.com/openimsdk/$IMAGE_NAME:$tag") | ||||
|                   done <<< "${{ steps.meta.outputs.tags }}" | ||||
| 
 | ||||
|                   # Build and push the Docker image with all tags | ||||
|                   docker buildx build --platform linux/amd64,linux/arm64 \ | ||||
|                     --file "$dockerfile" \ | ||||
|                     "${tag_args[@]}" \ | ||||
|                     --push "$dir" | ||||
|               else | ||||
|                   echo "No valid Dockerfile found in $dir" | ||||
|               fi | ||||
|           done | ||||
							
								
								
									
										6
									
								
								.github/workflows/go-build-test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/go-build-test.yml
									
									
									
									
										vendored
									
									
								
							| @ -2,11 +2,7 @@ name: Go Build Test | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - main | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - main | ||||
|     paths-ignore: | ||||
|       - '**/*.md' | ||||
| 
 | ||||
| @ -153,7 +149,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     strategy: | ||||
|       matrix: | ||||
|         go_version: ["1.21"] | ||||
|         go_version: ["1.22"] | ||||
| 
 | ||||
|     steps: | ||||
|       - name: Checkout Repository | ||||
|  | ||||
							
								
								
									
										218
									
								
								.github/workflows/merge-from-milestone.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								.github/workflows/merge-from-milestone.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,218 @@ | ||||
| name: Create Pre-Release PR from Milestone | ||||
| 
 | ||||
| permissions: | ||||
|   contents: write | ||||
|   pull-requests: write | ||||
|   issues: write | ||||
| 
 | ||||
| on: | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       milestone_name: | ||||
|         description: 'Milestone name to collect closed PRs from' | ||||
|         required: true | ||||
|         default: 'v3.8.2' | ||||
|       target_branch: | ||||
|         description: 'Target branch to merge the consolidated PR' | ||||
|         required: true | ||||
|         default: 'pre-release-v3.8.2' | ||||
| 
 | ||||
| env: | ||||
|   MILESTONE_NAME: ${{ github.event.inputs.milestone_name || 'v3.8.2' }} | ||||
|   TARGET_BRANCH: ${{ github.event.inputs.target_branch || 'pre-release-v3.8.2' }} | ||||
|   GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|   BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||||
|   LABEL_NAME: cherry-picked | ||||
|   TEMP_DIR: /tmp  # Using /tmp as the temporary directory | ||||
| 
 | ||||
| jobs: | ||||
|   cherry_pick_milestone_prs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - name: Setup temp directory | ||||
|         run: | | ||||
|           # Create the temporary directory and initialize necessary files | ||||
|           mkdir -p ${{ env.TEMP_DIR }} | ||||
|           touch ${{ env.TEMP_DIR }}/pr_numbers.txt | ||||
|           touch ${{ env.TEMP_DIR }}/commit_hashes.txt | ||||
|           touch ${{ env.TEMP_DIR }}/pr_title.txt | ||||
|           touch ${{ env.TEMP_DIR }}/pr_body.txt | ||||
|           touch ${{ env.TEMP_DIR }}/created_pr_number.txt | ||||
| 
 | ||||
|       - name: Checkout repository | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           token: ${{ secrets.BOT_TOKEN }} | ||||
| 
 | ||||
|       - name: Setup Git User for OpenIM-Robot | ||||
|         run: | | ||||
|           # Set up Git credentials for the bot | ||||
|           git config --global user.email "OpenIM-Robot@users.noreply.github.com" | ||||
|           git config --global user.name "OpenIM-Robot" | ||||
| 
 | ||||
|       - name: Fetch Milestone ID and Filter PR Numbers | ||||
|         env: | ||||
|           MILESTONE_NAME: ${{ env.MILESTONE_NAME }} | ||||
|         run: | | ||||
|           # Fetch milestone details and extract milestone ID | ||||
|           milestones=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | ||||
|             -H "Accept: application/vnd.github+json" \ | ||||
|             "https://api.github.com/repos/${{ github.repository }}/milestones") | ||||
|           milestone_id=$(echo "$milestones" | grep -B3 "\"title\": \"$MILESTONE_NAME\"" | grep '"number":' | head -n1 | grep -o '[0-9]\+') | ||||
|           if [ -z "$milestone_id" ]; then | ||||
|             echo "Milestone '$MILESTONE_NAME' not found. Exiting." | ||||
|             exit 1 | ||||
|           fi | ||||
|           echo "Milestone ID: $milestone_id" | ||||
|           echo "MILESTONE_ID=$milestone_id" >> $GITHUB_ENV | ||||
| 
 | ||||
|           # Fetch issues for the milestone | ||||
|           issues=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | ||||
|                 -H "Accept: application/vnd.github+json" \ | ||||
|                 "https://api.github.com/repos/${{ github.repository }}/issues?milestone=$milestone_id&state=closed&per_page=100") | ||||
| 
 | ||||
|           > ${{ env.TEMP_DIR }}/pr_numbers.txt | ||||
| 
 | ||||
|           # Filter PRs that do not have the 'cherry-picked' label | ||||
|           for pr_number in $(echo "$issues" | jq -r '.[] | select(.pull_request != null) | .number'); do | ||||
|             labels=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | ||||
|               -H "Accept: application/vnd.github+json" \ | ||||
|               "https://api.github.com/repos/${{ github.repository }}/issues/$pr_number/labels" | jq -r '.[].name') | ||||
| 
 | ||||
|             if ! echo "$labels" | grep -q "${LABEL_NAME}"; then | ||||
|               echo "PR #$pr_number does not have the 'cherry-picked' label. Adding to the list." | ||||
|               echo "$pr_number" >> ${{ env.TEMP_DIR }}/pr_numbers.txt | ||||
|             else | ||||
|               echo "PR #$pr_number already has the 'cherry-picked' label. Skipping." | ||||
|             fi | ||||
|           done | ||||
| 
 | ||||
|           # Sort the filtered PR numbers | ||||
|           sort -n ${{ env.TEMP_DIR }}/pr_numbers.txt -o ${{ env.TEMP_DIR }}/pr_numbers.txt | ||||
| 
 | ||||
|           echo "Filtered and sorted PR numbers:" | ||||
|           cat ${{ env.TEMP_DIR }}/pr_numbers.txt || echo "No closed PR numbers found for milestone." | ||||
| 
 | ||||
|       - name: Fetch Merge Commits for PRs and Generate Title and Body | ||||
|         run: | | ||||
|           # Ensure the files are initialized | ||||
|           > ${{ env.TEMP_DIR }}/commit_hashes.txt | ||||
|           > ${{ env.TEMP_DIR }}/pr_title.txt | ||||
|           > ${{ env.TEMP_DIR }}/pr_body.txt | ||||
| 
 | ||||
|           # Write description to the PR body | ||||
|           echo "### Description:" >> ${{ env.TEMP_DIR }}/pr_body.txt | ||||
|           echo "Merging PRs from milestone \`$MILESTONE_NAME\` into target branch \`$TARGET_BRANCH\`." >> ${{ env.TEMP_DIR }}/pr_body.txt | ||||
|           echo "" >> ${{ env.TEMP_DIR }}/pr_body.txt | ||||
|           echo "### Need Merge PRs:" >> ${{ env.TEMP_DIR }}/pr_body.txt | ||||
| 
 | ||||
|           pr_numbers_in_title="" | ||||
| 
 | ||||
|           # Process sorted PR numbers and generate commit hashes | ||||
|           for pr_number in $(cat ${{ env.TEMP_DIR }}/pr_numbers.txt); do | ||||
|             echo "Processing PR #$pr_number" | ||||
|             pr_details=$(curl -s -H "Authorization: token $BOT_TOKEN" \ | ||||
|               -H "Accept: application/vnd.github+json" \ | ||||
|               "https://api.github.com/repos/${{ github.repository }}/pulls/$pr_number") | ||||
|             pr_title=$(echo "$pr_details" | jq -r '.title') | ||||
|             merge_commit=$(echo "$pr_details" | jq -r '.merge_commit_sha') | ||||
|             short_commit_hash=$(echo "$merge_commit" | cut -c 1-7) | ||||
| 
 | ||||
|             # Append PR details to the body | ||||
|             echo "- $pr_title: (#$pr_number) ($short_commit_hash)" >> ${{ env.TEMP_DIR }}/pr_body.txt | ||||
| 
 | ||||
|             if [ "$merge_commit" != "null" ];then | ||||
|               echo "$merge_commit" >> ${{ env.TEMP_DIR }}/commit_hashes.txt | ||||
|               echo "#$pr_number" >> ${{ env.TEMP_DIR }}/pr_title.txt | ||||
|               pr_numbers_in_title="$pr_numbers_in_title #$pr_number" | ||||
|             fi | ||||
|           done | ||||
| 
 | ||||
|           commit_hashes=$(cat ${{ env.TEMP_DIR }}/commit_hashes.txt | tr '\n' ' ') | ||||
|           first_commit_hash=$(head -n 1 ${{ env.TEMP_DIR }}/commit_hashes.txt) | ||||
|           cherry_pick_branch="cherry-pick-${first_commit_hash:0:7}" | ||||
|           echo "COMMIT_HASHES=$commit_hashes" >> $GITHUB_ENV | ||||
|           echo "CHERRY_PICK_BRANCH=$cherry_pick_branch" >> $GITHUB_ENV | ||||
|           echo "pr_numbers_in_title=$pr_numbers_in_title" >> $GITHUB_ENV | ||||
| 
 | ||||
|       - name: Pull and Cherry-pick Commits, Then Push | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|           BOT_TOKEN: ${{ secrets.BOT_TOKEN }} | ||||
|         run: | | ||||
|           # Fetch and pull the latest changes from the target branch | ||||
|           git fetch origin | ||||
|           git checkout $TARGET_BRANCH | ||||
|           git pull origin $TARGET_BRANCH | ||||
| 
 | ||||
|           # Create a new branch for cherry-picking | ||||
|           git checkout -b $CHERRY_PICK_BRANCH | ||||
| 
 | ||||
|           # Cherry-pick the commits and handle conflicts | ||||
|           for commit_hash in $COMMIT_HASHES; do | ||||
|             echo "Attempting to cherry-pick commit $commit_hash" | ||||
|             if ! git cherry-pick "$commit_hash" --strategy=recursive -X theirs; then | ||||
|               echo "Conflict detected for $commit_hash. Resolving with incoming changes." | ||||
|               conflict_files=$(git diff --name-only --diff-filter=U) | ||||
|               echo "Conflicting files:" | ||||
|               echo "$conflict_files" | ||||
| 
 | ||||
|               for file in $conflict_files; do | ||||
|                 if [ -f "$file" ]; then | ||||
|                   echo "Resolving conflict for $file" | ||||
|                   git add "$file" | ||||
|                 else | ||||
|                   echo "File $file has been deleted. Skipping." | ||||
|                   git rm "$file" | ||||
|                 fi | ||||
|               done | ||||
| 
 | ||||
|               echo "Conflicts resolved. Continuing cherry-pick." | ||||
|               git cherry-pick --continue | ||||
|             else | ||||
|               echo "Cherry-pick successful for commit $commit_hash." | ||||
|             fi | ||||
|           done | ||||
| 
 | ||||
|           # Push the cherry-pick branch to the repository | ||||
|           git remote set-url origin "https://${BOT_TOKEN}@github.com/${{ github.repository }}.git" | ||||
|           git push origin $CHERRY_PICK_BRANCH --force | ||||
| 
 | ||||
|       - name: Create Pull Request | ||||
|         run: | | ||||
|           # Prepare and create the PR | ||||
|           pr_title="deps: Merge ${{ env.pr_numbers_in_title }} PRs into $TARGET_BRANCH" | ||||
|           pr_body=$(cat ${{ env.TEMP_DIR }}/pr_body.txt) | ||||
| 
 | ||||
|           echo "Prepared PR title:" | ||||
|           echo "$pr_title" | ||||
|           echo "Prepared PR body:" | ||||
|           echo "$pr_body" | ||||
| 
 | ||||
|           # Create the PR using the GitHub API | ||||
|           response=$(curl -s -X POST -H "Authorization: token $BOT_TOKEN" \ | ||||
|             -H "Accept: application/vnd.github+json" \ | ||||
|             https://api.github.com/repos/${{ github.repository }}/pulls \ | ||||
|             -d "$(jq -n --arg title "$pr_title" \ | ||||
|               --arg head "$CHERRY_PICK_BRANCH" \ | ||||
|               --arg base "$TARGET_BRANCH" \ | ||||
|               --arg body "$pr_body" \ | ||||
|               '{title: $title, head: $head, base: $base, body: $body}')") | ||||
| 
 | ||||
|           pr_number=$(echo "$response" | jq -r '.number') | ||||
|           echo "$pr_number" > ${{ env.TEMP_DIR }}/created_pr_number.txt | ||||
|           echo "Created PR #$pr_number" | ||||
| 
 | ||||
|       - name: Add Label to Created Pull Request | ||||
|         run: | | ||||
|           # Add 'milestone-merge' label to the created PR | ||||
|           pr_number=$(cat ${{ env.TEMP_DIR }}/created_pr_number.txt) | ||||
|           echo "Adding label to PR #$pr_number" | ||||
| 
 | ||||
|           curl -s -X POST -H "Authorization: token $GITHUB_TOKEN" \ | ||||
|             -H "Accept: application/vnd.github+json" \ | ||||
|             -d '{"labels": ["milestone-merge"]}' \ | ||||
|             "https://api.github.com/repos/${{ github.repository }}/issues/$pr_number/labels" | ||||
| 
 | ||||
|           echo "Added 'milestone-merge' label to PR #$pr_number." | ||||
							
								
								
									
										156
									
								
								.github/workflows/publish-docker-image.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										156
									
								
								.github/workflows/publish-docker-image.yml
									
									
									
									
										vendored
									
									
								
							| @ -4,45 +4,80 @@ on: | ||||
|   push: | ||||
|     branches: | ||||
|       - release-* | ||||
|     # tags: | ||||
|     #   - 'v*' | ||||
| 
 | ||||
|   release: | ||||
|     types: [published] | ||||
| 
 | ||||
|   workflow_dispatch: | ||||
|     inputs: | ||||
|       tag: | ||||
|         description: "Tag version to be used for Docker image" | ||||
|         required: true | ||||
|         default: "v3.8.0" | ||||
|         default: "v3.8.3" | ||||
| 
 | ||||
| env: | ||||
|   GO_VERSION: "1.22" | ||||
|   IMAGE_NAME: "openim-server" | ||||
|   # IMAGE_NAME: ${{ github.event.repository.name }} | ||||
|   DOCKER_BUILDKIT: 1 | ||||
| 
 | ||||
| jobs: | ||||
|   build-and-test: | ||||
|   publish-docker-images: | ||||
|     runs-on: ubuntu-latest | ||||
|     if: ${{ !(github.event_name == 'pull_request' && github.event.pull_request.merged == false) }} | ||||
|     steps: | ||||
|       - uses: actions/checkout@v4 | ||||
|       - name: Checkout main repository | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           path: main-repo | ||||
| 
 | ||||
|       - name: Set up QEMU | ||||
|         uses: docker/setup-qemu-action@v3 | ||||
|         uses: docker/setup-qemu-action@v3.3.0 | ||||
| 
 | ||||
|       - name: Set up Docker Buildx | ||||
|         id: buildx | ||||
|         uses: docker/setup-buildx-action@v3 | ||||
| 
 | ||||
|       - name: Build Docker image | ||||
|         id: build | ||||
|         uses: docker/build-push-action@v5 | ||||
|         with: | ||||
|           context: ./main-repo | ||||
|           load: true | ||||
|           tags: "openim/openim-server:local" | ||||
|           cache-from: type=gha | ||||
|           cache-to: type=gha,mode=max | ||||
|           driver-opts: network=host | ||||
| 
 | ||||
|       - name: Save Docker image to file | ||||
|         run: docker save -o image.tar openim/openim-server:local | ||||
|       - name: Extract metadata for Docker | ||||
|         id: meta | ||||
|         uses: docker/metadata-action@v5.6.0 | ||||
|         with: | ||||
|           images: | | ||||
|             ${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }} | ||||
|             ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} | ||||
|             registry.cn-hangzhou.aliyuncs.com/openimsdk/${{ env.IMAGE_NAME }} | ||||
|           tags: | | ||||
|             type=ref,event=tag | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=ref,event=pr | ||||
|             type=semver,pattern={{version}} | ||||
|             type=semver,pattern=v{{version}} | ||||
|             type=semver,pattern={{major}}.{{minor}} | ||||
|             type=semver,pattern={{major}} | ||||
|             type=sha | ||||
| 
 | ||||
|       - name: Install skopeo | ||||
|         run: | | ||||
|           sudo apt-get update && sudo apt-get install -y skopeo | ||||
| 
 | ||||
|       - name: Build multi-arch images as OCI | ||||
|         run: | | ||||
|           mkdir -p /tmp/oci-image /tmp/docker-cache | ||||
| 
 | ||||
|           # Build multi-architecture image and save in OCI format         | ||||
|           docker buildx build \ | ||||
|             --platform linux/amd64,linux/arm64 \ | ||||
|             --output type=oci,dest=/tmp/oci-image/multi-arch.tar \ | ||||
|             --cache-to type=local,dest=/tmp/docker-cache \ | ||||
|             --cache-from type=gha \ | ||||
|             ./main-repo | ||||
| 
 | ||||
|             # Use skopeo to convert the amd64 image from OCI format to Docker format and load it | ||||
|           skopeo copy --override-arch amd64 oci-archive:/tmp/oci-image/multi-arch.tar docker-daemon:${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:local | ||||
| 
 | ||||
|           # check image | ||||
|           docker image ls | grep openim | ||||
| 
 | ||||
|       - name: Checkout compose repository | ||||
|         uses: actions/checkout@v4 | ||||
| @ -55,18 +90,19 @@ jobs: | ||||
|         run: | | ||||
|           IP=$(hostname -I | awk '{print $1}') | ||||
|           echo "The IP Address is: $IP" | ||||
|           echo "::set-output name=ip::$IP" | ||||
|           echo "ip=$IP" >> $GITHUB_OUTPUT | ||||
| 
 | ||||
|       - name: Update .env to use the local image | ||||
|         run: | | ||||
|           sed -i 's|OPENIM_SERVER_IMAGE=.*|OPENIM_SERVER_IMAGE=openim/openim-server:local|' ${{ github.workspace }}/compose-repo/.env | ||||
|           sed -i 's|OPENIM_SERVER_IMAGE=.*|OPENIM_SERVER_IMAGE=${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:local|' ${{ github.workspace }}/compose-repo/.env | ||||
|           sed -i 's|MINIO_EXTERNAL_ADDRESS=.*|MINIO_EXTERNAL_ADDRESS=http://${{ steps.get-ip.outputs.ip }}:10005|' ${{ github.workspace }}/compose-repo/.env | ||||
| 
 | ||||
|       - name: Start services using Docker Compose | ||||
|         run: | | ||||
|           cd ${{ github.workspace }}/compose-repo | ||||
|           docker compose up -d | ||||
|           sleep 60 | ||||
| 
 | ||||
|           docker compose ps | ||||
| 
 | ||||
|       # - name: Check openim-server health | ||||
|       #   run: | | ||||
| @ -97,54 +133,62 @@ jobs: | ||||
|       #         exit 0 | ||||
|       #       fi | ||||
| 
 | ||||
|       - name: Load Docker image from file | ||||
|         run: docker load -i image.tar | ||||
| 
 | ||||
|       - name: Extract metadata for Docker (tags, labels) | ||||
|         id: meta | ||||
|         uses: docker/metadata-action@v5.5.1 | ||||
|         with: | ||||
|           images: | | ||||
|             openim/openim-server | ||||
|             ghcr.io/openimsdk/openim-server | ||||
|             registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server | ||||
|           tags: | | ||||
|             type=ref,event=tag | ||||
|             type=schedule | ||||
|             type=ref,event=branch | ||||
|             type=semver,pattern={{version}} | ||||
|             type=semver,pattern=v{{version}} | ||||
|             type=semver,pattern={{major}}.{{minor}} | ||||
|             type=semver,pattern={{major}} | ||||
|             type=semver,pattern=release-{{raw}} | ||||
|             type=sha | ||||
|             type=raw,value=${{ github.event.inputs.tag }} | ||||
| 
 | ||||
|       - name: Log in to Docker Hub | ||||
|         uses: docker/login-action@v2 | ||||
|         uses: docker/login-action@v3.3.0 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKER_USERNAME }} | ||||
|           password: ${{ secrets.DOCKER_PASSWORD }} | ||||
| 
 | ||||
|       - name: Log in to GitHub Container Registry | ||||
|         uses: docker/login-action@v2 | ||||
|         uses: docker/login-action@v3.3.0 | ||||
|         with: | ||||
|           registry: ghcr.io | ||||
|           username: ${{ github.repository_owner }} | ||||
|           password: ${{ secrets.GITHUB_TOKEN }} | ||||
| 
 | ||||
|       - name: Log in to Aliyun Container Registry | ||||
|         uses: docker/login-action@v2 | ||||
|         uses: docker/login-action@v3.3.0 | ||||
|         with: | ||||
|           registry: registry.cn-hangzhou.aliyuncs.com | ||||
|           username: ${{ secrets.ALIREGISTRY_USERNAME }} | ||||
|           password: ${{ secrets.ALIREGISTRY_TOKEN }} | ||||
| 
 | ||||
|       - name: Push Docker images | ||||
|         uses: docker/build-push-action@v5 | ||||
|         with: | ||||
|           context: ./main-repo | ||||
|           push: true | ||||
|           platforms: linux/amd64,linux/arm64 | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
|       - name: Push multi-architecture images | ||||
|         if: success() | ||||
|         run: | | ||||
|           docker buildx build \ | ||||
|             --platform linux/amd64,linux/arm64 \ | ||||
|             $(echo "${{ steps.meta.outputs.tags }}" | sed 's/,/ --tag /g' | sed 's/^/--tag /') \ | ||||
|             --cache-from type=local,src=/tmp/docker-cache \ | ||||
|             --push \ | ||||
|             ./main-repo | ||||
| 
 | ||||
|       - name: Verify multi-platform support | ||||
|         run: | | ||||
|           images=( | ||||
|             "${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}" | ||||
|             "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}" | ||||
|             "registry.cn-hangzhou.aliyuncs.com/openimsdk/${{ env.IMAGE_NAME }}" | ||||
|           ) | ||||
| 
 | ||||
|           for image in "${images[@]}"; do | ||||
|               for tag in $(echo "${{ steps.meta.outputs.tags }}" | tr ',' '\n' | cut -d':' -f2); do | ||||
|                   echo "Verifying multi-arch support for $image:$tag" | ||||
|                   manifest=$(docker manifest inspect "$image:$tag" || echo "error") | ||||
|                   if [[ "$manifest" == "error" ]]; then | ||||
|                       echo "Manifest not found for $image:$tag" | ||||
|                       exit 1 | ||||
|                   fi | ||||
|                   amd64_found=$(echo "$manifest" | jq '.manifests[] | select(.platform.architecture == "amd64")') | ||||
|                   arm64_found=$(echo "$manifest" | jq '.manifests[] | select(.platform.architecture == "arm64")') | ||||
|                   if [[ -z "$amd64_found" ]]; then | ||||
|                       echo "Multi-platform support check failed for $image:$tag - missing amd64" | ||||
|                       exit 1 | ||||
|                   fi | ||||
|                   if [[ -z "$arm64_found" ]]; then | ||||
|                       echo "Multi-platform support check failed for $image:$tag - missing arm64" | ||||
|                       exit 1 | ||||
|                   fi | ||||
|                   echo "✅ $image:$tag supports both amd64 and arm64 architectures" | ||||
|               done | ||||
|           done | ||||
|  | ||||
							
								
								
									
										119
									
								
								.github/workflows/update-version-file-on-release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								.github/workflows/update-version-file-on-release.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,119 @@ | ||||
| name: Update Version File on Release | ||||
| 
 | ||||
| on: | ||||
|   release: | ||||
|     types: [created] | ||||
| 
 | ||||
| jobs: | ||||
|   update-version: | ||||
|     runs-on: ubuntu-latest | ||||
|     env: | ||||
|       TAG_VERSION: ${{ github.event.release.tag_name }} | ||||
|     steps: | ||||
|       # Step 1: Checkout the original repository's code | ||||
|       - name: Checkout code | ||||
|         uses: actions/checkout@v4 | ||||
|         with: | ||||
|           fetch-depth: 0 | ||||
|           # submodules: "recursive" | ||||
| 
 | ||||
|       - name: Safe submodule initialization | ||||
|         run: | | ||||
|           echo "Checking for submodules..." | ||||
|           if [ -f .gitmodules ]; then | ||||
|             if [ -s .gitmodules ]; then | ||||
|               echo "Initializing submodules..." | ||||
|               if git submodule sync --recursive 2>/dev/null; then | ||||
|                 git submodule update --init --force --recursive || { | ||||
|                   echo "Warning: Some submodules failed to initialize, continuing anyway..." | ||||
|                 } | ||||
|               else | ||||
|                 echo "Warning: Submodule sync failed, continuing without submodules..." | ||||
|               fi | ||||
|             else | ||||
|               echo ".gitmodules exists but is empty, skipping submodule initialization" | ||||
|             fi | ||||
|           else | ||||
|             echo "No .gitmodules file found, no submodules to initialize" | ||||
|           fi | ||||
| 
 | ||||
|       # Step 2: Set up Git with official account | ||||
|       - name: Set up Git | ||||
|         run: | | ||||
|           git config --global user.name "github-actions[bot]" | ||||
|           git config --global user.email "github-actions[bot]@users.noreply.github.com" | ||||
| 
 | ||||
|       # Step 3: Check and delete existing tag | ||||
|       - name: Check and delete existing tag | ||||
|         run: | | ||||
|           if git rev-parse ${{ env.TAG_VERSION }} >/dev/null 2>&1; then | ||||
|             git tag -d ${{ env.TAG_VERSION }} | ||||
|             git push --delete origin ${{ env.TAG_VERSION }} | ||||
|           fi | ||||
| 
 | ||||
|       # Step 4: Update version file | ||||
|       - name: Update version file | ||||
|         run: | | ||||
|           mkdir -p version | ||||
|           echo -n "${{ env.TAG_VERSION }}" > version/version | ||||
| 
 | ||||
|       # Step 5: Commit and push changes | ||||
|       - name: Commit and push changes | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|         run: | | ||||
|           git add version/version | ||||
|           git commit -m "Update version to ${{ env.TAG_VERSION }}" | ||||
| 
 | ||||
|       # Step 6: Update tag | ||||
|       - name: Update tag | ||||
|         run: | | ||||
|           git tag -fa ${{ env.TAG_VERSION }} -m "Update version to ${{ env.TAG_VERSION }}" | ||||
|           git push origin ${{ env.TAG_VERSION }} --force | ||||
| 
 | ||||
|       # Step 7: Find and Publish Draft Release | ||||
|       - name: Find and Publish Draft Release | ||||
|         uses: actions/github-script@v7 | ||||
|         with: | ||||
|           github-token: ${{ secrets.GITHUB_TOKEN }} | ||||
|           script: | | ||||
|             const { owner, repo } = context.repo; | ||||
|             const tagName = process.env.TAG_VERSION; | ||||
| 
 | ||||
|             try { | ||||
|               let release; | ||||
|               try { | ||||
|                 const response = await github.rest.repos.getReleaseByTag({ | ||||
|                   owner, | ||||
|                   repo, | ||||
|                   tag: tagName | ||||
|                 }); | ||||
|                 release = response.data; | ||||
|               } catch (tagError) { | ||||
|                 core.info(`Release not found by tag, searching all releases...`); | ||||
|                 const releases = await github.rest.repos.listReleases({ | ||||
|                   owner, | ||||
|                   repo, | ||||
|                   per_page: 100 | ||||
|                 }); | ||||
|                  | ||||
|                 release = releases.data.find(r => r.draft && r.tag_name === tagName);             | ||||
|                 if (!release) { | ||||
|                   throw new Error(`No release found with tag ${tagName}`); | ||||
|                 } | ||||
|               } | ||||
|                | ||||
|               await github.rest.repos.updateRelease({ | ||||
|                 owner, | ||||
|                 repo, | ||||
|                 release_id: release.id, | ||||
|                 draft: false, | ||||
|                 prerelease: release.prerelease | ||||
|               }); | ||||
|                | ||||
|               const status = release.draft ? "was draft" : "was already published"; | ||||
|               core.info(`Release ${tagName} ensured to be published (${status}).`); | ||||
|                | ||||
|             } catch (error) { | ||||
|               core.warning(`Could not find or update release for tag ${tagName}: ${error.message}`); | ||||
|             } | ||||
| @ -1,62 +0,0 @@ | ||||
| # Version logging for OpenIM | ||||
| 
 | ||||
| <!-- BEGIN MUNGE: GENERATED_TOC --> | ||||
| 
 | ||||
| <!-- END MUNGE: GENERATED_TOC --> | ||||
| 
 | ||||
| {{ if .Versions -}} | ||||
| <a name="unreleased"></a> | ||||
| ## [Unreleased] | ||||
| 
 | ||||
| {{ if .Unreleased.CommitGroups -}} | ||||
| {{ range .Unreleased.CommitGroups -}} | ||||
| ### {{ .Title }} | ||||
| {{ range .Commits -}} | ||||
| - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} | ||||
| {{ end }} | ||||
| {{ end -}} | ||||
| {{ end -}} | ||||
| {{ end -}} | ||||
| 
 | ||||
| {{ range .Versions }} | ||||
| <a name="{{ .Tag.Name }}"></a> | ||||
| ## {{ if .Tag.Previous }}[{{ .Tag.Name }}]{{ else }}{{ .Tag.Name }}{{ end }} - {{ datetime "2006-01-02" .Tag.Date }} | ||||
| {{ range .CommitGroups -}} | ||||
| ### {{ .Title }} | ||||
| {{ range .Commits -}} | ||||
| - {{ if .Scope }}**{{ .Scope }}:** {{ end }}{{ .Subject }} | ||||
| {{ end }} | ||||
| {{ end -}} | ||||
| 
 | ||||
| {{- if .RevertCommits -}} | ||||
| ### Reverts | ||||
| {{ range .RevertCommits -}} | ||||
| - {{ .Revert.Header }} | ||||
| {{ end }} | ||||
| {{ end -}} | ||||
| 
 | ||||
| {{- if .MergeCommits -}} | ||||
| ### Pull Requests | ||||
| {{ range .MergeCommits -}} | ||||
| - {{ .Header }} | ||||
| {{ end }} | ||||
| {{ end -}} | ||||
| 
 | ||||
| {{- if .NoteGroups -}} | ||||
| {{ range .NoteGroups -}} | ||||
| ### {{ .Title }} | ||||
| {{ range .Notes }} | ||||
| {{ .Body }} | ||||
| {{ end }} | ||||
| {{ end -}} | ||||
| {{ end -}} | ||||
| {{ end -}} | ||||
| 
 | ||||
| {{- if .Versions }} | ||||
| [Unreleased]: {{ .Info.RepositoryURL }}/compare/{{ $latest := index .Versions 0 }}{{ $latest.Tag.Name }}...HEAD | ||||
| {{ range .Versions -}} | ||||
| {{ if .Tag.Previous -}} | ||||
| [{{ .Tag.Name }}]: {{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }} | ||||
| {{ end -}} | ||||
| {{ end -}} | ||||
| {{ end -}} | ||||
| @ -1,67 +0,0 @@ | ||||
| bin: git | ||||
| style: github | ||||
| template: CHANGELOG.tpl.md | ||||
| info: | ||||
|   title: CHANGELOG | ||||
|   repository_url: https://github.com/openimsdk/open-im-server | ||||
| options: | ||||
|   tag_filter_pattern: '^v' | ||||
|   sort: "date" | ||||
| 
 | ||||
|   commits: | ||||
|     filters: | ||||
|       Type: | ||||
|         - feat | ||||
|         - fix | ||||
|         - perf | ||||
|         - refactor | ||||
|         - docs | ||||
|         - test | ||||
|         - chore | ||||
|         - ci | ||||
|         - build | ||||
|     sort_by: Scope | ||||
| 
 | ||||
|   commit_groups: | ||||
|     group_by: Type | ||||
|     sort_by: Title | ||||
|     title_order: | ||||
|       - feat | ||||
|       - fix | ||||
|       - perf | ||||
|       - refactor | ||||
|       - docs | ||||
|       - test | ||||
|       - chore | ||||
|       - ci | ||||
|       - build | ||||
|     title_maps: | ||||
|       feat: Features | ||||
| 
 | ||||
|   header: | ||||
|     pattern: "<regexp>" | ||||
|     pattern_maps: | ||||
|       - PropName | ||||
| 
 | ||||
|   issues: | ||||
|     prefix: | ||||
|       - # | ||||
| 
 | ||||
|   refs: | ||||
|     actions: | ||||
|       - Closes | ||||
|       - Fixes | ||||
| 
 | ||||
|   merges: | ||||
|     pattern: "^Merge branch '(\\w+)'$" | ||||
|     pattern_maps: | ||||
|       - Source | ||||
| 
 | ||||
|   reverts: | ||||
|     pattern: "^Revert \"([\\s\\S]*)\"$" | ||||
|     pattern_maps: | ||||
|       - Header | ||||
| 
 | ||||
|   notes: | ||||
|     keywords: | ||||
|       - BREAKING CHANGE | ||||
							
								
								
									
										10
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								Dockerfile
									
									
									
									
									
								
							| @ -1,5 +1,5 @@ | ||||
| # Use Go 1.21 Alpine as the base image for building the application | ||||
| FROM golang:1.21-alpine AS builder | ||||
| # Use Go 1.22 Alpine as the base image for building the application | ||||
| FROM golang:1.22-alpine AS builder | ||||
| 
 | ||||
| # Define the base directory for the application as an environment variable | ||||
| ENV SERVER_DIR=/openim-server | ||||
| @ -8,7 +8,7 @@ ENV SERVER_DIR=/openim-server | ||||
| WORKDIR $SERVER_DIR | ||||
| 
 | ||||
| # Set the Go proxy to improve dependency resolution speed | ||||
| ENV GOPROXY=https://goproxy.io,direct | ||||
| # ENV GOPROXY=https://goproxy.io,direct | ||||
| 
 | ||||
| # Copy all files from the current directory into the container | ||||
| COPY . . | ||||
| @ -22,7 +22,7 @@ RUN go install github.com/magefile/mage@v1.15.0 | ||||
| RUN mage build | ||||
| 
 | ||||
| # Using Alpine Linux with Go environment for the final image | ||||
| FROM golang:1.21-alpine | ||||
| FROM golang:1.22-alpine | ||||
| 
 | ||||
| # Install necessary packages, such as bash | ||||
| RUN apk add --no-cache bash | ||||
| @ -43,7 +43,7 @@ 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.14-alpha.5 | ||||
| RUN go get github.com/openimsdk/gomake@v0.0.15-alpha.5 | ||||
| 
 | ||||
| # Set the command to run when the container starts | ||||
| ENTRYPOINT ["sh", "-c", "mage start && tail -f /dev/null"] | ||||
|  | ||||
| @ -53,15 +53,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-cmdutils | ||||
|     id: openim-cmdutils | ||||
| @ -71,15 +64,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-crontask | ||||
|     id: openim-crontask | ||||
| @ -89,15 +75,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-msggateway | ||||
|     id: openim-msggateway | ||||
| @ -107,15 +86,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-msgtransfer | ||||
|     id: openim-msgtransfer | ||||
| @ -125,15 +97,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-push | ||||
|     id: openim-push | ||||
| @ -143,15 +108,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-auth | ||||
|     id: openim-rpc-auth | ||||
| @ -161,15 +119,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-conversation | ||||
|     id: openim-rpc-conversation | ||||
| @ -179,15 +130,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-friend | ||||
|     id: openim-rpc-friend | ||||
| @ -197,15 +141,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-group | ||||
|     id: openim-rpc-group | ||||
| @ -215,15 +152,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-msg | ||||
|     id: openim-rpc-msg | ||||
| @ -233,15 +163,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-third | ||||
|     id: openim-rpc-third | ||||
| @ -251,15 +174,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
|   - binary: openim-rpc-user | ||||
|     id: openim-rpc-user | ||||
| @ -269,15 +185,8 @@ builds: | ||||
|       - windows | ||||
|       - linux | ||||
|     goarch: | ||||
|       - s390x | ||||
|       - mips64 | ||||
|       - mips64le | ||||
|       - amd64 | ||||
|       - ppc64le | ||||
|       - arm64 | ||||
|     goarm: | ||||
|       - "6" | ||||
|       - "7" | ||||
| 
 | ||||
| 
 | ||||
| # TODO:Need a script, such as the init - release to help binary to find the right directory | ||||
|  | ||||
| @ -15,10 +15,9 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	_ "net/http/pprof" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/cmd" | ||||
| 	"github.com/openimsdk/tools/system/program" | ||||
| 	_ "net/http/pprof" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
|  | ||||
| @ -5,9 +5,4 @@ etcd: | ||||
|   username: '' | ||||
|   password: '' | ||||
| 
 | ||||
| zookeeper: | ||||
|   schema: openim | ||||
|   address: [ localhost:12181 ] | ||||
|   username: '' | ||||
|   password: '' | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,8 @@ database: openim_v3 | ||||
| username: openIM | ||||
| # Password for database authentication | ||||
| password: openIM123 | ||||
| # Authentication source for database authentication, if use root user, set it to admin | ||||
| authSource: openim_v3 | ||||
| # Maximum number of connections in the connection pool | ||||
| maxPoolSize: 100 | ||||
| # Maximum number of retry attempts for a failed database connection | ||||
|  | ||||
| @ -1,20 +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. | ||||
| 
 | ||||
| # 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. | ||||
| @ -50,7 +33,7 @@ joinGroupApplication: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: false | ||||
|     enable: true | ||||
|     title: joinGroupApplication title | ||||
|     desc: joinGroupApplication desc | ||||
|     ext: joinGroupApplication ext | ||||
| @ -70,7 +53,7 @@ groupApplicationAccepted: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: false | ||||
|     enable: true | ||||
|     title: groupApplicationAccepted title | ||||
|     desc: groupApplicationAccepted desc | ||||
|     ext: groupApplicationAccepted ext | ||||
| @ -80,7 +63,7 @@ groupApplicationRejected: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: false | ||||
|     enable: true | ||||
|     title: groupApplicationRejected title | ||||
|     desc: groupApplicationRejected desc | ||||
|     ext: groupApplicationRejected ext | ||||
| @ -217,7 +200,7 @@ friendApplicationAdded: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: false | ||||
|     enable: true | ||||
|     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 | ||||
| @ -247,7 +230,7 @@ friendAdded: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: We have become friends | ||||
|     desc: We have become friends | ||||
|     ext: We have become friends | ||||
| @ -257,7 +240,7 @@ friendDeleted: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: deleted a friend | ||||
|     desc: deleted a friend | ||||
|     ext: deleted a friend | ||||
| @ -267,7 +250,7 @@ friendRemarkSet: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: Your friend's profile has been changed | ||||
|     desc: Your friend's profile has been changed | ||||
|     ext: Your friend's profile has been changed | ||||
| @ -277,7 +260,7 @@ blackAdded: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: blocked a user | ||||
|     desc: blocked a user | ||||
|     ext: blocked a user | ||||
| @ -287,7 +270,7 @@ blackDeleted: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: Remove a blocked user | ||||
|     desc: Remove a blocked user | ||||
|     ext: Remove a blocked user | ||||
| @ -297,7 +280,7 @@ friendInfoUpdated: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: friend info updated | ||||
|     desc: friend info updated | ||||
|     ext: friend info updated | ||||
| @ -308,10 +291,10 @@ userInfoUpdated: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     title: Remove a blocked user | ||||
|     desc: Remove a blocked user | ||||
|     ext: Remove a blocked user | ||||
|     enable: false | ||||
|     title: userInfo updated | ||||
|     desc: userInfo updated | ||||
|     ext: userInfo updated | ||||
| 
 | ||||
| userStatusChanged: | ||||
|   isSendMsg: false | ||||
| @ -329,7 +312,7 @@ conversationChanged: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: conversation changed | ||||
|     desc: conversation changed | ||||
|     ext: conversation changed | ||||
| @ -339,7 +322,7 @@ conversationSetPrivate: | ||||
|   reliabilityLevel: 1 | ||||
|   unreadCount: false | ||||
|   offlinePush: | ||||
|     enable: true | ||||
|     enable: false | ||||
|     title: burn after reading | ||||
|     desc: burn after reading | ||||
|     ext: burn after reading | ||||
|  | ||||
| @ -10,7 +10,10 @@ api: | ||||
| prometheus: | ||||
|   # Whether to enable prometheus | ||||
|   enable: true | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # Prometheus listening ports, must match the number of api.ports | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12002 ] | ||||
|   # This address can be accessed via a browser | ||||
|   grafanaURL: http://127.0.0.1:13000/ | ||||
|  | ||||
| @ -1,3 +1,4 @@ | ||||
| cronExecuteTime: 0 2 * * * | ||||
| retainChatRecords: 365 | ||||
| fileExpireTime: 90 | ||||
| fileExpireTime: 180 | ||||
| deleteObjectType: ["msg-picture","msg-file", "msg-voice","msg-video","msg-video-snapshot","sdklog"] | ||||
| @ -1,13 +1,17 @@ | ||||
| rpc: | ||||
|   # The IP address where this RPC service registers itself; if left blank, it defaults to the internal network IP | ||||
|   registerIP: | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10140, 10141, 10142, 10143, 10144, 10145, 10146, 10147, 10148, 10149, 10150, 10151, 10152, 10153, 10154, 10155 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12140, 12141, 12142, 12143, 12144, 12145, 12146, 12147, 12148, 12149, 12150, 12151, 12152, 12153, 12154, 12155 ] | ||||
| 
 | ||||
| # 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 | ||||
| @ -22,5 +26,3 @@ longConnSvr: | ||||
|   websocketMaxMsgLen: 4096 | ||||
|   # WebSocket connection handshake timeout in seconds | ||||
|   websocketTimeout: 10 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| prometheus: | ||||
|   # Enable or disable Prometheus monitoring | ||||
|   enable: true | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12020, 12021, 12022, 12023, 12024, 12025, 12026, 12027, 12028, 12029, 12030, 12031, 12032, 12033, 12034, 12035 ] | ||||
|  | ||||
| @ -3,17 +3,21 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10170, 10171, 10172, 10173, 10174, 10175, 10176, 10177, 10178, 10179, 10180, 10181, 10182, 10183, 10184, 10185 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12170, 12171, 12172, 12173, 12174, 12175, 12176, 12177, 12178, 12179, 12180, 12182, 12183, 12184, 12185, 12186 ] | ||||
| 
 | ||||
| maxConcurrentWorkers: 3 | ||||
| #Use geTui for offline push notifications, or choose fcm or jpns; corresponding configuration settings must be specified. | ||||
| #Use geTui for offline push notifications, or choose fcm or jpush; corresponding configuration settings must be specified. | ||||
| enable: geTui | ||||
| geTui: | ||||
|   pushUrl: https://restapi.getui.com/v2/$appId | ||||
| @ -26,7 +30,7 @@ fcm: | ||||
|   # Prioritize using file paths. If the file path is empty, use URL | ||||
|   filePath:   # File path is concatenated with the parameters passed in through - c(`mage` default pass in `config/`) and filePath. | ||||
|   authURL:   #  Must start with https or http. | ||||
| jpns: | ||||
| jpush: | ||||
|   appKey: | ||||
|   masterSecret: | ||||
|   pushURL: | ||||
|  | ||||
| @ -3,13 +3,17 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10200 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12200 ] | ||||
| 
 | ||||
| tokenPolicy: | ||||
|  | ||||
| @ -3,11 +3,15 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10220 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12220 ] | ||||
|  | ||||
| @ -3,11 +3,15 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10240 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12240 ] | ||||
|  | ||||
| @ -3,13 +3,17 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10260 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12260 ] | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -3,13 +3,17 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10280 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12280 ] | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -3,13 +3,17 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10300 ] | ||||
| 
 | ||||
| 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12300 ] | ||||
| 
 | ||||
| 
 | ||||
| @ -38,3 +42,10 @@ object: | ||||
|     accessKeySecret:  | ||||
|     sessionToken:  | ||||
|     publicRead: false | ||||
|   aws: | ||||
|     region: ap-southeast-2 | ||||
|     bucket: testdemo832234 | ||||
|     accessKeyID: | ||||
|     secretAccessKey: | ||||
|     sessionToken: | ||||
|     publicRead: false | ||||
|  | ||||
| @ -3,11 +3,15 @@ rpc: | ||||
|   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 | ||||
|   # autoSetPorts indicates whether to automatically set the ports | ||||
|   autoSetPorts: true | ||||
|   # 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 | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 10320 ] | ||||
| 
 | ||||
| prometheus: | ||||
|   # Whether to enable prometheus | ||||
|   enable: true | ||||
|   # Prometheus listening ports, must be consistent with the number of rpc.ports | ||||
|   # It will only take effect when autoSetPorts is set to false. | ||||
|   ports: [ 12320 ] | ||||
|  | ||||
| @ -8,7 +8,7 @@ global: | ||||
| alerting: | ||||
|   alertmanagers: | ||||
|     - static_configs: | ||||
|         - targets: [internal_ip:19093] | ||||
|         - targets: [127.0.0.1:19093] | ||||
| 
 | ||||
| # Load rules once and periodically evaluate them according to the global evaluation_interval. | ||||
| rule_files: | ||||
| @ -25,62 +25,95 @@ scrape_configs: | ||||
|   # prometheus fetches application services | ||||
|   - job_name: node_exporter | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:20500 ] | ||||
|       - targets: [ 127.0.0.1:19100 ] | ||||
| 
 | ||||
|   - job_name: openimserver-openim-api | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12002 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/api" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12002 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-msggateway | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12140 ] | ||||
| #      - targets: [ internal_ip:12140, internal_ip:12141, internal_ip:12142, internal_ip:12143, internal_ip:12144, internal_ip:12145, internal_ip:12146, internal_ip:12147, internal_ip:12148, internal_ip:12149, internal_ip:12150, internal_ip:12151, internal_ip:12152, internal_ip:12153, internal_ip:12154, internal_ip:12155 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/msg_gateway" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12140 ] | ||||
| #        #      - targets: [ 127.0.0.1:12140, 127.0.0.1:12141, 127.0.0.1:12142, 127.0.0.1:12143, 127.0.0.1:12144, 127.0.0.1:12145, 127.0.0.1:12146, 127.0.0.1:12147, 127.0.0.1:12148, 127.0.0.1:12149, 127.0.0.1:12150, 127.0.0.1:12151, 127.0.0.1:12152, 127.0.0.1:12153, 127.0.0.1:12154, 127.0.0.1:12155 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-msgtransfer | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12020, internal_ip:12021, internal_ip:12022, internal_ip:12023, internal_ip:12024, internal_ip:12025, internal_ip:12026, internal_ip:12027 ] | ||||
| #      - targets: [ internal_ip:12020, internal_ip:12021, internal_ip:12022, internal_ip:12023, internal_ip:12024, internal_ip:12025, internal_ip:12026, internal_ip:12027, internal_ip:12028, internal_ip:12029, internal_ip:12030, internal_ip:12031, internal_ip:12032, internal_ip:12033, internal_ip:12034, internal_ip:12035 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/msg_transfer" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12020, 127.0.0.1:12021, 127.0.0.1:12022, 127.0.0.1:12023, 127.0.0.1:12024, 127.0.0.1:12025, 127.0.0.1:12026, 127.0.0.1:12027 ] | ||||
| #        #      - targets: [ 127.0.0.1:12020, 127.0.0.1:12021, 127.0.0.1:12022, 127.0.0.1:12023, 127.0.0.1:12024, 127.0.0.1:12025, 127.0.0.1:12026, 127.0.0.1:12027, 127.0.0.1:12028, 127.0.0.1:12029, 127.0.0.1:12030, 127.0.0.1:12031, 127.0.0.1:12032, 127.0.0.1:12033, 127.0.0.1:12034, 127.0.0.1:12035 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-push | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12170, internal_ip:12171, internal_ip:12172, internal_ip:12173, internal_ip:12174, internal_ip:12175, internal_ip:12176, internal_ip:12177 ] | ||||
| #      - targets: [ internal_ip:12170, internal_ip:12171, internal_ip:12172, internal_ip:12173, internal_ip:12174, internal_ip:12175, internal_ip:12176, internal_ip:12177, internal_ip:12178, internal_ip:12179, internal_ip:12180,  internal_ip:12182, internal_ip:12183, internal_ip:12184, internal_ip:12185, internal_ip:12186 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/push" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12170, 127.0.0.1:12171, 127.0.0.1:12172, 127.0.0.1:12173, 127.0.0.1:12174, 127.0.0.1:12175, 127.0.0.1:12176, 127.0.0.1:12177 ] | ||||
| ##      - targets: [ 127.0.0.1:12170, 127.0.0.1:12171, 127.0.0.1:12172, 127.0.0.1:12173, 127.0.0.1:12174, 127.0.0.1:12175, 127.0.0.1:12176, 127.0.0.1:12177, 127.0.0.1:12178, 127.0.0.1:12179, 127.0.0.1:12180,  127.0.0.1:12182, 127.0.0.1:12183, 127.0.0.1:12184, 127.0.0.1:12185, 127.0.0.1:12186 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-auth | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12200 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/auth" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12200 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-conversation | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12220 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/conversation" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12220 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-friend | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12240 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/friend" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12240 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-group | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12260 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/group" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12260 ] | ||||
| #        labels: | ||||
| #          namespace: default. | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-msg | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12280 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/msg" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12280 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-third | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12300 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/third" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12300 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| 
 | ||||
|   - job_name: openimserver-openim-rpc-user | ||||
|     static_configs: | ||||
|       - targets: [ internal_ip:12320 ] | ||||
|         labels: | ||||
|           namespace: default | ||||
|     http_sd_configs: | ||||
|       - url: "http://127.0.0.1:10002/prometheus_discovery/user" | ||||
| #    static_configs: | ||||
| #      - targets: [ 127.0.0.1:12320 ] | ||||
| #        labels: | ||||
| #          namespace: default | ||||
| @ -1,7 +1,8 @@ | ||||
| address: [ localhost:16379 ] | ||||
| username:  | ||||
| password: openIM123 | ||||
| clusterMode: false | ||||
| # redisMode can be "cluster", "sentinel", or "standalone" | ||||
| redisMode: "standalone" | ||||
| db: 0 | ||||
| maxRetry: 10 | ||||
| poolSize: 100 | ||||
|  | ||||
| @ -13,4 +13,10 @@ rpcRegisterName: | ||||
| imAdminUserID: [ imAdmin ] | ||||
| 
 | ||||
| # 1: For Android, iOS, Windows, Mac, and web platforms, only one instance can be online at a time | ||||
| multiLoginPolicy: 1 | ||||
| multiLogin: | ||||
|   policy: 1 | ||||
|   maxNumOneEnd: 30 | ||||
| 
 | ||||
| rpcMaxBodySize: | ||||
|   requestMaxBodySize: 8388608 | ||||
|   responseMaxBodySize: 8388608 | ||||
|  | ||||
| @ -1,175 +1,188 @@ | ||||
| # OpenIM Application Containerization Deployment Guide | ||||
| # Kubernetes Deployment | ||||
| 
 | ||||
| OpenIM supports a variety of cluster deployment methods, including but not limited to `helm`, `sealos`, `kustomize` | ||||
| ## Resource Requests | ||||
| 
 | ||||
| Various contributors, as well as previous official releases, have provided some referenceable solutions: | ||||
| - CPU: 2 cores | ||||
| - Memory: 4 GiB | ||||
| - Disk usage: 20 GiB (on Node) | ||||
| 
 | ||||
| + [k8s-jenkins Repository](https://github.com/OpenIMSDK/k8s-jenkins) | ||||
| + [open-im-server-k8s-deploy Repository](https://github.com/openimsdk/open-im-server-k8s-deploy) | ||||
| + [openim-charts Repository](https://github.com/OpenIMSDK/openim-charts) | ||||
| + [deploy-openim Repository](https://github.com/showurl/deploy-openim) | ||||
| ## Preconditions | ||||
| 
 | ||||
| ### Dependency Check | ||||
| ensure that you have already deployed the following components: | ||||
| 
 | ||||
| ```bash | ||||
| Kubernetes: >= 1.16.0-0 | ||||
| Helm: >= 3.0 | ||||
| ``` | ||||
| - Redis | ||||
| - MongoDB | ||||
| - Kafka | ||||
| - MinIO | ||||
| 
 | ||||
| ### Minimum Configuration | ||||
| ## Origin Deploy | ||||
| 
 | ||||
| The recommended minimum configuration for a production environment is as follows: | ||||
| ### Enter the target dir | ||||
| 
 | ||||
| `cd ./deployments/deploy/` | ||||
| 
 | ||||
| ### Deploy configs and dependencies | ||||
| 
 | ||||
| Upate your configMap `openim-config.yml`. **You can check the official docs for more details.** | ||||
| 
 | ||||
| In `openim-config.yml`, you need modify the following configurations: | ||||
| 
 | ||||
| **discovery.yml** | ||||
| 
 | ||||
| - `kubernetes.namespace`: default is `default`, you can change it to your namespace. | ||||
| 
 | ||||
| **mongodb.yml** | ||||
| 
 | ||||
| - `address`: set to your already mongodb address or mongo Service name and port in your deployed. | ||||
| - `database`: set to your mongodb database name.(Need have a created database.) | ||||
| - `authSource`: set to your mongodb authSource. (authSource is specify the database name associated with the user's credentials, user need create in this database.) | ||||
| 
 | ||||
| **kafka.yml** | ||||
| 
 | ||||
| - `address`: set to your already kafka address or kafka Service name and port in your deployed. | ||||
| 
 | ||||
| **redis.yml** | ||||
| 
 | ||||
| - `address`: set to your already redis address or redis Service name and port in your deployed. | ||||
| 
 | ||||
| **minio.yml** | ||||
| 
 | ||||
| - `internalAddress`: set to your minio Service name and port in your deployed. | ||||
| - `externalAddress`: set to your already expose minio external address. | ||||
| 
 | ||||
| ### Set the secret | ||||
| 
 | ||||
| A Secret is an object that contains a small amount of sensitive data. Such as password and secret. Secret is similar to ConfigMaps. | ||||
| 
 | ||||
| #### Redis: | ||||
| 
 | ||||
| Update the `redis-password` value in `redis-secret.yml` to your Redis password encoded in base64. | ||||
| 
 | ||||
| ```yaml | ||||
| CPU: 4 | ||||
| Memory: 8G | ||||
| Disk: 100G | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-redis-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   redis-password: b3BlbklNMTIz # update to your redis password encoded in base64, if need empty, you can set to "" | ||||
| ``` | ||||
| 
 | ||||
| ## Configuration File Generation | ||||
| #### Mongo: | ||||
| 
 | ||||
| We have automated all the files, making the generation of configuration files optional for OpenIM. However, if you desire custom configurations, you can follow the steps below: | ||||
| Update the `mongo_openim_username`, `mongo_openim_password` value in `mongo-secret.yml` to your Mongo username and password encoded in base64. | ||||
| 
 | ||||
| ```bash | ||||
| $ make init | ||||
| # Alternatively, use script: | ||||
| # ./scripts/init-config.sh | ||||
| ```yaml | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-mongo-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   mongo_openim_username: b3BlbklN # update to your mongo username encoded in base64, if need empty, you can set to "" (this user credentials need in authSource database). | ||||
|   mongo_openim_password: b3BlbklNMTIz # update to your mongo password encoded in base64, if need empty, you can set to "" | ||||
| ``` | ||||
| 
 | ||||
| At this point, configuration files will be generated under `deployments/openim/config`, which you can modify as per your requirements. | ||||
| #### Minio: | ||||
| 
 | ||||
| ## Cluster Setup | ||||
| Update the `minio-root-user` and `minio-root-password` value in `minio-secret.yml` to your MinIO accessKeyID and secretAccessKey encoded in base64. | ||||
| 
 | ||||
| If you already have a `kubernetes` cluster, or if you wish to build a `kubernetes` cluster from scratch, you can skip this step. | ||||
| 
 | ||||
| For a quick start, I used [sealos](https://github.com/labring/sealos) to rapidly set up the cluster, with sealos also being a wrapper for kubeadm at its core: | ||||
| 
 | ||||
| ```bash | ||||
| $ SEALOS_VERSION=`curl -s https://api.github.com/repos/labring/sealos/releases/latest | grep -oE '"tag_name": "[^"]+"' | head -n1 | cut -d'"' -f4` && \ | ||||
|   curl -sfL https://raw.githubusercontent.com/labring/sealos/${SEALOS_VERSION}/scripts/install.sh | | ||||
|   sh -s ${SEALOS_VERSION} labring/sealos | ||||
| ```yaml | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-minio-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   minio-root-user: cm9vdA== # update to your minio accessKeyID encoded in base64, if need empty, you can set to "" | ||||
|   minio-root-password: b3BlbklNMTIz # update to your minio secretAccessKey encoded in base64, if need empty, you can set to "" | ||||
| ``` | ||||
| 
 | ||||
| **Supported Versions:** | ||||
| #### Kafka: | ||||
| 
 | ||||
| + docker: `labring/kubernetes-docker`:(v1.24.0~v1.27.0) | ||||
| + containerd: `labring/kubernetes`:(v1.24.0~v1.27.0) | ||||
| Update the `kafka-password` value in `kafka-secret.yml` to your Kafka password encoded in base64. | ||||
| 
 | ||||
| #### Cluster Installation: | ||||
| 
 | ||||
| Cluster details are as follows: | ||||
| 
 | ||||
| | Hostname | IP Address | System Info                                                  | | ||||
| | -------- | ---------- | ------------------------------------------------------------ | | ||||
| | master01 | 10.0.0.9   | `Linux VM-0-9-ubuntu 5.15.0-76-generic #83-Ubuntu SMP Thu Jun 15 19:16:32 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux` | | ||||
| | node01   | 10.0.0.4   | Similar to master01                                          | | ||||
| | node02   | 10.0.0.10  | Similar to master01                                          | | ||||
| 
 | ||||
| ```bash | ||||
| $ export CLUSTER_USERNAME=ubuntu | ||||
| $ export CLUSTER_PASSWORD=123456 | ||||
| $ sudo sealos run labring/kubernetes:v1.25.0 labring/helm:v3.8.2 labring/calico:v3.24.1 \ | ||||
|     --masters 10.0.0.9 \ | ||||
|     --nodes 10.0.0.4,10.0.0.10 \ | ||||
|     -u "$CLUSTER_USERNAME" \ | ||||
|     -p "$CLUSTER_PASSWORD" | ||||
| ```yaml | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-kafka-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   kafka-password: b3BlbklNMTIz # update to your kafka password encoded in base64, if need empty, you can set to "" | ||||
| ``` | ||||
| 
 | ||||
| > **Node** Uninstallation method: using `kubeadm` for uninstallation does not remove `etcd` and `cni` related configurations. Manual clearance or using `sealos` for uninstallation is needed. | ||||
| ### Apply the secret. | ||||
| 
 | ||||
| ```shell | ||||
| kubectl apply -f redis-secret.yml -f minio-secret.yml -f mongo-secret.yml -f kafka-secret.yml | ||||
| ``` | ||||
| 
 | ||||
| ### Apply all config | ||||
| 
 | ||||
| `kubectl apply -f ./openim-config.yml` | ||||
| 
 | ||||
| > Attation: If you use `default` namespace, you can excute `clusterRile.yml` to create a cluster role binding for default service account. | ||||
| > | ||||
| > ```bash | ||||
| > $ sealos reset | ||||
| > ``` | ||||
| > Namespace is modify to `discovery.yml` in `openim-config.yml`, you can change `kubernetes.namespace` to your namespace. | ||||
| 
 | ||||
| If you are local, you can also use Kind and Minikube to test, for example, using Kind: | ||||
| **Excute `clusterRole.yml`** | ||||
| 
 | ||||
| `kubectl apply -f ./clusterRole.yml` | ||||
| 
 | ||||
| ### run all deployments and services | ||||
| 
 | ||||
| > Note: Ensure that infrastructure services like MinIO, Redis, and Kafka are running before deploying the main applications. | ||||
| 
 | ||||
| ```bash | ||||
| $ GO111MODULE="on" go get sigs.k8s.io/kind@v0.11.1 | ||||
| $ kind create cluster | ||||
| kubectl apply \ | ||||
|   -f openim-api-deployment.yml \ | ||||
|   -f openim-api-service.yml \ | ||||
|   -f openim-crontask-deployment.yml \ | ||||
|   -f openim-rpc-user-deployment.yml \ | ||||
|   -f openim-rpc-user-service.yml \ | ||||
|   -f openim-msggateway-deployment.yml \ | ||||
|   -f openim-msggateway-service.yml \ | ||||
|   -f openim-push-deployment.yml \ | ||||
|   -f openim-push-service.yml \ | ||||
|   -f openim-msgtransfer-service.yml \ | ||||
|   -f openim-msgtransfer-deployment.yml \ | ||||
|   -f openim-rpc-conversation-deployment.yml \ | ||||
|   -f openim-rpc-conversation-service.yml \ | ||||
|   -f openim-rpc-auth-deployment.yml \ | ||||
|   -f openim-rpc-auth-service.yml \ | ||||
|   -f openim-rpc-group-deployment.yml \ | ||||
|   -f openim-rpc-group-service.yml \ | ||||
|   -f openim-rpc-friend-deployment.yml \ | ||||
|   -f openim-rpc-friend-service.yml \ | ||||
|   -f openim-rpc-msg-deployment.yml \ | ||||
|   -f openim-rpc-msg-service.yml \ | ||||
|   -f openim-rpc-third-deployment.yml \ | ||||
|   -f openim-rpc-third-service.yml | ||||
| ``` | ||||
| 
 | ||||
| ### Installing helm | ||||
| ### Verification | ||||
| 
 | ||||
| Helm simplifies the deployment and management of Kubernetes applications to a large extent by offering version control and release management through packaging. | ||||
| 
 | ||||
| **Using Script:** | ||||
| After deploying the services, verify that everything is running smoothly: | ||||
| 
 | ||||
| ```bash | ||||
| $ curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash | ||||
| # Check the status of all pods | ||||
| kubectl get pods | ||||
| 
 | ||||
| # Check the status of services | ||||
| kubectl get svc | ||||
| 
 | ||||
| # Check the status of deployments | ||||
| kubectl get deployments | ||||
| 
 | ||||
| # View all resources | ||||
| kubectl get all | ||||
| ``` | ||||
| 
 | ||||
| **Adding Repository:** | ||||
| ### clean all | ||||
| 
 | ||||
| ```bash | ||||
| $ helm repo add brigade https://openimsdk.github.io/openim-charts | ||||
| ``` | ||||
| `kubectl delete -f ./` | ||||
| 
 | ||||
| ### OpenIM Image Strategy | ||||
| ### Notes: | ||||
| 
 | ||||
| Automated offerings include aliyun, ghcr, docker hub: [Image Documentation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) | ||||
| 
 | ||||
| **Local Test Build Method:** | ||||
| 
 | ||||
| ```bash | ||||
| $ make image | ||||
| ``` | ||||
| 
 | ||||
| > This command assists in quickly building the required images locally. For a detailed build strategy, refer to the [Build Documentation](https://github.com/openimsdk/open-im-server/blob/main/build/README.md). | ||||
| 
 | ||||
| ## Installation | ||||
| 
 | ||||
| Explore our Helm-Charts repository and read through: [Helm-Charts Repository](https://github.com/openimsdk/helm-charts) | ||||
| 
 | ||||
| 
 | ||||
| Using the helm charts repository, you can ignore the following configuration, but if you want to just use the server and scale on top of it, you can go ahead: | ||||
| 
 | ||||
| **Use the Helm template to generate the deployment yaml file: `openim-charts.yaml`** | ||||
| 
 | ||||
| **Gen Image:** | ||||
| 
 | ||||
| ```bash | ||||
| ../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/helm-image.yaml > ./charts/generated-configs/helm-image.yaml | ||||
| ``` | ||||
| 
 | ||||
| **Gen Charts:** | ||||
| 
 | ||||
| ```bash | ||||
| for chart in ./charts/*/; do | ||||
|     if [[ "$chart" == *"generated-configs"* || "$chart" == *"helmfile.yaml"* ]]; then | ||||
|         continue | ||||
|     fi | ||||
| 
 | ||||
|     if [ -f "${chart}values.yaml" ]; then | ||||
|         helm template "$chart" -f "./charts/generated-configs/helm-image.yaml" -f "./charts/generated-configs/config.yaml" -f "./charts/generated-configs/notification.yaml" >> openim-charts.yaml | ||||
|     else | ||||
|         helm template "$chart" >> openim-charts.yaml | ||||
|     fi | ||||
| done | ||||
| ``` | ||||
| 
 | ||||
| **Use Helmfile:** | ||||
| 
 | ||||
| ```bash | ||||
| GO111MODULE=on go get github.com/roboll/helmfile@latest | ||||
| ``` | ||||
| 
 | ||||
| ```bash | ||||
| export MONGO_ADDRESS=im-mongo | ||||
| export MONGO_PORT=27017 | ||||
| export REDIS_ADDRESS=im-redis-master | ||||
| export REDIS_PORT=6379 | ||||
| export KAFKA_ADDRESS=im-kafka | ||||
| export KAFKA_PORT=9092 | ||||
| export OBJECT_APIURL="https://openim.server.com/api" | ||||
| export MINIO_ENDPOINT="http://im-minio:9000" | ||||
| export MINIO_SIGN_ENDPOINT="https://openim.server.com/im-minio-api" | ||||
| 
 | ||||
| mkdir ./charts/generated-configs | ||||
| ../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/config.yaml > ./charts/generated-configs/config.yaml | ||||
| cp ../config/notification.yaml ./charts/generated-configs/notification.yaml | ||||
| ../scripts/genconfig.sh ../scripts/install/environment.sh ./templates/helm-image.yaml > ./charts/generated-configs/helm-image.yaml | ||||
| ``` | ||||
| 
 | ||||
| ```bash | ||||
| helmfile apply | ||||
| ``` | ||||
| - If you use a specific namespace for your deployment, be sure to append the -n <namespace> flag to your kubectl commands. | ||||
|  | ||||
							
								
								
									
										7
									
								
								deployments/deploy/kafka-secret.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								deployments/deploy/kafka-secret.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-kafka-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   kafka-password: "" | ||||
							
								
								
									
										8
									
								
								deployments/deploy/minio-secret.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								deployments/deploy/minio-secret.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-minio-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   minio-root-user: cm9vdA== # Base64 encoded "root" | ||||
|   minio-root-password: b3BlbklNMTIz # Base64 encoded "openIM123" | ||||
							
								
								
									
										79
									
								
								deployments/deploy/minio-statefulset.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								deployments/deploy/minio-statefulset.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: minio | ||||
|   labels: | ||||
|     app: minio | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: minio | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: minio | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: minio | ||||
|           image: minio/minio:RELEASE.2024-01-11T07-46-16Z | ||||
|           ports: | ||||
|             - containerPort: 9000 # MinIO service port | ||||
|             - containerPort: 9090 # MinIO console port | ||||
|           volumeMounts: | ||||
|             - name: minio-data | ||||
|               mountPath: /data | ||||
|             - name: minio-config | ||||
|               mountPath: /root/.minio | ||||
|           env: | ||||
|             - name: TZ | ||||
|               value: "Asia/Shanghai" | ||||
|             - name: MINIO_ROOT_USER | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-minio-secret | ||||
|                   key: minio-root-user | ||||
|             - name: MINIO_ROOT_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-minio-secret | ||||
|                   key: minio-root-password | ||||
|           command: | ||||
|             - "/bin/sh" | ||||
|             - "-c" | ||||
|             - | | ||||
|               mkdir -p /data && \ | ||||
|               minio server /data --console-address ":9090" | ||||
|       volumes: | ||||
|         - name: minio-data | ||||
|           persistentVolumeClaim: | ||||
|             claimName: minio-pvc | ||||
|         - name: minio-config | ||||
|           persistentVolumeClaim: | ||||
|             claimName: minio-config-pvc | ||||
| 
 | ||||
| --- | ||||
| apiVersion: v1 | ||||
| kind: PersistentVolumeClaim | ||||
| metadata: | ||||
|   name: minio-pvc | ||||
| spec: | ||||
|   accessModes: | ||||
|     - ReadWriteOnce | ||||
|   resources: | ||||
|     requests: | ||||
|       storage: 10Gi | ||||
| 
 | ||||
| --- | ||||
| apiVersion: v1 | ||||
| kind: PersistentVolumeClaim | ||||
| metadata: | ||||
|   name: minio-config-pvc | ||||
| spec: | ||||
|   accessModes: | ||||
|     - ReadWriteOnce | ||||
|   resources: | ||||
|     requests: | ||||
|       storage: 2Gi | ||||
| 
 | ||||
| 
 | ||||
							
								
								
									
										8
									
								
								deployments/deploy/mongo-secret.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								deployments/deploy/mongo-secret.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-mongo-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   mongo_openim_username: b3BlbklN # base64 for "openIM", this user credentials need in authSource database. | ||||
|   mongo_openim_password: b3BlbklNMTIz # base64 for "openIM123" | ||||
							
								
								
									
										108
									
								
								deployments/deploy/mongo-statefulset.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								deployments/deploy/mongo-statefulset.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,108 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: StatefulSet | ||||
| metadata: | ||||
|   name: mongo-statefulset | ||||
| spec: | ||||
|   serviceName: "mongo" | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: mongo | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: mongo | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: mongo | ||||
|           image: mongo:7.0 | ||||
|           command: ["/bin/bash", "-c"] | ||||
|           args: | ||||
|             - > | ||||
|               docker-entrypoint.sh mongod --wiredTigerCacheSizeGB ${wiredTigerCacheSizeGB} --auth & | ||||
|               until mongosh -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD} --authenticationDatabase admin --eval "db.runCommand({ ping: 1 })" &>/dev/null; do | ||||
|                 echo "Waiting for MongoDB to start..."; | ||||
|                 sleep 1; | ||||
|               done && | ||||
|               mongosh -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD} --authenticationDatabase admin --eval " | ||||
|               db = db.getSiblingDB(\"${MONGO_INITDB_DATABASE}\"); | ||||
|               if (!db.getUser(\"${MONGO_OPENIM_USERNAME}\")) { | ||||
|                 db.createUser({ | ||||
|                   user: \"${MONGO_OPENIM_USERNAME}\", | ||||
|                   pwd: \"${MONGO_OPENIM_PASSWORD}\", | ||||
|                   roles: [{role: \"readWrite\", db: \"${MONGO_INITDB_DATABASE}\"}] | ||||
|                 }); | ||||
|                 print(\"User created successfully: \"); | ||||
|                 print(\"Username: ${MONGO_OPENIM_USERNAME}\"); | ||||
|                 print(\"Password: ${MONGO_OPENIM_PASSWORD}\"); | ||||
|                 print(\"Database: ${MONGO_INITDB_DATABASE}\"); | ||||
|               } else { | ||||
|                 print(\"User already exists in database: ${MONGO_INITDB_DATABASE}, Username: ${MONGO_OPENIM_USERNAME}\"); | ||||
|               } | ||||
|               " && | ||||
|               tail -f /dev/null | ||||
|           ports: | ||||
|             - containerPort: 27017 | ||||
|           env: | ||||
|             - name: MONGO_INITDB_ROOT_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-init-secret | ||||
|                   key: mongo_initdb_root_username | ||||
|             - name: MONGO_INITDB_ROOT_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-init-secret | ||||
|                   key: mongo_initdb_root_password | ||||
|             - name: MONGO_INITDB_DATABASE | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-init-secret | ||||
|                   key: mongo_initdb_database | ||||
|             - name: MONGO_OPENIM_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-init-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: MONGO_OPENIM_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-init-secret | ||||
|                   key: mongo_openim_password | ||||
|             - name: TZ | ||||
|               value: "Asia/Shanghai" | ||||
|             - name: wiredTigerCacheSizeGB | ||||
|               value: "1" | ||||
|           volumeMounts: | ||||
|             - name: mongo-storage | ||||
|               mountPath: /data/db | ||||
| 
 | ||||
|       volumes: | ||||
|         - name: mongo-storage | ||||
|           persistentVolumeClaim: | ||||
|             claimName: mongo-pvc | ||||
| 
 | ||||
| --- | ||||
| apiVersion: v1 | ||||
| kind: PersistentVolumeClaim | ||||
| metadata: | ||||
|   name: mongo-pvc | ||||
| spec: | ||||
|   accessModes: | ||||
|     - ReadWriteOnce | ||||
|   resources: | ||||
|     requests: | ||||
|       storage: 5Gi | ||||
| 
 | ||||
| --- | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-mongo-init-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   mongo_initdb_root_username: cm9vdA== # base64 for "root" | ||||
|   mongo_initdb_root_password: b3BlbklNMTIz # base64 for "openIM123" | ||||
|   mongo_initdb_database: b3BlbmltX3Yz # base64 for "openim_v3" | ||||
|   mongo_openim_username: b3BlbklN # base64 for "openIM" | ||||
|   mongo_openim_password: b3BlbklNMTIz # base64 for "openIM123" | ||||
							
								
								
									
										47
									
								
								deployments/deploy/openim-api-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								deployments/deploy/openim-api-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,47 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: openim-api | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: openim-api | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: openim-api | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: openim-api-container | ||||
|           image: openim/openim-api:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
| 
 | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10002 | ||||
|             - containerPort: 12002 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										1056
									
								
								deployments/deploy/openim-config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1056
									
								
								deployments/deploy/openim-config.yml
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										36
									
								
								deployments/deploy/openim-msggateway-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								deployments/deploy/openim-msggateway-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: messagegateway-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: messagegateway-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: messagegateway-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: openim-msggateway-container | ||||
|           image: openim/openim-msggateway:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10140 | ||||
|             - containerPort: 12001 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										50
									
								
								deployments/deploy/openim-msgtransfer-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								deployments/deploy/openim-msgtransfer-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: openim-msgtransfer-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: openim-msgtransfer-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: openim-msgtransfer-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: openim-msgtransfer-container | ||||
|           image: openim/openim-msgtransfer:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|             - name: IMENV_KAFKA_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-kafka-secret | ||||
|                   key: kafka-password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 12020 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										41
									
								
								deployments/deploy/openim-push-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								deployments/deploy/openim-push-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: push-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: push-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: push-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: push-rpc-server-container | ||||
|           image: openim/openim-push:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_KAFKA_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-kafka-secret | ||||
|                   key: kafka-password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10170 | ||||
|             - containerPort: 12170 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										37
									
								
								deployments/deploy/openim-rpc-auth-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								deployments/deploy/openim-rpc-auth-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: auth-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: auth-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: auth-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: auth-rpc-server-container | ||||
|           image: openim/openim-rpc-auth:v3.8.3 | ||||
|           imagePullPolicy: Never | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10200 | ||||
|             - containerPort: 12200 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										46
									
								
								deployments/deploy/openim-rpc-conversation-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								deployments/deploy/openim-rpc-conversation-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: conversation-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: conversation-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: conversation-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: conversation-rpc-server-container | ||||
|           image: openim/openim-rpc-conversation:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10220 | ||||
|             - containerPort: 12220 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										46
									
								
								deployments/deploy/openim-rpc-friend-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								deployments/deploy/openim-rpc-friend-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: friend-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: friend-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: friend-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: friend-rpc-server-container | ||||
|           image: openim/openim-rpc-friend:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10240 | ||||
|             - containerPort: 12240 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										46
									
								
								deployments/deploy/openim-rpc-group-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								deployments/deploy/openim-rpc-group-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,46 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: group-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: group-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: group-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: group-rpc-server-container | ||||
|           image: openim/openim-rpc-group:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10260 | ||||
|             - containerPort: 12260 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										51
									
								
								deployments/deploy/openim-rpc-msg-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								deployments/deploy/openim-rpc-msg-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: msg-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: msg-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: msg-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: msg-rpc-server-container | ||||
|           image: openim/openim-rpc-msg:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|             - name: IMENV_KAFKA_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-kafka-secret | ||||
|                   key: kafka-password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10280 | ||||
|             - containerPort: 12280 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										56
									
								
								deployments/deploy/openim-rpc-third-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								deployments/deploy/openim-rpc-third-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: third-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: third-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: third-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: third-rpc-server-container | ||||
|           image: openim/openim-rpc-third:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_MINIO_ACCESSKEYID | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-minio-secret | ||||
|                   key: minio-root-user | ||||
|             - name: IMENV_MINIO_SECRETACCESSKEY | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-minio-secret | ||||
|                   key: minio-root-password | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10300 | ||||
|             - containerPort: 12300 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										51
									
								
								deployments/deploy/openim-rpc-user-deployment.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								deployments/deploy/openim-rpc-user-deployment.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: Deployment | ||||
| metadata: | ||||
|   name: user-rpc-server | ||||
| spec: | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: user-rpc-server | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: user-rpc-server | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: user-rpc-server-container | ||||
|           image: openim/openim-rpc-user:v3.8.3 | ||||
|           env: | ||||
|             - name: CONFIG_PATH | ||||
|               value: "/config" | ||||
|             - name: IMENV_REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-redis-secret | ||||
|                   key: redis-password | ||||
|             - name: IMENV_MONGODB_USERNAME | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_username | ||||
|             - name: IMENV_MONGODB_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-mongo-secret | ||||
|                   key: mongo_openim_password | ||||
|             - name: IMENV_KAFKA_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: openim-kafka-secret | ||||
|                   key: kafka-password | ||||
|           volumeMounts: | ||||
|             - name: openim-config | ||||
|               mountPath: "/config" | ||||
|               readOnly: true | ||||
|           ports: | ||||
|             - containerPort: 10320 | ||||
|             - containerPort: 12320 | ||||
|       volumes: | ||||
|         - name: openim-config | ||||
|           configMap: | ||||
|             name: openim-config | ||||
							
								
								
									
										7
									
								
								deployments/deploy/redis-secret.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								deployments/deploy/redis-secret.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| apiVersion: v1 | ||||
| kind: Secret | ||||
| metadata: | ||||
|   name: openim-redis-secret | ||||
| type: Opaque | ||||
| data: | ||||
|   redis-password: b3BlbklNMTIz # "openIM123" in base64 | ||||
							
								
								
									
										55
									
								
								deployments/deploy/redis-statefulset.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								deployments/deploy/redis-statefulset.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | ||||
| apiVersion: apps/v1 | ||||
| kind: StatefulSet | ||||
| metadata: | ||||
|   name: redis-statefulset | ||||
| spec: | ||||
|   serviceName: "redis" | ||||
|   replicas: 2 | ||||
|   selector: | ||||
|     matchLabels: | ||||
|       app: redis | ||||
|   template: | ||||
|     metadata: | ||||
|       labels: | ||||
|         app: redis | ||||
|     spec: | ||||
|       containers: | ||||
|         - name: redis | ||||
|           image: redis:7.0.0 | ||||
|           ports: | ||||
|             - containerPort: 6379 | ||||
|           env: | ||||
|             - name: TZ | ||||
|               value: "Asia/Shanghai" | ||||
|             - name: REDIS_PASSWORD | ||||
|               valueFrom: | ||||
|                 secretKeyRef: | ||||
|                   name: redis-secret | ||||
|                   key: redis-password | ||||
|           volumeMounts: | ||||
|             - name: redis-data | ||||
|               mountPath: /data | ||||
|           command: | ||||
|             [ | ||||
|               "/bin/sh", | ||||
|               "-c", | ||||
|               'redis-server  --requirepass "$REDIS_PASSWORD" --appendonly yes', | ||||
|             ] | ||||
|       volumes: | ||||
|         - name: redis-config-volume | ||||
|           configMap: | ||||
|             name: openim-config | ||||
|         - name: redis-data | ||||
|           persistentVolumeClaim: | ||||
|             claimName: redis-pvc | ||||
| --- | ||||
| apiVersion: v1 | ||||
| kind: PersistentVolumeClaim | ||||
| metadata: | ||||
|   name: redis-pvc | ||||
| spec: | ||||
|   accessModes: | ||||
|     - ReadWriteOnce | ||||
|   resources: | ||||
|     requests: | ||||
|       storage: 5Gi | ||||
| @ -240,11 +240,11 @@ push: | ||||
|     channelName: ${GETUI_CHANNEL_NAME} | ||||
|   fcm: | ||||
|     serviceAccount: "${FCM_SERVICE_ACCOUNT}" | ||||
|   jpns: | ||||
|     appKey: ${JPNS_APP_KEY} | ||||
|     masterSecret: ${JPNS_MASTER_SECRET} | ||||
|     pushUrl: ${JPNS_PUSH_URL} | ||||
|     pushIntent: ${JPNS_PUSH_INTENT} | ||||
|   jpush: | ||||
|     appKey: ${JPUSH_APP_KEY} | ||||
|     masterSecret: ${JPUSH_MASTER_SECRET} | ||||
|     pushUrl: ${JPUSH_PUSH_URL} | ||||
|     pushIntent: ${JPUSH_PUSH_INTENT} | ||||
| 
 | ||||
| # App manager configuration | ||||
| # | ||||
|  | ||||
| @ -8,12 +8,36 @@ services: | ||||
|     ports: | ||||
|       - "37017:27017" | ||||
|     container_name: mongo | ||||
|     command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"] | ||||
|     command: > | ||||
|       bash -c ' | ||||
|       docker-entrypoint.sh mongod --wiredTigerCacheSizeGB $$wiredTigerCacheSizeGB --auth & | ||||
|       until mongosh -u $$MONGO_INITDB_ROOT_USERNAME -p $$MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval "db.runCommand({ ping: 1 })" &>/dev/null; do | ||||
|         echo "Waiting for MongoDB to start..." | ||||
|         sleep 1 | ||||
|       done && | ||||
|       mongosh -u $$MONGO_INITDB_ROOT_USERNAME -p $$MONGO_INITDB_ROOT_PASSWORD --authenticationDatabase admin --eval " | ||||
|       db = db.getSiblingDB(\"$$MONGO_INITDB_DATABASE\"); | ||||
|       if (!db.getUser(\"$$MONGO_OPENIM_USERNAME\")) { | ||||
|         db.createUser({ | ||||
|           user: \"$$MONGO_OPENIM_USERNAME\", | ||||
|           pwd: \"$$MONGO_OPENIM_PASSWORD\", | ||||
|           roles: [{role: \"readWrite\", db: \"$$MONGO_INITDB_DATABASE\"}] | ||||
|         }); | ||||
|         print(\"User created successfully: \"); | ||||
|         print(\"Username: $$MONGO_OPENIM_USERNAME\"); | ||||
|         print(\"Password: $$MONGO_OPENIM_PASSWORD\"); | ||||
|         print(\"Database: $$MONGO_INITDB_DATABASE\"); | ||||
|       } else { | ||||
|         print(\"User already exists in database: $$MONGO_INITDB_DATABASE, Username: $$MONGO_OPENIM_USERNAME\"); | ||||
|       } | ||||
|       " && | ||||
|       tail -f /dev/null | ||||
|       ' | ||||
|     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" | ||||
|       - "${MONGO_BACKUP_DIR}:/data/backup" | ||||
|     environment: | ||||
|       - TZ=Asia/Shanghai | ||||
|       - wiredTigerCacheSizeGB=1 | ||||
| @ -39,20 +63,12 @@ services: | ||||
|     restart: always | ||||
|     sysctls: | ||||
|       net.core.somaxconn: 1024 | ||||
|     command: redis-server /usr/local/redis/config/redis.conf --requirepass openIM123 --appendonly yes | ||||
|     networks: | ||||
|       - openim | ||||
| 
 | ||||
|   zookeeper: | ||||
|     image: "${ZOOKEEPER_IMAGE}" | ||||
|     container_name: zookeeper | ||||
|     ports: | ||||
|       - "12181:2181" | ||||
|     environment: | ||||
|       #JVMFLAGS: "-Xms32m -Xmx128m" | ||||
|       TZ: "Asia/Shanghai" | ||||
|       ALLOW_ANONYMOUS_LOGIN: "yes" | ||||
|     restart: always | ||||
|     command: > | ||||
|       redis-server | ||||
|       --requirepass openIM123 | ||||
|       --appendonly yes | ||||
|       --aof-use-rdb-preamble yes | ||||
|       --save "" | ||||
|     networks: | ||||
|       - openim | ||||
| 
 | ||||
| @ -84,24 +100,69 @@ services: | ||||
|     ports: | ||||
|       - "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 & /opt/bitnami/kafka/create-topic.sh; wait" | ||||
|     environment: | ||||
|       #KAFKA_HEAP_OPTS: "-Xms128m -Xmx256m" | ||||
|       TZ: Asia/Shanghai | ||||
|       # Unique identifier for the Kafka node (required in controller mode) | ||||
|       KAFKA_CFG_NODE_ID: 0 | ||||
|       # Defines the roles this Kafka node plays: broker, controller, or both | ||||
|       KAFKA_CFG_PROCESS_ROLES: controller,broker | ||||
|       # Specifies which nodes are controller nodes for quorum voting. | ||||
|       # The syntax follows the KRaft mode (no ZooKeeper): node.id@host:port | ||||
|       # The controller listener endpoint here is kafka:9093 | ||||
|       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 | ||||
|       # Specifies which listener is used for controller-to-controller communication | ||||
|       KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER | ||||
|       # Default number of partitions for new topics | ||||
|       KAFKA_NUM_PARTITIONS: 8 | ||||
|       # Whether to enable automatic topic creation | ||||
|       KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "true" | ||||
|       # Kafka internal listeners; Kafka supports multiple ports with different protocols | ||||
|       # Each port is used for a specific purpose: INTERNAL for internal broker communication, | ||||
|       # CONTROLLER for controller communication, EXTERNAL for external client connections. | ||||
|       # These logical listener names are mapped to actual protocols via KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP | ||||
|       # In short, Kafka is listening on three logical ports: 9092 for internal communication, | ||||
|       # 9093 for controller traffic, and 9094 for external access. | ||||
|       KAFKA_CFG_LISTENERS: "INTERNAL://:9092,CONTROLLER://:9093,EXTERNAL://:9094" | ||||
|       # Addresses advertised to clients. INTERNAL://kafka:9092 uses the internal Docker service name 'kafka', | ||||
|       # so other containers can access Kafka via kafka:9092. | ||||
|       # EXTERNAL://localhost:19094 is the address external clients (e.g., in the LAN) should use to connect. | ||||
|       # If Kafka is deployed on a different machine than IM, 'localhost' should be replaced with the LAN IP. | ||||
|       KAFKA_CFG_ADVERTISED_LISTENERS: "INTERNAL://kafka:9092,EXTERNAL://localhost:19094" | ||||
|       # Maps logical listener names to actual protocols. | ||||
|       # Supported protocols include: PLAINTEXT, SSL, SASL_PLAINTEXT, SASL_SSL | ||||
|       KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,INTERNAL:PLAINTEXT" | ||||
|       # Defines which listener is used for inter-broker communication within the Kafka cluster | ||||
|       KAFKA_CFG_INTER_BROKER_LISTENER_NAME: "INTERNAL" | ||||
| 
 | ||||
| 
 | ||||
|       # Authentication configuration variables - comment out to disable auth | ||||
|       # KAFKA_USERNAME: "openIM" | ||||
|       # KAFKA_PASSWORD: "openIM123" | ||||
|     command: > | ||||
|       /bin/sh -c ' | ||||
|         if [ -n "$${KAFKA_USERNAME}" ] && [ -n "$${KAFKA_PASSWORD}" ]; then | ||||
|           echo "=== Kafka SASL Authentication ENABLED ===" | ||||
|           echo "Username: $${KAFKA_USERNAME}" | ||||
|            | ||||
|           # Set environment variables for SASL authentication | ||||
|           export KAFKA_CFG_LISTENERS="SASL_PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094" | ||||
|           export KAFKA_CFG_ADVERTISED_LISTENERS="SASL_PLAINTEXT://kafka:9092,EXTERNAL://localhost:19094" | ||||
|           export KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP="CONTROLLER:PLAINTEXT,EXTERNAL:SASL_PLAINTEXT,SASL_PLAINTEXT:SASL_PLAINTEXT" | ||||
|           export KAFKA_CFG_SASL_ENABLED_MECHANISMS="PLAIN" | ||||
|           export KAFKA_CFG_SASL_MECHANISM_INTER_BROKER_PROTOCOL="PLAIN" | ||||
|           export KAFKA_CFG_INTER_BROKER_LISTENER_NAME="SASL_PLAINTEXT" | ||||
|           export KAFKA_CLIENT_USERS="$${KAFKA_USERNAME}" | ||||
|           export KAFKA_CLIENT_PASSWORDS="$${KAFKA_PASSWORD}" | ||||
|         fi | ||||
|          | ||||
|         # Start Kafka with the configured environment | ||||
|         exec /opt/bitnami/scripts/kafka/entrypoint.sh /opt/bitnami/scripts/kafka/run.sh | ||||
|       ' | ||||
|     networks: | ||||
|       - openim | ||||
| 
 | ||||
| 
 | ||||
|   minio: | ||||
|     image: "${MINIO_IMAGE}" | ||||
|     ports: | ||||
| @ -138,49 +199,68 @@ services: | ||||
|     networks: | ||||
|       - openim | ||||
|        | ||||
| #  prometheus: | ||||
| #    image: ${PROMETHEUS_IMAGE} | ||||
| #    container_name: prometheus | ||||
| #    restart: always | ||||
| #    volumes: | ||||
| #      - ./config/prometheus.yml:/etc/prometheus/prometheus.yml | ||||
| #      - ./config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml | ||||
| #      - ${DATA_DIR}/components/prometheus/data:/prometheus | ||||
| #    command: | ||||
| #      - '--config.file=/etc/prometheus/prometheus.yml' | ||||
| #      - '--storage.tsdb.path=/prometheus' | ||||
| #    ports: | ||||
| #      - "19091:9090" | ||||
| #    networks: | ||||
| #      - openim | ||||
| # | ||||
| #  alertmanager: | ||||
| #    image: ${ALERTMANAGER_IMAGE} | ||||
| #    container_name: alertmanager | ||||
| #    restart: always | ||||
| #    volumes: | ||||
| #      - ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml | ||||
| #      - ./config/email.tmpl:/etc/alertmanager/email.tmpl | ||||
| #    ports: | ||||
| #      - "19093:9093" | ||||
| #    networks: | ||||
| #      - openim | ||||
| # | ||||
| #  grafana: | ||||
| #    image: ${GRAFANA_IMAGE} | ||||
| #    container_name: grafana | ||||
| #    user: root | ||||
| #    restart: always | ||||
| #    environment: | ||||
| #      - GF_SECURITY_ALLOW_EMBEDDING=true | ||||
| #      - GF_SESSION_COOKIE_SAMESITE=none | ||||
| #      - GF_SESSION_COOKIE_SECURE=true | ||||
| #      - GF_AUTH_ANONYMOUS_ENABLED=true | ||||
| #      - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin | ||||
| #    ports: | ||||
| #      - "13000:3000" | ||||
| #    volumes: | ||||
| #      - ${DATA_DIR:-./}/components/grafana:/var/lib/grafana | ||||
| #    networks: | ||||
| #      - openim | ||||
|   prometheus: | ||||
|     image: ${PROMETHEUS_IMAGE} | ||||
|     container_name: prometheus | ||||
|     restart: always | ||||
|     user: root | ||||
|     profiles: | ||||
|       - m | ||||
|     volumes: | ||||
|       - ./config/prometheus.yml:/etc/prometheus/prometheus.yml | ||||
|       - ./config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml | ||||
|       - ${DATA_DIR}/components/prometheus/data:/prometheus | ||||
|     command: | ||||
|       - '--config.file=/etc/prometheus/prometheus.yml' | ||||
|       - '--storage.tsdb.path=/prometheus' | ||||
|       - '--web.listen-address=:${PROMETHEUS_PORT}' | ||||
|     network_mode: host | ||||
| 
 | ||||
|   alertmanager: | ||||
|     image: ${ALERTMANAGER_IMAGE} | ||||
|     container_name: alertmanager | ||||
|     restart: always | ||||
|     profiles: | ||||
|       - m | ||||
|     volumes: | ||||
|       - ./config/alertmanager.yml:/etc/alertmanager/alertmanager.yml | ||||
|       - ./config/email.tmpl:/etc/alertmanager/email.tmpl | ||||
|     command: | ||||
|       - '--config.file=/etc/alertmanager/alertmanager.yml' | ||||
|       - '--web.listen-address=:${ALERTMANAGER_PORT}' | ||||
|     network_mode: host | ||||
| 
 | ||||
|   grafana: | ||||
|     image: ${GRAFANA_IMAGE} | ||||
|     container_name: grafana | ||||
|     user: root | ||||
|     restart: always | ||||
|     profiles: | ||||
|       - m | ||||
|     environment: | ||||
|       - GF_SECURITY_ALLOW_EMBEDDING=true | ||||
|       - GF_SESSION_COOKIE_SAMESITE=none | ||||
|       - GF_SESSION_COOKIE_SECURE=true | ||||
|       - GF_AUTH_ANONYMOUS_ENABLED=true | ||||
|       - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin | ||||
|       - GF_SERVER_HTTP_PORT=${GRAFANA_PORT} | ||||
|     volumes: | ||||
|       - ${DATA_DIR:-./}/components/grafana:/var/lib/grafana | ||||
|     network_mode: host | ||||
| 
 | ||||
|   node-exporter: | ||||
|     image: ${NODE_EXPORTER_IMAGE} | ||||
|     container_name: node-exporter | ||||
|     restart: always | ||||
|     profiles: | ||||
|       - m | ||||
|     volumes: | ||||
|       - /proc:/host/proc:ro | ||||
|       - /sys:/host/sys:ro | ||||
|       - /:/rootfs:ro | ||||
|     command: | ||||
|       - '--path.procfs=/host/proc' | ||||
|       - '--path.sysfs=/host/sys' | ||||
|       - '--path.rootfs=/rootfs' | ||||
|       - '--web.listen-address=:${NODE_EXPORTER_PORT}' | ||||
|     network_mode: host | ||||
|  | ||||
| @ -474,10 +474,10 @@ This section involves setting up additional configuration variables for Websocke | ||||
| | GETUI_CHANNEL_ID        | [User Defined]    | GeTui Channel ID                 | | ||||
| | GETUI_CHANNEL_NAME      | [User Defined]    | GeTui Channel Name               | | ||||
| | FCM_SERVICE_ACCOUNT     | "x.json"          | FCM Service Account              | | ||||
| | JPNS_APP_KEY            | [User Defined]    | JPNS Application Key             | | ||||
| | JPNS_MASTER_SECRET      | [User Defined]    | JPNS Master Secret               | | ||||
| | JPNS_PUSH_URL           | [User Defined]    | JPNS Push Notification URL       | | ||||
| | JPNS_PUSH_INTENT        | [User Defined]    | JPNS Push Intent                 | | ||||
| | JPUSH_APP_KEY            | [User Defined]    | JPUSH Application Key             | | ||||
| | JPUSH_MASTER_SECRET      | [User Defined]    | JPUSH Master Secret               | | ||||
| | JPUSH_PUSH_URL           | [User Defined]    | JPUSH Push Notification URL       | | ||||
| | JPUSH_PUSH_INTENT        | [User Defined]    | JPUSH Push Intent                 | | ||||
| | IM_ADMIN_USERID         | "imAdmin"         | IM Administrator ID              | | ||||
| | IM_ADMIN_NAME           | "imAdmin"         | IM Administrator Nickname        | | ||||
| | MULTILOGIN_POLICY       | "1"               | Multi-login Policy               | | ||||
|  | ||||
							
								
								
									
										100
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								go.mod
									
									
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| module github.com/openimsdk/open-im-server/v3 | ||||
| 
 | ||||
| go 1.21.2 | ||||
| go 1.22.7 | ||||
| 
 | ||||
| require ( | ||||
| 	firebase.google.com/go/v4 v4.14.1 | ||||
| @ -8,19 +8,19 @@ require ( | ||||
| 	github.com/gin-gonic/gin v1.9.1 | ||||
| 	github.com/go-playground/validator/v10 v10.20.0 | ||||
| 	github.com/gogo/protobuf v1.3.2 // indirect | ||||
| 	github.com/golang-jwt/jwt/v4 v4.5.0 | ||||
| 	github.com/golang-jwt/jwt/v4 v4.5.1 | ||||
| 	github.com/gorilla/websocket v1.5.1 | ||||
| 	github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 | ||||
| 	github.com/openimsdk/protocol v0.0.72 | ||||
| 	github.com/openimsdk/tools v0.0.50-alpha.16 | ||||
| 	github.com/openimsdk/protocol v0.0.73-alpha.12 | ||||
| 	github.com/openimsdk/tools v0.0.50-alpha.106 | ||||
| 	github.com/pkg/errors v0.9.1 // indirect | ||||
| 	github.com/prometheus/client_golang v1.18.0 | ||||
| 	github.com/stretchr/testify v1.9.0 | ||||
| 	github.com/stretchr/testify v1.10.0 | ||||
| 	go.mongodb.org/mongo-driver v1.14.0 | ||||
| 	google.golang.org/api v0.170.0 | ||||
| 	google.golang.org/grpc v1.66.2 | ||||
| 	google.golang.org/protobuf v1.34.2 | ||||
| 	google.golang.org/grpc v1.71.0 | ||||
| 	google.golang.org/protobuf v1.36.4 | ||||
| 	gopkg.in/yaml.v3 v3.0.1 | ||||
| ) | ||||
| 
 | ||||
| @ -35,44 +35,42 @@ require ( | ||||
| 	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.14-alpha.5 | ||||
| 	github.com/openimsdk/gomake v0.0.15-alpha.5 | ||||
| 	github.com/redis/go-redis/v9 v9.4.0 | ||||
| 	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 | ||||
| 	go.uber.org/automaxprocs v1.5.3 | ||||
| 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 | ||||
| 	golang.org/x/sync v0.8.0 | ||||
| 	golang.org/x/sync v0.10.0 | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| 	cloud.google.com/go v0.112.1 // indirect | ||||
| 	cloud.google.com/go/compute/metadata v0.3.0 // indirect | ||||
| 	cloud.google.com/go/compute/metadata v0.6.0 // indirect | ||||
| 	cloud.google.com/go/firestore v1.15.0 // indirect | ||||
| 	cloud.google.com/go/iam v1.1.7 // indirect | ||||
| 	cloud.google.com/go/longrunning v0.5.5 // indirect | ||||
| 	cloud.google.com/go/storage v1.40.0 // indirect | ||||
| 	github.com/MicahParks/keyfunc v1.9.0 // indirect | ||||
| 	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2 v1.23.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2 v1.32.5 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/config v1.25.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/credentials v1.16.3 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/config v1.28.5 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.4 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/s3 v1.43.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/sso v1.17.3 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/sts v1.25.4 // indirect | ||||
| 	github.com/aws/smithy-go v1.17.0 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect | ||||
| 	github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect | ||||
| 	github.com/aws/smithy-go v1.22.1 // indirect | ||||
| 	github.com/beorn7/perks v1.0.1 // indirect | ||||
| 	github.com/bytedance/sonic v1.11.6 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.1.1 // indirect | ||||
| @ -88,19 +86,27 @@ require ( | ||||
| 	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/emicklei/go-restful/v3 v3.11.0 // indirect | ||||
| 	github.com/felixge/httpsnoop v1.0.4 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.7.0 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.9.0 // indirect | ||||
| 	github.com/fxamacker/cbor/v2 v2.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/logr v1.4.2 // indirect | ||||
| 	github.com/go-logr/stdr v1.2.2 // indirect | ||||
| 	github.com/go-ole/go-ole v1.2.6 // indirect | ||||
| 	github.com/go-openapi/jsonpointer v0.19.6 // indirect | ||||
| 	github.com/go-openapi/jsonreference v0.20.2 // indirect | ||||
| 	github.com/go-openapi/swag v0.22.4 // 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.4 // indirect | ||||
| 	github.com/golang/snappy v0.0.4 // indirect | ||||
| 	github.com/google/gnostic-models v0.6.8 // indirect | ||||
| 	github.com/google/go-cmp v0.6.0 // indirect | ||||
| 	github.com/google/go-querystring v1.1.0 // indirect | ||||
| 	github.com/google/gofuzz v1.2.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.3 // indirect | ||||
| @ -117,6 +123,7 @@ require ( | ||||
| 	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/josharian/intern v1.0.0 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/kelindar/simd v1.1.2 // indirect | ||||
| 	github.com/klauspost/compress v1.17.7 // indirect | ||||
| @ -126,6 +133,7 @@ require ( | ||||
| 	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/mailru/easyjson v0.7.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 | ||||
| @ -135,6 +143,7 @@ require ( | ||||
| 	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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect | ||||
| 	github.com/pierrec/lz4/v4 v4.1.21 // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect | ||||
| @ -146,6 +155,7 @@ require ( | ||||
| 	github.com/rs/xid v1.5.0 // indirect | ||||
| 	github.com/sagikazarmark/locafero v0.4.0 // indirect | ||||
| 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect | ||||
| 	github.com/sercand/kuberesolver/v6 v6.0.1 // 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 | ||||
| @ -156,6 +166,7 @@ require ( | ||||
| 	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/x448/float16 v0.8.4 // 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 | ||||
| @ -165,26 +176,39 @@ require ( | ||||
| 	go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect | ||||
| 	go.etcd.io/etcd/client/v3 v3.5.13 // indirect | ||||
| 	go.opencensus.io v0.24.0 // indirect | ||||
| 	go.opentelemetry.io/auto/sdk v1.1.0 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect | ||||
| 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect | ||||
| 	go.opentelemetry.io/otel v1.24.0 // indirect | ||||
| 	go.opentelemetry.io/otel/metric v1.24.0 // indirect | ||||
| 	go.opentelemetry.io/otel/trace v1.24.0 // indirect | ||||
| 	go.opentelemetry.io/otel v1.34.0 // indirect | ||||
| 	go.opentelemetry.io/otel/metric v1.34.0 // indirect | ||||
| 	go.opentelemetry.io/otel/trace v1.34.0 // indirect | ||||
| 	go.uber.org/atomic v1.9.0 // indirect | ||||
| 	go.uber.org/multierr v1.11.0 // indirect | ||||
| 	golang.org/x/arch v0.7.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.29.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.21.0 // indirect | ||||
| 	golang.org/x/sys v0.25.0 // indirect | ||||
| 	golang.org/x/text v0.18.0 // indirect | ||||
| 	golang.org/x/net v0.34.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.25.0 // indirect | ||||
| 	golang.org/x/sys v0.29.0 // indirect | ||||
| 	golang.org/x/term v0.28.0 // indirect | ||||
| 	golang.org/x/text v0.21.0 // indirect | ||||
| 	golang.org/x/time v0.5.0 // indirect | ||||
| 	google.golang.org/appengine/v2 v2.0.2 // indirect | ||||
| 	google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect | ||||
| 	google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 // indirect | ||||
| 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect | ||||
| 	google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect | ||||
| 	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect | ||||
| 	gopkg.in/inf.v0 v0.9.1 // indirect | ||||
| 	gopkg.in/yaml.v2 v2.4.0 // indirect | ||||
| 	gorm.io/gorm v1.25.8 // indirect | ||||
| 	stathat.com/c/consistent v1.0.0 // indirect | ||||
| 	k8s.io/api v0.31.2 // indirect | ||||
| 	k8s.io/apimachinery v0.31.2 // indirect | ||||
| 	k8s.io/client-go v0.31.2 // indirect | ||||
| 	k8s.io/klog/v2 v2.130.1 // indirect | ||||
| 	k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect | ||||
| 	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect | ||||
| 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect | ||||
| 	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect | ||||
| 	sigs.k8s.io/yaml v1.4.0 // indirect | ||||
| ) | ||||
| 
 | ||||
| require ( | ||||
| @ -194,6 +218,6 @@ require ( | ||||
| 	github.com/spf13/cobra v1.8.0 | ||||
| 	github.com/ugorji/go/codec v1.2.12 // indirect | ||||
| 	go.uber.org/zap v1.24.0 // indirect | ||||
| 	golang.org/x/crypto v0.27.0 // indirect | ||||
| 	golang.org/x/crypto v0.32.0 // indirect | ||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||
| ) | ||||
|  | ||||
							
								
								
									
										212
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										212
									
								
								go.sum
									
									
									
									
									
								
							| @ -1,8 +1,8 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM= | ||||
| cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZLo4= | ||||
| cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= | ||||
| cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= | ||||
| cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= | ||||
| cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= | ||||
| cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBpbFF8= | ||||
| cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= | ||||
| cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM= | ||||
| @ -21,42 +21,42 @@ github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x9 | ||||
| github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= | ||||
| 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/aws/aws-sdk-go-v2 v1.23.1 h1:qXaFsOOMA+HsZtX8WoCa+gJnbyW7qyFFBlPqvTSzbaI= | ||||
| github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= | ||||
| github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= | ||||
| github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= | ||||
| github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1 h1:ZY3108YtBNq96jNZTICHxN1gSBSbnvIdYwwqnvCV4Mc= | ||||
| github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.1/go.mod h1:t8PYl/6LzdAqsU4/9tz28V/kU+asFePvpOMkdul0gEQ= | ||||
| github.com/aws/aws-sdk-go-v2/config v1.25.4 h1:r+X1x8QI6FEPdJDWCNBDZHyAcyFwSjHN8q8uuus+Axs= | ||||
| github.com/aws/aws-sdk-go-v2/config v1.25.4/go.mod h1:8GTjImECskr7D88P/Nn9uM4M4rLY9i77hLJZgkZEWV8= | ||||
| github.com/aws/aws-sdk-go-v2/credentials v1.16.3 h1:8PeI2krzzjDJ5etmgaMiD1JswsrLrWvKKu/uBUtNy1g= | ||||
| github.com/aws/aws-sdk-go-v2/credentials v1.16.3/go.mod h1:Kdh/okh+//vQ/AjEt81CjvkTo64+/zIE4OewP7RpfXk= | ||||
| github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5 h1:KehRNiVzIfAcj6gw98zotVbb/K67taJE0fkfgM6vzqU= | ||||
| github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5/go.mod h1:VhnExhw6uXy9QzetvpXDolo1/hjhx4u9qukBGkuUwjs= | ||||
| github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 h1:LAm3Ycm9HJfbSCd5I+wqC2S9Ej7FPrgr5CQoOljJZcE= | ||||
| github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0= | ||||
| github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 h1:4GV0kKZzUxiWxSVpn/9gwR0g21NF1Jsyduzo9rHgC/Q= | ||||
| github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU= | ||||
| github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= | ||||
| github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= | ||||
| github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= | ||||
| github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= | ||||
| github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= | ||||
| github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= | ||||
| github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= | ||||
| github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= | ||||
| github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= | ||||
| github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= | ||||
| github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= | ||||
| github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= | ||||
| github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= | ||||
| github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= | ||||
| github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.4 h1:40Q4X5ebZruRtknEZH/bg91sT5pR853F7/1X9QRbI54= | ||||
| github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.4/go.mod h1:u77N7eEECzUv7F0xl2gcfK/vzc8wcjWobpy+DcrLJ5E= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 h1:rpkF4n0CyFcrJUG/rNNohoTmhtWlFTRI4BsZOh9PvLs= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1/go.mod h1:l9ymW25HOqymeU2m1gbUQ3rUIsTwKs8gYHXkqDQUhiI= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.4 h1:6DRKQc+9cChgzL5gplRGusI5dBGeiEod4m/pmGbcX48= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.4/go.mod h1:s8ORvrW4g4v7IvYKIAoBg17w3GQ+XuwXDXYrQ5SkzU0= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4 h1:rdovz3rEu0vZKbzoMYPTehp0E8veoE9AyfzqCr5Eeao= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4/go.mod h1:aYCGNjyUCUelhofxlZyj63srdxWUSsBSGg5l6MCuXuE= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.4 h1:o3DcfCxGDIT20pTbVKVhp3vWXOj/VvgazNJvumWeYW0= | ||||
| github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.4/go.mod h1:Uy0KVOxuTK2ne+/PKQ+VvEeWmjMMksE17k/2RK/r5oM= | ||||
| github.com/aws/aws-sdk-go-v2/service/s3 v1.43.1 h1:1w11lfXOa8HoHoSlNtt4mqv/N3HmDOa+OnUH3Y9DHm8= | ||||
| github.com/aws/aws-sdk-go-v2/service/s3 v1.43.1/go.mod h1:dqJ5JBL0clzgHriH35Amx3LRFY6wNIPUX7QO/BerSBo= | ||||
| github.com/aws/aws-sdk-go-v2/service/sso v1.17.3 h1:CdsSOGlFF3Pn+koXOIpTtvX7st0IuGsZ8kJqcWMlX54= | ||||
| github.com/aws/aws-sdk-go-v2/service/sso v1.17.3/go.mod h1:oA6VjNsLll2eVuUoF2D+CMyORgNzPEW/3PyUdq6WQjI= | ||||
| github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1 h1:cbRqFTVnJV+KRpwFl76GJdIZJKKCdTPnjUZ7uWh3pIU= | ||||
| github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1/go.mod h1:hHL974p5auvXlZPIjJTblXJpbkfK4klBczlsEaMCGVY= | ||||
| github.com/aws/aws-sdk-go-v2/service/sts v1.25.4 h1:yEvZ4neOQ/KpUqyR+X0ycUTW/kVRNR4nDZ38wStHGAA= | ||||
| github.com/aws/aws-sdk-go-v2/service/sts v1.25.4/go.mod h1:feTnm2Tk/pJxdX+eooEsxvlvTWBvDm6CasRZ+JOs2IY= | ||||
| github.com/aws/smithy-go v1.17.0 h1:wWJD7LX6PBV6etBUwO0zElG0nWN9rUhp0WdYeHSHAaI= | ||||
| github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= | ||||
| github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= | ||||
| github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= | ||||
| github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= | ||||
| github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= | ||||
| github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= | ||||
| github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= | ||||
| github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= | ||||
| github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= | ||||
| 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= | ||||
| @ -103,6 +103,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4A | ||||
| 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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= | ||||
| github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= | ||||
| 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= | ||||
| @ -115,8 +117,10 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 | ||||
| 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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= | ||||
| github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= | ||||
| github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= | ||||
| github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= | ||||
| github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= | ||||
| github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= | ||||
| 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/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE= | ||||
| @ -126,12 +130,19 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm | ||||
| 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/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/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= | ||||
| github.com/go-logr/logr v1.4.2/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-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= | ||||
| github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= | ||||
| github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= | ||||
| github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= | ||||
| github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= | ||||
| github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= | ||||
| github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= | ||||
| github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||||
| 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= | ||||
| @ -150,6 +161,8 @@ github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGK | ||||
| github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= | ||||
| github.com/go-redis/redismock/v9 v9.2.0 h1:ZrMYQeKPECZPjOj5u9eyOjg8Nnb0BS9lkVIZ6IpsKLw= | ||||
| github.com/go-redis/redismock/v9 v9.2.0/go.mod h1:18KHfGDK4Y6c2R0H38EUGWAdc7ZQS9gfYxc94k7rWT0= | ||||
| github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= | ||||
| github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= | ||||
| github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= | ||||
| github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= | ||||
| github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= | ||||
| @ -158,8 +171,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x | ||||
| github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= | ||||
| github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= | ||||
| github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= | ||||
| github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= | ||||
| github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= | ||||
| github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo= | ||||
| github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= | ||||
| @ -179,6 +192,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek | ||||
| github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= | ||||
| github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= | ||||
| github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= | ||||
| github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= | ||||
| github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| @ -186,14 +201,19 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= | ||||
| github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= | ||||
| 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/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | ||||
| github.com/google/gofuzz v1.2.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-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= | ||||
| github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= | ||||
| github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= | ||||
| github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= | ||||
| github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| @ -244,6 +264,8 @@ 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/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= | ||||
| github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= | ||||
| 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/kelindar/bitmap v1.5.2 h1:XwX7CTvJtetQZ64zrOkApoZZHBJRkjE23NfqUALA/HE= | ||||
| @ -285,6 +307,8 @@ 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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= | ||||
| github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= | ||||
| 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= | ||||
| @ -311,18 +335,22 @@ 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= | ||||
| github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= | ||||
| github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= | ||||
| github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= | ||||
| github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= | ||||
| github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= | ||||
| github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= | ||||
| github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= | ||||
| github.com/onsi/gomega v1.25.0 h1:Vw7br2PCDYijJHSfBOWhov+8cAnUf8MfMaIOV323l6Y= | ||||
| github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= | ||||
| github.com/openimsdk/gomake v0.0.14-alpha.5 h1:VY9c5x515lTfmdhhPjMvR3BBRrRquAUCFsz7t7vbv7Y= | ||||
| github.com/openimsdk/gomake v0.0.14-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= | ||||
| github.com/openimsdk/protocol v0.0.72 h1:K+vslwaR7lDXyBzb07UuEQITaqsgighz7NyXVIWsu6A= | ||||
| github.com/openimsdk/protocol v0.0.72/go.mod h1:OZQA9FR55lseYoN2Ql1XAHYKHJGu7OMNkUbuekrKCM8= | ||||
| github.com/openimsdk/tools v0.0.50-alpha.16 h1:bC1AQvJMuOHtZm8LZRvN8L5mH1Ws2VYdL+TLTs1iGSc= | ||||
| github.com/openimsdk/tools v0.0.50-alpha.16/go.mod h1:h1cYmfyaVtgFbKmb1Cfsl8XwUOMTt8ubVUQrdGtsUh4= | ||||
| github.com/openimsdk/gomake v0.0.15-alpha.5 h1:eEZCEHm+NsmcO3onXZPIUbGFCYPYbsX5beV3ZyOsGhY= | ||||
| github.com/openimsdk/gomake v0.0.15-alpha.5/go.mod h1:PndCozNc2IsQIciyn9mvEblYWZwJmAI+06z94EY+csI= | ||||
| github.com/openimsdk/protocol v0.0.73-alpha.12 h1:2NYawXeHChYUeSme6QJ9pOLh+Empce2WmwEtbP4JvKk= | ||||
| github.com/openimsdk/protocol v0.0.73-alpha.12/go.mod h1:WF7EuE55vQvpyUAzDXcqg+B+446xQyEba0X35lTINmw= | ||||
| github.com/openimsdk/tools v0.0.50-alpha.106 h1:gaqU08IbRxOdL16ZEQNyYLhCTf7K1HNkrik8KZLA+BM= | ||||
| github.com/openimsdk/tools v0.0.50-alpha.106/go.mod h1:x9i/e+WJFW4tocy6RNJQ9NofQiP3KJ1Y576/06TqOG4= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= | ||||
| github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= | ||||
| @ -356,8 +384,8 @@ 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.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= | ||||
| github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= | ||||
| 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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= | ||||
| github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= | ||||
| 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= | ||||
| @ -365,6 +393,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke | ||||
| 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/sercand/kuberesolver/v6 v6.0.1 h1:XZUTA0gy/lgDYp/UhEwv7Js24F1j8NJ833QrWv0Xux4= | ||||
| github.com/sercand/kuberesolver/v6 v6.0.1/go.mod h1:C0tsTuRMONSY+Xf7pv7RMW1/JlewY1+wS8SZE+1lf1s= | ||||
| 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= | ||||
| @ -379,8 +409,6 @@ 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/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| @ -394,8 +422,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ | ||||
| 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.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/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||||
| github.com/stretchr/testify v1.10.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= | ||||
| @ -410,6 +439,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||
| github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | ||||
| github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | ||||
| github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= | ||||
| github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= | ||||
| 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= | ||||
| @ -433,24 +464,28 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd | ||||
| go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= | ||||
| go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= | ||||
| go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= | ||||
| go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= | ||||
| go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= | ||||
| go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg= | ||||
| go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0= | ||||
| go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= | ||||
| go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= | ||||
| go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= | ||||
| go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo= | ||||
| go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI= | ||||
| go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco= | ||||
| go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw= | ||||
| go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc= | ||||
| go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI= | ||||
| go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU= | ||||
| go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= | ||||
| go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= | ||||
| go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= | ||||
| go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= | ||||
| go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= | ||||
| go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= | ||||
| go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= | ||||
| go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= | ||||
| go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= | ||||
| go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= | ||||
| 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/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= | ||||
| go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= | ||||
| 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= | ||||
| @ -465,8 +500,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= | ||||
| golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= | ||||
| golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= | ||||
| golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= | ||||
| golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||||
| golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | ||||
| 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= | ||||
| @ -494,19 +529,19 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug | ||||
| golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= | ||||
| 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= | ||||
| golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= | ||||
| golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||||
| golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= | ||||
| golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | ||||
| golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= | ||||
| golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= | ||||
| golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= | ||||
| golang.org/x/sync v0.10.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @ -521,12 +556,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc | ||||
| golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= | ||||
| golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | ||||
| golang.org/x/sys v0.29.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.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= | ||||
| golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= | ||||
| golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= | ||||
| 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= | ||||
| @ -535,8 +572,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= | ||||
| golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= | ||||
| golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||||
| golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= | ||||
| golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= | ||||
| golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= | ||||
| golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| @ -548,6 +585,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn | ||||
| golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
| golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= | ||||
| golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| @ -565,17 +604,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-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y= | ||||
| google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117 h1:+rdxYoE3E5htTEWIe15GlN6IfvbURM//Jt0mmkmm6ZU= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20240604185151-ef581f913117/go.mod h1:OimBR/bc1wPO9iV4NC2bpyjy3VnAwZh5EBPQdtaE5oo= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= | ||||
| google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50= | ||||
| 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.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= | ||||
| google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= | ||||
| google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= | ||||
| google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= | ||||
| 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= | ||||
| @ -585,18 +624,21 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= | ||||
| google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= | ||||
| google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= | ||||
| google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | ||||
| 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= | ||||
| gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= | ||||
| 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/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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||
| 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= | ||||
| @ -607,7 +649,23 @@ 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= | ||||
| k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0= | ||||
| k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk= | ||||
| k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw= | ||||
| k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= | ||||
| k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc= | ||||
| k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs= | ||||
| k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= | ||||
| k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= | ||||
| k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= | ||||
| k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= | ||||
| k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= | ||||
| k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= | ||||
| nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= | ||||
| rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= | ||||
| stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c= | ||||
| stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0= | ||||
| sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= | ||||
| sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= | ||||
| sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= | ||||
| sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= | ||||
| sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= | ||||
| sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= | ||||
|  | ||||
| @ -16,29 +16,30 @@ package api | ||||
| 
 | ||||
| import ( | ||||
| 	"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 | ||||
| type AuthApi struct { | ||||
| 	Client auth.AuthClient | ||||
| } | ||||
| 
 | ||||
| func NewAuthApi(client rpcclient.Auth) AuthApi { | ||||
| 	return AuthApi(client) | ||||
| func NewAuthApi(client auth.AuthClient) AuthApi { | ||||
| 	return AuthApi{client} | ||||
| } | ||||
| 
 | ||||
| func (o *AuthApi) GetAdminToken(c *gin.Context) { | ||||
| 	a2r.Call(auth.AuthClient.GetAdminToken, o.Client, c) | ||||
| 	a2r.Call(c, auth.AuthClient.GetAdminToken, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *AuthApi) GetUserToken(c *gin.Context) { | ||||
| 	a2r.Call(auth.AuthClient.GetUserToken, o.Client, c) | ||||
| 	a2r.Call(c, auth.AuthClient.GetUserToken, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *AuthApi) ParseToken(c *gin.Context) { | ||||
| 	a2r.Call(auth.AuthClient.ParseToken, o.Client, c) | ||||
| 	a2r.Call(c, auth.AuthClient.ParseToken, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *AuthApi) ForceLogout(c *gin.Context) { | ||||
| 	a2r.Call(auth.AuthClient.ForceLogout, o.Client, c) | ||||
| 	a2r.Call(c, auth.AuthClient.ForceLogout, o.Client) | ||||
| } | ||||
|  | ||||
							
								
								
									
										313
									
								
								internal/api/config_manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								internal/api/config_manager.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,313 @@ | ||||
| package api | ||||
| 
 | ||||
| // | ||||
| //import ( | ||||
| //	"encoding/json" | ||||
| //	"reflect" | ||||
| //	"strconv" | ||||
| //	"time" | ||||
| // | ||||
| //	"github.com/gin-gonic/gin" | ||||
| //	"github.com/openimsdk/open-im-server/v3/pkg/apistruct" | ||||
| //	"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/discovery/etcd" | ||||
| //	"github.com/openimsdk/open-im-server/v3/version" | ||||
| //	"github.com/openimsdk/tools/apiresp" | ||||
| //	"github.com/openimsdk/tools/errs" | ||||
| //	"github.com/openimsdk/tools/log" | ||||
| //	"github.com/openimsdk/tools/utils/runtimeenv" | ||||
| //	clientv3 "go.etcd.io/etcd/client/v3" | ||||
| //) | ||||
| // | ||||
| //const ( | ||||
| //	// wait for Restart http call return | ||||
| //	waitHttp = time.Millisecond * 200 | ||||
| //) | ||||
| // | ||||
| //type ConfigManager struct { | ||||
| //	imAdminUserID []string | ||||
| //	config        *config.AllConfig | ||||
| //	client        *clientv3.Client | ||||
| // | ||||
| //	configPath string | ||||
| //	runtimeEnv string | ||||
| //} | ||||
| // | ||||
| //func NewConfigManager(IMAdminUserID []string, cfg *config.AllConfig, client *clientv3.Client, configPath string, runtimeEnv string) *ConfigManager { | ||||
| //	cm := &ConfigManager{ | ||||
| //		imAdminUserID: IMAdminUserID, | ||||
| //		config:        cfg, | ||||
| //		client:        client, | ||||
| //		configPath:    configPath, | ||||
| //		runtimeEnv:    runtimeEnv, | ||||
| //	} | ||||
| //	return cm | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) CheckAdmin(c *gin.Context) { | ||||
| //	if err := authverify.CheckAdmin(c, cm.imAdminUserID); err != nil { | ||||
| //		apiresp.GinError(c, err) | ||||
| //		c.Abort() | ||||
| //	} | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) GetConfig(c *gin.Context) { | ||||
| //	var req apistruct.GetConfigReq | ||||
| //	if err := c.BindJSON(&req); err != nil { | ||||
| //		apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	conf := cm.config.Name2Config(req.ConfigName) | ||||
| //	if conf == nil { | ||||
| //		apiresp.GinError(c, errs.ErrArgs.WithDetail("config name not found").Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	b, err := json.Marshal(conf) | ||||
| //	if err != nil { | ||||
| //		apiresp.GinError(c, err) | ||||
| //		return | ||||
| //	} | ||||
| //	apiresp.GinSuccess(c, string(b)) | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) GetConfigList(c *gin.Context) { | ||||
| //	var resp apistruct.GetConfigListResp | ||||
| //	resp.ConfigNames = cm.config.GetConfigNames() | ||||
| //	resp.Environment = runtimeenv.PrintRuntimeEnvironment() | ||||
| //	resp.Version = version.Version | ||||
| // | ||||
| //	apiresp.GinSuccess(c, resp) | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) SetConfig(c *gin.Context) { | ||||
| //	if cm.config.Discovery.Enable != config.ETCD { | ||||
| //		apiresp.GinError(c, errs.New("only etcd support set config").Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	var req apistruct.SetConfigReq | ||||
| //	if err := c.BindJSON(&req); err != nil { | ||||
| //		apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	var err error | ||||
| //	switch req.ConfigName { | ||||
| //	case cm.config.Discovery.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Discovery](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Kafka.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Kafka](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.LocalCache.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.LocalCache](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Log.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Log](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Minio.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Minio](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Mongo.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Mongo](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Notification.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Notification](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.API.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.API](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.CronTask.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.CronTask](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.MsgGateway.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.MsgGateway](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.MsgTransfer.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.MsgTransfer](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Push.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Push](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Auth.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Auth](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Conversation.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Conversation](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Friend.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Friend](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Group.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Group](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Msg.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Msg](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Third.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Third](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.User.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.User](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Redis.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Redis](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Share.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Share](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	case cm.config.Webhooks.GetConfigFileName(): | ||||
| //		err = compareAndSave[config.Webhooks](c, cm.config.Name2Config(req.ConfigName), &req, cm) | ||||
| //	default: | ||||
| //		apiresp.GinError(c, errs.ErrArgs.Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	if err != nil { | ||||
| //		apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	apiresp.GinSuccess(c, nil) | ||||
| //} | ||||
| // | ||||
| //func compareAndSave[T any](c *gin.Context, old any, req *apistruct.SetConfigReq, cm *ConfigManager) error { | ||||
| //	conf := new(T) | ||||
| //	err := json.Unmarshal([]byte(req.Data), &conf) | ||||
| //	if err != nil { | ||||
| //		return errs.ErrArgs.WithDetail(err.Error()).Wrap() | ||||
| //	} | ||||
| //	eq := reflect.DeepEqual(old, conf) | ||||
| //	if eq { | ||||
| //		return nil | ||||
| //	} | ||||
| //	data, err := json.Marshal(conf) | ||||
| //	if err != nil { | ||||
| //		return errs.ErrArgs.WithDetail(err.Error()).Wrap() | ||||
| //	} | ||||
| //	_, err = cm.client.Put(c, etcd.BuildKey(req.ConfigName), string(data)) | ||||
| //	if err != nil { | ||||
| //		return errs.WrapMsg(err, "save to etcd failed") | ||||
| //	} | ||||
| //	return nil | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) ResetConfig(c *gin.Context) { | ||||
| //	go func() { | ||||
| //		if err := cm.resetConfig(c, true); err != nil { | ||||
| //			log.ZError(c, "reset config err", err) | ||||
| //		} | ||||
| //	}() | ||||
| //	apiresp.GinSuccess(c, nil) | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) resetConfig(c *gin.Context, checkChange bool, ops ...clientv3.Op) error { | ||||
| //	txn := cm.client.Txn(c) | ||||
| //	type initConf struct { | ||||
| //		old any | ||||
| //		new any | ||||
| //	} | ||||
| //	configMap := map[string]*initConf{ | ||||
| //		cm.config.Discovery.GetConfigFileName():    {old: &cm.config.Discovery, new: new(config.Discovery)}, | ||||
| //		cm.config.Kafka.GetConfigFileName():        {old: &cm.config.Kafka, new: new(config.Kafka)}, | ||||
| //		cm.config.LocalCache.GetConfigFileName():   {old: &cm.config.LocalCache, new: new(config.LocalCache)}, | ||||
| //		cm.config.Log.GetConfigFileName():          {old: &cm.config.Log, new: new(config.Log)}, | ||||
| //		cm.config.Minio.GetConfigFileName():        {old: &cm.config.Minio, new: new(config.Minio)}, | ||||
| //		cm.config.Mongo.GetConfigFileName():        {old: &cm.config.Mongo, new: new(config.Mongo)}, | ||||
| //		cm.config.Notification.GetConfigFileName(): {old: &cm.config.Notification, new: new(config.Notification)}, | ||||
| //		cm.config.API.GetConfigFileName():          {old: &cm.config.API, new: new(config.API)}, | ||||
| //		cm.config.CronTask.GetConfigFileName():     {old: &cm.config.CronTask, new: new(config.CronTask)}, | ||||
| //		cm.config.MsgGateway.GetConfigFileName():   {old: &cm.config.MsgGateway, new: new(config.MsgGateway)}, | ||||
| //		cm.config.MsgTransfer.GetConfigFileName():  {old: &cm.config.MsgTransfer, new: new(config.MsgTransfer)}, | ||||
| //		cm.config.Push.GetConfigFileName():         {old: &cm.config.Push, new: new(config.Push)}, | ||||
| //		cm.config.Auth.GetConfigFileName():         {old: &cm.config.Auth, new: new(config.Auth)}, | ||||
| //		cm.config.Conversation.GetConfigFileName(): {old: &cm.config.Conversation, new: new(config.Conversation)}, | ||||
| //		cm.config.Friend.GetConfigFileName():       {old: &cm.config.Friend, new: new(config.Friend)}, | ||||
| //		cm.config.Group.GetConfigFileName():        {old: &cm.config.Group, new: new(config.Group)}, | ||||
| //		cm.config.Msg.GetConfigFileName():          {old: &cm.config.Msg, new: new(config.Msg)}, | ||||
| //		cm.config.Third.GetConfigFileName():        {old: &cm.config.Third, new: new(config.Third)}, | ||||
| //		cm.config.User.GetConfigFileName():         {old: &cm.config.User, new: new(config.User)}, | ||||
| //		cm.config.Redis.GetConfigFileName():        {old: &cm.config.Redis, new: new(config.Redis)}, | ||||
| //		cm.config.Share.GetConfigFileName():        {old: &cm.config.Share, new: new(config.Share)}, | ||||
| //		cm.config.Webhooks.GetConfigFileName():     {old: &cm.config.Webhooks, new: new(config.Webhooks)}, | ||||
| //	} | ||||
| // | ||||
| //	changedKeys := make([]string, 0, len(configMap)) | ||||
| //	for k, v := range configMap { | ||||
| //		err := config.Load( | ||||
| //			cm.configPath, | ||||
| //			k, | ||||
| //			config.EnvPrefixMap[k], | ||||
| //			cm.runtimeEnv, | ||||
| //			v.new, | ||||
| //		) | ||||
| //		if err != nil { | ||||
| //			log.ZError(c, "load config failed", err) | ||||
| //			continue | ||||
| //		} | ||||
| //		equal := reflect.DeepEqual(v.old, v.new) | ||||
| //		if !checkChange || !equal { | ||||
| //			changedKeys = append(changedKeys, k) | ||||
| //		} | ||||
| //	} | ||||
| // | ||||
| //	for _, k := range changedKeys { | ||||
| //		data, err := json.Marshal(configMap[k].new) | ||||
| //		if err != nil { | ||||
| //			log.ZError(c, "marshal config failed", err) | ||||
| //			continue | ||||
| //		} | ||||
| //		ops = append(ops, clientv3.OpPut(etcd.BuildKey(k), string(data))) | ||||
| //	} | ||||
| //	if len(ops) > 0 { | ||||
| //		txn.Then(ops...) | ||||
| //		_, err := txn.Commit() | ||||
| //		if err != nil { | ||||
| //			return errs.WrapMsg(err, "commit etcd txn failed") | ||||
| //		} | ||||
| //	} | ||||
| //	return nil | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) Restart(c *gin.Context) { | ||||
| //	go cm.restart(c) | ||||
| //	apiresp.GinSuccess(c, nil) | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) restart(c *gin.Context) { | ||||
| //	time.Sleep(waitHttp) // wait for Restart http call return | ||||
| //	t := time.Now().Unix() | ||||
| //	_, err := cm.client.Put(c, etcd.BuildKey(etcd.RestartKey), strconv.Itoa(int(t))) | ||||
| //	if err != nil { | ||||
| //		log.ZError(c, "restart etcd put key failed", err) | ||||
| //	} | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) SetEnableConfigManager(c *gin.Context) { | ||||
| //	if cm.config.Discovery.Enable != config.ETCD { | ||||
| //		apiresp.GinError(c, errs.New("only etcd support config manager").Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	var req apistruct.SetEnableConfigManagerReq | ||||
| //	if err := c.BindJSON(&req); err != nil { | ||||
| //		apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) | ||||
| //		return | ||||
| //	} | ||||
| //	var enableStr string | ||||
| //	if req.Enable { | ||||
| //		enableStr = etcd.Enable | ||||
| //	} else { | ||||
| //		enableStr = etcd.Disable | ||||
| //	} | ||||
| //	resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey)) | ||||
| //	if err != nil { | ||||
| //		apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed")) | ||||
| //		return | ||||
| //	} | ||||
| //	if !(resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable) && req.Enable { | ||||
| //		go func() { | ||||
| //			time.Sleep(waitHttp) // wait for Restart http call return | ||||
| //			err := cm.resetConfig(c, false, clientv3.OpPut(etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr)) | ||||
| //			if err != nil { | ||||
| //				log.ZError(c, "resetConfig failed", err) | ||||
| //			} | ||||
| //		}() | ||||
| //	} else { | ||||
| //		_, err = cm.client.Put(c, etcd.BuildKey(etcd.EnableConfigCenterKey), enableStr) | ||||
| //		if err != nil { | ||||
| //			apiresp.GinError(c, errs.WrapMsg(err, "setEnableConfigManager failed")) | ||||
| //			return | ||||
| //		} | ||||
| //	} | ||||
| // | ||||
| //	apiresp.GinSuccess(c, nil) | ||||
| //} | ||||
| // | ||||
| //func (cm *ConfigManager) GetEnableConfigManager(c *gin.Context) { | ||||
| //	resp, err := cm.client.Get(c, etcd.BuildKey(etcd.EnableConfigCenterKey)) | ||||
| //	if err != nil { | ||||
| //		apiresp.GinError(c, errs.WrapMsg(err, "getEnableConfigManager failed")) | ||||
| //		return | ||||
| //	} | ||||
| //	var enable bool | ||||
| //	if resp.Count > 0 && string(resp.Kvs[0].Value) == etcd.Enable { | ||||
| //		enable = true | ||||
| //	} | ||||
| //	apiresp.GinSuccess(c, &apistruct.GetEnableConfigManagerResp{Enable: enable}) | ||||
| //} | ||||
| @ -16,57 +16,58 @@ package api | ||||
| 
 | ||||
| import ( | ||||
| 	"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 | ||||
| type ConversationApi struct { | ||||
| 	Client conversation.ConversationClient | ||||
| } | ||||
| 
 | ||||
| func NewConversationApi(client rpcclient.Conversation) ConversationApi { | ||||
| 	return ConversationApi(client) | ||||
| func NewConversationApi(client conversation.ConversationClient) ConversationApi { | ||||
| 	return ConversationApi{client} | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetAllConversations(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetAllConversations, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetAllConversations, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetSortedConversationList(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetSortedConversationList, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetSortedConversationList, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetConversation(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetConversation, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetConversation, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetConversations(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetConversations, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetConversations, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) SetConversations(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.SetConversations, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.SetConversations, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetConversationOfflinePushUserIDs(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetConversationOfflinePushUserIDs, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetConversationOfflinePushUserIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetFullOwnerConversationIDs(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetFullOwnerConversationIDs, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetFullOwnerConversationIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetIncrementalConversation(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetIncrementalConversation, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetIncrementalConversation, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetOwnerConversation(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetOwnerConversation, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetOwnerConversation, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetNotNotifyConversationIDs(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetNotNotifyConversationIDs, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetNotNotifyConversationIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ConversationApi) GetPinnedConversationIDs(c *gin.Context) { | ||||
| 	a2r.Call(conversation.ConversationClient.GetPinnedConversationIDs, o.Client, c) | ||||
| 	a2r.Call(c, conversation.ConversationClient.GetPinnedConversationIDs, o.Client) | ||||
| } | ||||
|  | ||||
| @ -17,99 +17,104 @@ package api | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" | ||||
| 	"github.com/openimsdk/protocol/relation" | ||||
| 	"github.com/openimsdk/tools/a2r" | ||||
| ) | ||||
| 
 | ||||
| type FriendApi rpcclient.Friend | ||||
| type FriendApi struct { | ||||
| 	Client relation.FriendClient | ||||
| } | ||||
| 
 | ||||
| func NewFriendApi(client rpcclient.Friend) FriendApi { | ||||
| 	return FriendApi(client) | ||||
| func NewFriendApi(client relation.FriendClient) FriendApi { | ||||
| 	return FriendApi{client} | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) ApplyToAddFriend(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.ApplyToAddFriend, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.ApplyToAddFriend, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) RespondFriendApply(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.RespondFriendApply, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.RespondFriendApply, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) DeleteFriend(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.DeleteFriend, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.DeleteFriend, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetFriendApplyList(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetPaginationFriendsApplyTo, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetPaginationFriendsApplyTo, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetDesignatedFriendsApply(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetDesignatedFriendsApply, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetDesignatedFriendsApply, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetSelfApplyList(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetPaginationFriendsApplyFrom, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetPaginationFriendsApplyFrom, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetFriendList(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetPaginationFriends, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetPaginationFriends, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetDesignatedFriends(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetDesignatedFriends, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetDesignatedFriends, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) SetFriendRemark(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.SetFriendRemark, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.SetFriendRemark, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) AddBlack(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.AddBlack, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.AddBlack, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetPaginationBlacks(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetPaginationBlacks, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetPaginationBlacks, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetSpecifiedBlacks(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetSpecifiedBlacks, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetSpecifiedBlacks, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) RemoveBlack(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.RemoveBlack, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.RemoveBlack, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) ImportFriends(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.ImportFriends, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.ImportFriends, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) IsFriend(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.IsFriend, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.IsFriend, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetFriendIDs(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetFriendIDs, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetFriendIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetSpecifiedFriendsInfo(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetSpecifiedFriendsInfo, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetSpecifiedFriendsInfo, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) UpdateFriends(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.UpdateFriends, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.UpdateFriends, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetIncrementalFriends(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetIncrementalFriends, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetIncrementalFriends, o.Client) | ||||
| } | ||||
| 
 | ||||
| // GetIncrementalBlacks is temporarily unused. | ||||
| // Deprecated: This function is currently unused and may be removed in future versions. | ||||
| func (o *FriendApi) GetIncrementalBlacks(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetIncrementalBlacks, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetIncrementalBlacks, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetFullFriendUserIDs(c *gin.Context) { | ||||
| 	a2r.Call(relation.FriendClient.GetFullFriendUserIDs, o.Client, c) | ||||
| 	a2r.Call(c, relation.FriendClient.GetFullFriendUserIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *FriendApi) GetSelfUnhandledApplyCount(c *gin.Context) { | ||||
| 	a2r.Call(c, relation.FriendClient.GetSelfUnhandledApplyCount, o.Client) | ||||
| } | ||||
|  | ||||
| @ -16,151 +16,156 @@ package api | ||||
| 
 | ||||
| import ( | ||||
| 	"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 | ||||
| type GroupApi struct { | ||||
| 	Client group.GroupClient | ||||
| } | ||||
| 
 | ||||
| func NewGroupApi(client rpcclient.Group) GroupApi { | ||||
| 	return GroupApi(client) | ||||
| func NewGroupApi(client group.GroupClient) GroupApi { | ||||
| 	return GroupApi{client} | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) CreateGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.CreateGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.CreateGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) SetGroupInfo(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.SetGroupInfo, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.SetGroupInfo, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) SetGroupInfoEx(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.SetGroupInfoEx, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.SetGroupInfoEx, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) JoinGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.JoinGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.JoinGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) QuitGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.QuitGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.QuitGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) ApplicationGroupResponse(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GroupApplicationResponse, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GroupApplicationResponse, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) TransferGroupOwner(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.TransferGroupOwner, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.TransferGroupOwner, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetRecvGroupApplicationList(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupApplicationList, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupApplicationList, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetUserReqGroupApplicationList(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetUserReqApplicationList, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetUserReqApplicationList, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupUsersReqApplicationList(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupUsersReqApplicationList, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupUsersReqApplicationList, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetSpecifiedUserGroupRequestInfo(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetSpecifiedUserGroupRequestInfo, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetSpecifiedUserGroupRequestInfo, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupsInfo(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupsInfo, o.Client, c) | ||||
| 	//a2r.Call(group.GroupClient.GetGroupsInfo, o.Client, c, a2r.NewNilReplaceOption(group.GroupClient.GetGroupsInfo)) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupsInfo, o.Client) | ||||
| 	//a2r.Call(c, group.GroupClient.GetGroupsInfo, o.Client, c, a2r.NewNilReplaceOption(group.GroupClient.GetGroupsInfo)) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) KickGroupMember(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.KickGroupMember, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.KickGroupMember, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupMembersInfo(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupMembersInfo, o.Client, c) | ||||
| 	//a2r.Call(group.GroupClient.GetGroupMembersInfo, o.Client, c, a2r.NewNilReplaceOption(group.GroupClient.GetGroupMembersInfo)) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupMembersInfo, o.Client) | ||||
| 	//a2r.Call(c, group.GroupClient.GetGroupMembersInfo, o.Client, c, a2r.NewNilReplaceOption(group.GroupClient.GetGroupMembersInfo)) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupMemberList(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupMemberList, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupMemberList, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) InviteUserToGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.InviteUserToGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.InviteUserToGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetJoinedGroupList(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetJoinedGroupList, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetJoinedGroupList, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) DismissGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.DismissGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.DismissGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) MuteGroupMember(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.MuteGroupMember, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.MuteGroupMember, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) CancelMuteGroupMember(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.CancelMuteGroupMember, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.CancelMuteGroupMember, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) MuteGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.MuteGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.MuteGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) CancelMuteGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.CancelMuteGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.CancelMuteGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) SetGroupMemberInfo(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.SetGroupMemberInfo, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.SetGroupMemberInfo, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupAbstractInfo(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupAbstractInfo, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupAbstractInfo, o.Client) | ||||
| } | ||||
| 
 | ||||
| // func (g *Group) SetGroupMemberNickname(c *gin.Context) { | ||||
| //	a2r.Call(group.GroupClient.SetGroupMemberNickname, g.userClient, c) | ||||
| //	a2r.Call(c, group.GroupClient.SetGroupMemberNickname, g.userClient) | ||||
| //} | ||||
| // | ||||
| // func (g *Group) GetGroupAllMemberList(c *gin.Context) { | ||||
| //	a2r.Call(group.GroupClient.GetGroupAllMember, g.userClient, c) | ||||
| //	a2r.Call(c, group.GroupClient.GetGroupAllMember, g.userClient) | ||||
| //} | ||||
| 
 | ||||
| func (o *GroupApi) GroupCreateCount(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GroupCreateCount, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GroupCreateCount, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroups(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroups, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroups, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupMemberUserIDs(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetGroupMemberUserIDs, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupMemberUserIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetIncrementalJoinGroup(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetIncrementalJoinGroup, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetIncrementalJoinGroup, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetIncrementalGroupMember(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetIncrementalGroupMember, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetIncrementalGroupMember, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetIncrementalGroupMemberBatch(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.BatchGetIncrementalGroupMember, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.BatchGetIncrementalGroupMember, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetFullGroupMemberUserIDs(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetFullGroupMemberUserIDs, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetFullGroupMemberUserIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetFullJoinGroupIDs(c *gin.Context) { | ||||
| 	a2r.Call(group.GroupClient.GetFullJoinGroupIDs, o.Client, c) | ||||
| 	a2r.Call(c, group.GroupClient.GetFullJoinGroupIDs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *GroupApi) GetGroupApplicationUnhandledCount(c *gin.Context) { | ||||
| 	a2r.Call(c, group.GroupClient.GetGroupApplicationUnhandledCount, o.Client) | ||||
| } | ||||
|  | ||||
| @ -1,25 +1,9 @@ | ||||
| // 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" | ||||
| 	"errors" | ||||
| 	"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" | ||||
| @ -28,12 +12,21 @@ import ( | ||||
| 	"syscall" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"github.com/openimsdk/tools/mw" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| 	"github.com/openimsdk/tools/utils/network" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/credentials/insecure" | ||||
| 
 | ||||
| 	kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||
| 	"github.com/openimsdk/tools/discovery" | ||||
| 	"github.com/openimsdk/tools/discovery/etcd" | ||||
| 	"github.com/openimsdk/tools/errs" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 	"github.com/openimsdk/tools/system/program" | ||||
| 	"github.com/openimsdk/tools/utils/jsonutil" | ||||
| ) | ||||
| 
 | ||||
| type Config struct { | ||||
| @ -42,8 +35,8 @@ type Config struct { | ||||
| 	Discovery config.Discovery | ||||
| } | ||||
| 
 | ||||
| func Start(ctx context.Context, index int, config *Config) error { | ||||
| 	apiPort, err := datautil.GetElemByIndex(config.API.Api.Ports, index) | ||||
| func Start(ctx context.Context, index int, cfg *Config) error { | ||||
| 	apiPort, err := datautil.GetElemByIndex(cfg.API.Api.Ports, index) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -51,10 +44,14 @@ func Start(ctx context.Context, index int, config *Config) error { | ||||
| 	var client discovery.SvcDiscoveryRegistry | ||||
| 
 | ||||
| 	// Determine whether zk is passed according to whether it is a clustered deployment | ||||
| 	client, err = kdisc.NewDiscoveryRegister(&config.Discovery, &config.Share) | ||||
| 	client, err = kdisc.NewDiscoveryRegister(&cfg.Discovery, &cfg.Share, []string{ | ||||
| 		cfg.Share.RpcRegisterName.MessageGateway, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return errs.WrapMsg(err, "failed to register discovery service") | ||||
| 	} | ||||
| 	client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), | ||||
| 		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) | ||||
| 
 | ||||
| 	var ( | ||||
| 		netDone        = make(chan struct{}, 1) | ||||
| @ -62,29 +59,80 @@ func Start(ctx context.Context, index int, config *Config) error { | ||||
| 		prometheusPort int | ||||
| 	) | ||||
| 
 | ||||
| 	router := newGinRouter(client, config) | ||||
| 	if config.API.Prometheus.Enable { | ||||
| 		go func() { | ||||
| 			prometheusPort, err = datautil.GetElemByIndex(config.API.Prometheus.Ports, index) | ||||
| 	router, err := newGinRouter(ctx, client, cfg) | ||||
| 	if err != nil { | ||||
| 				netErr = err | ||||
| 				netDone <- struct{}{} | ||||
| 				return | ||||
| 		return err | ||||
| 	} | ||||
| 			if err := prommetrics.ApiInit(prometheusPort); err != nil && err != http.ErrServerClosed { | ||||
| 	registerIP, err := network.GetRpcRegisterIP("") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	getAutoPort := func() (net.Listener, int, error) { | ||||
| 		registerAddr := net.JoinHostPort(registerIP, "0") | ||||
| 		listener, err := net.Listen("tcp", registerAddr) | ||||
| 		if err != nil { | ||||
| 			return nil, 0, errs.WrapMsg(err, "listen err", "registerAddr", registerAddr) | ||||
| 		} | ||||
| 		_, portStr, _ := net.SplitHostPort(listener.Addr().String()) | ||||
| 		port, _ := strconv.Atoi(portStr) | ||||
| 		return listener, port, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.API.Prometheus.AutoSetPorts && cfg.Discovery.Enable != config.ETCD { | ||||
| 		return errs.New("only etcd support autoSetPorts", "RegisterName", "api").Wrap() | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.API.Prometheus.Enable { | ||||
| 		var ( | ||||
| 			listener net.Listener | ||||
| 		) | ||||
| 
 | ||||
| 		if cfg.API.Prometheus.AutoSetPorts { | ||||
| 			listener, prometheusPort, err = getAutoPort() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			etcdClient, ok := client.(*etcd.SvcDiscoveryRegistryImpl) | ||||
| 			if !ok { | ||||
| 				return errs.New("only etcd support autoSetPorts").Wrap() | ||||
| 			} | ||||
| 
 | ||||
| 			target, err := jsonutil.JsonMarshal(prommetrics.BuildDefaultTarget(registerIP, prometheusPort)) | ||||
| 			if err != nil { | ||||
| 				return errs.Wrap(err) | ||||
| 			} | ||||
| 			err = etcdClient.SetWithLease(ctx, prommetrics.BuildDiscoveryKey(prommetrics.APIKeyName, index), target, prommetrics.TTL) | ||||
| 			if err != nil { | ||||
| 				return errs.WrapMsg(err, "etcd put err") | ||||
| 			} | ||||
| 		} else { | ||||
| 			prometheusPort, err = datautil.GetElemByIndex(cfg.API.Prometheus.Ports, index) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			listener, err = net.Listen("tcp", fmt.Sprintf(":%d", prometheusPort)) | ||||
| 			if err != nil { | ||||
| 				return errs.WrapMsg(err, "listen err", "addr", fmt.Sprintf(":%d", prometheusPort)) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		go func() { | ||||
| 			if err := prommetrics.ApiInit(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||||
| 				netErr = errs.WrapMsg(err, fmt.Sprintf("api prometheus start err: %d", prometheusPort)) | ||||
| 				netDone <- struct{}{} | ||||
| 			} | ||||
| 		}() | ||||
| 
 | ||||
| 	} | ||||
| 	address := net.JoinHostPort(network.GetListenIP(config.API.Api.ListenIP), strconv.Itoa(apiPort)) | ||||
| 	address := net.JoinHostPort(network.GetListenIP(cfg.API.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 { | ||||
| 		if err != nil && !errors.Is(err, http.ErrServerClosed) { | ||||
| 			netErr = errs.WrapMsg(err, fmt.Sprintf("api start err: %s", server.Addr)) | ||||
| 			netDone <- struct{}{} | ||||
| 
 | ||||
|  | ||||
| @ -1,14 +1,22 @@ | ||||
| package jssdk | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sort" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 
 | ||||
| 	"github.com/openimsdk/protocol/conversation" | ||||
| 	"github.com/openimsdk/protocol/jssdk" | ||||
| 	"github.com/openimsdk/protocol/msg" | ||||
| 	"github.com/openimsdk/protocol/relation" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| 	"github.com/openimsdk/tools/a2r" | ||||
| 	"github.com/openimsdk/tools/mcontext" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| 	"sort" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @ -16,16 +24,23 @@ const ( | ||||
| 	defaultGetActiveConversation = 100 | ||||
| ) | ||||
| 
 | ||||
| func NewJSSdkApi(msg msg.MsgClient, conv conversation.ConversationClient) *JSSdk { | ||||
| func NewJSSdkApi(userClient *rpcli.UserClient, relationClient *rpcli.RelationClient, groupClient *rpcli.GroupClient, | ||||
| 	conversationClient *rpcli.ConversationClient, msgClient *rpcli.MsgClient) *JSSdk { | ||||
| 	return &JSSdk{ | ||||
| 		msg:  msg, | ||||
| 		conv: conv, | ||||
| 		userClient:         userClient, | ||||
| 		relationClient:     relationClient, | ||||
| 		groupClient:        groupClient, | ||||
| 		conversationClient: conversationClient, | ||||
| 		msgClient:          msgClient, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type JSSdk struct { | ||||
| 	msg  msg.MsgClient | ||||
| 	conv conversation.ConversationClient | ||||
| 	userClient         *rpcli.UserClient | ||||
| 	relationClient     *rpcli.RelationClient | ||||
| 	groupClient        *rpcli.GroupClient | ||||
| 	conversationClient *rpcli.ConversationClient | ||||
| 	msgClient          *rpcli.MsgClient | ||||
| } | ||||
| 
 | ||||
| func (x *JSSdk) GetActiveConversations(c *gin.Context) { | ||||
| @ -36,91 +51,130 @@ func (x *JSSdk) GetConversations(c *gin.Context) { | ||||
| 	call(c, x.getConversations) | ||||
| } | ||||
| 
 | ||||
| func (x *JSSdk) getActiveConversations(ctx *gin.Context) (*ConversationsResp, error) { | ||||
| 	req, err := a2r.ParseRequest[ActiveConversationsReq](ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| func (x *JSSdk) fillConversations(ctx context.Context, conversations []*jssdk.ConversationMsg) error { | ||||
| 	if len(conversations) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	var ( | ||||
| 		userIDs  []string | ||||
| 		groupIDs []string | ||||
| 	) | ||||
| 	for _, c := range conversations { | ||||
| 		if c.Conversation.GroupID == "" { | ||||
| 			userIDs = append(userIDs, c.Conversation.UserID) | ||||
| 		} else { | ||||
| 			groupIDs = append(groupIDs, c.Conversation.GroupID) | ||||
| 		} | ||||
| 	} | ||||
| 	var ( | ||||
| 		userMap   map[string]*sdkws.UserInfo | ||||
| 		friendMap map[string]*relation.FriendInfoOnly | ||||
| 		groupMap  map[string]*sdkws.GroupInfo | ||||
| 	) | ||||
| 	if len(userIDs) > 0 { | ||||
| 		users, err := x.userClient.GetUsersInfo(ctx, userIDs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		friends, err := x.relationClient.GetFriendsInfo(ctx, conversations[0].Conversation.OwnerUserID, userIDs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		userMap = datautil.SliceToMap(users, (*sdkws.UserInfo).GetUserID) | ||||
| 		friendMap = datautil.SliceToMap(friends, (*relation.FriendInfoOnly).GetFriendUserID) | ||||
| 	} | ||||
| 	if len(groupIDs) > 0 { | ||||
| 		groups, err := x.groupClient.GetGroupsInfo(ctx, groupIDs) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		groupMap = datautil.SliceToMap(groups, (*sdkws.GroupInfo).GetGroupID) | ||||
| 	} | ||||
| 	for _, c := range conversations { | ||||
| 		if c.Conversation.GroupID == "" { | ||||
| 			c.User = userMap[c.Conversation.UserID] | ||||
| 			c.Friend = friendMap[c.Conversation.UserID] | ||||
| 		} else { | ||||
| 			c.Group = groupMap[c.Conversation.GroupID] | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (x *JSSdk) getActiveConversations(ctx context.Context, req *jssdk.GetActiveConversationsReq) (*jssdk.GetActiveConversationsResp, error) { | ||||
| 	if req.Count <= 0 || req.Count > maxGetActiveConversation { | ||||
| 		req.Count = defaultGetActiveConversation | ||||
| 	} | ||||
| 	opUserID := mcontext.GetOpUserID(ctx) | ||||
| 	conversationIDs, err := field(ctx, x.conv.GetConversationIDs, | ||||
| 		&conversation.GetConversationIDsReq{UserID: opUserID}, (*conversation.GetConversationIDsResp).GetConversationIDs) | ||||
| 	req.OwnerUserID = mcontext.GetOpUserID(ctx) | ||||
| 	conversationIDs, err := x.conversationClient.GetConversationIDs(ctx, req.OwnerUserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(conversationIDs) == 0 { | ||||
| 		return &ConversationsResp{}, nil | ||||
| 		return &jssdk.GetActiveConversationsResp{}, nil | ||||
| 	} | ||||
| 	readSeq, err := field(ctx, x.msg.GetHasReadSeqs, | ||||
| 		&msg.GetHasReadSeqsReq{UserID: opUserID, ConversationIDs: conversationIDs}, (*msg.SeqsInfoResp).GetMaxSeqs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	activeConversation, err := field(ctx, x.msg.GetActiveConversation, | ||||
| 		&msg.GetActiveConversationReq{ConversationIDs: conversationIDs}, (*msg.GetActiveConversationResp).GetConversations) | ||||
| 
 | ||||
| 	activeConversation, err := x.msgClient.GetActiveConversation(ctx, conversationIDs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(activeConversation) == 0 { | ||||
| 		return &ConversationsResp{}, nil | ||||
| 		return &jssdk.GetActiveConversationsResp{}, nil | ||||
| 	} | ||||
| 	readSeq, err := x.msgClient.GetHasReadSeqs(ctx, conversationIDs, req.OwnerUserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	sortConversations := sortActiveConversations{ | ||||
| 		Conversation: activeConversation, | ||||
| 	} | ||||
| 	if len(activeConversation) > 1 { | ||||
| 		pinnedConversationIDs, err := field(ctx, x.conv.GetPinnedConversationIDs, | ||||
| 			&conversation.GetPinnedConversationIDsReq{UserID: opUserID}, (*conversation.GetPinnedConversationIDsResp).GetConversationIDs) | ||||
| 		pinnedConversationIDs, err := x.conversationClient.GetPinnedConversationIDs(ctx, req.OwnerUserID) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		sortConversations.PinnedConversationIDs = datautil.SliceSet(pinnedConversationIDs) | ||||
| 	} | ||||
| 	sort.Sort(&sortConversations) | ||||
| 	sortList := sortConversations.Top(req.Count) | ||||
| 	conversations, err := field(ctx, x.conv.GetConversations, | ||||
| 		&conversation.GetConversationsReq{ | ||||
| 			OwnerUserID: opUserID, | ||||
| 			ConversationIDs: datautil.Slice(sortList, func(c *msg.ActiveConversation) string { | ||||
| 	sortList := sortConversations.Top(int(req.Count)) | ||||
| 	conversations, err := x.conversationClient.GetConversations(ctx, datautil.Slice(sortList, func(c *msg.ActiveConversation) string { | ||||
| 		return c.ConversationID | ||||
| 			})}, (*conversation.GetConversationsResp).GetConversations) | ||||
| 	}), req.OwnerUserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	msgs, err := field(ctx, x.msg.GetSeqMessage, | ||||
| 		&msg.GetSeqMessageReq{ | ||||
| 			UserID: opUserID, | ||||
| 			Conversations: datautil.Slice(sortList, func(c *msg.ActiveConversation) *msg.ConversationSeqs { | ||||
| 	msgs, err := x.msgClient.GetSeqMessage(ctx, req.OwnerUserID, datautil.Slice(sortList, func(c *msg.ActiveConversation) *msg.ConversationSeqs { | ||||
| 		return &msg.ConversationSeqs{ | ||||
| 			ConversationID: c.ConversationID, | ||||
| 			Seqs:           []int64{c.MaxSeq}, | ||||
| 		} | ||||
| 			}), | ||||
| 		}, (*msg.GetSeqMessageResp).GetMsgs) | ||||
| 	})) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	x.checkMessagesAndGetLastMessage(ctx, req.OwnerUserID, msgs) | ||||
| 	conversationMap := datautil.SliceToMap(conversations, func(c *conversation.Conversation) string { | ||||
| 		return c.ConversationID | ||||
| 	}) | ||||
| 	resp := make([]ConversationMsg, 0, len(sortList)) | ||||
| 	resp := make([]*jssdk.ConversationMsg, 0, len(sortList)) | ||||
| 	for _, c := range sortList { | ||||
| 		conv, ok := conversationMap[c.ConversationID] | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
| 		var lastMsg *sdkws.MsgData | ||||
| 		if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { | ||||
| 			lastMsg = msgList.Msgs[0] | ||||
| 		} | ||||
| 		resp = append(resp, ConversationMsg{ | ||||
| 			resp = append(resp, &jssdk.ConversationMsg{ | ||||
| 				Conversation: conv, | ||||
| 			LastMsg:      lastMsg, | ||||
| 				LastMsg:      msgList.Msgs[0], | ||||
| 				MaxSeq:       c.MaxSeq, | ||||
| 				ReadSeq:      readSeq[c.ConversationID], | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 	if err := x.fillConversations(ctx, resp); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var unreadCount int64 | ||||
| 	for _, c := range activeConversation { | ||||
| 		count := c.MaxSeq - readSeq[c.ConversationID] | ||||
| @ -128,35 +182,29 @@ func (x *JSSdk) getActiveConversations(ctx *gin.Context) (*ConversationsResp, er | ||||
| 			unreadCount += count | ||||
| 		} | ||||
| 	} | ||||
| 	return &ConversationsResp{ | ||||
| 	return &jssdk.GetActiveConversationsResp{ | ||||
| 		Conversations: resp, | ||||
| 		UnreadCount:   unreadCount, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (x *JSSdk) getConversations(ctx *gin.Context) (*ConversationsResp, error) { | ||||
| 	req, err := a2r.ParseRequest[conversation.GetConversationsReq](ctx) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| func (x *JSSdk) getConversations(ctx context.Context, req *jssdk.GetConversationsReq) (*jssdk.GetConversationsResp, error) { | ||||
| 	req.OwnerUserID = mcontext.GetOpUserID(ctx) | ||||
| 	conversations, err := field(ctx, x.conv.GetConversations, req, (*conversation.GetConversationsResp).GetConversations) | ||||
| 	conversations, err := x.conversationClient.GetConversations(ctx, req.ConversationIDs, req.OwnerUserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(conversations) == 0 { | ||||
| 		return &ConversationsResp{}, nil | ||||
| 		return &jssdk.GetConversationsResp{}, nil | ||||
| 	} | ||||
| 	req.ConversationIDs = datautil.Slice(conversations, func(c *conversation.Conversation) string { | ||||
| 		return c.ConversationID | ||||
| 	}) | ||||
| 	maxSeqs, err := field(ctx, x.msg.GetMaxSeqs, | ||||
| 		&msg.GetMaxSeqsReq{ConversationIDs: req.ConversationIDs}, (*msg.SeqsInfoResp).GetMaxSeqs) | ||||
| 	maxSeqs, err := x.msgClient.GetMaxSeqs(ctx, req.ConversationIDs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	readSeqs, err := field(ctx, x.msg.GetHasReadSeqs, | ||||
| 		&msg.GetHasReadSeqsReq{UserID: req.OwnerUserID, ConversationIDs: req.ConversationIDs}, (*msg.SeqsInfoResp).GetMaxSeqs) | ||||
| 	readSeqs, err := x.msgClient.GetHasReadSeqs(ctx, req.ConversationIDs, req.OwnerUserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -171,25 +219,27 @@ func (x *JSSdk) getConversations(ctx *gin.Context) (*ConversationsResp, error) { | ||||
| 	} | ||||
| 	var msgs map[string]*sdkws.PullMsgs | ||||
| 	if len(conversationSeqs) > 0 { | ||||
| 		msgs, err = field(ctx, x.msg.GetSeqMessage, | ||||
| 			&msg.GetSeqMessageReq{UserID: req.OwnerUserID, Conversations: conversationSeqs}, (*msg.GetSeqMessageResp).GetMsgs) | ||||
| 		msgs, err = x.msgClient.GetSeqMessage(ctx, req.OwnerUserID, conversationSeqs) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	resp := make([]ConversationMsg, 0, len(conversations)) | ||||
| 	x.checkMessagesAndGetLastMessage(ctx, req.OwnerUserID, msgs) | ||||
| 	resp := make([]*jssdk.ConversationMsg, 0, len(conversations)) | ||||
| 	for _, c := range conversations { | ||||
| 		var lastMsg *sdkws.MsgData | ||||
| 		if msgList, ok := msgs[c.ConversationID]; ok && len(msgList.Msgs) > 0 { | ||||
| 			lastMsg = msgList.Msgs[0] | ||||
| 		} | ||||
| 		resp = append(resp, ConversationMsg{ | ||||
| 			resp = append(resp, &jssdk.ConversationMsg{ | ||||
| 				Conversation: c, | ||||
| 			LastMsg:      lastMsg, | ||||
| 				LastMsg:      msgList.Msgs[0], | ||||
| 				MaxSeq:       maxSeqs[c.ConversationID], | ||||
| 				ReadSeq:      readSeqs[c.ConversationID], | ||||
| 			}) | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 	if err := x.fillConversations(ctx, resp); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var unreadCount int64 | ||||
| 	for conversationID, maxSeq := range maxSeqs { | ||||
| 		count := maxSeq - readSeqs[conversationID] | ||||
| @ -197,8 +247,43 @@ func (x *JSSdk) getConversations(ctx *gin.Context) (*ConversationsResp, error) { | ||||
| 			unreadCount += count | ||||
| 		} | ||||
| 	} | ||||
| 	return &ConversationsResp{ | ||||
| 	return &jssdk.GetConversationsResp{ | ||||
| 		Conversations: resp, | ||||
| 		UnreadCount:   unreadCount, | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| // This function checks whether the latest MaxSeq message is valid. | ||||
| // If not, it needs to fetch a valid message again. | ||||
| func (x *JSSdk) checkMessagesAndGetLastMessage(ctx context.Context, userID string, messages map[string]*sdkws.PullMsgs) { | ||||
| 	var conversationIDs []string | ||||
| 
 | ||||
| 	for conversationID, message := range messages { | ||||
| 		allInValid := true | ||||
| 		for _, data := range message.Msgs { | ||||
| 			if data.Status < constant.MsgStatusHasDeleted { | ||||
| 				allInValid = false | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// when the conversation has been deleted by the user, the length of message.Msgs is empty | ||||
| 		if allInValid && len(message.Msgs) > 0 { | ||||
| 			conversationIDs = append(conversationIDs, conversationID) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(conversationIDs) > 0 { | ||||
| 		resp, err := x.msgClient.GetLastMessage(ctx, &msg.GetLastMessageReq{ | ||||
| 			UserID:          userID, | ||||
| 			ConversationIDs: conversationIDs, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			log.ZError(ctx, "fetchLatestValidMessages", err, "conversationIDs", conversationIDs) | ||||
| 			return | ||||
| 		} | ||||
| 		for conversationID, message := range resp.Msgs { | ||||
| 			messages[conversationID] = &sdkws.PullMsgs{Msgs: []*sdkws.MsgData{message}} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -1,22 +0,0 @@ | ||||
| package jssdk | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/openimsdk/protocol/conversation" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| ) | ||||
| 
 | ||||
| type ActiveConversationsReq struct { | ||||
| 	Count int `json:"count"` | ||||
| } | ||||
| 
 | ||||
| type ConversationMsg struct { | ||||
| 	Conversation *conversation.Conversation `json:"conversation"` | ||||
| 	LastMsg      *sdkws.MsgData             `json:"lastMsg"` | ||||
| 	MaxSeq       int64                      `json:"maxSeq"` | ||||
| 	ReadSeq      int64                      `json:"readSeq"` | ||||
| } | ||||
| 
 | ||||
| type ConversationsResp struct { | ||||
| 	UnreadCount   int64             `json:"unreadCount"` | ||||
| 	Conversations []ConversationMsg `json:"conversations"` | ||||
| } | ||||
| @ -3,8 +3,14 @@ package jssdk | ||||
| import ( | ||||
| 	"context" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/openimsdk/tools/a2r" | ||||
| 	"github.com/openimsdk/tools/apiresp" | ||||
| 	"github.com/openimsdk/tools/checker" | ||||
| 	"github.com/openimsdk/tools/errs" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| func field[A, B, C any](ctx context.Context, fn func(ctx context.Context, req *A, opts ...grpc.CallOption) (*B, error), req *A, get func(*B) C) (C, error) { | ||||
| @ -16,11 +22,56 @@ func field[A, B, C any](ctx context.Context, fn func(ctx context.Context, req *A | ||||
| 	return get(resp), nil | ||||
| } | ||||
| 
 | ||||
| func call[R any](c *gin.Context, fn func(ctx *gin.Context) (R, error)) { | ||||
| 	resp, err := fn(c) | ||||
| func call[A, B any](c *gin.Context, fn func(ctx context.Context, req *A) (*B, error)) { | ||||
| 	var isJSON bool | ||||
| 	switch contentType := c.GetHeader("Content-Type"); { | ||||
| 	case contentType == "": | ||||
| 		isJSON = true | ||||
| 	case strings.Contains(contentType, "application/json"): | ||||
| 		isJSON = true | ||||
| 	case strings.Contains(contentType, "application/protobuf"): | ||||
| 	case strings.Contains(contentType, "application/x-protobuf"): | ||||
| 	default: | ||||
| 		apiresp.GinError(c, errs.ErrArgs.WrapMsg("unsupported content type")) | ||||
| 		return | ||||
| 	} | ||||
| 	var req *A | ||||
| 	if isJSON { | ||||
| 		var err error | ||||
| 		req, err = a2r.ParseRequest[A](c) | ||||
| 		if err != nil { | ||||
| 			apiresp.GinError(c, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} else { | ||||
| 		body, err := io.ReadAll(c.Request.Body) | ||||
| 		if err != nil { | ||||
| 			apiresp.GinError(c, err) | ||||
| 			return | ||||
| 		} | ||||
| 		req = new(A) | ||||
| 		if err := proto.Unmarshal(body, any(req).(proto.Message)); err != nil { | ||||
| 			apiresp.GinError(c, err) | ||||
| 			return | ||||
| 		} | ||||
| 		if err := checker.Validate(&req); err != nil { | ||||
| 			apiresp.GinError(c, err) | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	resp, err := fn(c, req) | ||||
| 	if err != nil { | ||||
| 		apiresp.GinError(c, err) | ||||
| 		return | ||||
| 	} | ||||
| 	if isJSON { | ||||
| 		apiresp.GinSuccess(c, resp) | ||||
| 		return | ||||
| 	} | ||||
| 	body, err := proto.Marshal(any(resp).(proto.Message)) | ||||
| 	if err != nil { | ||||
| 		apiresp.GinError(c, err) | ||||
| 		return | ||||
| 	} | ||||
| 	apiresp.GinSuccess(c, body) | ||||
| } | ||||
|  | ||||
| @ -1,37 +0,0 @@ | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/openimsdk/protocol/msg" | ||||
| 	"sort" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestName(t *testing.T) { | ||||
| 	val := sortActiveConversations{ | ||||
| 		Conversation: []*msg.ActiveConversation{ | ||||
| 			{ | ||||
| 				ConversationID: "100", | ||||
| 				LastTime:       100, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ConversationID: "200", | ||||
| 				LastTime:       200, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ConversationID: "300", | ||||
| 				LastTime:       300, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ConversationID: "400", | ||||
| 				LastTime:       400, | ||||
| 			}, | ||||
| 		}, | ||||
| 		//PinnedConversationIDs: map[string]struct{}{ | ||||
| 		//	"100": {}, | ||||
| 		//	"300": {}, | ||||
| 		//}, | ||||
| 	} | ||||
| 	sort.Sort(&val) | ||||
| 	t.Log(val) | ||||
| 
 | ||||
| } | ||||
| @ -21,7 +21,7 @@ import ( | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/apistruct" | ||||
| 	"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/open-im-server/v3/pkg/rpcli" | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	"github.com/openimsdk/protocol/msg" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| @ -37,16 +37,14 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| type MessageApi struct { | ||||
| 	*rpcclient.Message | ||||
| 	validate      *validator.Validate | ||||
| 	userRpcClient *rpcclient.UserRpcClient | ||||
| 	Client        msg.MsgClient | ||||
| 	userClient    *rpcli.UserClient | ||||
| 	imAdminUserID []string | ||||
| 	validate      *validator.Validate | ||||
| } | ||||
| 
 | ||||
| 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 NewMessageApi(client msg.MsgClient, userClient *rpcli.UserClient, imAdminUserID []string) MessageApi { | ||||
| 	return MessageApi{Client: client, userClient: userClient, imAdminUserID: imAdminUserID, validate: validator.New()} | ||||
| } | ||||
| 
 | ||||
| func (*MessageApi) SetOptions(options map[string]bool, value bool) { | ||||
| @ -108,51 +106,51 @@ func (m *MessageApi) newUserSendMsgReq(_ *gin.Context, params *apistruct.SendMsg | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetSeq(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetMaxSeq, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetMaxSeq, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) PullMsgBySeqs(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.PullMessageBySeqs, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.PullMessageBySeqs, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) RevokeMsg(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.RevokeMsg, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.RevokeMsg, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) MarkMsgsAsRead(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.MarkMsgsAsRead, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.MarkMsgsAsRead, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) MarkConversationAsRead(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.MarkConversationAsRead, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.MarkConversationAsRead, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetConversationsHasReadAndMaxSeq(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetConversationsHasReadAndMaxSeq, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetConversationsHasReadAndMaxSeq, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) SetConversationHasReadSeq(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.SetConversationHasReadSeq, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.SetConversationHasReadSeq, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) ClearConversationsMsg(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.ClearConversationsMsg, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.ClearConversationsMsg, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) UserClearAllMsg(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.UserClearAllMsg, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.UserClearAllMsg, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) DeleteMsgs(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.DeleteMsgs, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.DeleteMsgs, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) DeleteMsgPhysicalBySeq(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.DeleteMsgPhysicalBySeq, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.DeleteMsgPhysicalBySeq, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) DeleteMsgPhysical(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.DeleteMsgPhysical, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.DeleteMsgPhysical, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendMsgReq *msg.SendMsgReq, err error) { | ||||
| @ -176,7 +174,7 @@ func (m *MessageApi) getSendMsgReq(c *gin.Context, req apistruct.SendMsg) (sendM | ||||
| 	case constant.OANotification: | ||||
| 		data = apistruct.OANotificationElem{} | ||||
| 		req.SessionType = constant.NotificationChatType | ||||
| 		if err = m.userRpcClient.GetNotificationByID(c, req.SendID); err != nil { | ||||
| 		if err = m.userClient.GetNotificationByID(c, req.SendID); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	default: | ||||
| @ -283,7 +281,7 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) { | ||||
| 				IsSendMsg:        false, | ||||
| 				ReliabilityLevel: 1, | ||||
| 				UnreadCount:      false, | ||||
| 			}), | ||||
| 			}, nil), | ||||
| 		}, | ||||
| 	} | ||||
| 	respPb, err := m.Client.SendMsg(c, &sendMsgReq) | ||||
| @ -310,10 +308,10 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) { | ||||
| 
 | ||||
| 	var recvIDs []string | ||||
| 	if req.IsSendAll { | ||||
| 		pageNumber := 1 | ||||
| 		showNumber := 500 | ||||
| 		var pageNumber int32 = 1 | ||||
| 		const showNumber = 500 | ||||
| 		for { | ||||
| 			recvIDsPart, err := m.userRpcClient.GetAllUserIDs(c, int32(pageNumber), int32(showNumber)) | ||||
| 			recvIDsPart, err := m.userClient.GetAllUserIDs(c, pageNumber, showNumber) | ||||
| 			if err != nil { | ||||
| 				apiresp.GinError(c, err) | ||||
| 				return | ||||
| @ -351,25 +349,33 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) { | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) CheckMsgIsSendSuccess(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetSendMsgStatus, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetSendMsgStatus, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetUsersOnlineStatus(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetSendMsgStatus, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetSendMsgStatus, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetActiveUser(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetActiveUser, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetActiveUser, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetActiveGroup(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetActiveGroup, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetActiveGroup, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) SearchMsg(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.SearchMessage, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.SearchMessage, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetServerTime(c *gin.Context) { | ||||
| 	a2r.Call(msg.MsgClient.GetServerTime, m.Client, c) | ||||
| 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) GetStreamMsg(c *gin.Context) { | ||||
| 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) | ||||
| } | ||||
| 
 | ||||
| func (m *MessageApi) AppendStreamMsg(c *gin.Context) { | ||||
| 	a2r.Call(c, msg.MsgClient.GetServerTime, m.Client) | ||||
| } | ||||
|  | ||||
							
								
								
									
										110
									
								
								internal/api/prometheus_discovery.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								internal/api/prometheus_discovery.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"errors" | ||||
| 	"net/http" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||
| 	"github.com/openimsdk/tools/apiresp" | ||||
| 	"github.com/openimsdk/tools/discovery" | ||||
| 	"github.com/openimsdk/tools/discovery/etcd" | ||||
| 	"github.com/openimsdk/tools/errs" | ||||
| ) | ||||
| 
 | ||||
| type PrometheusDiscoveryApi struct { | ||||
| 	config *Config | ||||
| 	client discovery.SvcDiscoveryRegistry | ||||
| } | ||||
| 
 | ||||
| func NewPrometheusDiscoveryApi(cfg *Config, client discovery.SvcDiscoveryRegistry) *PrometheusDiscoveryApi { | ||||
| 	api := &PrometheusDiscoveryApi{ | ||||
| 		config: cfg, | ||||
| 	} | ||||
| 	if cfg.Discovery.Enable == config.ETCD { | ||||
| 		api.client = client.(*etcd.SvcDiscoveryRegistryImpl) | ||||
| 	} | ||||
| 	return api | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Enable(c *gin.Context) { | ||||
| 	if p.config.Discovery.Enable != config.ETCD { | ||||
| 		c.JSON(http.StatusOK, []struct{}{}) | ||||
| 		c.Abort() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) discovery(c *gin.Context, key string) { | ||||
| 	value, err := p.client.GetKeyWithPrefix(c, prommetrics.BuildDiscoveryKeyPrefix(key)) | ||||
| 	if err != nil { | ||||
| 		if errors.Is(err, discovery.ErrNotSupported) { | ||||
| 			c.JSON(http.StatusOK, []struct{}{}) | ||||
| 			return | ||||
| 		} | ||||
| 		apiresp.GinError(c, errs.WrapMsg(err, "get key value")) | ||||
| 		return | ||||
| 	} | ||||
| 	if len(value) == 0 { | ||||
| 		c.JSON(http.StatusOK, []*prommetrics.RespTarget{}) | ||||
| 		return | ||||
| 	} | ||||
| 	var resp prommetrics.RespTarget | ||||
| 	for i := range value { | ||||
| 		var tmp prommetrics.Target | ||||
| 		if err = json.Unmarshal(value[i], &tmp); err != nil { | ||||
| 			apiresp.GinError(c, errs.WrapMsg(err, "json unmarshal err")) | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		resp.Targets = append(resp.Targets, tmp.Target) | ||||
| 		resp.Labels = tmp.Labels // default label is fixed. See prommetrics.BuildDefaultTarget | ||||
| 	} | ||||
| 
 | ||||
| 	c.JSON(http.StatusOK, []*prommetrics.RespTarget{&resp}) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Api(c *gin.Context) { | ||||
| 	p.discovery(c, prommetrics.APIKeyName) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) User(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.User) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Group(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Group) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Msg(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Msg) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Friend(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Friend) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Conversation(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Conversation) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Third(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Third) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Auth(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Auth) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) Push(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.Push) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) MessageGateway(c *gin.Context) { | ||||
| 	p.discovery(c, p.config.Share.RpcRegisterName.MessageGateway) | ||||
| } | ||||
| 
 | ||||
| func (p *PrometheusDiscoveryApi) MessageTransfer(c *gin.Context) { | ||||
| 	p.discovery(c, prommetrics.MessageTransferKeyName) | ||||
| } | ||||
| @ -1,7 +1,18 @@ | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"context" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 	pbAuth "github.com/openimsdk/protocol/auth" | ||||
| 	"github.com/openimsdk/protocol/conversation" | ||||
| 	"github.com/openimsdk/protocol/group" | ||||
| 	"github.com/openimsdk/protocol/msg" | ||||
| 	"github.com/openimsdk/protocol/relation" | ||||
| 	"github.com/openimsdk/protocol/third" | ||||
| 	"github.com/openimsdk/protocol/user" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/internal/api/jssdk" | ||||
| 
 | ||||
| @ -10,15 +21,8 @@ import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/gin-gonic/gin/binding" | ||||
| 	"github.com/go-playground/validator/v10" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"google.golang.org/grpc/credentials/insecure" | ||||
| 
 | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"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/openimsdk/protocol/constant" | ||||
| 	"github.com/openimsdk/tools/apiresp" | ||||
| 	"github.com/openimsdk/tools/discovery" | ||||
| @ -48,23 +52,40 @@ func prommetricsGin() gin.HandlerFunc { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| 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"))) | ||||
| func newGinRouter(ctx context.Context, client discovery.SvcDiscoveryRegistry, config *Config) (*gin.Engine, error) { | ||||
| 	authConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Auth) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	friendConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Friend) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	thirdConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Third) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	gin.SetMode(gin.ReleaseMode) | ||||
| 	r := gin.New() | ||||
| 	if v, ok := binding.Validator.Engine().(*validator.Validate); ok { | ||||
| 		_ = v.RegisterValidation("required_if", RequiredIf) | ||||
| 	} | ||||
| 	// init rpc client here | ||||
| 	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.API.Prometheus.GrafanaURL) | ||||
| 	switch config.API.Api.CompressionLevel { | ||||
| 	case NoCompression: | ||||
| 	case DefaultCompression: | ||||
| @ -74,10 +95,9 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 	case BestSpeed: | ||||
| 		r.Use(gzip.Gzip(gzip.BestSpeed)) | ||||
| 	} | ||||
| 	r.Use(prommetricsGin(), gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID(), GinParseToken(authRpc)) | ||||
| 	u := NewUserApi(*userRpc) | ||||
| 	m := NewMessageApi(messageRpc, userRpc, config.Share.IMAdminUserID) | ||||
| 	j := jssdk.NewJSSdkApi(messageRpc.Client, conversationRpc.Client) | ||||
| 	r.Use(prommetricsGin(), gin.RecoveryWithWriter(gin.DefaultErrorWriter, mw.GinPanicErr), mw.CorsHandler(), mw.GinParseOperationID(), GinParseToken(rpcli.NewAuthClient(authConn))) | ||||
| 	u := NewUserApi(user.NewUserClient(userConn), client, config.Share.RpcRegisterName) | ||||
| 	m := NewMessageApi(msg.NewMsgClient(msgConn), rpcli.NewUserClient(userConn), config.Share.IMAdminUserID) | ||||
| 	userRouterGroup := r.Group("/user") | ||||
| 	{ | ||||
| 		userRouterGroup.POST("/user_register", u.UserRegister) | ||||
| @ -105,9 +125,9 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 		userRouterGroup.POST("/search_notification_account", u.SearchNotificationAccount) | ||||
| 	} | ||||
| 	// friend routing group | ||||
| 	friendRouterGroup := r.Group("/friend") | ||||
| 	{ | ||||
| 		f := NewFriendApi(*friendRpc) | ||||
| 		f := NewFriendApi(relation.NewFriendClient(friendConn)) | ||||
| 		friendRouterGroup := r.Group("/friend") | ||||
| 		friendRouterGroup.POST("/delete_friend", f.DeleteFriend) | ||||
| 		friendRouterGroup.POST("/get_friend_apply_list", f.GetFriendApplyList) | ||||
| 		friendRouterGroup.POST("/get_designated_friend_apply", f.GetDesignatedFriendsApply) | ||||
| @ -129,10 +149,12 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 		friendRouterGroup.POST("/update_friends", f.UpdateFriends) | ||||
| 		friendRouterGroup.POST("/get_incremental_friends", f.GetIncrementalFriends) | ||||
| 		friendRouterGroup.POST("/get_full_friend_user_ids", f.GetFullFriendUserIDs) | ||||
| 		friendRouterGroup.POST("/get_self_unhandled_apply_count", f.GetSelfUnhandledApplyCount) | ||||
| 	} | ||||
| 	g := NewGroupApi(*groupRpc) | ||||
| 	groupRouterGroup := r.Group("/group") | ||||
| 
 | ||||
| 	g := NewGroupApi(group.NewGroupClient(groupConn)) | ||||
| 	{ | ||||
| 		groupRouterGroup := r.Group("/group") | ||||
| 		groupRouterGroup.POST("/create_group", g.CreateGroup) | ||||
| 		groupRouterGroup.POST("/set_group_info", g.SetGroupInfo) | ||||
| 		groupRouterGroup.POST("/set_group_info_ex", g.SetGroupInfoEx) | ||||
| @ -164,20 +186,22 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 		groupRouterGroup.POST("/get_incremental_group_members_batch", g.GetIncrementalGroupMemberBatch) | ||||
| 		groupRouterGroup.POST("/get_full_group_member_user_ids", g.GetFullGroupMemberUserIDs) | ||||
| 		groupRouterGroup.POST("/get_full_join_group_ids", g.GetFullJoinGroupIDs) | ||||
| 		groupRouterGroup.POST("/get_group_application_unhandled_count", g.GetGroupApplicationUnhandledCount) | ||||
| 	} | ||||
| 	// certificate | ||||
| 	authRouterGroup := r.Group("/auth") | ||||
| 	{ | ||||
| 		a := NewAuthApi(*authRpc) | ||||
| 		a := NewAuthApi(pbAuth.NewAuthClient(authConn)) | ||||
| 		authRouterGroup := r.Group("/auth") | ||||
| 		authRouterGroup.POST("/get_admin_token", a.GetAdminToken) | ||||
| 		authRouterGroup.POST("/get_user_token", a.GetUserToken) | ||||
| 		authRouterGroup.POST("/parse_token", a.ParseToken) | ||||
| 		authRouterGroup.POST("/force_logout", a.ForceLogout) | ||||
| 
 | ||||
| 	} | ||||
| 	// Third service | ||||
| 	thirdGroup := r.Group("/third") | ||||
| 	{ | ||||
| 		t := NewThirdApi(*thirdRpc) | ||||
| 		t := NewThirdApi(third.NewThirdClient(thirdConn), config.API.Prometheus.GrafanaURL) | ||||
| 		thirdGroup := r.Group("/third") | ||||
| 		thirdGroup.GET("/prometheus", t.GetPrometheus) | ||||
| 		thirdGroup.POST("/fcm_update_token", t.FcmUpdateToken) | ||||
| 		thirdGroup.POST("/set_app_badge", t.SetAppBadge) | ||||
| @ -200,8 +224,8 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 		objectGroup.GET("/*name", t.ObjectRedirect) | ||||
| 	} | ||||
| 	// Message | ||||
| 	msgGroup := r.Group("/msg") | ||||
| 	{ | ||||
| 		msgGroup := r.Group("/msg") | ||||
| 		msgGroup.POST("/newest_seq", m.GetSeq) | ||||
| 		msgGroup.POST("/search_msg", m.SearchMsg) | ||||
| 		msgGroup.POST("/send_msg", m.SendMessage) | ||||
| @ -224,9 +248,9 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 		msgGroup.POST("/get_server_time", m.GetServerTime) | ||||
| 	} | ||||
| 	// Conversation | ||||
| 	conversationGroup := r.Group("/conversation") | ||||
| 	{ | ||||
| 		c := NewConversationApi(*conversationRpc) | ||||
| 		c := NewConversationApi(conversation.NewConversationClient(conversationConn)) | ||||
| 		conversationGroup := r.Group("/conversation") | ||||
| 		conversationGroup.POST("/get_sorted_conversation_list", c.GetSortedConversationList) | ||||
| 		conversationGroup.POST("/get_all_conversations", c.GetAllConversations) | ||||
| 		conversationGroup.POST("/get_conversation", c.GetConversation) | ||||
| @ -240,22 +264,40 @@ func newGinRouter(disCov discovery.SvcDiscoveryRegistry, config *Config) *gin.En | ||||
| 		conversationGroup.POST("/get_pinned_conversation_ids", c.GetPinnedConversationIDs) | ||||
| 	} | ||||
| 
 | ||||
| 	statisticsGroup := r.Group("/statistics") | ||||
| 	{ | ||||
| 		statisticsGroup := r.Group("/statistics") | ||||
| 		statisticsGroup.POST("/user/register", u.UserRegisterCount) | ||||
| 		statisticsGroup.POST("/user/active", m.GetActiveUser) | ||||
| 		statisticsGroup.POST("/group/create", g.GroupCreateCount) | ||||
| 		statisticsGroup.POST("/group/active", m.GetActiveGroup) | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		j := jssdk.NewJSSdkApi(rpcli.NewUserClient(userConn), rpcli.NewRelationClient(friendConn), | ||||
| 			rpcli.NewGroupClient(groupConn), rpcli.NewConversationClient(conversationConn), rpcli.NewMsgClient(msgConn)) | ||||
| 		jssdk := r.Group("/jssdk") | ||||
| 		jssdk.POST("/get_conversations", j.GetConversations) | ||||
| 		jssdk.POST("/get_active_conversations", j.GetActiveConversations) | ||||
| 
 | ||||
| 	return r | ||||
| 	} | ||||
| 	{ | ||||
| 		pd := NewPrometheusDiscoveryApi(config, client) | ||||
| 		proDiscoveryGroup := r.Group("/prometheus_discovery", pd.Enable) | ||||
| 		proDiscoveryGroup.GET("/api", pd.Api) | ||||
| 		proDiscoveryGroup.GET("/user", pd.User) | ||||
| 		proDiscoveryGroup.GET("/group", pd.Group) | ||||
| 		proDiscoveryGroup.GET("/msg", pd.Msg) | ||||
| 		proDiscoveryGroup.GET("/friend", pd.Friend) | ||||
| 		proDiscoveryGroup.GET("/conversation", pd.Conversation) | ||||
| 		proDiscoveryGroup.GET("/third", pd.Third) | ||||
| 		proDiscoveryGroup.GET("/auth", pd.Auth) | ||||
| 		proDiscoveryGroup.GET("/push", pd.Push) | ||||
| 		proDiscoveryGroup.GET("/msg_gateway", pd.MessageGateway) | ||||
| 		proDiscoveryGroup.GET("/msg_transfer", pd.MessageTransfer) | ||||
| 	} | ||||
| 	return r, nil | ||||
| } | ||||
| 
 | ||||
| func GinParseToken(authRPC *rpcclient.Auth) gin.HandlerFunc { | ||||
| func GinParseToken(authClient *rpcli.AuthClient) gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		switch c.Request.Method { | ||||
| 		case http.MethodPost: | ||||
| @ -273,7 +315,7 @@ func GinParseToken(authRPC *rpcclient.Auth) gin.HandlerFunc { | ||||
| 				c.Abort() | ||||
| 				return | ||||
| 			} | ||||
| 			resp, err := authRPC.ParseToken(c, token) | ||||
| 			resp, err := authClient.ParseToken(c, token) | ||||
| 			if err != nil { | ||||
| 				apiresp.GinError(c, err) | ||||
| 				c.Abort() | ||||
|  | ||||
| @ -1,32 +0,0 @@ | ||||
| // Copyright © 2023 OpenIM. All rights reserved. | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"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 | ||||
| 
 | ||||
| func NewStatisticsApi(client rpcclient.User) StatisticsApi { | ||||
| 	return StatisticsApi(client) | ||||
| } | ||||
| 
 | ||||
| func (s *StatisticsApi) UserRegister(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.UserRegisterCount, s.Client, c) | ||||
| } | ||||
| @ -24,25 +24,27 @@ import ( | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"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 | ||||
| type ThirdApi struct { | ||||
| 	GrafanaUrl string | ||||
| 	Client     third.ThirdClient | ||||
| } | ||||
| 
 | ||||
| func NewThirdApi(client rpcclient.Third) ThirdApi { | ||||
| 	return ThirdApi(client) | ||||
| func NewThirdApi(client third.ThirdClient, grafanaUrl string) ThirdApi { | ||||
| 	return ThirdApi{Client: client, GrafanaUrl: grafanaUrl} | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) FcmUpdateToken(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.FcmUpdateToken, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.FcmUpdateToken, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) SetAppBadge(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.SetAppBadge, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.SetAppBadge, o.Client) | ||||
| } | ||||
| 
 | ||||
| // #################### s3 #################### | ||||
| @ -77,44 +79,44 @@ func setURLPrefix(c *gin.Context, urlPrefix *string) error { | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) PartLimit(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.PartLimit, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.PartLimit, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) PartSize(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.PartSize, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.PartSize, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) InitiateMultipartUpload(c *gin.Context) { | ||||
| 	opt := setURLPrefixOption(third.ThirdClient.InitiateMultipartUpload, func(req *third.InitiateMultipartUploadReq) error { | ||||
| 		return setURLPrefix(c, &req.UrlPrefix) | ||||
| 	}) | ||||
| 	a2r.Call(third.ThirdClient.InitiateMultipartUpload, o.Client, c, opt) | ||||
| 	a2r.Call(c, third.ThirdClient.InitiateMultipartUpload, o.Client, opt) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) AuthSign(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.AuthSign, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.AuthSign, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) CompleteMultipartUpload(c *gin.Context) { | ||||
| 	opt := setURLPrefixOption(third.ThirdClient.CompleteMultipartUpload, func(req *third.CompleteMultipartUploadReq) error { | ||||
| 		return setURLPrefix(c, &req.UrlPrefix) | ||||
| 	}) | ||||
| 	a2r.Call(third.ThirdClient.CompleteMultipartUpload, o.Client, c, opt) | ||||
| 	a2r.Call(c, third.ThirdClient.CompleteMultipartUpload, o.Client, opt) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) AccessURL(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.AccessURL, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.AccessURL, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) InitiateFormData(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.InitiateFormData, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.InitiateFormData, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) CompleteFormData(c *gin.Context) { | ||||
| 	opt := setURLPrefixOption(third.ThirdClient.CompleteFormData, func(req *third.CompleteFormDataReq) error { | ||||
| 		return setURLPrefix(c, &req.UrlPrefix) | ||||
| 	}) | ||||
| 	a2r.Call(third.ThirdClient.CompleteFormData, o.Client, c, opt) | ||||
| 	a2r.Call(c, third.ThirdClient.CompleteFormData, o.Client, opt) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) ObjectRedirect(c *gin.Context) { | ||||
| @ -156,15 +158,15 @@ func (o *ThirdApi) ObjectRedirect(c *gin.Context) { | ||||
| 
 | ||||
| // #################### logs ####################. | ||||
| func (o *ThirdApi) UploadLogs(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.UploadLogs, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.UploadLogs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) DeleteLogs(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.DeleteLogs, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.DeleteLogs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) SearchLogs(c *gin.Context) { | ||||
| 	a2r.Call(third.ThirdClient.SearchLogs, o.Client, c) | ||||
| 	a2r.Call(c, third.ThirdClient.SearchLogs, o.Client) | ||||
| } | ||||
| 
 | ||||
| func (o *ThirdApi) GetPrometheus(c *gin.Context) { | ||||
|  | ||||
| @ -16,52 +16,57 @@ package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"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/discovery" | ||||
| 	"github.com/openimsdk/tools/errs" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| ) | ||||
| 
 | ||||
| type UserApi rpcclient.User | ||||
| type UserApi struct { | ||||
| 	Client user.UserClient | ||||
| 	discov discovery.SvcDiscoveryRegistry | ||||
| 	config config.RpcRegisterName | ||||
| } | ||||
| 
 | ||||
| func NewUserApi(client rpcclient.User) UserApi { | ||||
| 	return UserApi(client) | ||||
| func NewUserApi(client user.UserClient, discov discovery.SvcDiscoveryRegistry, config config.RpcRegisterName) UserApi { | ||||
| 	return UserApi{Client: client, discov: discov, config: config} | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) UserRegister(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.UserRegister, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.UserRegister, u.Client) | ||||
| } | ||||
| 
 | ||||
| // UpdateUserInfo is deprecated. Use UpdateUserInfoEx | ||||
| func (u *UserApi) UpdateUserInfo(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.UpdateUserInfo, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.UpdateUserInfo, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) UpdateUserInfoEx(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.UpdateUserInfoEx, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.UpdateUserInfoEx, u.Client) | ||||
| } | ||||
| func (u *UserApi) SetGlobalRecvMessageOpt(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.SetGlobalRecvMessageOpt, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.SetGlobalRecvMessageOpt, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) GetUsersPublicInfo(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.GetDesignateUsers, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.GetDesignateUsers, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) GetAllUsersID(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.GetAllUserID, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.GetAllUserID, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) AccountCheck(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.AccountCheck, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.AccountCheck, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) GetUsers(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.GetPaginationUsers, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.GetPaginationUsers, u.Client) | ||||
| } | ||||
| 
 | ||||
| // GetUsersOnlineStatus Get user online status. | ||||
| @ -71,7 +76,7 @@ func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) { | ||||
| 		apiresp.GinError(c, err) | ||||
| 		return | ||||
| 	} | ||||
| 	conns, err := u.Discov.GetConns(c, u.MessageGateWayRpcName) | ||||
| 	conns, err := u.discov.GetConns(c, u.config.MessageGateway) | ||||
| 	if err != nil { | ||||
| 		apiresp.GinError(c, err) | ||||
| 		return | ||||
| @ -122,7 +127,7 @@ func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) { | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) UserRegisterCount(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.UserRegisterCount, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.UserRegisterCount, u.Client) | ||||
| } | ||||
| 
 | ||||
| // GetUsersOnlineTokenDetail Get user online token details. | ||||
| @ -135,7 +140,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.MessageGateWayRpcName) | ||||
| 	conns, err := u.discov.GetConns(c, u.config.MessageGateway) | ||||
| 	if err != nil { | ||||
| 		apiresp.GinError(c, err) | ||||
| 		return | ||||
| @ -188,52 +193,52 @@ func (u *UserApi) GetUsersOnlineTokenDetail(c *gin.Context) { | ||||
| 
 | ||||
| // SubscriberStatus Presence status of subscribed users. | ||||
| func (u *UserApi) SubscriberStatus(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.SubscribeOrCancelUsersStatus, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.SubscribeOrCancelUsersStatus, u.Client) | ||||
| } | ||||
| 
 | ||||
| // GetUserStatus Get the online status of the user. | ||||
| func (u *UserApi) GetUserStatus(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.GetUserStatus, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.GetUserStatus, u.Client) | ||||
| } | ||||
| 
 | ||||
| // GetSubscribeUsersStatus Get the online status of subscribers. | ||||
| func (u *UserApi) GetSubscribeUsersStatus(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.GetSubscribeUsersStatus, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.GetSubscribeUsersStatus, u.Client) | ||||
| } | ||||
| 
 | ||||
| // ProcessUserCommandAdd user general function add. | ||||
| func (u *UserApi) ProcessUserCommandAdd(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.ProcessUserCommandAdd, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.ProcessUserCommandAdd, u.Client) | ||||
| } | ||||
| 
 | ||||
| // ProcessUserCommandDelete user general function delete. | ||||
| func (u *UserApi) ProcessUserCommandDelete(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.ProcessUserCommandDelete, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.ProcessUserCommandDelete, u.Client) | ||||
| } | ||||
| 
 | ||||
| // ProcessUserCommandUpdate  user general function update. | ||||
| func (u *UserApi) ProcessUserCommandUpdate(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.ProcessUserCommandUpdate, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.ProcessUserCommandUpdate, u.Client) | ||||
| } | ||||
| 
 | ||||
| // ProcessUserCommandGet user general function get. | ||||
| func (u *UserApi) ProcessUserCommandGet(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.ProcessUserCommandGet, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.ProcessUserCommandGet, u.Client) | ||||
| } | ||||
| 
 | ||||
| // ProcessUserCommandGet user general function get all. | ||||
| func (u *UserApi) ProcessUserCommandGetAll(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.ProcessUserCommandGetAll, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.ProcessUserCommandGetAll, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) AddNotificationAccount(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.AddNotificationAccount, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.AddNotificationAccount, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) UpdateNotificationAccountInfo(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.UpdateNotificationAccountInfo, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.UpdateNotificationAccountInfo, u.Client) | ||||
| } | ||||
| 
 | ||||
| func (u *UserApi) SearchNotificationAccount(c *gin.Context) { | ||||
| 	a2r.Call(user.UserClient.SearchNotificationAccount, u.Client, c) | ||||
| 	a2r.Call(c, user.UserClient.SearchNotificationAccount, u.Client) | ||||
| } | ||||
|  | ||||
| @ -16,8 +16,8 @@ package msggateway | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"runtime/debug" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| @ -69,6 +69,8 @@ type Client struct { | ||||
| 	IsCompress     bool   `json:"isCompress"` | ||||
| 	UserID         string `json:"userID"` | ||||
| 	IsBackground   bool   `json:"isBackground"` | ||||
| 	SDKType        string `json:"sdkType"` | ||||
| 	Encoder        Encoder | ||||
| 	ctx            *UserConnContext | ||||
| 	longConnServer LongConnServer | ||||
| 	closed         atomic.Bool | ||||
| @ -94,11 +96,17 @@ func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, longConnServer | ||||
| 	c.closed.Store(false) | ||||
| 	c.closedErr = nil | ||||
| 	c.token = ctx.GetToken() | ||||
| 	c.SDKType = ctx.GetSDKType() | ||||
| 	c.hbCtx, c.hbCancel = context.WithCancel(c.ctx) | ||||
| 	c.subLock = new(sync.Mutex) | ||||
| 	if c.subUserIDs != nil { | ||||
| 		clear(c.subUserIDs) | ||||
| 	} | ||||
| 	if c.SDKType == GoSDK { | ||||
| 		c.Encoder = NewGobEncoder() | ||||
| 	} else { | ||||
| 		c.Encoder = NewJsonEncoder() | ||||
| 	} | ||||
| 	c.subUserIDs = make(map[string]struct{}) | ||||
| } | ||||
| 
 | ||||
| @ -123,7 +131,7 @@ func (c *Client) readMessage() { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			c.closedErr = ErrPanic | ||||
| 			fmt.Println("socket have panic err:", r, string(debug.Stack())) | ||||
| 			log.ZPanic(c.ctx, "socket have panic err:", errs.ErrPanic(r)) | ||||
| 		} | ||||
| 		c.close() | ||||
| 	}() | ||||
| @ -159,9 +167,12 @@ func (c *Client) readMessage() { | ||||
| 				return | ||||
| 			} | ||||
| 		case MessageText: | ||||
| 			c.closedErr = ErrNotSupportMessageProtocol | ||||
| 			_ = c.conn.SetReadDeadline(pongWait) | ||||
| 			parseDataErr := c.handlerTextMessage(message) | ||||
| 			if parseDataErr != nil { | ||||
| 				c.closedErr = parseDataErr | ||||
| 				return | ||||
| 
 | ||||
| 			} | ||||
| 		case PingMessage: | ||||
| 			err := c.writePongMsg("") | ||||
| 			log.ZError(c.ctx, "writePongMsg", err) | ||||
| @ -188,7 +199,7 @@ func (c *Client) handleMessage(message []byte) error { | ||||
| 	var binaryReq = getReq() | ||||
| 	defer freeReq(binaryReq) | ||||
| 
 | ||||
| 	err := c.longConnServer.Decode(message, binaryReq) | ||||
| 	err := c.Encoder.Decode(message, binaryReq) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -225,6 +236,8 @@ func (c *Client) handleMessage(message []byte) error { | ||||
| 		resp, messageErr = c.longConnServer.GetSeqMessage(ctx, binaryReq) | ||||
| 	case WSGetConvMaxReadSeq: | ||||
| 		resp, messageErr = c.longConnServer.GetConversationsHasReadAndMaxSeq(ctx, binaryReq) | ||||
| 	case WsPullConvLastMessage: | ||||
| 		resp, messageErr = c.longConnServer.GetLastMessage(ctx, binaryReq) | ||||
| 	case WsLogoutMsg: | ||||
| 		resp, messageErr = c.longConnServer.UserLogout(ctx, binaryReq) | ||||
| 	case WsSetBackgroundStatus: | ||||
| @ -335,7 +348,7 @@ func (c *Client) writeBinaryMsg(resp Resp) error { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	encodedBuf, err := c.longConnServer.Encode(resp) | ||||
| 	encodedBuf, err := c.Encoder.Encode(resp) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -363,6 +376,11 @@ func (c *Client) writeBinaryMsg(resp Resp) error { | ||||
| func (c *Client) activeHeartbeat(ctx context.Context) { | ||||
| 	if c.PlatformID == constant.WebPlatformID { | ||||
| 		go func() { | ||||
| 			defer func() { | ||||
| 				if r := recover(); r != nil { | ||||
| 					log.ZPanic(ctx, "activeHeartbeat Panic", errs.ErrPanic(r)) | ||||
| 				} | ||||
| 			}() | ||||
| 			log.ZDebug(ctx, "server initiative send heartbeat start.") | ||||
| 			ticker := time.NewTicker(pingPeriod) | ||||
| 			defer ticker.Stop() | ||||
| @ -419,3 +437,28 @@ func (c *Client) writePongMsg(appData string) error { | ||||
| 
 | ||||
| 	return errs.Wrap(err) | ||||
| } | ||||
| 
 | ||||
| func (c *Client) handlerTextMessage(b []byte) error { | ||||
| 	var msg TextMessage | ||||
| 	if err := json.Unmarshal(b, &msg); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	switch msg.Type { | ||||
| 	case TextPong: | ||||
| 		return nil | ||||
| 	case TextPing: | ||||
| 		msg.Type = TextPong | ||||
| 		msgData, err := json.Marshal(msg) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		c.w.Lock() | ||||
| 		defer c.w.Unlock() | ||||
| 		if err := c.conn.SetWriteDeadline(writeWait); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return c.conn.WriteMessage(MessageText, msgData) | ||||
| 	default: | ||||
| 		return fmt.Errorf("not support message type %s", msg.Type) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -27,6 +27,12 @@ const ( | ||||
| 	GzipCompressionProtocol = "gzip" | ||||
| 	BackgroundStatus        = "isBackground" | ||||
| 	SendResponse            = "isMsgResp" | ||||
| 	SDKType                 = "sdkType" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	GoSDK = "go" | ||||
| 	JsSDK = "js" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @ -41,6 +47,7 @@ const ( | ||||
| 	WSSendSignalMsg       = 1004 | ||||
| 	WSPullMsg             = 1005 | ||||
| 	WSGetConvMaxReadSeq   = 1006 | ||||
| 	WsPullConvLastMessage = 1007 | ||||
| 	WSPushMsg             = 2001 | ||||
| 	WSKickOnlineMsg       = 2002 | ||||
| 	WsLogoutMsg           = 2003 | ||||
|  | ||||
| @ -153,6 +153,14 @@ func (c *UserConnContext) GetCompression() bool { | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (c *UserConnContext) GetSDKType() string { | ||||
| 	sdkType := c.Req.URL.Query().Get(SDKType) | ||||
| 	if sdkType == "" { | ||||
| 		sdkType = GoSDK | ||||
| 	} | ||||
| 	return sdkType | ||||
| } | ||||
| 
 | ||||
| func (c *UserConnContext) ShouldSendResp() bool { | ||||
| 	errResp, exists := c.Query(SendResponse) | ||||
| 	if exists { | ||||
| @ -193,7 +201,11 @@ func (c *UserConnContext) ParseEssentialArgs() error { | ||||
| 	_, err := strconv.Atoi(platformIDStr) | ||||
| 	if err != nil { | ||||
| 		return servererrs.ErrConnArgsErr.WrapMsg("platformID is not int") | ||||
| 
 | ||||
| 	} | ||||
| 	switch sdkType, _ := c.Query(SDKType); sdkType { | ||||
| 	case "", GoSDK, JsSDK: | ||||
| 	default: | ||||
| 		return servererrs.ErrConnArgsErr.WrapMsg("sdkType is not go or js") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -17,6 +17,7 @@ package msggateway | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"encoding/gob" | ||||
| 	"encoding/json" | ||||
| 
 | ||||
| 	"github.com/openimsdk/tools/errs" | ||||
| ) | ||||
| @ -28,12 +29,12 @@ type Encoder interface { | ||||
| 
 | ||||
| type GobEncoder struct{} | ||||
| 
 | ||||
| func NewGobEncoder() *GobEncoder { | ||||
| 	return &GobEncoder{} | ||||
| func NewGobEncoder() Encoder { | ||||
| 	return GobEncoder{} | ||||
| } | ||||
| 
 | ||||
| func (g *GobEncoder) Encode(data any) ([]byte, error) { | ||||
| 	buff := bytes.Buffer{} | ||||
| func (g GobEncoder) Encode(data any) ([]byte, error) { | ||||
| 	var buff bytes.Buffer | ||||
| 	enc := gob.NewEncoder(&buff) | ||||
| 	if err := enc.Encode(data); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "GobEncoder.Encode failed", "action", "encode") | ||||
| @ -41,7 +42,7 @@ func (g *GobEncoder) Encode(data any) ([]byte, error) { | ||||
| 	return buff.Bytes(), nil | ||||
| } | ||||
| 
 | ||||
| func (g *GobEncoder) Decode(encodeData []byte, decodeData any) error { | ||||
| func (g GobEncoder) Decode(encodeData []byte, decodeData any) error { | ||||
| 	buff := bytes.NewBuffer(encodeData) | ||||
| 	dec := gob.NewDecoder(buff) | ||||
| 	if err := dec.Decode(decodeData); err != nil { | ||||
| @ -49,3 +50,25 @@ func (g *GobEncoder) Decode(encodeData []byte, decodeData any) error { | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| type JsonEncoder struct{} | ||||
| 
 | ||||
| func NewJsonEncoder() Encoder { | ||||
| 	return JsonEncoder{} | ||||
| } | ||||
| 
 | ||||
| func (g JsonEncoder) Encode(data any) ([]byte, error) { | ||||
| 	b, err := json.Marshal(data) | ||||
| 	if err != nil { | ||||
| 		return nil, errs.New("JsonEncoder.Encode failed", "action", "encode") | ||||
| 	} | ||||
| 	return b, nil | ||||
| } | ||||
| 
 | ||||
| func (g JsonEncoder) Decode(encodeData []byte, decodeData any) error { | ||||
| 	err := json.Unmarshal(encodeData, decodeData) | ||||
| 	if err != nil { | ||||
| 		return errs.New("JsonEncoder.Decode failed", "action", "decode") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -18,10 +18,11 @@ import ( | ||||
| 	"context" | ||||
| 	"sync/atomic" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/authverify" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	"github.com/openimsdk/protocol/msggateway" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| @ -35,9 +36,15 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| func (s *Server) InitServer(ctx context.Context, config *Config, disCov discovery.SvcDiscoveryRegistry, server *grpc.Server) error { | ||||
| 	s.LongConnServer.SetDiscoveryRegistry(disCov, config) | ||||
| 	userConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.User) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	s.userClient = rpcli.NewUserClient(userConn) | ||||
| 	if err := s.LongConnServer.SetDiscoveryRegistry(ctx, disCov, config); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	msggateway.RegisterMsgGatewayServer(server, s) | ||||
| 	s.userRcp = rpcclient.NewUserRpcClient(disCov, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID) | ||||
| 	if s.ready != nil { | ||||
| 		return s.ready(s) | ||||
| 	} | ||||
| @ -47,31 +54,35 @@ func (s *Server) InitServer(ctx context.Context, config *Config, disCov discover | ||||
| func (s *Server) Start(ctx context.Context, index int, conf *Config) error { | ||||
| 	return startrpc.Start(ctx, &conf.Discovery, &conf.MsgGateway.Prometheus, conf.MsgGateway.ListenIP, | ||||
| 		conf.MsgGateway.RPC.RegisterIP, | ||||
| 		conf.MsgGateway.RPC.AutoSetPorts, | ||||
| 		conf.MsgGateway.RPC.Ports, index, | ||||
| 		conf.Share.RpcRegisterName.MessageGateway, | ||||
| 		&conf.Share, | ||||
| 		conf, | ||||
| 		[]string{ | ||||
| 			conf.Share.RpcRegisterName.MessageGateway, | ||||
| 		}, | ||||
| 		s.InitServer, | ||||
| 	) | ||||
| } | ||||
| 
 | ||||
| type Server struct { | ||||
| 	msggateway.UnimplementedMsgGatewayServer | ||||
| 	rpcPort        int | ||||
| 	LongConnServer LongConnServer | ||||
| 	config         *Config | ||||
| 	pushTerminal   map[int]struct{} | ||||
| 	ready          func(srv *Server) error | ||||
| 	userRcp        rpcclient.UserRpcClient | ||||
| 	queue          *memamq.MemoryQueue | ||||
| 	userClient     *rpcli.UserClient | ||||
| } | ||||
| 
 | ||||
| func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { | ||||
| 	s.LongConnServer = LongConnServer | ||||
| } | ||||
| 
 | ||||
| func NewServer(rpcPort int, longConnServer LongConnServer, conf *Config, ready func(srv *Server) error) *Server { | ||||
| func NewServer(longConnServer LongConnServer, conf *Config, ready func(srv *Server) error) *Server { | ||||
| 	s := &Server{ | ||||
| 		rpcPort:        rpcPort, | ||||
| 		LongConnServer: longConnServer, | ||||
| 		pushTerminal:   make(map[int]struct{}), | ||||
| 		config:         conf, | ||||
| @ -83,17 +94,7 @@ func NewServer(rpcPort int, longConnServer LongConnServer, conf *Config, ready f | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| func (s *Server) OnlinePushMsg( | ||||
| 	context context.Context, | ||||
| 	req *msggateway.OnlinePushMsgReq, | ||||
| ) (*msggateway.OnlinePushMsgResp, error) { | ||||
| 	panic("implement me") | ||||
| } | ||||
| 
 | ||||
| func (s *Server) GetUsersOnlineStatus( | ||||
| 	ctx context.Context, | ||||
| 	req *msggateway.GetUsersOnlineStatusReq, | ||||
| ) (*msggateway.GetUsersOnlineStatusResp, error) { | ||||
| func (s *Server) GetUsersOnlineStatus(ctx context.Context, req *msggateway.GetUsersOnlineStatusReq) (*msggateway.GetUsersOnlineStatusResp, error) { | ||||
| 	if !authverify.IsAppManagerUid(ctx, s.config.Share.IMAdminUserID) { | ||||
| 		return nil, errs.ErrNoPermission.WrapMsg("only app manager") | ||||
| 	} | ||||
| @ -126,11 +127,6 @@ func (s *Server) GetUsersOnlineStatus( | ||||
| 	return &resp, nil | ||||
| } | ||||
| 
 | ||||
| func (s *Server) OnlineBatchPushOneMsg(ctx context.Context, req *msggateway.OnlineBatchPushOneMsgReq) (*msggateway.OnlineBatchPushOneMsgResp, error) { | ||||
| 	// todo implement | ||||
| 	return nil, nil | ||||
| } | ||||
| 
 | ||||
| func (s *Server) pushToUser(ctx context.Context, userID string, msgData *sdkws.MsgData) *msggateway.SingleMsgToUserResults { | ||||
| 	clients, ok := s.LongConnServer.GetUserAllCons(userID) | ||||
| 	if !ok { | ||||
| @ -155,6 +151,7 @@ func (s *Server) pushToUser(ctx context.Context, userID string, msgData *sdkws.M | ||||
| 			(client.IsBackground && client.PlatformID != constant.IOSPlatformID) { | ||||
| 			err := client.PushMessage(ctx, msgData) | ||||
| 			if err != nil { | ||||
| 				log.ZWarn(ctx, "online push msg failed", err, "userID", userID, "platformID", client.PlatformID) | ||||
| 				userPlatform.ResultCode = int64(servererrs.ErrPushMsgErr.Code()) | ||||
| 			} else { | ||||
| 				if _, ok := s.pushTerminal[client.PlatformID]; ok { | ||||
| @ -220,10 +217,7 @@ func (s *Server) SuperGroupOnlineBatchPushOneMsg(ctx context.Context, req *msgga | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (s *Server) KickUserOffline( | ||||
| 	ctx context.Context, | ||||
| 	req *msggateway.KickUserOfflineReq, | ||||
| ) (*msggateway.KickUserOfflineResp, error) { | ||||
| func (s *Server) KickUserOffline(ctx context.Context, req *msggateway.KickUserOfflineReq) (*msggateway.KickUserOfflineResp, error) { | ||||
| 	for _, v := range req.KickUserIDList { | ||||
| 		clients, _, ok := s.LongConnServer.GetUserPlatformCons(v, int(req.PlatformID)) | ||||
| 		if !ok { | ||||
| @ -250,6 +244,7 @@ func (s *Server) MultiTerminalLoginCheck(ctx context.Context, req *msggateway.Mu | ||||
| 		tempUserCtx.SetOperationID(mcontext.GetOperationID(ctx)) | ||||
| 		client := &Client{} | ||||
| 		client.ctx = tempUserCtx | ||||
| 		client.token = req.Token | ||||
| 		client.UserID = req.UserID | ||||
| 		client.PlatformID = int(req.PlatformID) | ||||
| 		i := &kickHandler{ | ||||
|  | ||||
| @ -16,13 +16,13 @@ package msggateway | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpccache" | ||||
| 	"github.com/openimsdk/tools/db/redisutil" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| ) | ||||
| 
 | ||||
| type Config struct { | ||||
| @ -35,16 +35,13 @@ type Config struct { | ||||
| 
 | ||||
| // 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, | ||||
| 	log.CInfo(ctx, "MSG-GATEWAY server is initializing", "autoSetPorts", conf.MsgGateway.RPC.AutoSetPorts, | ||||
| 		"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 | ||||
| 	} | ||||
| 	rpcPort, err := datautil.GetElemByIndex(conf.MsgGateway.RPC.Ports, index) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	rdb, err := redisutil.NewRedisClient(ctx, conf.RedisConfig.Build()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @ -57,9 +54,10 @@ func Start(ctx context.Context, index int, conf *Config) error { | ||||
| 		WithMessageMaxMsgLength(conf.MsgGateway.LongConnSvr.WebsocketMaxMsgLen), | ||||
| 	) | ||||
| 
 | ||||
| 	hubServer := NewServer(rpcPort, longServer, conf, func(srv *Server) error { | ||||
| 		longServer.online, _ = rpccache.NewOnlineCache(srv.userRcp, nil, rdb, false, longServer.subscriberUserOnlineStatusChanges) | ||||
| 		return nil | ||||
| 	hubServer := NewServer(longServer, conf, func(srv *Server) error { | ||||
| 		var err error | ||||
| 		longServer.online, err = rpccache.NewOnlineCache(srv.userClient, nil, rdb, false, longServer.subscriberUserOnlineStatusChanges) | ||||
| 		return err | ||||
| 	}) | ||||
| 
 | ||||
| 	go longServer.ChangeOnlineStatus(4) | ||||
|  | ||||
| @ -16,21 +16,30 @@ package msggateway | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/go-playground/validator/v10" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| 
 | ||||
| 	"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" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	TextPing = "ping" | ||||
| 	TextPong = "pong" | ||||
| ) | ||||
| 
 | ||||
| type TextMessage struct { | ||||
| 	Type string          `json:"type"` | ||||
| 	Body json.RawMessage `json:"body"` | ||||
| } | ||||
| 
 | ||||
| type Req struct { | ||||
| 	ReqIdentifier int32  `json:"reqIdentifier" validate:"required"` | ||||
| 	Token         string `json:"token"` | ||||
| @ -91,34 +100,34 @@ func (r *Resp) String() string { | ||||
| } | ||||
| 
 | ||||
| type MessageHandler interface { | ||||
| 	GetSeq(context context.Context, data *Req) ([]byte, error) | ||||
| 	SendMessage(context context.Context, data *Req) ([]byte, error) | ||||
| 	SendSignalMessage(context context.Context, data *Req) ([]byte, error) | ||||
| 	PullMessageBySeqList(context context.Context, data *Req) ([]byte, error) | ||||
| 	GetConversationsHasReadAndMaxSeq(context context.Context, data *Req) ([]byte, error) | ||||
| 	GetSeqMessage(context context.Context, data *Req) ([]byte, error) | ||||
| 	UserLogout(context context.Context, data *Req) ([]byte, error) | ||||
| 	SetUserDeviceBackground(context context.Context, data *Req) ([]byte, bool, error) | ||||
| 	GetSeq(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	SendMessage(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	SendSignalMessage(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	PullMessageBySeqList(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	GetConversationsHasReadAndMaxSeq(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	GetSeqMessage(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	UserLogout(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	SetUserDeviceBackground(ctx context.Context, data *Req) ([]byte, bool, error) | ||||
| 	GetLastMessage(ctx context.Context, data *Req) ([]byte, error) | ||||
| } | ||||
| 
 | ||||
| var _ MessageHandler = (*GrpcHandler)(nil) | ||||
| 
 | ||||
| type GrpcHandler struct { | ||||
| 	msgRpcClient *rpcclient.MessageRpcClient | ||||
| 	pushClient   *rpcclient.PushRpcClient | ||||
| 	validate   *validator.Validate | ||||
| 	msgClient  *rpcli.MsgClient | ||||
| 	pushClient *rpcli.PushMsgServiceClient | ||||
| } | ||||
| 
 | ||||
| func NewGrpcHandler(validate *validator.Validate, client discovery.SvcDiscoveryRegistry, rpcRegisterName *config.RpcRegisterName) *GrpcHandler { | ||||
| 	msgRpcClient := rpcclient.NewMessageRpcClient(client, rpcRegisterName.Msg) | ||||
| 	pushRpcClient := rpcclient.NewPushRpcClient(client, rpcRegisterName.Push) | ||||
| func NewGrpcHandler(validate *validator.Validate, msgClient *rpcli.MsgClient, pushClient *rpcli.PushMsgServiceClient) *GrpcHandler { | ||||
| 	return &GrpcHandler{ | ||||
| 		msgRpcClient: &msgRpcClient, | ||||
| 		pushClient:   &pushRpcClient, validate: validate, | ||||
| 		validate:   validate, | ||||
| 		msgClient:  msgClient, | ||||
| 		pushClient: pushClient, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) GetSeq(ctx 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.WrapMsg(err, "GetSeq: error unmarshaling request", "action", "unmarshal", "dataType", "GetMaxSeqReq") | ||||
| @ -126,7 +135,7 @@ func (g GrpcHandler) GetSeq(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	if err := g.validate.Struct(&req); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "GetSeq: validation failed", "action", "validate", "dataType", "GetMaxSeqReq") | ||||
| 	} | ||||
| 	resp, err := g.msgRpcClient.GetMaxSeq(ctx, &req) | ||||
| 	resp, err := g.msgClient.MsgClient.GetMaxSeq(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -139,7 +148,7 @@ func (g GrpcHandler) GetSeq(ctx 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) { | ||||
| func (g *GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	var msgData sdkws.MsgData | ||||
| 	if err := proto.Unmarshal(data.Data, &msgData); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "SendMessage: error unmarshaling message data", "action", "unmarshal", "dataType", "MsgData") | ||||
| @ -150,7 +159,7 @@ func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	} | ||||
| 
 | ||||
| 	req := msg.SendMsgReq{MsgData: &msgData} | ||||
| 	resp, err := g.msgRpcClient.SendMsg(ctx, &req) | ||||
| 	resp, err := g.msgClient.MsgClient.SendMsg(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -163,8 +172,8 @@ func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error) | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]byte, error) { | ||||
| 	resp, err := g.msgRpcClient.SendMsg(context, nil) | ||||
| func (g *GrpcHandler) SendSignalMessage(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	resp, err := g.msgClient.MsgClient.SendMsg(ctx, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -175,7 +184,7 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([]byte, error) { | ||||
| func (g *GrpcHandler) PullMessageBySeqList(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	req := sdkws.PullMessageBySeqsReq{} | ||||
| 	if err := proto.Unmarshal(data.Data, &req); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "err proto unmarshal", "action", "unmarshal", "dataType", "PullMessageBySeqsReq") | ||||
| @ -183,7 +192,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([ | ||||
| 	if err := g.validate.Struct(data); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "PullMessageBySeqsReq") | ||||
| 	} | ||||
| 	resp, err := g.msgRpcClient.PullMessageBySeqList(context, &req) | ||||
| 	resp, err := g.msgClient.MsgClient.PullMessageBySeqs(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -194,7 +203,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([ | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) GetConversationsHasReadAndMaxSeq(context context.Context, data *Req) ([]byte, error) { | ||||
| func (g *GrpcHandler) GetConversationsHasReadAndMaxSeq(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	req := msg.GetConversationsHasReadAndMaxSeqReq{} | ||||
| 	if err := proto.Unmarshal(data.Data, &req); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "err proto unmarshal", "action", "unmarshal", "dataType", "GetConversationsHasReadAndMaxSeq") | ||||
| @ -202,7 +211,7 @@ func (g GrpcHandler) GetConversationsHasReadAndMaxSeq(context context.Context, d | ||||
| 	if err := g.validate.Struct(data); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "GetConversationsHasReadAndMaxSeq") | ||||
| 	} | ||||
| 	resp, err := g.msgRpcClient.GetConversationsHasReadAndMaxSeq(context, &req) | ||||
| 	resp, err := g.msgClient.MsgClient.GetConversationsHasReadAndMaxSeq(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -213,7 +222,7 @@ func (g GrpcHandler) GetConversationsHasReadAndMaxSeq(context context.Context, d | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) GetSeqMessage(context context.Context, data *Req) ([]byte, error) { | ||||
| func (g *GrpcHandler) GetSeqMessage(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	req := msg.GetSeqMessageReq{} | ||||
| 	if err := proto.Unmarshal(data.Data, &req); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "GetSeqMessage") | ||||
| @ -221,7 +230,7 @@ func (g GrpcHandler) GetSeqMessage(context context.Context, data *Req) ([]byte, | ||||
| 	if err := g.validate.Struct(data); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "validation failed", "action", "validate", "dataType", "GetSeqMessage") | ||||
| 	} | ||||
| 	resp, err := g.msgRpcClient.GetSeqMessage(context, &req) | ||||
| 	resp, err := g.msgClient.MsgClient.GetSeqMessage(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -232,12 +241,12 @@ func (g GrpcHandler) GetSeqMessage(context context.Context, data *Req) ([]byte, | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, error) { | ||||
| func (g *GrpcHandler) UserLogout(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	req := push.DelUserPushTokenReq{} | ||||
| 	if err := proto.Unmarshal(data.Data, &req); err != nil { | ||||
| 		return nil, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "DelUserPushTokenReq") | ||||
| 	} | ||||
| 	resp, err := g.pushClient.DelUserPushToken(context, &req) | ||||
| 	resp, err := g.pushClient.PushMsgServiceClient.DelUserPushToken(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -248,7 +257,7 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err | ||||
| 	return c, nil | ||||
| } | ||||
| 
 | ||||
| func (g GrpcHandler) SetUserDeviceBackground(_ context.Context, data *Req) ([]byte, bool, error) { | ||||
| func (g *GrpcHandler) SetUserDeviceBackground(ctx context.Context, data *Req) ([]byte, bool, error) { | ||||
| 	req := sdkws.SetAppBackgroundStatusReq{} | ||||
| 	if err := proto.Unmarshal(data.Data, &req); err != nil { | ||||
| 		return nil, false, errs.WrapMsg(err, "error unmarshaling request", "action", "unmarshal", "dataType", "SetAppBackgroundStatusReq") | ||||
| @ -258,3 +267,15 @@ func (g GrpcHandler) SetUserDeviceBackground(_ context.Context, data *Req) ([]by | ||||
| 	} | ||||
| 	return nil, req.IsBackground, nil | ||||
| } | ||||
| 
 | ||||
| func (g *GrpcHandler) GetLastMessage(ctx context.Context, data *Req) ([]byte, error) { | ||||
| 	var req msg.GetLastMessageReq | ||||
| 	if err := proto.Unmarshal(data.Data, &req); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	resp, err := g.msgClient.GetLastMessage(ctx, &req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return proto.Marshal(resp) | ||||
| } | ||||
|  | ||||
| @ -87,9 +87,22 @@ func (ws *WsServer) ChangeOnlineStatus(concurrent int) { | ||||
| 		opIdCtx := mcontext.SetOperationID(context.Background(), operationIDPrefix+strconv.FormatInt(count.Add(1), 10)) | ||||
| 		ctx, cancel := context.WithTimeout(opIdCtx, time.Second*5) | ||||
| 		defer cancel() | ||||
| 		if _, err := ws.userClient.Client.SetUserOnlineStatus(ctx, req); err != nil { | ||||
| 		if err := ws.userClient.SetUserOnlineStatus(ctx, req); err != nil { | ||||
| 			log.ZError(ctx, "update user online status", err) | ||||
| 		} | ||||
| 		for _, ss := range req.Status { | ||||
| 			for _, online := range ss.Online { | ||||
| 				client, _, _ := ws.clients.Get(ss.UserID, int(online)) | ||||
| 				back := false | ||||
| 				if len(client) > 0 { | ||||
| 					back = client[0].IsBackground | ||||
| 				} | ||||
| 				ws.webhookAfterUserOnline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOnline, ss.UserID, int(online), back, ss.ConnID) | ||||
| 			} | ||||
| 			for _, offline := range ss.Offline { | ||||
| 				ws.webhookAfterUserOffline(ctx, &ws.msgGatewayConfig.WebhooksConfig.AfterUserOffline, ss.UserID, int(offline), ss.ConnID) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for i := 0; i < concurrent; i++ { | ||||
|  | ||||
| @ -1,40 +1,27 @@ | ||||
| // 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 msggateway | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/webhook" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpccache" | ||||
| 	pbAuth "github.com/openimsdk/protocol/auth" | ||||
| 	"github.com/openimsdk/tools/mcontext" | ||||
| 	"net/http" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 
 | ||||
| 	"github.com/go-playground/validator/v10" | ||||
| 	"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/openimsdk/open-im-server/v3/pkg/common/webhook" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpccache" | ||||
| 	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/utils/stringutil" | ||||
| 	"golang.org/x/sync/errgroup" | ||||
| ) | ||||
| @ -45,13 +32,12 @@ type LongConnServer interface { | ||||
| 	GetUserAllCons(userID string) ([]*Client, bool) | ||||
| 	GetUserPlatformCons(userID string, platform int) ([]*Client, bool, bool) | ||||
| 	Validate(s any) error | ||||
| 	SetDiscoveryRegistry(client discovery.SvcDiscoveryRegistry, config *Config) | ||||
| 	SetDiscoveryRegistry(ctx context.Context, client discovery.SvcDiscoveryRegistry, config *Config) error | ||||
| 	KickUserConn(client *Client) error | ||||
| 	UnRegister(c *Client) | ||||
| 	SetKickHandlerInfo(i *kickHandler) | ||||
| 	SubUserOnlineStatus(ctx context.Context, client *Client, data *Req) ([]byte, error) | ||||
| 	Compressor | ||||
| 	Encoder | ||||
| 	MessageHandler | ||||
| } | ||||
| 
 | ||||
| @ -71,13 +57,13 @@ type WsServer struct { | ||||
| 	handshakeTimeout  time.Duration | ||||
| 	writeBufferSize   int | ||||
| 	validate          *validator.Validate | ||||
| 	userClient        *rpcclient.UserRpcClient | ||||
| 	authClient        *rpcclient.Auth | ||||
| 	disCov            discovery.SvcDiscoveryRegistry | ||||
| 	Compressor | ||||
| 	Encoder | ||||
| 	//Encoder | ||||
| 	MessageHandler | ||||
| 	webhookClient *webhook.Client | ||||
| 	userClient    *rpcli.UserClient | ||||
| 	authClient    *rpcli.AuthClient | ||||
| } | ||||
| 
 | ||||
| type kickHandler struct { | ||||
| @ -86,12 +72,28 @@ type kickHandler struct { | ||||
| 	newClient  *Client | ||||
| } | ||||
| 
 | ||||
| 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 | ||||
| func (ws *WsServer) SetDiscoveryRegistry(ctx context.Context, disCov discovery.SvcDiscoveryRegistry, config *Config) error { | ||||
| 	userConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.User) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	pushConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.Push) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	authConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.Auth) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	msgConn, err := disCov.GetConn(ctx, config.Share.RpcRegisterName.Msg) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ws.userClient = rpcli.NewUserClient(userConn) | ||||
| 	ws.authClient = rpcli.NewAuthClient(authConn) | ||||
| 	ws.MessageHandler = NewGrpcHandler(ws.validate, rpcli.NewMsgClient(msgConn), rpcli.NewPushMsgServiceClient(pushConn)) | ||||
| 	ws.disCov = disCov | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| //func (ws *WsServer) SetUserOnlineStatus(ctx context.Context, client *Client, status int32) { | ||||
| @ -149,7 +151,6 @@ func NewWsServer(msgGatewayConfig *Config, opts ...Option) *WsServer { | ||||
| 		clients:         newUserMap(), | ||||
| 		subscription:    newSubscription(), | ||||
| 		Compressor:      NewGzipCompressor(), | ||||
| 		Encoder:         NewGobEncoder(), | ||||
| 		webhookClient:   webhook.NewWebhookClient(msgGatewayConfig.WebhooksConfig.URL), | ||||
| 	} | ||||
| } | ||||
| @ -212,6 +213,9 @@ func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *C | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if len(conns) == 0 || (len(conns) == 1 && ws.disCov.IsSelfNode(conns[0])) { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	wg := errgroup.Group{} | ||||
| 	wg.SetLimit(concurrentRequest) | ||||
| @ -219,9 +223,9 @@ func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *C | ||||
| 	// Online push user online message to other node | ||||
| 	for _, v := range conns { | ||||
| 		v := v | ||||
| 		log.ZDebug(ctx, " sendUserOnlineInfoToOtherNode conn ", "target", v.Target()) | ||||
| 		if v.Target() == ws.disCov.GetSelfConnTarget() { | ||||
| 			log.ZDebug(ctx, "Filter out this node", "node", v.Target()) | ||||
| 		log.ZDebug(ctx, "sendUserOnlineInfoToOtherNode conn") | ||||
| 		if ws.disCov.IsSelfNode(v) { | ||||
| 			log.ZDebug(ctx, "Filter out this node") | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| @ -232,7 +236,7 @@ func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *C | ||||
| 				PlatformID: int32(client.PlatformID), Token: client.token, | ||||
| 			}) | ||||
| 			if err != nil { | ||||
| 				log.ZWarn(ctx, "MultiTerminalLoginCheck err", err, "node", v.Target()) | ||||
| 				log.ZWarn(ctx, "MultiTerminalLoginCheck err", err) | ||||
| 			} | ||||
| 			return nil | ||||
| 		}) | ||||
| @ -293,14 +297,7 @@ func (ws *WsServer) registerClient(client *Client) { | ||||
| 
 | ||||
| 	wg.Wait() | ||||
| 
 | ||||
| 	log.ZDebug( | ||||
| 		client.ctx, | ||||
| 		"user online", | ||||
| 		"online user Num", | ||||
| 		ws.onlineUserNum.Load(), | ||||
| 		"online user conn Num", | ||||
| 		ws.onlineUserConnNum.Load(), | ||||
| 	) | ||||
| 	log.ZDebug(client.ctx, "user online", "online user Num", ws.onlineUserNum.Load(), "online user conn Num", ws.onlineUserConnNum.Load()) | ||||
| } | ||||
| 
 | ||||
| func getRemoteAdders(client []*Client) string { | ||||
| @ -321,17 +318,70 @@ func (ws *WsServer) KickUserConn(client *Client) error { | ||||
| } | ||||
| 
 | ||||
| func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Client, newClient *Client) { | ||||
| 	switch ws.msgGatewayConfig.Share.MultiLoginPolicy { | ||||
| 	kickTokenFunc := func(kickClients []*Client) { | ||||
| 		var kickTokens []string | ||||
| 		ws.clients.DeleteClients(newClient.UserID, kickClients) | ||||
| 		for _, c := range kickClients { | ||||
| 			kickTokens = append(kickTokens, c.token) | ||||
| 			err := c.KickOnlineMessage() | ||||
| 			if err != nil { | ||||
| 				log.ZWarn(c.ctx, "KickOnlineMessage", err) | ||||
| 			} | ||||
| 		} | ||||
| 		ctx := mcontext.WithMustInfoCtx( | ||||
| 			[]string{newClient.ctx.GetOperationID(), newClient.ctx.GetUserID(), | ||||
| 				constant.PlatformIDToName(newClient.PlatformID), newClient.ctx.GetConnID()}, | ||||
| 		) | ||||
| 		if err := ws.authClient.KickTokens(ctx, kickTokens); err != nil { | ||||
| 			log.ZWarn(newClient.ctx, "kickTokens err", err) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// If reconnect: When multiple msgGateway instances are deployed, a client may disconnect from instance A and reconnect to instance B. | ||||
| 	// During this process, instance A might still be executing, resulting in two clients with the same token existing simultaneously. | ||||
| 	// This situation needs to be filtered to prevent duplicate clients. | ||||
| 	checkSameTokenFunc := func(oldClients []*Client) []*Client { | ||||
| 		var clientsNeedToKick []*Client | ||||
| 
 | ||||
| 		for _, c := range oldClients { | ||||
| 			if c.token == newClient.token { | ||||
| 				log.ZDebug(newClient.ctx, "token is same, not kick", | ||||
| 					"userID", newClient.UserID, | ||||
| 					"platformID", newClient.PlatformID, | ||||
| 					"token", newClient.token) | ||||
| 				continue | ||||
| 			} | ||||
| 
 | ||||
| 			clientsNeedToKick = append(clientsNeedToKick, c) | ||||
| 		} | ||||
| 
 | ||||
| 		return clientsNeedToKick | ||||
| 	} | ||||
| 
 | ||||
| 	switch ws.msgGatewayConfig.Share.MultiLogin.Policy { | ||||
| 	case constant.DefalutNotKick: | ||||
| 	case constant.PCAndOther: | ||||
| 		if constant.PlatformIDToClass(newClient.PlatformID) == constant.TerminalPC { | ||||
| 			return | ||||
| 		} | ||||
| 		clients, ok := ws.clients.GetAll(newClient.UserID) | ||||
| 		clientOK = ok | ||||
| 		oldClients = make([]*Client, 0, len(clients)) | ||||
| 		for _, c := range clients { | ||||
| 			if constant.PlatformIDToClass(c.PlatformID) == constant.TerminalPC { | ||||
| 				continue | ||||
| 			} | ||||
| 			oldClients = append(oldClients, c) | ||||
| 		} | ||||
| 
 | ||||
| 		fallthrough | ||||
| 	case constant.AllLoginButSameTermKick: | ||||
| 		if !clientOK { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		oldClients = checkSameTokenFunc(oldClients) | ||||
| 
 | ||||
| 		ws.clients.DeleteClients(newClient.UserID, oldClients) | ||||
| 		for _, c := range oldClients { | ||||
| 			err := c.KickOnlineMessage() | ||||
| @ -343,10 +393,30 @@ func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Clien | ||||
| 			[]string{newClient.ctx.GetOperationID(), newClient.ctx.GetUserID(), | ||||
| 				constant.PlatformIDToName(newClient.PlatformID), newClient.ctx.GetConnID()}, | ||||
| 		) | ||||
| 		if _, err := ws.authClient.InvalidateToken(ctx, newClient.token, newClient.UserID, newClient.PlatformID); err != nil { | ||||
| 		req := &pbAuth.InvalidateTokenReq{ | ||||
| 			PreservedToken: newClient.token, | ||||
| 			UserID:         newClient.UserID, | ||||
| 			PlatformID:     int32(newClient.PlatformID), | ||||
| 		} | ||||
| 		if err := ws.authClient.InvalidateToken(ctx, req); err != nil { | ||||
| 			log.ZWarn(newClient.ctx, "InvalidateToken err", err, "userID", newClient.UserID, | ||||
| 				"platformID", newClient.PlatformID) | ||||
| 		} | ||||
| 	case constant.AllLoginButSameClassKick: | ||||
| 		clients, ok := ws.clients.GetAll(newClient.UserID) | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 
 | ||||
| 		var kickClients []*Client | ||||
| 		for _, client := range clients { | ||||
| 			if constant.PlatformIDToClass(client.PlatformID) == constant.PlatformIDToClass(newClient.PlatformID) { | ||||
| 				kickClients = append(kickClients, client) | ||||
| 			} | ||||
| 		} | ||||
| 		kickClients = checkSameTokenFunc(kickClients) | ||||
| 
 | ||||
| 		kickTokenFunc(kickClients) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -18,11 +18,17 @@ import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"strconv" | ||||
| 	"syscall" | ||||
| 
 | ||||
| 	"github.com/openimsdk/tools/discovery/etcd" | ||||
| 	"github.com/openimsdk/tools/utils/jsonutil" | ||||
| 	"github.com/openimsdk/tools/utils/network" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" | ||||
| @ -30,10 +36,10 @@ import ( | ||||
| 	"github.com/openimsdk/tools/db/redisutil" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	conf "github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	discRegister "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" | ||||
| 	kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" | ||||
| 	"github.com/openimsdk/tools/errs" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 	"github.com/openimsdk/tools/mw" | ||||
| @ -54,16 +60,17 @@ type MsgTransfer struct { | ||||
| } | ||||
| 
 | ||||
| type Config struct { | ||||
| 	MsgTransfer    config.MsgTransfer | ||||
| 	RedisConfig    config.Redis | ||||
| 	MongodbConfig  config.Mongo | ||||
| 	KafkaConfig    config.Kafka | ||||
| 	Share          config.Share | ||||
| 	WebhooksConfig config.Webhooks | ||||
| 	Discovery      config.Discovery | ||||
| 	MsgTransfer    conf.MsgTransfer | ||||
| 	RedisConfig    conf.Redis | ||||
| 	MongodbConfig  conf.Mongo | ||||
| 	KafkaConfig    conf.Kafka | ||||
| 	Share          conf.Share | ||||
| 	WebhooksConfig conf.Webhooks | ||||
| 	Discovery      conf.Discovery | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| 
 | ||||
| @ -75,18 +82,18 @@ func Start(ctx context.Context, index int, config *Config) error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	client, err := discRegister.NewDiscoveryRegister(&config.Discovery, &config.Share) | ||||
| 	client, err := discRegister.NewDiscoveryRegister(&config.Discovery, &config.Share, nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), | ||||
| 		grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) | ||||
| 
 | ||||
| 	msgModel := redis.NewMsgCache(rdb) | ||||
| 	msgDocModel, err := mgo.NewMsgMongo(mgocli.GetDB()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	msgModel := redis.NewMsgCache(rdb, msgDocModel) | ||||
| 	seqConversation, err := mgo.NewSeqConversationMongo(mgocli.GetDB()) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @ -101,9 +108,7 @@ func Start(ctx context.Context, index int, config *Config) error { | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	conversationRpcClient := rpcclient.NewConversationRpcClient(client, config.Share.RpcRegisterName.Conversation) | ||||
| 	groupRpcClient := rpcclient.NewGroupRpcClient(client, config.Share.RpcRegisterName.Group) | ||||
| 	historyCH, err := NewOnlineHistoryRedisConsumerHandler(&config.KafkaConfig, msgTransferDatabase, &conversationRpcClient, &groupRpcClient) | ||||
| 	historyCH, err := NewOnlineHistoryRedisConsumerHandler(ctx, client, config, msgTransferDatabase) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| @ -119,7 +124,7 @@ func Start(ctx context.Context, index int, config *Config) error { | ||||
| 	return msgTransfer.Start(index, config) | ||||
| } | ||||
| 
 | ||||
| func (m *MsgTransfer) Start(index int, config *Config) error { | ||||
| func (m *MsgTransfer) Start(index int, cfg *Config) error { | ||||
| 	m.ctx, m.cancel = context.WithCancel(context.Background()) | ||||
| 	var ( | ||||
| 		netDone = make(chan struct{}, 1) | ||||
| @ -128,21 +133,80 @@ func (m *MsgTransfer) Start(index int, config *Config) error { | ||||
| 
 | ||||
| 	go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH) | ||||
| 	go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH) | ||||
| 	go m.historyCH.HandleUserHasReadSeqMessages(m.ctx) | ||||
| 	err := m.historyCH.redisMessageBatches.Start() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if config.MsgTransfer.Prometheus.Enable { | ||||
| 		go func() { | ||||
| 			prometheusPort, err := datautil.GetElemByIndex(config.MsgTransfer.Prometheus.Ports, index) | ||||
| 	client, err := kdisc.NewDiscoveryRegister(&cfg.Discovery, &cfg.Share, nil) | ||||
| 	if err != nil { | ||||
| 				netErr = err | ||||
| 				netDone <- struct{}{} | ||||
| 				return | ||||
| 		return errs.WrapMsg(err, "failed to register discovery service") | ||||
| 	} | ||||
| 
 | ||||
| 			if err := prommetrics.TransferInit(prometheusPort); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||||
| 	registerIP, err := network.GetRpcRegisterIP("") | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	getAutoPort := func() (net.Listener, int, error) { | ||||
| 		registerAddr := net.JoinHostPort(registerIP, "0") | ||||
| 		listener, err := net.Listen("tcp", registerAddr) | ||||
| 		if err != nil { | ||||
| 			return nil, 0, errs.WrapMsg(err, "listen err", "registerAddr", registerAddr) | ||||
| 		} | ||||
| 		_, portStr, _ := net.SplitHostPort(listener.Addr().String()) | ||||
| 		port, _ := strconv.Atoi(portStr) | ||||
| 		return listener, port, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.MsgTransfer.Prometheus.AutoSetPorts && cfg.Discovery.Enable != conf.ETCD { | ||||
| 		return errs.New("only etcd support autoSetPorts", "RegisterName", "api").Wrap() | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.MsgTransfer.Prometheus.Enable { | ||||
| 		var ( | ||||
| 			listener       net.Listener | ||||
| 			prometheusPort int | ||||
| 		) | ||||
| 
 | ||||
| 		if cfg.MsgTransfer.Prometheus.AutoSetPorts { | ||||
| 			listener, prometheusPort, err = getAutoPort() | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			etcdClient, ok := client.(*etcd.SvcDiscoveryRegistryImpl) | ||||
| 			if !ok { | ||||
| 				return errs.New("only etcd support autoSetPorts").Wrap() | ||||
| 			} | ||||
| 
 | ||||
| 			target, err := jsonutil.JsonMarshal(prommetrics.BuildDefaultTarget(registerIP, prometheusPort)) | ||||
| 			if err != nil { | ||||
| 				return errs.Wrap(err) | ||||
| 			} | ||||
| 			err = etcdClient.SetWithLease(context.TODO(), prommetrics.BuildDiscoveryKey(prommetrics.MessageTransferKeyName, index), target, prommetrics.TTL) | ||||
| 			if err != nil { | ||||
| 				return errs.WrapMsg(err, "etcd put err") | ||||
| 			} | ||||
| 		} else { | ||||
| 			prometheusPort, err = datautil.GetElemByIndex(cfg.MsgTransfer.Prometheus.Ports, index) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			listener, err = net.Listen("tcp", fmt.Sprintf(":%d", prometheusPort)) | ||||
| 			if err != nil { | ||||
| 				return errs.WrapMsg(err, "listen err", "addr", fmt.Sprintf(":%d", prometheusPort)) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		go func() { | ||||
| 			defer func() { | ||||
| 				if r := recover(); r != nil { | ||||
| 					log.ZPanic(m.ctx, "MsgTransfer Start Panic", errs.ErrPanic(r)) | ||||
| 				} | ||||
| 			}() | ||||
| 			if err := prommetrics.TransferInit(listener); err != nil && !errors.Is(err, http.ErrServerClosed) { | ||||
| 				netErr = errs.WrapMsg(err, "prometheus start error", "prometheusPort", prometheusPort) | ||||
| 				netDone <- struct{}{} | ||||
| 			} | ||||
| @ -157,12 +221,14 @@ func (m *MsgTransfer) Start(index int, config *Config) error { | ||||
| 		// graceful close kafka client. | ||||
| 		m.cancel() | ||||
| 		m.historyCH.redisMessageBatches.Close() | ||||
| 		m.historyCH.Close() | ||||
| 		m.historyCH.historyConsumerGroup.Close() | ||||
| 		m.historyMongoCH.historyConsumerGroup.Close() | ||||
| 		return nil | ||||
| 	case <-netDone: | ||||
| 		m.cancel() | ||||
| 		m.historyCH.redisMessageBatches.Close() | ||||
| 		m.historyCH.Close() | ||||
| 		m.historyCH.historyConsumerGroup.Close() | ||||
| 		m.historyMongoCH.historyConsumerGroup.Close() | ||||
| 		close(netDone) | ||||
|  | ||||
| @ -20,23 +20,29 @@ import ( | ||||
| 	"errors" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/IBM/sarama" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 	"github.com/openimsdk/tools/discovery" | ||||
| 
 | ||||
| 	"github.com/go-redis/redis" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||
| 
 | ||||
| 	"github.com/IBM/sarama" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka" | ||||
| 	"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/tools/batcher" | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	pbconv "github.com/openimsdk/protocol/conversation" | ||||
| 	"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/stringutil" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| @ -45,6 +51,7 @@ const ( | ||||
| 	subChanBuffer     = 50 | ||||
| 	worker            = 50 | ||||
| 	interval          = 100 * time.Millisecond | ||||
| 	hasReadChanBuffer = 1000 | ||||
| ) | ||||
| 
 | ||||
| type ContextMsg struct { | ||||
| @ -52,24 +59,46 @@ type ContextMsg struct { | ||||
| 	ctx     context.Context | ||||
| } | ||||
| 
 | ||||
| // This structure is used for asynchronously writing the sender’s read sequence (seq) regarding a message into MongoDB. | ||||
| // For example, if the sender sends a message with a seq of 10, then their own read seq for this conversation should be set to 10. | ||||
| type userHasReadSeq struct { | ||||
| 	conversationID string | ||||
| 	userHasReadMap map[string]int64 | ||||
| } | ||||
| 
 | ||||
| type OnlineHistoryRedisConsumerHandler struct { | ||||
| 	historyConsumerGroup *kafka.MConsumerGroup | ||||
| 
 | ||||
| 	redisMessageBatches *batcher.Batcher[sarama.ConsumerMessage] | ||||
| 
 | ||||
| 	msgTransferDatabase         controller.MsgTransferDatabase | ||||
| 	conversationRpcClient *rpcclient.ConversationRpcClient | ||||
| 	groupRpcClient        *rpcclient.GroupRpcClient | ||||
| 	conversationUserHasReadChan chan *userHasReadSeq | ||||
| 	wg                          sync.WaitGroup | ||||
| 
 | ||||
| 	groupClient        *rpcli.GroupClient | ||||
| 	conversationClient *rpcli.ConversationClient | ||||
| } | ||||
| 
 | ||||
| func NewOnlineHistoryRedisConsumerHandler(kafkaConf *config.Kafka, database controller.MsgTransferDatabase, | ||||
| 	conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*OnlineHistoryRedisConsumerHandler, error) { | ||||
| func NewOnlineHistoryRedisConsumerHandler(ctx context.Context, client discovery.SvcDiscoveryRegistry, config *Config, database controller.MsgTransferDatabase) (*OnlineHistoryRedisConsumerHandler, error) { | ||||
| 	kafkaConf := config.KafkaConfig | ||||
| 	historyConsumerGroup, err := kafka.NewMConsumerGroup(kafkaConf.Build(), kafkaConf.ToRedisGroupID, []string{kafkaConf.ToRedisTopic}, false) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	var och OnlineHistoryRedisConsumerHandler | ||||
| 	och.msgTransferDatabase = database | ||||
| 	och.conversationUserHasReadChan = make(chan *userHasReadSeq, hasReadChanBuffer) | ||||
| 	och.groupClient = rpcli.NewGroupClient(groupConn) | ||||
| 	och.conversationClient = rpcli.NewConversationClient(conversationConn) | ||||
| 	och.wg.Add(1) | ||||
| 
 | ||||
| 	b := batcher.New[sarama.ConsumerMessage]( | ||||
| 		batcher.WithSize(size), | ||||
| @ -88,25 +117,21 @@ func NewOnlineHistoryRedisConsumerHandler(kafkaConf *config.Kafka, database cont | ||||
| 	} | ||||
| 	b.Do = och.do | ||||
| 	och.redisMessageBatches = b | ||||
| 	och.conversationRpcClient = conversationRpcClient | ||||
| 	och.groupRpcClient = groupRpcClient | ||||
| 	och.historyConsumerGroup = historyConsumerGroup | ||||
| 
 | ||||
| 	return &och, err | ||||
| 	return &och, nil | ||||
| } | ||||
| func (och *OnlineHistoryRedisConsumerHandler) do(ctx context.Context, channelID int, val *batcher.Msg[sarama.ConsumerMessage]) { | ||||
| 	ctx = mcontext.WithTriggerIDContext(ctx, val.TriggerID()) | ||||
| 	ctxMessages := och.parseConsumerMessages(ctx, val.Val()) | ||||
| 	ctx = withAggregationCtx(ctx, ctxMessages) | ||||
| 	log.ZInfo(ctx, "msg arrived channel", "channel id", channelID, "msgList length", len(ctxMessages), | ||||
| 		"key", val.Key()) | ||||
| 	log.ZInfo(ctx, "msg arrived channel", "channel id", channelID, "msgList length", len(ctxMessages), "key", val.Key()) | ||||
| 	och.doSetReadSeq(ctx, ctxMessages) | ||||
| 
 | ||||
| 	storageMsgList, notStorageMsgList, storageNotificationList, notStorageNotificationList := | ||||
| 		och.categorizeMessageLists(ctxMessages) | ||||
| 	log.ZDebug(ctx, "number of categorized messages", "storageMsgList", len(storageMsgList), "notStorageMsgList", | ||||
| 		len(notStorageMsgList), "storageNotificationList", len(storageNotificationList), "notStorageNotificationList", | ||||
| 		len(notStorageNotificationList)) | ||||
| 		len(notStorageMsgList), "storageNotificationList", len(storageNotificationList), "notStorageNotificationList", len(notStorageNotificationList)) | ||||
| 
 | ||||
| 	conversationIDMsg := msgprocessor.GetChatConversationIDByMsg(ctxMessages[0].message) | ||||
| 	conversationIDNotification := msgprocessor.GetNotificationConversationIDByMsg(ctxMessages[0].message) | ||||
| @ -115,57 +140,51 @@ func (och *OnlineHistoryRedisConsumerHandler) do(ctx context.Context, channelID | ||||
| } | ||||
| 
 | ||||
| func (och *OnlineHistoryRedisConsumerHandler) doSetReadSeq(ctx context.Context, msgs []*ContextMsg) { | ||||
| 	type seqKey struct { | ||||
| 		conversationID string | ||||
| 		userID         string | ||||
| 	} | ||||
| 	var readSeq map[seqKey]int64 | ||||
| 
 | ||||
| 	// Outer map: conversationID -> (userID -> maxHasReadSeq) | ||||
| 	conversationUserSeq := make(map[string]map[string]int64) | ||||
| 
 | ||||
| 	for _, msg := range msgs { | ||||
| 		if msg.message.ContentType != constant.HasReadReceipt { | ||||
| 			continue | ||||
| 		} | ||||
| 		var elem sdkws.NotificationElem | ||||
| 		if err := json.Unmarshal(msg.message.Content, &elem); err != nil { | ||||
| 			log.ZError(ctx, "handlerConversationRead Unmarshal NotificationElem msg err", err, "msg", msg) | ||||
| 			log.ZWarn(ctx, "Unmarshal NotificationElem error", err, "msg", msg) | ||||
| 			continue | ||||
| 		} | ||||
| 		var tips sdkws.MarkAsReadTips | ||||
| 		if err := json.Unmarshal([]byte(elem.Detail), &tips); err != nil { | ||||
| 			log.ZError(ctx, "handlerConversationRead Unmarshal MarkAsReadTips msg err", err, "msg", msg) | ||||
| 			log.ZWarn(ctx, "Unmarshal MarkAsReadTips error", err, "msg", msg) | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(tips.Seqs) > 0 { | ||||
| 		if len(tips.ConversationID) == 0 || tips.HasReadSeq < 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		// Calculate the max seq from tips.Seqs | ||||
| 		for _, seq := range tips.Seqs { | ||||
| 			if tips.HasReadSeq < seq { | ||||
| 				tips.HasReadSeq = seq | ||||
| 			} | ||||
| 		} | ||||
| 			clear(tips.Seqs) | ||||
| 			tips.Seqs = nil | ||||
| 
 | ||||
| 		if _, ok := conversationUserSeq[tips.ConversationID]; !ok { | ||||
| 			conversationUserSeq[tips.ConversationID] = make(map[string]int64) | ||||
| 		} | ||||
| 		if tips.HasReadSeq < 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		if readSeq == nil { | ||||
| 			readSeq = make(map[seqKey]int64) | ||||
| 		} | ||||
| 		key := seqKey{ | ||||
| 			conversationID: tips.ConversationID, | ||||
| 			userID:         tips.MarkAsReadUserID, | ||||
| 		} | ||||
| 		if readSeq[key] > tips.HasReadSeq { | ||||
| 			continue | ||||
| 		} | ||||
| 		readSeq[key] = tips.HasReadSeq | ||||
| 	} | ||||
| 	if readSeq == nil { | ||||
| 		return | ||||
| 	} | ||||
| 	for key, seq := range readSeq { | ||||
| 		if err := och.msgTransferDatabase.SetHasReadSeqToDB(ctx, key.userID, key.conversationID, seq); err != nil { | ||||
| 			log.ZError(ctx, "set read seq to db error", err, "userID", key.userID, "conversationID", key.conversationID, "seq", seq) | ||||
| 		if conversationUserSeq[tips.ConversationID][tips.MarkAsReadUserID] < tips.HasReadSeq { | ||||
| 			conversationUserSeq[tips.ConversationID][tips.MarkAsReadUserID] = tips.HasReadSeq | ||||
| 		} | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "doSetReadSeq", "conversationUserSeq", conversationUserSeq) | ||||
| 
 | ||||
| 	// persist to db | ||||
| 	for convID, userSeqMap := range conversationUserSeq { | ||||
| 		if err := och.msgTransferDatabase.SetHasReadSeqToDB(ctx, convID, userSeqMap); err != nil { | ||||
| 			log.ZWarn(ctx, "SetHasReadSeqToDB error", err, "conversationID", convID, "userSeqMap", userSeqMap) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| func (och *OnlineHistoryRedisConsumerHandler) parseConsumerMessages(ctx context.Context, consumerMessages []*sarama.ConsumerMessage) []*ContextMsg { | ||||
| @ -250,34 +269,49 @@ func (och *OnlineHistoryRedisConsumerHandler) handleMsg(ctx context.Context, key | ||||
| 	} | ||||
| 	if len(storageMessageList) > 0 { | ||||
| 		msg := storageMessageList[0] | ||||
| 		lastSeq, isNewConversation, err := och.msgTransferDatabase.BatchInsertChat2Cache(ctx, conversationID, storageMessageList) | ||||
| 		lastSeq, isNewConversation, userSeqMap, err := och.msgTransferDatabase.BatchInsertChat2Cache(ctx, conversationID, storageMessageList) | ||||
| 		if err != nil && !errors.Is(errs.Unwrap(err), redis.Nil) { | ||||
| 			log.ZError(ctx, "batch data insert to redis err", err, "storageMsgList", storageMessageList) | ||||
| 			log.ZWarn(ctx, "batch data insert to redis err", err, "storageMsgList", storageMessageList) | ||||
| 			return | ||||
| 		} | ||||
| 		log.ZInfo(ctx, "BatchInsertChat2Cache end") | ||||
| 		err = och.msgTransferDatabase.SetHasReadSeqs(ctx, conversationID, userSeqMap) | ||||
| 		if err != nil { | ||||
| 			log.ZWarn(ctx, "SetHasReadSeqs error", err, "userSeqMap", userSeqMap, "conversationID", conversationID) | ||||
| 			prommetrics.SeqSetFailedCounter.Inc() | ||||
| 		} | ||||
| 		och.conversationUserHasReadChan <- &userHasReadSeq{ | ||||
| 			conversationID: conversationID, | ||||
| 			userHasReadMap: userSeqMap, | ||||
| 		} | ||||
| 
 | ||||
| 		if isNewConversation { | ||||
| 			ctx := storageList[0].ctx | ||||
| 			switch msg.SessionType { | ||||
| 			case constant.ReadGroupChatType: | ||||
| 				log.ZDebug(ctx, "group chat first create conversation", "conversationID", | ||||
| 					conversationID) | ||||
| 				userIDs, err := och.groupRpcClient.GetGroupMemberIDs(ctx, msg.GroupID) | ||||
| 
 | ||||
| 				userIDs, err := och.groupClient.GetGroupMemberUserIDs(ctx, msg.GroupID) | ||||
| 				if err != nil { | ||||
| 					log.ZWarn(ctx, "get group member ids error", err, "conversationID", | ||||
| 						conversationID) | ||||
| 				} else { | ||||
| 					log.ZInfo(ctx, "GetGroupMemberIDs end") | ||||
| 
 | ||||
| 					if err := och.conversationRpcClient.GroupChatFirstCreateConversation(ctx, | ||||
| 						msg.GroupID, userIDs); err != nil { | ||||
| 					if err := och.conversationClient.CreateGroupChatConversations(ctx, msg.GroupID, userIDs); err != nil { | ||||
| 						log.ZWarn(ctx, "single chat first create conversation error", err, | ||||
| 							"conversationID", conversationID) | ||||
| 					} | ||||
| 				} | ||||
| 			case constant.SingleChatType, constant.NotificationChatType: | ||||
| 				if err := och.conversationRpcClient.SingleChatFirstCreateConversation(ctx, msg.RecvID, | ||||
| 					msg.SendID, conversationID, msg.SessionType); err != nil { | ||||
| 				req := &pbconv.CreateSingleChatConversationsReq{ | ||||
| 					RecvID:           msg.RecvID, | ||||
| 					SendID:           msg.SendID, | ||||
| 					ConversationID:   conversationID, | ||||
| 					ConversationType: msg.SessionType, | ||||
| 				} | ||||
| 				if err := och.conversationClient.CreateSingleChatConversations(ctx, req); err != nil { | ||||
| 					log.ZWarn(ctx, "single chat or notification first create conversation error", err, | ||||
| 						"conversationID", conversationID, "sessionType", msg.SessionType) | ||||
| 				} | ||||
| @ -308,7 +342,7 @@ func (och *OnlineHistoryRedisConsumerHandler) handleNotification(ctx context.Con | ||||
| 		storageMessageList = append(storageMessageList, msg.message) | ||||
| 	} | ||||
| 	if len(storageMessageList) > 0 { | ||||
| 		lastSeq, _, err := och.msgTransferDatabase.BatchInsertChat2Cache(ctx, conversationID, storageMessageList) | ||||
| 		lastSeq, _, _, err := och.msgTransferDatabase.BatchInsertChat2Cache(ctx, conversationID, storageMessageList) | ||||
| 		if err != nil { | ||||
| 			log.ZError(ctx, "notification batch insert to redis error", err, "conversationID", conversationID, | ||||
| 				"storageList", storageMessageList) | ||||
| @ -323,6 +357,27 @@ func (och *OnlineHistoryRedisConsumerHandler) handleNotification(ctx context.Con | ||||
| 		och.toPushTopic(ctx, key, conversationID, storageList) | ||||
| 	} | ||||
| } | ||||
| func (och *OnlineHistoryRedisConsumerHandler) HandleUserHasReadSeqMessages(ctx context.Context) { | ||||
| 	defer func() { | ||||
| 		if r := recover(); r != nil { | ||||
| 			log.ZPanic(ctx, "HandleUserHasReadSeqMessages Panic", errs.ErrPanic(r)) | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	defer och.wg.Done() | ||||
| 
 | ||||
| 	for msg := range och.conversationUserHasReadChan { | ||||
| 		if err := och.msgTransferDatabase.SetHasReadSeqToDB(ctx, msg.conversationID, msg.userHasReadMap); err != nil { | ||||
| 			log.ZWarn(ctx, "set read seq to db error", err, "conversationID", msg.conversationID, "userSeqMap", msg.userHasReadMap) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	log.ZInfo(ctx, "Channel closed, exiting handleUserHasReadSeqMessages") | ||||
| } | ||||
| func (och *OnlineHistoryRedisConsumerHandler) Close() { | ||||
| 	close(och.conversationUserHasReadChan) | ||||
| 	och.wg.Wait() | ||||
| } | ||||
| 
 | ||||
| func (och *OnlineHistoryRedisConsumerHandler) toPushTopic(ctx context.Context, key, conversationID string, msgs []*ContextMsg) { | ||||
| 	for _, v := range msgs { | ||||
|  | ||||
| @ -21,9 +21,9 @@ import ( | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka" | ||||
| 	pbmsg "github.com/openimsdk/protocol/msg" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 	"github.com/openimsdk/tools/mq/kafka" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| ) | ||||
| 
 | ||||
| @ -73,31 +73,21 @@ func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo(ctx context.Cont | ||||
| 	} else { | ||||
| 		prommetrics.MsgInsertMongoSuccessCounter.Inc() | ||||
| 	} | ||||
| 	var seqs []int64 | ||||
| 	for _, msg := range msgFromMQ.MsgData { | ||||
| 		seqs = append(seqs, msg.Seq) | ||||
| 	} | ||||
| 	err = mc.msgTransferDatabase.DeleteMessagesFromCache(ctx, msgFromMQ.ConversationID, seqs) | ||||
| 	if err != nil { | ||||
| 		log.ZError( | ||||
| 			ctx, | ||||
| 			"remove cache msg from redis err", | ||||
| 			err, | ||||
| 			"msg", | ||||
| 			msgFromMQ.MsgData, | ||||
| 			"conversationID", | ||||
| 			msgFromMQ.ConversationID, | ||||
| 		) | ||||
| 	} | ||||
| 	//var seqs []int64 | ||||
| 	//for _, msg := range msgFromMQ.MsgData { | ||||
| 	//	seqs = append(seqs, msg.Seq) | ||||
| 	//} | ||||
| 	//if err := mc.msgTransferDatabase.DeleteMessagesFromCache(ctx, msgFromMQ.ConversationID, seqs); err != nil { | ||||
| 	//	log.ZError(ctx, "remove cache msg from redis err", err, "msg", | ||||
| 	//		msgFromMQ.MsgData, "conversationID", msgFromMQ.ConversationID) | ||||
| 	//} | ||||
| } | ||||
| 
 | ||||
| func (*OnlineHistoryMongoConsumerHandler) Setup(_ sarama.ConsumerGroupSession) error { return nil } | ||||
| 
 | ||||
| func (*OnlineHistoryMongoConsumerHandler) Cleanup(_ sarama.ConsumerGroupSession) error { return nil } | ||||
| 
 | ||||
| func (mc *OnlineHistoryMongoConsumerHandler) ConsumeClaim( | ||||
| 	sess sarama.ConsumerGroupSession, | ||||
| 	claim sarama.ConsumerGroupClaim, | ||||
| ) error { // an instance in the consumer group | ||||
| func (mc *OnlineHistoryMongoConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error { // an instance in the consumer group | ||||
| 	log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset", | ||||
| 		claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition()) | ||||
| 	for msg := range claim.Messages() { | ||||
|  | ||||
| @ -1,29 +0,0 @@ | ||||
| package push | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestName(t *testing.T) { | ||||
| 	var c ConsumerHandler | ||||
| 	c.readCh = make(chan *sdkws.MarkAsReadTips) | ||||
| 
 | ||||
| 	go c.loopRead() | ||||
| 
 | ||||
| 	go func() { | ||||
| 		for i := 0; ; i++ { | ||||
| 			seq := int64(i + 1) | ||||
| 			if seq%3 == 0 { | ||||
| 				seq = 1 | ||||
| 			} | ||||
| 			c.readCh <- &sdkws.MarkAsReadTips{ | ||||
| 				ConversationID:   "c100", | ||||
| 				MarkAsReadUserID: "u100", | ||||
| 				HasReadSeq:       seq, | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	select {} | ||||
| } | ||||
| @ -24,7 +24,6 @@ import ( | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| 	"github.com/openimsdk/tools/mcontext" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| ) | ||||
| 
 | ||||
| func (c *ConsumerHandler) webhookBeforeOfflinePush(ctx context.Context, before *config.BeforeConfig, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error { | ||||
| @ -70,7 +69,7 @@ func (c *ConsumerHandler) webhookBeforeOfflinePush(ctx context.Context, before * | ||||
| 
 | ||||
| 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 { | ||||
| 		if msg.ContentType == constant.Typing { | ||||
| 			return nil | ||||
| 		} | ||||
| 		req := callbackstruct.CallbackBeforePushReq{ | ||||
|  | ||||
| @ -18,6 +18,7 @@ import ( | ||||
| 	"context" | ||||
| 	"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 	"sync/atomic" | ||||
| ) | ||||
| 
 | ||||
| func NewClient() *Dummy { | ||||
| @ -25,9 +26,12 @@ func NewClient() *Dummy { | ||||
| } | ||||
| 
 | ||||
| type Dummy struct { | ||||
| 	v atomic.Bool | ||||
| } | ||||
| 
 | ||||
| func (d *Dummy) Push(ctx context.Context, userIDs []string, title, content string, opts *options.Opts) error { | ||||
| 	log.ZDebug(ctx, "dummy push") | ||||
| 	if d.v.CompareAndSwap(false, true) { | ||||
| 		log.ZWarn(ctx, "dummy push", nil, "ps", "the offline push is not configured. to configure it, please go to config/openim-push.yml") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -15,6 +15,7 @@ | ||||
| package body | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| ) | ||||
| 
 | ||||
| @ -26,38 +27,44 @@ type Notification struct { | ||||
| 
 | ||||
| type Android struct { | ||||
| 	Alert  string `json:"alert,omitempty"` | ||||
| 	Title  string `json:"title,omitempty"` | ||||
| 	Intent struct { | ||||
| 		URL string `json:"url,omitempty"` | ||||
| 	} `json:"intent,omitempty"` | ||||
| 	Extras Extras `json:"extras"` | ||||
| 	Extras map[string]string `json:"extras,omitempty"` | ||||
| } | ||||
| type Ios struct { | ||||
| 	Alert          string `json:"alert,omitempty"` | ||||
| 	Alert          IosAlert          `json:"alert,omitempty"` | ||||
| 	Sound          string            `json:"sound,omitempty"` | ||||
| 	Badge          string            `json:"badge,omitempty"` | ||||
| 	Extras         Extras `json:"extras"` | ||||
| 	Extras         map[string]string `json:"extras,omitempty"` | ||||
| 	MutableContent bool              `json:"mutable-content"` | ||||
| } | ||||
| 
 | ||||
| type Extras struct { | ||||
| 	ClientMsgID string `json:"clientMsgID"` | ||||
| type IosAlert struct { | ||||
| 	Title string `json:"title,omitempty"` | ||||
| 	Body  string `json:"body,omitempty"` | ||||
| } | ||||
| 
 | ||||
| func (n *Notification) SetAlert(alert string) { | ||||
| func (n *Notification) SetAlert(alert string, title string, opts *options.Opts) { | ||||
| 	n.Alert = alert | ||||
| 	n.Android.Alert = alert | ||||
| 	n.IOS.Alert = alert | ||||
| 	n.IOS.Sound = "default" | ||||
| 	n.Android.Title = title | ||||
| 	n.IOS.Alert.Body = alert | ||||
| 	n.IOS.Alert.Title = title | ||||
| 	n.IOS.Sound = opts.IOSPushSound | ||||
| 	if opts.IOSBadgeCount { | ||||
| 		n.IOS.Badge = "+1" | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (n *Notification) SetExtras(extras Extras) { | ||||
| func (n *Notification) SetExtras(extras map[string]string) { | ||||
| 	n.IOS.Extras = extras | ||||
| 	n.Android.Extras = extras | ||||
| } | ||||
| 
 | ||||
| func (n *Notification) SetAndroidIntent(pushConf *config.Push) { | ||||
| 	n.Android.Intent.URL = pushConf.JPNS.PushIntent | ||||
| 	n.Android.Intent.URL = pushConf.JPush.PushIntent | ||||
| } | ||||
| 
 | ||||
| func (n *Notification) IOSEnableMutableContent() { | ||||
|  | ||||
| @ -18,9 +18,9 @@ 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/jpush/body" | ||||
| 	"github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/options" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"github.com/openimsdk/tools/utils/httputil" | ||||
| ) | ||||
| @ -57,17 +57,23 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin | ||||
| 	var au body.Audience | ||||
| 	au.SetAlias(userIDs) | ||||
| 	var no body.Notification | ||||
| 	var extras body.Extras | ||||
| 	extras := make(map[string]string) | ||||
| 	extras["ex"] = opts.Ex | ||||
| 	if opts.Signal.ClientMsgID != "" { | ||||
| 		extras.ClientMsgID = opts.Signal.ClientMsgID | ||||
| 		extras["ClientMsgID"] = opts.Signal.ClientMsgID | ||||
| 	} | ||||
| 	no.IOSEnableMutableContent() | ||||
| 	no.SetExtras(extras) | ||||
| 	no.SetAlert(title) | ||||
| 	no.SetAlert(content, title, opts) | ||||
| 	no.SetAndroidIntent(j.pushConf) | ||||
| 
 | ||||
| 	var msg body.Message | ||||
| 	msg.SetMsgContent(content) | ||||
| 	msg.SetTitle(title) | ||||
| 	if opts.Signal.ClientMsgID != "" { | ||||
| 		msg.SetExtras("ClientMsgID", opts.Signal.ClientMsgID) | ||||
| 	} | ||||
| 	msg.SetExtras("ex", opts.Ex) | ||||
| 	var opt body.Options | ||||
| 	opt.SetApnsProduction(j.pushConf.IOSPush.Production) | ||||
| 	var pushObj body.PushObj | ||||
| @ -76,19 +82,26 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin | ||||
| 	pushObj.SetNotification(&no) | ||||
| 	pushObj.SetMessage(&msg) | ||||
| 	pushObj.SetOptions(&opt) | ||||
| 	var resp any | ||||
| 	return j.request(ctx, pushObj, resp, 5) | ||||
| 	var resp map[string]any | ||||
| 	return j.request(ctx, pushObj, &resp, 5) | ||||
| } | ||||
| 
 | ||||
| func (j *JPush) request(ctx context.Context, po body.PushObj, resp any, timeout int) error { | ||||
| 	return j.httpClient.PostReturn( | ||||
| func (j *JPush) request(ctx context.Context, po body.PushObj, resp *map[string]any, timeout int) error { | ||||
| 	err := j.httpClient.PostReturn( | ||||
| 		ctx, | ||||
| 		j.pushConf.JPNS.PushURL, | ||||
| 		j.pushConf.JPush.PushURL, | ||||
| 		map[string]string{ | ||||
| 			"Authorization": j.getAuthorization(j.pushConf.JPNS.AppKey, j.pushConf.JPNS.MasterSecret), | ||||
| 			"Authorization": j.getAuthorization(j.pushConf.JPush.AppKey, j.pushConf.JPush.MasterSecret), | ||||
| 		}, | ||||
| 		po, | ||||
| 		resp, | ||||
| 		timeout, | ||||
| 	) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if (*resp)["sendno"] != "0" { | ||||
| 		return fmt.Errorf("jpush push failed %v", resp) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -23,10 +23,11 @@ import ( | ||||
| 	"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/storage/cache" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	geTUI    = "geTui" | ||||
| 	geTUI    = "getui" | ||||
| 	firebase = "fcm" | ||||
| 	jPush    = "jpush" | ||||
| ) | ||||
| @ -38,6 +39,7 @@ type OfflinePusher interface { | ||||
| 
 | ||||
| func NewOfflinePusher(pushConf *config.Push, cache cache.ThirdCache, fcmConfigPath string) (OfflinePusher, error) { | ||||
| 	var offlinePusher OfflinePusher | ||||
| 	pushConf.Enable = strings.ToLower(pushConf.Enable) | ||||
| 	switch pushConf.Enable { | ||||
| 	case geTUI: | ||||
| 		offlinePusher = getui.NewClient(pushConf, cache) | ||||
|  | ||||
| @ -7,12 +7,12 @@ import ( | ||||
| 	"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/storage/kafka" | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	pbpush "github.com/openimsdk/protocol/push" | ||||
| 	"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/jsonutil" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| ) | ||||
| @ -55,6 +55,9 @@ func (o *OfflinePushConsumerHandler) handleMsg2OfflinePush(ctx context.Context, | ||||
| 		log.ZError(ctx, "offline push msg is empty", errs.New("offlinePushMsg is empty"), "userIDs", offlinePushMsg.UserIDs, "msg", offlinePushMsg.MsgData) | ||||
| 		return | ||||
| 	} | ||||
| 	if offlinePushMsg.MsgData.Status == constant.MsgStatusSending { | ||||
| 		offlinePushMsg.MsgData.Status = constant.MsgStatusSendSuccess | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "receive to OfflinePush MQ", "userIDs", offlinePushMsg.UserIDs, "msg", offlinePushMsg.MsgData) | ||||
| 
 | ||||
| 	err := o.offlinePushMsg(ctx, offlinePushMsg.MsgData, offlinePushMsg.UserIDs) | ||||
| @ -70,7 +73,7 @@ func (o *OfflinePushConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (ti | ||||
| 		IsAtSelf   bool     `json:"isAtSelf"` | ||||
| 	} | ||||
| 
 | ||||
| 	opts = &options.Opts{Signal: &options.Signal{}} | ||||
| 	opts = &options.Opts{Signal: &options.Signal{ClientMsgID: msg.ClientMsgID}} | ||||
| 	if msg.OfflinePushInfo != nil { | ||||
| 		opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount | ||||
| 		opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound | ||||
|  | ||||
| @ -2,6 +2,8 @@ package push | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/openimsdk/protocol/msggateway" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| 	"github.com/openimsdk/tools/discovery" | ||||
| @ -9,7 +11,6 @@ import ( | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| 	"golang.org/x/sync/errgroup" | ||||
| 	"google.golang.org/grpc" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| type OnlinePusher interface { | ||||
| @ -160,7 +161,7 @@ func (k *K8sStaticConsistentHash) GetConnsAndOnlinePush(ctx context.Context, msg | ||||
| 		} | ||||
| 	} | ||||
| 	log.ZDebug(ctx, "genUsers send hosts struct:", "usersHost", usersHost) | ||||
| 	var usersConns = make(map[*grpc.ClientConn][]string) | ||||
| 	var usersConns = make(map[grpc.ClientConnInterface][]string) | ||||
| 	for host, userIds := range usersHost { | ||||
| 		tconn, _ := k.disCov.GetConn(ctx, host) | ||||
| 		usersConns[tconn] = userIds | ||||
|  | ||||
| @ -14,6 +14,7 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| type pushServer struct { | ||||
| 	pbpush.UnimplementedPushMsgServiceServer | ||||
| 	database      controller.PushDatabase | ||||
| 	disCov        discovery.SvcDiscoveryRegistry | ||||
| 	offlinePusher offlinepush.OfflinePusher | ||||
| @ -31,11 +32,8 @@ type Config struct { | ||||
| 	LocalCacheConfig   config.LocalCache | ||||
| 	Discovery          config.Discovery | ||||
| 	FcmConfigPath      string | ||||
| } | ||||
| 
 | ||||
| func (p pushServer) PushMsg(ctx context.Context, req *pbpush.PushMsgReq) (*pbpush.PushMsgResp, error) { | ||||
| 	//todo reserved Interface | ||||
| 	return nil, nil | ||||
| 	runTimeEnv string | ||||
| } | ||||
| 
 | ||||
| func (p pushServer) DelUserPushToken(ctx context.Context, | ||||
| @ -59,7 +57,7 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg | ||||
| 
 | ||||
| 	database := controller.NewPushDatabase(cacheModel, &config.KafkaConfig) | ||||
| 
 | ||||
| 	consumer, err := NewConsumerHandler(config, database, offlinePusher, rdb, client) | ||||
| 	consumer, err := NewConsumerHandler(ctx, config, database, offlinePusher, rdb, client) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| @ -3,16 +3,21 @@ package push | ||||
| import ( | ||||
| 	"context" | ||||
| 	"encoding/json" | ||||
| 	"math/rand" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 
 | ||||
| 	"github.com/IBM/sarama" | ||||
| 	"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/storage/controller" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/kafka" | ||||
| 	"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/constant" | ||||
| 	"github.com/openimsdk/protocol/msggateway" | ||||
| @ -21,15 +26,11 @@ import ( | ||||
| 	"github.com/openimsdk/tools/discovery" | ||||
| 	"github.com/openimsdk/tools/log" | ||||
| 	"github.com/openimsdk/tools/mcontext" | ||||
| 	"github.com/openimsdk/tools/mq/kafka" | ||||
| 	"github.com/openimsdk/tools/utils/datautil" | ||||
| 	"github.com/openimsdk/tools/utils/jsonutil" | ||||
| 	"github.com/openimsdk/tools/utils/timeutil" | ||||
| 	"github.com/redis/go-redis/v9" | ||||
| 	"google.golang.org/protobuf/proto" | ||||
| 	"math/rand" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type ConsumerHandler struct { | ||||
| @ -40,14 +41,15 @@ type ConsumerHandler struct { | ||||
| 	onlineCache            *rpccache.OnlineCache | ||||
| 	groupLocalCache        *rpccache.GroupLocalCache | ||||
| 	conversationLocalCache *rpccache.ConversationLocalCache | ||||
| 	msgRpcClient           rpcclient.MessageRpcClient | ||||
| 	conversationRpcClient  rpcclient.ConversationRpcClient | ||||
| 	groupRpcClient         rpcclient.GroupRpcClient | ||||
| 	webhookClient          *webhook.Client | ||||
| 	config                 *Config | ||||
| 	userClient             *rpcli.UserClient | ||||
| 	groupClient            *rpcli.GroupClient | ||||
| 	msgClient              *rpcli.MsgClient | ||||
| 	conversationClient     *rpcli.ConversationClient | ||||
| } | ||||
| 
 | ||||
| func NewConsumerHandler(config *Config, database controller.PushDatabase, offlinePusher offlinepush.OfflinePusher, rdb redis.UniversalClient, | ||||
| func NewConsumerHandler(ctx context.Context, config *Config, database controller.PushDatabase, offlinePusher offlinepush.OfflinePusher, rdb redis.UniversalClient, | ||||
| 	client discovery.SvcDiscoveryRegistry) (*ConsumerHandler, error) { | ||||
| 	var consumerHandler ConsumerHandler | ||||
| 	var err error | ||||
| @ -56,20 +58,35 @@ func NewConsumerHandler(config *Config, database controller.PushDatabase, offlin | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID) | ||||
| 	userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	conversationConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Conversation) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	consumerHandler.userClient = rpcli.NewUserClient(userConn) | ||||
| 	consumerHandler.groupClient = rpcli.NewGroupClient(groupConn) | ||||
| 	consumerHandler.msgClient = rpcli.NewMsgClient(msgConn) | ||||
| 	consumerHandler.conversationClient = rpcli.NewConversationClient(conversationConn) | ||||
| 
 | ||||
| 	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.groupLocalCache = rpccache.NewGroupLocalCache(consumerHandler.groupClient, &config.LocalCacheConfig, rdb) | ||||
| 	consumerHandler.conversationLocalCache = rpccache.NewConversationLocalCache(consumerHandler.conversationClient, &config.LocalCacheConfig, rdb) | ||||
| 	consumerHandler.webhookClient = webhook.NewWebhookClient(config.WebhooksConfig.URL) | ||||
| 	consumerHandler.config = config | ||||
| 	consumerHandler.pushDatabase = database | ||||
| 	consumerHandler.onlineCache, err = rpccache.NewOnlineCache(userRpcClient, consumerHandler.groupLocalCache, rdb, config.RpcConfig.FullUserCache, nil) | ||||
| 	consumerHandler.onlineCache, err = rpccache.NewOnlineCache(consumerHandler.userClient, consumerHandler.groupLocalCache, rdb, config.RpcConfig.FullUserCache, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -125,6 +142,7 @@ func (c *ConsumerHandler) ConsumeClaim(sess sarama.ConsumerGroupSession, claim s | ||||
| 
 | ||||
| 	for msg := range claim.Messages() { | ||||
| 		ctx := c.pushConsumerGroup.GetContextFromMsg(msg) | ||||
| 		ctx = mcontext.WithOpUserIDContext(ctx, c.config.Share.IMAdminUserID[0]) | ||||
| 		c.handleMs2PsChat(ctx, msg.Value) | ||||
| 		sess.MarkMessage(msg, "") | ||||
| 	} | ||||
| @ -136,24 +154,24 @@ func (c *ConsumerHandler) Push2User(ctx context.Context, userIDs []string, msg * | ||||
| 	log.ZInfo(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) | ||||
| 	defer func(duration time.Time) { | ||||
| 		t := time.Since(duration) | ||||
| 		log.ZInfo(ctx, "Get msg from msg_transfer And push msg", "msg", msg.String(), "time cost", t) | ||||
| 		log.ZInfo(ctx, "Get msg from msg_transfer And push msg end", "msg", msg.String(), "time cost", t) | ||||
| 	}(time.Now()) | ||||
| 	if err := c.webhookBeforeOnlinePush(ctx, &c.config.WebhooksConfig.BeforeOnlinePush, userIDs, msg); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "webhookBeforeOnlinePush end") | ||||
| 
 | ||||
| 	wsResults, err := c.GetConnsAndOnlinePush(ctx, msg, userIDs) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	log.ZInfo(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs) | ||||
| 	log.ZDebug(ctx, "single and notification push result", "result", wsResults, "msg", msg, "push_to_userID", userIDs) | ||||
| 	log.ZInfo(ctx, "single and notification push end") | ||||
| 
 | ||||
| 	if !c.shouldPushOffline(ctx, msg) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "shouldPushOffline end") | ||||
| 	log.ZInfo(ctx, "pushOffline start") | ||||
| 
 | ||||
| 	for _, v := range wsResults { | ||||
| 		//message sender do not need offline push | ||||
| @ -165,17 +183,21 @@ func (c *ConsumerHandler) Push2User(ctx context.Context, userIDs []string, msg * | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
| 	offlinePushUserID := []string{msg.RecvID} | ||||
| 	needOfflinePushUserID := []string{msg.RecvID} | ||||
| 	var offlinePushUserID []string | ||||
| 
 | ||||
| 	//receiver offline push | ||||
| 	if err = c.webhookBeforeOfflinePush(ctx, &c.config.WebhooksConfig.BeforeOfflinePush, | ||||
| 		offlinePushUserID, msg, nil); err != nil { | ||||
| 	if err = c.webhookBeforeOfflinePush(ctx, &c.config.WebhooksConfig.BeforeOfflinePush, needOfflinePushUserID, msg, &offlinePushUserID); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "webhookBeforeOfflinePush end") | ||||
| 	err = c.offlinePushMsg(ctx, msg, offlinePushUserID) | ||||
| 
 | ||||
| 	if len(offlinePushUserID) > 0 { | ||||
| 		needOfflinePushUserID = offlinePushUserID | ||||
| 	} | ||||
| 	err = c.offlinePushMsg(ctx, msg, needOfflinePushUserID) | ||||
| 	if err != nil { | ||||
| 		log.ZWarn(ctx, "offlinePushMsg failed", err, "offlinePushUserID", offlinePushUserID, "msg", msg) | ||||
| 		log.ZDebug(ctx, "offlinePushMsg failed", err, "needOfflinePushUserID", needOfflinePushUserID, "msg", msg) | ||||
| 		log.ZWarn(ctx, "offlinePushMsg failed", err, "needOfflinePushUserID length", len(needOfflinePushUserID), "msg", msg) | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| @ -187,13 +209,19 @@ func (c *ConsumerHandler) shouldPushOffline(_ context.Context, msg *sdkws.MsgDat | ||||
| 	if !isOfflinePush { | ||||
| 		return false | ||||
| 	} | ||||
| 	if msg.ContentType == constant.SignalingNotification { | ||||
| 	switch msg.ContentType { | ||||
| 	case constant.RoomParticipantsConnectedNotification: | ||||
| 		return false | ||||
| 	case constant.RoomParticipantsDisconnectedNotification: | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (c *ConsumerHandler) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) ([]*msggateway.SingleMsgToUserResults, error) { | ||||
| 	if msg != nil && msg.Status == constant.MsgStatusSending { | ||||
| 		msg.Status = constant.MsgStatusSendSuccess | ||||
| 	} | ||||
| 	onlineUserIDs, offlineUserIDs, err := c.onlineCache.GetUsersOnline(ctx, pushToUserIDs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @ -227,26 +255,24 @@ func (c *ConsumerHandler) Push2Group(ctx context.Context, groupID string, msg *s | ||||
| 		&pushToUserIDs); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "webhookBeforeGroupOnlinePush end") | ||||
| 
 | ||||
| 	err = c.groupMessagesHandler(ctx, groupID, &pushToUserIDs, msg) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	log.ZInfo(ctx, "groupMessagesHandler end") | ||||
| 
 | ||||
| 	wsResults, err := c.GetConnsAndOnlinePush(ctx, msg, pushToUserIDs) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	log.ZInfo(ctx, "group push result", "result", wsResults, "msg", msg) | ||||
| 	log.ZDebug(ctx, "group push result", "result", wsResults, "msg", msg) | ||||
| 	log.ZInfo(ctx, "online group push end") | ||||
| 
 | ||||
| 	if !c.shouldPushOffline(ctx, msg) { | ||||
| 		return nil | ||||
| 	} | ||||
| 	needOfflinePushUserIDs := c.onlinePusher.GetOnlinePushFailedUserIDs(ctx, msg, wsResults, &pushToUserIDs) | ||||
| 	log.ZInfo(ctx, "GetOnlinePushFailedUserIDs end") | ||||
| 	//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 { | ||||
| @ -274,9 +300,11 @@ func (c *ConsumerHandler) asyncOfflinePush(ctx context.Context, needOfflinePushU | ||||
| 		needOfflinePushUserIDs = offlinePushUserIDs | ||||
| 	} | ||||
| 	if err := c.pushDatabase.MsgToOfflinePushMQ(ctx, conversationutil.GenConversationUniqueKeyForSingle(msg.SendID, msg.RecvID), needOfflinePushUserIDs, msg); err != nil { | ||||
| 		log.ZError(ctx, "Msg To OfflinePush MQ error", err, "needOfflinePushUserIDs", | ||||
| 		log.ZDebug(ctx, "Msg To OfflinePush MQ error", err, "needOfflinePushUserIDs", | ||||
| 			needOfflinePushUserIDs, "msg", msg) | ||||
| 		prommetrics.SingleChatMsgProcessFailedCounter.Inc() | ||||
| 		log.ZWarn(ctx, "Msg To OfflinePush MQ error", err, "needOfflinePushUserIDs length", | ||||
| 			len(needOfflinePushUserIDs), "msg", msg) | ||||
| 		prommetrics.GroupChatMsgProcessFailedCounter.Inc() | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
| @ -319,7 +347,7 @@ func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID stri | ||||
| 					ctx = mcontext.WithOpUserIDContext(ctx, c.config.Share.IMAdminUserID[0]) | ||||
| 				} | ||||
| 				defer func(groupID string) { | ||||
| 					if err = c.groupRpcClient.DismissGroup(ctx, groupID); err != nil { | ||||
| 					if err := c.groupClient.DismissGroup(ctx, groupID, true); err != nil { | ||||
| 						log.ZError(ctx, "DismissGroup Notification clear members", err, "groupID", groupID) | ||||
| 					} | ||||
| 				}(groupID) | ||||
| @ -332,6 +360,7 @@ func (c *ConsumerHandler) groupMessagesHandler(ctx context.Context, groupID stri | ||||
| func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData, offlinePushUserIDs []string) error { | ||||
| 	title, content, opts, err := c.getOfflinePushInfos(msg) | ||||
| 	if err != nil { | ||||
| 		log.ZError(ctx, "getOfflinePushInfos failed", err, "msg", msg) | ||||
| 		return err | ||||
| 	} | ||||
| 	err = c.offlinePusher.Push(ctx, offlinePushUserIDs, title, content, opts) | ||||
| @ -344,10 +373,7 @@ func (c *ConsumerHandler) offlinePushMsg(ctx context.Context, msg *sdkws.MsgData | ||||
| 
 | ||||
| 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) | ||||
| 	needOfflinePushUserIDs, err := c.conversationClient.GetConversationOfflinePushUserIDs(ctx, conversationutil.GenGroupConversationID(groupID), offlinePushUserIDs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -361,7 +387,7 @@ func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, conten | ||||
| 		IsAtSelf   bool     `json:"isAtSelf"` | ||||
| 	} | ||||
| 
 | ||||
| 	opts = &options.Opts{Signal: &options.Signal{}} | ||||
| 	opts = &options.Opts{Signal: &options.Signal{ClientMsgID: msg.ClientMsgID}} | ||||
| 	if msg.OfflinePushInfo != nil { | ||||
| 		opts.IOSBadgeCount = msg.OfflinePushInfo.IOSBadgeCount | ||||
| 		opts.IOSPushSound = msg.OfflinePushInfo.IOSPushSound | ||||
| @ -401,11 +427,11 @@ func (c *ConsumerHandler) getOfflinePushInfos(msg *sdkws.MsgData) (title, conten | ||||
| 
 | ||||
| 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) | ||||
| 	maxSeq, err := c.msgClient.GetConversationMaxSeq(ctx, conversationID) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return c.conversationRpcClient.SetConversationMaxSeq(ctx, userIDs, conversationID, maxSeq) | ||||
| 	return c.conversationClient.SetConversationMaxSeq(ctx, conversationID, userIDs, maxSeq) | ||||
| } | ||||
| 
 | ||||
| func unmarshalNotificationElem(bytes []byte, t any) error { | ||||
| @ -413,6 +439,5 @@ func unmarshalNotificationElem(bytes []byte, t any) error { | ||||
| 	if err := json.Unmarshal(bytes, ¬ification); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	return json.Unmarshal([]byte(notification.Detail), t) | ||||
| } | ||||
|  | ||||
| @ -16,6 +16,9 @@ package auth | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	redis2 "github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis" | ||||
| @ -27,7 +30,6 @@ import ( | ||||
| 	"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/common/storage/controller" | ||||
| 	"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" | ||||
| @ -39,10 +41,11 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| type authServer struct { | ||||
| 	pbauth.UnimplementedAuthServer | ||||
| 	authDatabase   controller.AuthDatabase | ||||
| 	userRpcClient  *rpcclient.UserRpcClient | ||||
| 	RegisterCenter discovery.SvcDiscoveryRegistry | ||||
| 	config         *Config | ||||
| 	userClient     *rpcli.UserClient | ||||
| } | ||||
| 
 | ||||
| type Config struct { | ||||
| @ -57,17 +60,21 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	userRpcClient := rpcclient.NewUserRpcClient(client, config.Share.RpcRegisterName.User, config.Share.IMAdminUserID) | ||||
| 	userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	pbauth.RegisterAuthServer(server, &authServer{ | ||||
| 		userRpcClient:  &userRpcClient, | ||||
| 		RegisterCenter: client, | ||||
| 		authDatabase: controller.NewAuthDatabase( | ||||
| 			redis2.NewTokenCacheModel(rdb, config.RpcConfig.TokenPolicy.Expire), | ||||
| 			config.Share.Secret, | ||||
| 			config.RpcConfig.TokenPolicy.Expire, | ||||
| 			config.Share.MultiLoginPolicy, | ||||
| 			config.Share.MultiLogin, | ||||
| 			config.Share.IMAdminUserID, | ||||
| 		), | ||||
| 		config:     config, | ||||
| 		userClient: rpcli.NewUserClient(userConn), | ||||
| 	}) | ||||
| 	return nil | ||||
| } | ||||
| @ -83,7 +90,7 @@ func (s *authServer) GetAdminToken(ctx context.Context, req *pbauth.GetAdminToke | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil { | ||||
| 	if err := s.userClient.CheckUser(ctx, []string{req.UserID}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| @ -112,9 +119,13 @@ func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenR | ||||
| 	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 { | ||||
| 	user, err := s.userClient.GetUserInfo(ctx, req.UserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if user.AppMangerLevel >= constant.AppNotificationAdmin { | ||||
| 		return nil, errs.ErrArgs.WrapMsg("app account can`t get token") | ||||
| 	} | ||||
| 	token, err := s.authDatabase.CreateToken(ctx, req.UserID, int(req.PlatformID)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| @ -127,7 +138,11 @@ func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenR | ||||
| func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) { | ||||
| 	claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret(s.config.Share.Secret)) | ||||
| 	if err != nil { | ||||
| 		return nil, errs.Wrap(err) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	isAdmin := authverify.IsManagerUserID(claims.UserID, s.config.Share.IMAdminUserID) | ||||
| 	if isAdmin { | ||||
| 		return claims, nil | ||||
| 	} | ||||
| 	m, err := s.authDatabase.GetTokensWithoutError(ctx, claims.UserID, claims.PlatformID) | ||||
| 	if err != nil { | ||||
| @ -149,10 +164,7 @@ func (s *authServer) parseToken(ctx context.Context, tokensString string) (claim | ||||
| 	return nil, servererrs.ErrTokenNotExist.Wrap() | ||||
| } | ||||
| 
 | ||||
| func (s *authServer) ParseToken( | ||||
| 	ctx context.Context, | ||||
| 	req *pbauth.ParseTokenReq, | ||||
| ) (resp *pbauth.ParseTokenResp, err error) { | ||||
| func (s *authServer) ParseToken(ctx context.Context, req *pbauth.ParseTokenReq) (resp *pbauth.ParseTokenResp, err error) { | ||||
| 	resp = &pbauth.ParseTokenResp{} | ||||
| 	claims, err := s.parseToken(ctx, req.Token) | ||||
| 	if err != nil { | ||||
| @ -180,7 +192,7 @@ func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID | ||||
| 		return err | ||||
| 	} | ||||
| 	for _, v := range conns { | ||||
| 		log.ZDebug(ctx, "forceKickOff", "conn", v.Target()) | ||||
| 		log.ZDebug(ctx, "forceKickOff", "userID", userID, "platformID", platformID) | ||||
| 		client := msggateway.NewMsgGatewayClient(v) | ||||
| 		kickReq := &msggateway.KickUserOfflineReq{KickUserIDList: []string{userID}, PlatformID: platformID} | ||||
| 		_, err := client.KickUserOffline(ctx, kickReq) | ||||
| @ -190,7 +202,7 @@ func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID | ||||
| 	} | ||||
| 
 | ||||
| 	m, err := s.authDatabase.GetTokensWithoutError(ctx, userID, int(platformID)) | ||||
| 	if err != nil && err != redis.Nil { | ||||
| 	if err != nil && !errors.Is(err, redis.Nil) { | ||||
| 		return err | ||||
| 	} | ||||
| 	for k := range m { | ||||
| @ -208,7 +220,7 @@ func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID | ||||
| 
 | ||||
| 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 { | ||||
| 	if err != nil && !errors.Is(err, redis.Nil) { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if m == nil { | ||||
| @ -230,3 +242,10 @@ func (s *authServer) InvalidateToken(ctx context.Context, req *pbauth.Invalidate | ||||
| 	} | ||||
| 	return &pbauth.InvalidateTokenResp{}, nil | ||||
| } | ||||
| 
 | ||||
| func (s *authServer) KickTokens(ctx context.Context, req *pbauth.KickTokensReq) (*pbauth.KickTokensResp, error) { | ||||
| 	if err := s.authDatabase.BatchSetTokenMapByUidPid(ctx, req.Tokens); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &pbauth.KickTokensResp{}, nil | ||||
| } | ||||
|  | ||||
| @ -19,18 +19,20 @@ import ( | ||||
| 	"sort" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/config" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/redis" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database/mgo" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||
| 	dbModel "github.com/openimsdk/open-im-server/v3/pkg/common/storage/model" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/localcache" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" | ||||
| 	"github.com/openimsdk/tools/db/redisutil" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/convert" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/servererrs" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/common/storage/controller" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcclient" | ||||
| 	"github.com/openimsdk/protocol/constant" | ||||
| 	pbconversation "github.com/openimsdk/protocol/conversation" | ||||
| 	"github.com/openimsdk/protocol/sdkws" | ||||
| @ -43,13 +45,15 @@ import ( | ||||
| ) | ||||
| 
 | ||||
| type conversationServer struct { | ||||
| 	msgRpcClient         *rpcclient.MessageRpcClient | ||||
| 	user                 *rpcclient.UserRpcClient | ||||
| 	groupRpcClient       *rpcclient.GroupRpcClient | ||||
| 	pbconversation.UnimplementedConversationServer | ||||
| 	conversationDatabase controller.ConversationDatabase | ||||
| 
 | ||||
| 	conversationNotificationSender *ConversationNotificationSender | ||||
| 	config                         *Config | ||||
| 
 | ||||
| 	userClient  *rpcli.UserClient | ||||
| 	msgClient   *rpcli.MsgClient | ||||
| 	groupClient *rpcli.GroupClient | ||||
| } | ||||
| 
 | ||||
| type Config struct { | ||||
| @ -75,17 +79,27 @@ func Start(ctx context.Context, config *Config, client discovery.SvcDiscoveryReg | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	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) | ||||
| 	userConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.User) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	groupConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Group) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	msgConn, err := client.GetConn(ctx, config.Share.RpcRegisterName.Msg) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	msgClient := rpcli.NewMsgClient(msgConn) | ||||
| 	localcache.InitLocalCache(&config.LocalCacheConfig) | ||||
| 	pbconversation.RegisterConversationServer(server, &conversationServer{ | ||||
| 		msgRpcClient:                   &msgRpcClient, | ||||
| 		user:                           &userRpcClient, | ||||
| 		conversationNotificationSender: NewConversationNotificationSender(&config.NotificationConfig, &msgRpcClient), | ||||
| 		groupRpcClient:                 &groupRpcClient, | ||||
| 		conversationNotificationSender: NewConversationNotificationSender(&config.NotificationConfig, msgClient), | ||||
| 		conversationDatabase: controller.NewConversationDatabase(conversationDB, | ||||
| 			redis.NewConversationRedis(rdb, &config.LocalCacheConfig, redis.GetRocksCacheOptions(), conversationDB), mgocli.GetTx()), | ||||
| 		userClient:  rpcli.NewUserClient(userConn), | ||||
| 		groupClient: rpcli.NewGroupClient(groupConn), | ||||
| 		msgClient:   msgClient, | ||||
| 	}) | ||||
| 	return nil | ||||
| } | ||||
| @ -122,13 +136,12 @@ func (c *conversationServer) GetSortedConversationList(ctx context.Context, req | ||||
| 	if len(conversations) == 0 { | ||||
| 		return nil, errs.ErrRecordNotFound.Wrap() | ||||
| 	} | ||||
| 
 | ||||
| 	maxSeqs, err := c.msgRpcClient.GetMaxSeqs(ctx, conversationIDs) | ||||
| 	maxSeqs, err := c.msgClient.GetMaxSeqs(ctx, conversationIDs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	chatLogs, err := c.msgRpcClient.GetMsgByConversationIDs(ctx, conversationIDs, maxSeqs) | ||||
| 	chatLogs, err := c.msgClient.GetMsgByConversationIDs(ctx, conversationIDs, maxSeqs) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -138,7 +151,7 @@ func (c *conversationServer) GetSortedConversationList(ctx context.Context, req | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	hasReadSeqs, err := c.msgRpcClient.GetHasReadSeqs(ctx, req.UserID, conversationIDs) | ||||
| 	hasReadSeqs, err := c.msgClient.GetHasReadSeqs(ctx, conversationIDs, req.UserID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| @ -155,7 +168,18 @@ func (c *conversationServer) GetSortedConversationList(ctx context.Context, req | ||||
| 	conversation_notPinTime := make(map[int64]string) | ||||
| 	for _, v := range conversations { | ||||
| 		conversationID := v.ConversationID | ||||
| 		time := conversationMsg[conversationID].MsgInfo.LatestMsgRecvTime | ||||
| 		var time int64 | ||||
| 		if _, ok := conversationMsg[conversationID]; ok { | ||||
| 			time = conversationMsg[conversationID].MsgInfo.LatestMsgRecvTime | ||||
| 		} else { | ||||
| 			conversationMsg[conversationID] = &pbconversation.ConversationElem{ | ||||
| 				ConversationID: conversationID, | ||||
| 				IsPinned:       v.IsPinned, | ||||
| 				MsgInfo:        nil, | ||||
| 			} | ||||
| 			time = v.CreateTime.UnixMilli() | ||||
| 		} | ||||
| 
 | ||||
| 		conversationMsg[conversationID].RecvMsgOpt = v.RecvMsgOpt | ||||
| 		if v.IsPinned { | ||||
| 			conversationMsg[conversationID].IsPinned = v.IsPinned | ||||
| @ -207,6 +231,7 @@ func (c *conversationServer) getConversations(ctx context.Context, ownerUserID s | ||||
| 	return convert.ConversationsDB2Pb(conversations), nil | ||||
| } | ||||
| 
 | ||||
| // Deprecated | ||||
| func (c *conversationServer) SetConversation(ctx context.Context, req *pbconversation.SetConversationReq) (*pbconversation.SetConversationResp, error) { | ||||
| 	var conversation dbModel.Conversation | ||||
| 	if err := datautil.CopyStructFields(&conversation, req.Conversation); err != nil { | ||||
| @ -227,7 +252,7 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbconver | ||||
| 	} | ||||
| 
 | ||||
| 	if req.Conversation.ConversationType == constant.WriteGroupChatType { | ||||
| 		groupInfo, err := c.groupRpcClient.GetGroupInfo(ctx, req.Conversation.GroupID) | ||||
| 		groupInfo, err := c.groupClient.GetGroupInfo(ctx, req.Conversation.GroupID) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @ -261,27 +286,35 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbconver | ||||
| 
 | ||||
| 	setConversationFieldsFunc := func() { | ||||
| 		if req.Conversation.RecvMsgOpt != nil { | ||||
| 			conversation.RecvMsgOpt = req.Conversation.RecvMsgOpt.Value | ||||
| 			m["recv_msg_opt"] = req.Conversation.RecvMsgOpt.Value | ||||
| 		} | ||||
| 		if req.Conversation.AttachedInfo != nil { | ||||
| 			conversation.AttachedInfo = req.Conversation.AttachedInfo.Value | ||||
| 			m["attached_info"] = req.Conversation.AttachedInfo.Value | ||||
| 		} | ||||
| 		if req.Conversation.Ex != nil { | ||||
| 			conversation.Ex = req.Conversation.Ex.Value | ||||
| 			m["ex"] = req.Conversation.Ex.Value | ||||
| 		} | ||||
| 		if req.Conversation.IsPinned != nil { | ||||
| 			conversation.IsPinned = req.Conversation.IsPinned.Value | ||||
| 			m["is_pinned"] = req.Conversation.IsPinned.Value | ||||
| 		} | ||||
| 		if req.Conversation.GroupAtType != nil { | ||||
| 			conversation.GroupAtType = req.Conversation.GroupAtType.Value | ||||
| 			m["group_at_type"] = req.Conversation.GroupAtType.Value | ||||
| 		} | ||||
| 		if req.Conversation.MsgDestructTime != nil { | ||||
| 			conversation.MsgDestructTime = req.Conversation.MsgDestructTime.Value | ||||
| 			m["msg_destruct_time"] = req.Conversation.MsgDestructTime.Value | ||||
| 		} | ||||
| 		if req.Conversation.IsMsgDestruct != nil { | ||||
| 			conversation.IsMsgDestruct = req.Conversation.IsMsgDestruct.Value | ||||
| 			m["is_msg_destruct"] = req.Conversation.IsMsgDestruct.Value | ||||
| 		} | ||||
| 		if req.Conversation.BurnDuration != nil { | ||||
| 			conversation.BurnDuration = req.Conversation.BurnDuration.Value | ||||
| 			m["burn_duration"] = req.Conversation.BurnDuration.Value | ||||
| 		} | ||||
| 	} | ||||
| @ -343,7 +376,15 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbconver | ||||
| 			needUpdateUsersList = append(needUpdateUsersList, userID) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(m) != 0 && len(needUpdateUsersList) != 0 { | ||||
| 		if err := c.conversationDatabase.SetUsersConversationFieldTx(ctx, needUpdateUsersList, &conversation, m); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 
 | ||||
| 		for _, v := range needUpdateUsersList { | ||||
| 			c.conversationNotificationSender.ConversationChangeNotification(ctx, v, []string{req.Conversation.ConversationID}) | ||||
| 		} | ||||
| 	} | ||||
| 	if req.Conversation.IsPrivateChat != nil && req.Conversation.ConversationType != constant.ReadGroupChatType { | ||||
| 		var conversations []*dbModel.Conversation | ||||
| 		for _, ownerUserID := range req.UserIDs { | ||||
| @ -361,16 +402,6 @@ func (c *conversationServer) SetConversations(ctx context.Context, req *pbconver | ||||
| 			c.conversationNotificationSender.ConversationSetPrivateNotification(ctx, userID, req.Conversation.UserID, | ||||
| 				req.Conversation.IsPrivateChat.Value, req.Conversation.ConversationID) | ||||
| 		} | ||||
| 	} else { | ||||
| 		if len(m) != 0 && len(needUpdateUsersList) != 0 { | ||||
| 			if err := c.conversationDatabase.SetUsersConversationFieldTx(ctx, needUpdateUsersList, &conversation, m); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 
 | ||||
| 			for _, v := range needUpdateUsersList { | ||||
| 				c.conversationNotificationSender.ConversationChangeNotification(ctx, v, []string{req.Conversation.ConversationID}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return &pbconversation.SetConversationsResp{}, nil | ||||
| @ -424,22 +455,38 @@ func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, r | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	conversationID := msgprocessor.GetConversationIDBySessionType(constant.ReadGroupChatType, req.GroupID) | ||||
| 	if err := c.msgClient.SetUserConversationMaxSeq(ctx, conversationID, req.UserIDs, 0); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &pbconversation.CreateGroupChatConversationsResp{}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conversationServer) SetConversationMaxSeq(ctx context.Context, req *pbconversation.SetConversationMaxSeqReq) (*pbconversation.SetConversationMaxSeqResp, error) { | ||||
| 	if err := c.msgClient.SetUserConversationMaxSeq(ctx, req.ConversationID, req.OwnerUserID, req.MaxSeq); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := c.conversationDatabase.UpdateUsersConversationField(ctx, req.OwnerUserID, req.ConversationID, | ||||
| 		map[string]any{"max_seq": req.MaxSeq}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, userID := range req.OwnerUserID { | ||||
| 		c.conversationNotificationSender.ConversationChangeNotification(ctx, userID, []string{req.ConversationID}) | ||||
| 	} | ||||
| 	return &pbconversation.SetConversationMaxSeqResp{}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conversationServer) SetConversationMinSeq(ctx context.Context, req *pbconversation.SetConversationMinSeqReq) (*pbconversation.SetConversationMinSeqResp, error) { | ||||
| 	if err := c.msgClient.SetUserConversationMin(ctx, req.ConversationID, req.OwnerUserID, req.MinSeq); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if err := c.conversationDatabase.UpdateUsersConversationField(ctx, req.OwnerUserID, req.ConversationID, | ||||
| 		map[string]any{"min_seq": req.MinSeq}); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for _, userID := range req.OwnerUserID { | ||||
| 		c.conversationNotificationSender.ConversationChangeNotification(ctx, userID, []string{req.ConversationID}) | ||||
| 	} | ||||
| 	return &pbconversation.SetConversationMinSeqResp{}, nil | ||||
| } | ||||
| 
 | ||||
| @ -540,7 +587,7 @@ func (c *conversationServer) getConversationInfo( | ||||
| 		} | ||||
| 	} | ||||
| 	if len(sendIDs) != 0 { | ||||
| 		sendInfos, err := c.user.GetUsersInfo(ctx, sendIDs) | ||||
| 		sendInfos, err := c.userClient.GetUsersInfo(ctx, sendIDs) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @ -549,7 +596,7 @@ func (c *conversationServer) getConversationInfo( | ||||
| 		} | ||||
| 	} | ||||
| 	if len(groupIDs) != 0 { | ||||
| 		groupInfos, err := c.groupRpcClient.GetGroupInfos(ctx, groupIDs, false) | ||||
| 		groupInfos, err := c.groupClient.GetGroupsInfo(ctx, groupIDs) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| @ -661,7 +708,7 @@ func (c *conversationServer) GetOwnerConversation(ctx context.Context, req *pbco | ||||
| 	}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conversationServer) GetConversationsNeedDestructMsgs(ctx context.Context, _ *pbconversation.GetConversationsNeedDestructMsgsReq) (*pbconversation.GetConversationsNeedDestructMsgsResp, error) { | ||||
| func (c *conversationServer) GetConversationsNeedClearMsg(ctx context.Context, _ *pbconversation.GetConversationsNeedClearMsgReq) (*pbconversation.GetConversationsNeedClearMsgResp, error) { | ||||
| 	num, err := c.conversationDatabase.GetAllConversationIDsNumber(ctx) | ||||
| 	if err != nil { | ||||
| 		log.ZError(ctx, "GetAllConversationIDsNumber failed", err) | ||||
| @ -685,7 +732,7 @@ func (c *conversationServer) GetConversationsNeedDestructMsgs(ctx context.Contex | ||||
| 
 | ||||
| 		conversationIDs, err := c.conversationDatabase.PageConversationIDs(ctx, pagination) | ||||
| 		if err != nil { | ||||
| 			// log.ZError(ctx, "PageConversationIDs failed", err, "pageNumber", pageNumber) | ||||
| 			log.ZError(ctx, "PageConversationIDs failed", err, "pageNumber", pageNumber) | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| @ -708,7 +755,7 @@ func (c *conversationServer) GetConversationsNeedDestructMsgs(ctx context.Contex | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return &pbconversation.GetConversationsNeedDestructMsgsResp{Conversations: convert.ConversationsDB2Pb(temp)}, nil | ||||
| 	return &pbconversation.GetConversationsNeedClearMsgResp{Conversations: convert.ConversationsDB2Pb(temp)}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conversationServer) GetNotNotifyConversationIDs(ctx context.Context, req *pbconversation.GetNotNotifyConversationIDsReq) (*pbconversation.GetNotNotifyConversationIDsResp, error) { | ||||
| @ -726,3 +773,51 @@ func (c *conversationServer) GetPinnedConversationIDs(ctx context.Context, req * | ||||
| 	} | ||||
| 	return &pbconversation.GetPinnedConversationIDsResp{ConversationIDs: conversationIDs}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conversationServer) ClearUserConversationMsg(ctx context.Context, req *pbconversation.ClearUserConversationMsgReq) (*pbconversation.ClearUserConversationMsgResp, error) { | ||||
| 	conversations, err := c.conversationDatabase.FindRandConversation(ctx, req.Timestamp, int(req.Limit)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	latestMsgDestructTime := time.UnixMilli(req.Timestamp) | ||||
| 	for i, conversation := range conversations { | ||||
| 		if conversation.IsMsgDestruct == false || conversation.MsgDestructTime == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		seq, err := c.msgClient.GetLastMessageSeqByTime(ctx, conversation.ConversationID, req.Timestamp-(conversation.MsgDestructTime*1000)) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if seq <= 0 { | ||||
| 			log.ZDebug(ctx, "ClearUserConversationMsg GetLastMessageSeqByTime seq <= 0", "index", i, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID, "msgDestructTime", conversation.MsgDestructTime, "seq", seq) | ||||
| 			if err := c.setConversationMinSeqAndLatestMsgDestructTime(ctx, conversation.ConversationID, conversation.OwnerUserID, -1, latestMsgDestructTime); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| 		seq++ | ||||
| 		if err := c.setConversationMinSeqAndLatestMsgDestructTime(ctx, conversation.ConversationID, conversation.OwnerUserID, seq, latestMsgDestructTime); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		log.ZDebug(ctx, "ClearUserConversationMsg set min seq", "index", i, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID, "seq", seq, "msgDestructTime", conversation.MsgDestructTime) | ||||
| 	} | ||||
| 	return &pbconversation.ClearUserConversationMsgResp{Count: int32(len(conversations))}, nil | ||||
| } | ||||
| 
 | ||||
| func (c *conversationServer) setConversationMinSeqAndLatestMsgDestructTime(ctx context.Context, conversationID string, ownerUserID string, minSeq int64, latestMsgDestructTime time.Time) error { | ||||
| 	update := map[string]any{ | ||||
| 		"latest_msg_destruct_time": latestMsgDestructTime, | ||||
| 	} | ||||
| 	if minSeq >= 0 { | ||||
| 		if err := c.msgClient.SetUserConversationMin(ctx, conversationID, []string{ownerUserID}, minSeq); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		update["min_seq"] = minSeq | ||||
| 	} | ||||
| 
 | ||||
| 	if err := c.conversationDatabase.UpdateUsersConversationField(ctx, []string{ownerUserID}, conversationID, update); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	c.conversationNotificationSender.ConversationChangeNotification(ctx, ownerUserID, []string{conversationID}) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -17,18 +17,23 @@ package conversation | ||||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/notification" | ||||
| 	"github.com/openimsdk/open-im-server/v3/pkg/rpcli" | ||||
| 	"github.com/openimsdk/protocol/msg" | ||||
| 
 | ||||
| 	"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 | ||||
| 	*notification.NotificationSender | ||||
| } | ||||
| 
 | ||||
| func NewConversationNotificationSender(conf *config.Notification, msgRpcClient *rpcclient.MessageRpcClient) *ConversationNotificationSender { | ||||
| 	return &ConversationNotificationSender{rpcclient.NewNotificationSender(conf, rpcclient.WithRpcClient(msgRpcClient))} | ||||
| func NewConversationNotificationSender(conf *config.Notification, msgClient *rpcli.MsgClient) *ConversationNotificationSender { | ||||
| 	return &ConversationNotificationSender{notification.NewNotificationSender(conf, notification.WithRpcClient(func(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) { | ||||
| 		return msgClient.SendMsg(ctx, req) | ||||
| 	}))} | ||||
| } | ||||
| 
 | ||||
| // SetPrivate invote. | ||||
|  | ||||
| @ -23,7 +23,7 @@ func (c *conversationServer) GetFullOwnerConversationIDs(ctx context.Context, re | ||||
| 		conversationIDs = nil | ||||
| 	} | ||||
| 	return &conversation.GetFullOwnerConversationIDsResp{ | ||||
| 		Version:         idHash, | ||||
| 		Version:         uint64(vl.Version), | ||||
| 		VersionID:       vl.ID.Hex(), | ||||
| 		Equal:           req.IdHash == idHash, | ||||
| 		ConversationIDs: conversationIDs, | ||||
|  | ||||
| @ -33,6 +33,9 @@ func (s *groupServer) GetGroupInfoCache(ctx context.Context, req *pbgroup.GetGro | ||||
| } | ||||
| 
 | ||||
| func (s *groupServer) GetGroupMemberCache(ctx context.Context, req *pbgroup.GetGroupMemberCacheReq) (*pbgroup.GetGroupMemberCacheResp, error) { | ||||
| 	if err := s.checkAdminOrInGroup(ctx, req.GroupID); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	members, err := s.db.TakeGroupMember(ctx, req.GroupID, req.GroupMemberID) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
|  | ||||
| @ -16,6 +16,7 @@ package group | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	pbgroup "github.com/openimsdk/protocol/group" | ||||
| @ -55,41 +56,52 @@ func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[s | ||||
| 	return m | ||||
| } | ||||
| 
 | ||||
| func UpdateGroupInfoExMap(ctx context.Context, group *pbgroup.SetGroupInfoExReq) (map[string]any, error) { | ||||
| 	m := make(map[string]any) | ||||
| func UpdateGroupInfoExMap(ctx context.Context, group *pbgroup.SetGroupInfoExReq) (m map[string]any, normalFlag, groupNameFlag, notificationFlag bool, err error) { | ||||
| 	m = make(map[string]any) | ||||
| 
 | ||||
| 	if group.GroupName != nil { | ||||
| 		if group.GroupName.Value != "" { | ||||
| 		if strings.TrimSpace(group.GroupName.Value) != "" { | ||||
| 			m["group_name"] = group.GroupName.Value | ||||
| 			groupNameFlag = true | ||||
| 		} else { | ||||
| 			return nil, errs.ErrArgs.WrapMsg("group name is empty") | ||||
| 			return nil, normalFlag, notificationFlag, groupNameFlag, errs.ErrArgs.WrapMsg("group name is empty") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if group.Notification != nil { | ||||
| 		notificationFlag = true | ||||
| 		group.Notification.Value = strings.TrimSpace(group.Notification.Value) // if Notification only contains spaces, set it to empty string | ||||
| 
 | ||||
| 		m["notification"] = group.Notification.Value | ||||
| 		m["notification_update_time"] = time.Now() | ||||
| 		m["notification_user_id"] = mcontext.GetOpUserID(ctx) | ||||
| 		m["notification_update_time"] = time.Now() | ||||
| 	} | ||||
| 	if group.Introduction != nil { | ||||
| 		m["introduction"] = group.Introduction.Value | ||||
| 		normalFlag = true | ||||
| 	} | ||||
| 	if group.FaceURL != nil { | ||||
| 		m["face_url"] = group.FaceURL.Value | ||||
| 		normalFlag = true | ||||
| 	} | ||||
| 	if group.NeedVerification != nil { | ||||
| 		m["need_verification"] = group.NeedVerification.Value | ||||
| 		normalFlag = true | ||||
| 	} | ||||
| 	if group.LookMemberInfo != nil { | ||||
| 		m["look_member_info"] = group.LookMemberInfo.Value | ||||
| 		normalFlag = true | ||||
| 	} | ||||
| 	if group.ApplyMemberFriend != nil { | ||||
| 		m["apply_member_friend"] = group.ApplyMemberFriend.Value | ||||
| 		normalFlag = true | ||||
| 	} | ||||
| 	if group.Ex != nil { | ||||
| 		m["ex"] = group.Ex.Value | ||||
| 		normalFlag = true | ||||
| 	} | ||||
| 
 | ||||
| 	return m, nil | ||||
| 	return m, normalFlag, groupNameFlag, notificationFlag, nil | ||||
| } | ||||
| 
 | ||||
| func UpdateGroupStatusMap(status int) map[string]any { | ||||
| @ -110,7 +122,7 @@ func UpdateGroupMemberMap(req *pbgroup.SetGroupMemberInfo) map[string]any { | ||||
| 		m["nickname"] = req.Nickname.Value | ||||
| 	} | ||||
| 	if req.FaceURL != nil { | ||||
| 		m["user_group_face_url"] = req.FaceURL.Value | ||||
| 		m["face_url"] = req.FaceURL.Value | ||||
| 	} | ||||
| 	if req.RoleLevel != nil { | ||||
| 		m["role_level"] = req.RoleLevel.Value | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user