merge: update code from main to v3.8-js-sdk-only. (#2818)
* feat: implement merge milestone PR to target-branch. (#2796) * build: improve workflows logic. (#2801) * fix: improve time condition check mehtod. (#2804) * fix: improve time condition check mehtod. * fix * fix: webhook before online push (#2805) * fix: set own read seq in MongoDB when sender send a message. (#2808) * fix: solve err Notification when setGroupInfo. (#2806) * fix: solve err Notification when setGroupInfo. * build: update checkout version. * fix: update notification contents. * Introducing OpenIM Guru on Gurubase.io (#2788) * feat: support app update service (#2811) * fix: GroupApplicationAcceptedNotification * fix: GroupApplicationAcceptedNotification * fix: NotificationUserInfoUpdate * cicd: robot automated Change * fix: component * fix: getConversationInfo * feat: cron task * feat: cron task * feat: cron task * feat: cron task * feat: cron task * fix: minio config url recognition error * update gomake version * update gomake version * fix: seq conversion bug * fix: redis pipe exec * fix: ImportFriends * fix: A large number of logs keysAndValues length is not even * feat: mark read aggregate write * feat: online status supports redis cluster * feat: online status supports redis cluster * feat: online status supports redis cluster * merge * merge * read seq is written to mongo * read seq is written to mongo * fix: invitation to join group notification * fix: friend op_user_id * feat: optimizing asynchronous context * feat: optimizing memamq size * feat: add GetSeqMessage * feat: GroupApplicationAgreeMemberEnterNotification * feat: GroupApplicationAgreeMemberEnterNotification * feat: go.mod * feat: go.mod * feat: join group notification and get seq * feat: join group notification and get seq * feat: avoid pulling messages from sessions with a large number of max seq values of 0 * feat: API supports gzip * go.mod * fix: nil pointer error on close * fix: listen error * fix: listen error * update go.mod * feat: add log * fix: token parse token value * fix: GetMsgBySeqs boundary issues * fix: sn_ not sort * fix: sn_ not sort * fix: sn_ not sort * fix: jssdk add * fix: jssdk support * fix: jssdk support * fix: jssdk support * fix: the message I sent is not set to read seq in mongodb * fix: cannot modify group member avatars * fix: MemberEnterNotification * fix: MemberEnterNotification * fix: MsgData status * feat: add ApplicationVersion * feat: add ApplicationVersion * feat: add ApplicationVersion --------- Co-authored-by: withchao <withchao@users.noreply.github.com> * feat: ApplicationVersion move chat (#2813) * fix: GroupApplicationAcceptedNotification * fix: GroupApplicationAcceptedNotification * fix: NotificationUserInfoUpdate * cicd: robot automated Change * fix: component * fix: getConversationInfo * feat: cron task * feat: cron task * feat: cron task * feat: cron task * feat: cron task * fix: minio config url recognition error * update gomake version * update gomake version * fix: seq conversion bug * fix: redis pipe exec * fix: ImportFriends * fix: A large number of logs keysAndValues length is not even * feat: mark read aggregate write * feat: online status supports redis cluster * feat: online status supports redis cluster * feat: online status supports redis cluster * merge * merge * read seq is written to mongo * read seq is written to mongo * fix: invitation to join group notification * fix: friend op_user_id * feat: optimizing asynchronous context * feat: optimizing memamq size * feat: add GetSeqMessage * feat: GroupApplicationAgreeMemberEnterNotification * feat: GroupApplicationAgreeMemberEnterNotification * feat: go.mod * feat: go.mod * feat: join group notification and get seq * feat: join group notification and get seq * feat: avoid pulling messages from sessions with a large number of max seq values of 0 * feat: API supports gzip * go.mod * fix: nil pointer error on close * fix: listen error * fix: listen error * update go.mod * feat: add log * fix: token parse token value * fix: GetMsgBySeqs boundary issues * fix: sn_ not sort * fix: sn_ not sort * fix: sn_ not sort * fix: jssdk add * fix: jssdk support * fix: jssdk support * fix: jssdk support * fix: the message I sent is not set to read seq in mongodb * fix: cannot modify group member avatars * fix: MemberEnterNotification * fix: MemberEnterNotification * fix: MsgData status * feat: add ApplicationVersion * feat: ApplicationVersion move chat --------- Co-authored-by: withchao <withchao@users.noreply.github.com> * fix: improve condition check. (#2815) --------- Co-authored-by: Monet Lee <monet_lee@163.com> Co-authored-by: icey-yu <119291641+icey-yu@users.noreply.github.com> Co-authored-by: Kürşat Aktaş <kursat.ce@gmail.com> Co-authored-by: chao <48119764+withchao@users.noreply.github.com> Co-authored-by: withchao <withchao@users.noreply.github.com>pull/2829/head
parent
646f1b9194
commit
ee04c156b5
@ -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 }}
|
@ -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."
|
@ -1,117 +0,0 @@
|
|||||||
package third
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/authverify"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
"github.com/openimsdk/protocol/third"
|
|
||||||
"github.com/openimsdk/tools/errs"
|
|
||||||
"github.com/openimsdk/tools/utils/datautil"
|
|
||||||
"github.com/redis/go-redis/v9"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsNotFound(err error) bool {
|
|
||||||
switch errs.Unwrap(err) {
|
|
||||||
case redis.Nil, mongo.ErrNoDocuments:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdServer) db2pbApplication(val *model.Application) *third.ApplicationVersion {
|
|
||||||
return &third.ApplicationVersion{
|
|
||||||
Id: val.ID.Hex(),
|
|
||||||
Platform: val.Platform,
|
|
||||||
Version: val.Version,
|
|
||||||
Url: val.Url,
|
|
||||||
Text: val.Text,
|
|
||||||
Force: val.Force,
|
|
||||||
Latest: val.Latest,
|
|
||||||
CreateTime: val.CreateTime.UnixMilli(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdServer) LatestApplicationVersion(ctx context.Context, req *third.LatestApplicationVersionReq) (*third.LatestApplicationVersionResp, error) {
|
|
||||||
res, err := t.applicationDatabase.LatestVersion(ctx, req.Platform)
|
|
||||||
if err == nil {
|
|
||||||
return &third.LatestApplicationVersionResp{Version: t.db2pbApplication(res)}, nil
|
|
||||||
} else if IsNotFound(err) {
|
|
||||||
return &third.LatestApplicationVersionResp{}, nil
|
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdServer) AddApplicationVersion(ctx context.Context, req *third.AddApplicationVersionReq) (*third.AddApplicationVersionResp, error) {
|
|
||||||
if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
val := &model.Application{
|
|
||||||
ID: primitive.NewObjectID(),
|
|
||||||
Platform: req.Platform,
|
|
||||||
Version: req.Version,
|
|
||||||
Url: req.Url,
|
|
||||||
Text: req.Text,
|
|
||||||
Force: req.Force,
|
|
||||||
Latest: req.Latest,
|
|
||||||
CreateTime: time.Now(),
|
|
||||||
}
|
|
||||||
if err := t.applicationDatabase.AddVersion(ctx, val); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &third.AddApplicationVersionResp{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdServer) UpdateApplicationVersion(ctx context.Context, req *third.UpdateApplicationVersionReq) (*third.UpdateApplicationVersionResp, error) {
|
|
||||||
if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
oid, err := primitive.ObjectIDFromHex(req.Id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
|
|
||||||
}
|
|
||||||
update := make(map[string]any)
|
|
||||||
putUpdate(update, "platform", req.Platform)
|
|
||||||
putUpdate(update, "version", req.Version)
|
|
||||||
putUpdate(update, "url", req.Url)
|
|
||||||
putUpdate(update, "text", req.Text)
|
|
||||||
putUpdate(update, "force", req.Force)
|
|
||||||
putUpdate(update, "latest", req.Latest)
|
|
||||||
if err := t.applicationDatabase.UpdateVersion(ctx, oid, update); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &third.UpdateApplicationVersionResp{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdServer) DeleteApplicationVersion(ctx context.Context, req *third.DeleteApplicationVersionReq) (*third.DeleteApplicationVersionResp, error) {
|
|
||||||
if err := authverify.CheckAdmin(ctx, t.config.Share.IMAdminUserID); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ids := make([]primitive.ObjectID, 0, len(req.Id))
|
|
||||||
for _, id := range req.Id {
|
|
||||||
oid, err := primitive.ObjectIDFromHex(id)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errs.ErrArgs.WrapMsg("invalid id " + err.Error())
|
|
||||||
}
|
|
||||||
ids = append(ids, oid)
|
|
||||||
}
|
|
||||||
if err := t.applicationDatabase.DeleteVersion(ctx, ids); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &third.DeleteApplicationVersionResp{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *thirdServer) PageApplicationVersion(ctx context.Context, req *third.PageApplicationVersionReq) (*third.PageApplicationVersionResp, error) {
|
|
||||||
total, res, err := t.applicationDatabase.PageVersion(ctx, req.Platform, req.Pagination)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &third.PageApplicationVersionResp{
|
|
||||||
Total: total,
|
|
||||||
Versions: datautil.Slice(res, t.db2pbApplication),
|
|
||||||
}, nil
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ApplicationCache interface {
|
|
||||||
LatestVersion(ctx context.Context, platform string) (*model.Application, error)
|
|
||||||
DeleteCache(ctx context.Context, platforms []string) error
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package cachekey
|
|
||||||
|
|
||||||
const (
|
|
||||||
ApplicationLatestVersion = "APPLICATION_LATEST_VERSION:"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GetApplicationLatestVersionKey(platform string) string {
|
|
||||||
return ApplicationLatestVersion + platform
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/dtm-labs/rockscache"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache/cachekey"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
"github.com/openimsdk/tools/utils/datautil"
|
|
||||||
"github.com/redis/go-redis/v9"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewApplicationRedisCache(db database.Application, rdb redis.UniversalClient) *ApplicationRedisCache {
|
|
||||||
return &ApplicationRedisCache{
|
|
||||||
db: db,
|
|
||||||
rcClient: rockscache.NewClient(rdb, *GetRocksCacheOptions()),
|
|
||||||
deleter: NewBatchDeleterRedis(rdb, GetRocksCacheOptions(), nil),
|
|
||||||
expireTime: time.Hour * 24 * 7,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApplicationRedisCache struct {
|
|
||||||
db database.Application
|
|
||||||
rcClient *rockscache.Client
|
|
||||||
deleter *BatchDeleterRedis
|
|
||||||
expireTime time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationRedisCache) LatestVersion(ctx context.Context, platform string) (*model.Application, error) {
|
|
||||||
return getCache(ctx, a.rcClient, cachekey.GetApplicationLatestVersionKey(platform), a.expireTime, func(ctx context.Context) (*model.Application, error) {
|
|
||||||
return a.db.LatestVersion(ctx, platform)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationRedisCache) DeleteCache(ctx context.Context, platforms []string) error {
|
|
||||||
if len(platforms) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return a.deleter.ExecDelWithKeys(ctx, datautil.Slice(platforms, func(platform string) string {
|
|
||||||
return cachekey.GetApplicationLatestVersionKey(platform)
|
|
||||||
}))
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/cache"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/database"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
"github.com/openimsdk/tools/db/pagination"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ApplicationDatabase interface {
|
|
||||||
LatestVersion(ctx context.Context, platform string) (*model.Application, error)
|
|
||||||
AddVersion(ctx context.Context, val *model.Application) error
|
|
||||||
UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error
|
|
||||||
DeleteVersion(ctx context.Context, id []primitive.ObjectID) error
|
|
||||||
PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*model.Application, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewApplicationDatabase(db database.Application, cache cache.ApplicationCache) ApplicationDatabase {
|
|
||||||
return &applicationDatabase{db: db, cache: cache}
|
|
||||||
}
|
|
||||||
|
|
||||||
type applicationDatabase struct {
|
|
||||||
db database.Application
|
|
||||||
cache cache.ApplicationCache
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationDatabase) LatestVersion(ctx context.Context, platform string) (*model.Application, error) {
|
|
||||||
return a.cache.LatestVersion(ctx, platform)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationDatabase) AddVersion(ctx context.Context, val *model.Application) error {
|
|
||||||
if err := a.db.AddVersion(ctx, val); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return a.cache.DeleteCache(ctx, []string{val.Platform})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationDatabase) UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error {
|
|
||||||
platforms, err := a.db.FindPlatform(ctx, []primitive.ObjectID{id})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := a.db.UpdateVersion(ctx, id, update); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if p, ok := update["platform"]; ok {
|
|
||||||
if val, ok := p.(string); ok {
|
|
||||||
platforms = append(platforms, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.cache.DeleteCache(ctx, platforms)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationDatabase) DeleteVersion(ctx context.Context, id []primitive.ObjectID) error {
|
|
||||||
platforms, err := a.db.FindPlatform(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := a.db.DeleteVersion(ctx, id); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return a.cache.DeleteCache(ctx, platforms)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *applicationDatabase) PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*model.Application, error) {
|
|
||||||
return a.db.PageVersion(ctx, platforms, page)
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
"github.com/openimsdk/tools/db/pagination"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Application interface {
|
|
||||||
LatestVersion(ctx context.Context, platform string) (*model.Application, error)
|
|
||||||
AddVersion(ctx context.Context, val *model.Application) error
|
|
||||||
UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error
|
|
||||||
DeleteVersion(ctx context.Context, id []primitive.ObjectID) error
|
|
||||||
PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*model.Application, error)
|
|
||||||
FindPlatform(ctx context.Context, id []primitive.ObjectID) ([]string, error)
|
|
||||||
}
|
|
@ -1,82 +0,0 @@
|
|||||||
package mgo
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/openimsdk/open-im-server/v3/pkg/common/storage/model"
|
|
||||||
"github.com/openimsdk/tools/db/mongoutil"
|
|
||||||
"github.com/openimsdk/tools/db/pagination"
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
|
||||||
"go.mongodb.org/mongo-driver/mongo/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewApplicationMgo(db *mongo.Database) (*ApplicationMgo, error) {
|
|
||||||
coll := db.Collection("application")
|
|
||||||
_, err := coll.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "platform", Value: 1},
|
|
||||||
{Key: "version", Value: 1},
|
|
||||||
},
|
|
||||||
Options: options.Index().SetUnique(true),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Keys: bson.D{
|
|
||||||
{Key: "latest", Value: -1},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &ApplicationMgo{coll: coll}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApplicationMgo struct {
|
|
||||||
coll *mongo.Collection
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) sort() any {
|
|
||||||
return bson.D{{"latest", -1}, {"_id", -1}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) LatestVersion(ctx context.Context, platform string) (*model.Application, error) {
|
|
||||||
return mongoutil.FindOne[*model.Application](ctx, a.coll, bson.M{"platform": platform}, options.FindOne().SetSort(a.sort()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) AddVersion(ctx context.Context, val *model.Application) error {
|
|
||||||
if val.ID.IsZero() {
|
|
||||||
val.ID = primitive.NewObjectID()
|
|
||||||
}
|
|
||||||
return mongoutil.InsertMany(ctx, a.coll, []*model.Application{val})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) UpdateVersion(ctx context.Context, id primitive.ObjectID, update map[string]any) error {
|
|
||||||
if len(update) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return mongoutil.UpdateOne(ctx, a.coll, bson.M{"_id": id}, bson.M{"$set": update}, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) DeleteVersion(ctx context.Context, id []primitive.ObjectID) error {
|
|
||||||
if len(id) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return mongoutil.DeleteMany(ctx, a.coll, bson.M{"_id": bson.M{"$in": id}})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) PageVersion(ctx context.Context, platforms []string, page pagination.Pagination) (int64, []*model.Application, error) {
|
|
||||||
filter := bson.M{}
|
|
||||||
if len(platforms) > 0 {
|
|
||||||
filter["platform"] = bson.M{"$in": platforms}
|
|
||||||
}
|
|
||||||
return mongoutil.FindPage[*model.Application](ctx, a.coll, filter, page, options.Find().SetSort(a.sort()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApplicationMgo) FindPlatform(ctx context.Context, id []primitive.ObjectID) ([]string, error) {
|
|
||||||
if len(id) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return mongoutil.Find[string](ctx, a.coll, bson.M{"_id": bson.M{"$in": id}}, options.Find().SetProjection(bson.M{"_id": 0, "platform": 1}))
|
|
||||||
}
|
|
Loading…
Reference in new issue