From 9698e45346156ced4f2e9806acf927214afab233 Mon Sep 17 00:00:00 2001 From: Harold Li Date: Tue, 15 Aug 2023 14:26:36 +0800 Subject: [PATCH 1/3] merge v3.25.1 --- .github/workflows/auto_lang.yml | 67 ---- .github/workflows/build.yml | 64 ++-- .github/workflows/build_docker.yml | 65 ---- .github/workflows/changelog.yml | 19 -- .github/workflows/issue_close_question.yml | 22 -- .github/workflows/issue_close_stale.yml | 21 -- .github/workflows/issue_duplicate.yml | 25 -- .github/workflows/issue_invalid.yml | 25 -- .github/workflows/issue_question.yml | 20 -- .github/workflows/issue_rm_working.yml | 17 - .github/workflows/issue_similarity.yml | 19 -- .github/workflows/issue_translate.yml | 13 - .github/workflows/issue_wontfix.yml | 25 -- .github/workflows/release.yml | 75 ----- .github/workflows/release_docker.yml | 68 ---- .github/workflows/release_linux_musl_arm.yml | 34 -- Dockerfile | 14 +- Dockerfile-host | 12 + build.sh | 34 +- drivers/alias/driver.go | 15 + drivers/alias/util.go | 11 + drivers/aliyundrive_open/driver.go | 3 + drivers/aliyundrive_open/meta.go | 1 + drivers/aliyundrive_open/util.go | 49 ++- drivers/aliyundrive_share2_open/driver.go | 287 ++++++++++++++++ drivers/aliyundrive_share2_open/meta.go | 37 ++ drivers/aliyundrive_share2_open/types.go | 92 +++++ drivers/aliyundrive_share2_open/util.go | 335 +++++++++++++++++++ drivers/all.go | 35 +- internal/conf/config.go | 4 +- internal/db/db.go | 4 +- internal/db/storage.go | 2 +- internal/db/token.go | 36 ++ internal/model/token.go | 10 + internal/op/storage.go | 2 +- internal/op/token.go | 39 +++ internal/setting/setting.go | 13 + internal/token/token.go | 32 ++ public/dist/README.md | 1 - release.sh | 4 + server/handles/helper.go | 11 + server/handles/setting.go | 14 + server/handles/token.go | 50 +++ server/router.go | 8 + 44 files changed, 1122 insertions(+), 612 deletions(-) delete mode 100644 .github/workflows/auto_lang.yml delete mode 100644 .github/workflows/build_docker.yml delete mode 100644 .github/workflows/changelog.yml delete mode 100644 .github/workflows/issue_close_question.yml delete mode 100644 .github/workflows/issue_close_stale.yml delete mode 100644 .github/workflows/issue_duplicate.yml delete mode 100644 .github/workflows/issue_invalid.yml delete mode 100644 .github/workflows/issue_question.yml delete mode 100644 .github/workflows/issue_rm_working.yml delete mode 100644 .github/workflows/issue_similarity.yml delete mode 100644 .github/workflows/issue_translate.yml delete mode 100644 .github/workflows/issue_wontfix.yml delete mode 100644 .github/workflows/release.yml delete mode 100644 .github/workflows/release_docker.yml delete mode 100644 .github/workflows/release_linux_musl_arm.yml create mode 100644 Dockerfile-host create mode 100644 drivers/aliyundrive_share2_open/driver.go create mode 100644 drivers/aliyundrive_share2_open/meta.go create mode 100644 drivers/aliyundrive_share2_open/types.go create mode 100644 drivers/aliyundrive_share2_open/util.go create mode 100644 internal/db/token.go create mode 100644 internal/model/token.go create mode 100644 internal/op/token.go create mode 100644 internal/token/token.go delete mode 100644 public/dist/README.md create mode 100644 release.sh create mode 100644 server/handles/token.go diff --git a/.github/workflows/auto_lang.yml b/.github/workflows/auto_lang.yml deleted file mode 100644 index ee508603..00000000 --- a/.github/workflows/auto_lang.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: auto_lang - -on: - push: - branches: - - 'main' - paths: - - 'drivers/**' - - 'internal/bootstrap/data/setting.go' - - 'internal/conf/const.go' - - 'cmd/lang.go' - workflow_dispatch: - -jobs: - auto_lang: - strategy: - matrix: - platform: [ ubuntu-latest ] - go-version: [ '1.20' ] - name: auto generate lang.json - runs-on: ${{ matrix.platform }} - steps: - - name: Setup go - uses: actions/setup-go@v4 - with: - go-version: ${{ matrix.go-version }} - - - name: Checkout alist - uses: actions/checkout@v3 - with: - path: alist - - - name: Checkout alist-web - uses: actions/checkout@v3 - with: - repository: 'alist-org/alist-web' - ref: main - persist-credentials: false - fetch-depth: 0 - path: alist-web - - - name: Generate lang - run: | - cd alist - go run ./main.go lang - cd .. - - - name: Copy lang file - run: | - cp -f ./alist/lang/*.json ./alist-web/src/lang/en/ 2>/dev/null || : - - - name: Commit git - run: | - cd alist-web - git add . - git config --local user.email "bot@nn.ci" - git config --local user.name "IlaBot" - git commit -m "chore: auto update i18n file" -a 2>/dev/null || : - cd .. - - - name: Push lang files - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.MY_TOKEN }} - branch: main - directory: alist-web - repository: alist-org/alist-web diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a3cd7f60..f82c9625 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,41 +1,41 @@ -name: build +name: 'release alist' on: + workflow_dispatch: push: - branches: [ 'main' ] - pull_request: - branches: [ 'main' ] + branches: + - main jobs: - build: - strategy: - matrix: - platform: [ubuntu-latest] - go-version: [ '1.20' ] - name: Build - runs-on: ${{ matrix.platform }} + release: + runs-on: ubuntu-latest steps: - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout uses: actions/checkout@v3 - - - name: Install dependencies - run: | - sudo snap install zig --classic --beta - docker pull crazymax/xgo:latest - go install github.com/crazy-max/xgo@latest - sudo apt install upx - - - name: Build - run: | - bash build.sh dev - - - name: Upload artifact - uses: actions/upload-artifact@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Build docker and push + uses: docker/build-push-action@v4 + with: + context: . + file: Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/alist:latest + cache-from: type=gha + cache-to: type=gha,mode=max + - name: Build hostmode docker and push + uses: docker/build-push-action@v4 with: - name: alist - path: dist \ No newline at end of file + context: . + file: Dockerfile-host + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/alist:hostmode + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/build_docker.yml b/.github/workflows/build_docker.yml deleted file mode 100644 index 0ff90e5f..00000000 --- a/.github/workflows/build_docker.yml +++ /dev/null @@ -1,65 +0,0 @@ -name: build_docker - -on: - push: - branches: [ main ] - -jobs: - build_docker: - name: Build docker - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: xhofe/alist - - name: Replace release with dev - run: | - sed -i 's/release/dev/g' Dockerfile - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: xhofe - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - id: docker_build - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64 - - build_docker_with_aria2: - needs: build_docker - name: Build docker with aria2 - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - with: - repository: alist-org/with_aria2 - ref: main - persist-credentials: false - fetch-depth: 0 - - - name: Commit - run: | - git config --local user.email "bot@nn.ci" - git config --local user.name "IlaBot" - git commit --allow-empty -m "Trigger build for ${{ github.sha }}" - - - name: Push commit - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.MY_TOKEN }} - branch: main - repository: alist-org/with_aria2 \ No newline at end of file diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml deleted file mode 100644 index b0cfeaa8..00000000 --- a/.github/workflows/changelog.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: auto changelog - -on: - push: - tags: - - '*' - -jobs: - changelog: - name: Create Release - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - run: npx changelogithub # or changelogithub@0.12 if ensure the stable result - env: - GITHUB_TOKEN: ${{secrets.MY_TOKEN}} diff --git a/.github/workflows/issue_close_question.yml b/.github/workflows/issue_close_question.yml deleted file mode 100644 index c6190d53..00000000 --- a/.github/workflows/issue_close_question.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Close need info - -on: - schedule: - - cron: "0 0 */1 * *" - workflow_dispatch: - -jobs: - close-need-info: - runs-on: ubuntu-latest - steps: - - name: close-issues - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issues' - token: ${{ secrets.GITHUB_TOKEN }} - labels: 'question' - inactive-day: 3 - close-reason: 'not_planned' - body: | - Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 3 days. - 你好 @${{ github.event.issue.user.login }},此issue因超过3天未回复被关闭。 \ No newline at end of file diff --git a/.github/workflows/issue_close_stale.yml b/.github/workflows/issue_close_stale.yml deleted file mode 100644 index 9691f052..00000000 --- a/.github/workflows/issue_close_stale.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Close inactive - -on: - schedule: - - cron: "0 0 */7 * *" - workflow_dispatch: - -jobs: - close-inactive: - runs-on: ubuntu-latest - steps: - - name: close-issues - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issues' - token: ${{ secrets.GITHUB_TOKEN }} - labels: 'stale' - inactive-day: 8 - close-reason: 'not_planned' - body: | - Hello @${{ github.event.issue.user.login }}, this issue was closed due to inactive more than 52 days. You can reopen or recreate it if you think it should continue. Thank you for your contributions again. \ No newline at end of file diff --git a/.github/workflows/issue_duplicate.yml b/.github/workflows/issue_duplicate.yml deleted file mode 100644 index 24238daa..00000000 --- a/.github/workflows/issue_duplicate.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Issue Duplicate - -on: - issues: - types: [labeled] - -jobs: - create-comment: - runs-on: ubuntu-latest - if: github.event.label.name == 'duplicate' - steps: - - name: Create comment - uses: actions-cool/issues-helper@v3 - with: - actions: 'create-comment' - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.issue.number }} - body: | - Hello @${{ github.event.issue.user.login }}, your issue is a duplicate and will be closed. - 你好 @${{ github.event.issue.user.login }},你的issue是重复的,将被关闭。 - - name: Close issue - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issue' - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/issue_invalid.yml b/.github/workflows/issue_invalid.yml deleted file mode 100644 index fdb0ba8b..00000000 --- a/.github/workflows/issue_invalid.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Issue Invalid - -on: - issues: - types: [labeled] - -jobs: - create-comment: - runs-on: ubuntu-latest - if: github.event.label.name == 'invalid' - steps: - - name: Create comment - uses: actions-cool/issues-helper@v3 - with: - actions: 'create-comment' - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.issue.number }} - body: | - Hello @${{ github.event.issue.user.login }}, your issue is invalid and will be closed. - 你好 @${{ github.event.issue.user.login }},你的issue无效,将被关闭。 - - name: Close issue - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issue' - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/issue_question.yml b/.github/workflows/issue_question.yml deleted file mode 100644 index 13873546..00000000 --- a/.github/workflows/issue_question.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Issue Question - -on: - issues: - types: [labeled] - -jobs: - create-comment: - runs-on: ubuntu-latest - if: github.event.label.name == 'question' - steps: - - name: Create comment - uses: actions-cool/issues-helper@v3.5.1 - with: - actions: 'create-comment' - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.issue.number }} - body: | - Hello @${{ github.event.issue.user.login }}, please input issue by template and add detail. Issues labeled by `question` will be closed if no activities in 3 days. - 你好 @${{ github.event.issue.user.login }},请按照issue模板填写, 并详细说明问题/日志记录/复现步骤/复现链接/实现思路或提供更多信息等, 3天内未回复issue自动关闭。 \ No newline at end of file diff --git a/.github/workflows/issue_rm_working.yml b/.github/workflows/issue_rm_working.yml deleted file mode 100644 index 56181366..00000000 --- a/.github/workflows/issue_rm_working.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Remove working label when issue closed - -on: - issues: - types: [closed] - -jobs: - rm-working: - runs-on: ubuntu-latest - steps: - - name: Remove working label - uses: actions-cool/issues-helper@v3 - with: - actions: 'remove-labels' - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.issue.number }} - labels: 'working' \ No newline at end of file diff --git a/.github/workflows/issue_similarity.yml b/.github/workflows/issue_similarity.yml deleted file mode 100644 index 254678d0..00000000 --- a/.github/workflows/issue_similarity.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Issues Similarity Analysis - -on: - issues: - types: [opened, edited] - -jobs: - similarity-analysis: - runs-on: ubuntu-latest - steps: - - name: analysis - uses: actions-cool/issues-similarity-analysis@v1 - with: - filter-threshold: 0.5 - comment-title: '### See' - comment-body: '${index}. ${similarity} #${number}' - show-footer: false - show-mentioned: true - since-days: 730 \ No newline at end of file diff --git a/.github/workflows/issue_translate.yml b/.github/workflows/issue_translate.yml deleted file mode 100644 index f7c5a997..00000000 --- a/.github/workflows/issue_translate.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: Translation Helper - -on: - pull_request_target: - types: [opened] - issues: - types: [opened] - -jobs: - translate: - runs-on: ubuntu-latest - steps: - - uses: actions-cool/translation-helper@v1.2.0 \ No newline at end of file diff --git a/.github/workflows/issue_wontfix.yml b/.github/workflows/issue_wontfix.yml deleted file mode 100644 index 316f6e4d..00000000 --- a/.github/workflows/issue_wontfix.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Issue Wontfix - -on: - issues: - types: [labeled] - -jobs: - lock-issue: - runs-on: ubuntu-latest - if: github.event.label.name == 'wontfix' - steps: - - name: Create comment - uses: actions-cool/issues-helper@v3 - with: - actions: 'create-comment' - token: ${{ secrets.GITHUB_TOKEN }} - issue-number: ${{ github.event.issue.number }} - body: | - Hello @${{ github.event.issue.user.login }}, this issue will not be worked on and will be closed. - 你好 @${{ github.event.issue.user.login }},这不会被处理,将被关闭。 - - name: Close issue - uses: actions-cool/issues-helper@v3 - with: - actions: 'close-issue' - token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 6697363d..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,75 +0,0 @@ -name: release - -on: - release: - types: [ published ] - -jobs: - release: - strategy: - matrix: - platform: [ ubuntu-latest ] - go-version: [ '1.20' ] - name: Release - runs-on: ${{ matrix.platform }} - steps: - - name: Prerelease - uses: irongut/EditRelease@v1.2.0 - with: - token: ${{ secrets.MY_TOKEN }} - id: ${{ github.event.release.id }} - prerelease: true - - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: ${{ matrix.go-version }} - - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Install dependencies - run: | - sudo snap install zig --classic --beta - docker pull crazymax/xgo:latest - go install github.com/crazy-max/xgo@latest - sudo apt install upx - - - name: Build - run: | - bash build.sh release - - - name: Upload assets - uses: softprops/action-gh-release@v1 - with: - files: build/compress/* - prerelease: false - - release_desktop: - needs: release - name: Release desktop - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - with: - repository: alist-org/desktop-release - ref: main - persist-credentials: false - fetch-depth: 0 - - - name: Add tag - run: | - git config --local user.email "bot@nn.ci" - git config --local user.name "IlaBot" - version=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') - git tag -a $version -m "release $version" - - - name: Push tags - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.MY_TOKEN }} - branch: main - repository: alist-org/desktop-release \ No newline at end of file diff --git a/.github/workflows/release_docker.yml b/.github/workflows/release_docker.yml deleted file mode 100644 index a16cf49d..00000000 --- a/.github/workflows/release_docker.yml +++ /dev/null @@ -1,68 +0,0 @@ -name: release_docker - -on: - push: - tags: - - '*' - -jobs: - release_docker: - name: Release Docker - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: xhofe/alist - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: xhofe - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Build and push - id: docker_build - uses: docker/build-push-action@v4 - with: - context: . - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x - - release_docker_with_aria2: - needs: release_docker - name: Release docker with aria2 - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v3 - with: - repository: alist-org/with_aria2 - ref: main - persist-credentials: false - fetch-depth: 0 - - - name: Add tag - run: | - git config --local user.email "bot@nn.ci" - git config --local user.name "IlaBot" - git tag -a ${{ github.ref_name }} -m "release ${{ github.ref_name }}" - - - name: Push tags - uses: ad-m/github-push-action@master - with: - github_token: ${{ secrets.MY_TOKEN }} - branch: main - repository: alist-org/with_aria2 diff --git a/.github/workflows/release_linux_musl_arm.yml b/.github/workflows/release_linux_musl_arm.yml deleted file mode 100644 index 4d424507..00000000 --- a/.github/workflows/release_linux_musl_arm.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: release_linux_musl_arm - -on: - release: - types: [ published ] - -jobs: - release_arm: - strategy: - matrix: - platform: [ ubuntu-latest ] - go-version: [ '1.20' ] - name: Release - runs-on: ${{ matrix.platform }} - steps: - - - name: Setup Go - uses: actions/setup-go@v4 - with: - go-version: ${{ matrix.go-version }} - - - name: Checkout - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Build - run: | - bash build.sh release linux_musl_arm - - - name: Upload assets - uses: softprops/action-gh-release@v1 - with: - files: build/compress/* diff --git a/Dockerfile b/Dockerfile index 97d1b9e8..037bd5ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,14 +5,8 @@ COPY ./ ./ RUN apk add --no-cache bash curl gcc git go musl-dev; \ bash build.sh release docker -FROM alpine:3.18 -LABEL MAINTAINER="i@nn.ci" -VOLUME /opt/alist/data/ -WORKDIR /opt/alist/ +FROM xiaoyaliu/alist:latest + +LABEL MAINTAINER="Har01d" + COPY --from=builder /app/bin/alist ./ -COPY entrypoint.sh /entrypoint.sh -RUN apk add --no-cache bash ca-certificates su-exec tzdata; \ - chmod +x /entrypoint.sh -ENV PUID=0 PGID=0 UMASK=022 -EXPOSE 5244 5245 -CMD [ "/entrypoint.sh" ] diff --git a/Dockerfile-host b/Dockerfile-host new file mode 100644 index 00000000..768ccbca --- /dev/null +++ b/Dockerfile-host @@ -0,0 +1,12 @@ +FROM alpine:3.18 as builder +LABEL stage=go-builder +WORKDIR /app/ +COPY ./ ./ +RUN apk add --no-cache bash curl gcc git go musl-dev; \ + bash build.sh release docker + +FROM xiaoyaliu/alist:hostmode + +LABEL MAINTAINER="Har01d" + +COPY --from=builder /app/bin/alist ./ diff --git a/build.sh b/build.sh index 7847b568..a666af48 100644 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ appName="alist" builtAt="$(date +'%F %T %z')" goVersion=$(go version | sed 's/go version //') -gitAuthor="Xhofe " +gitAuthor="Harold & Xhofe " gitCommit=$(git log --pretty=format:"%h" -1) if [ "$1" = "dev" ]; then @@ -9,7 +9,7 @@ if [ "$1" = "dev" ]; then webVersion="dev" else version=$(git describe --abbrev=0 --tags) - webVersion=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g') + webVersion=3.25.1 fi echo "backend version: $version" @@ -34,11 +34,20 @@ FetchWebDev() { } FetchWebRelease() { - curl -L https://github.com/alist-org/alist-web/releases/latest/download/dist.tar.gz -o dist.tar.gz + curl -L https://github.com/alist-org/alist-web/releases/download/3.25.1/dist.tar.gz -o dist.tar.gz tar -zxvf dist.tar.gz rm -rf public/dist mv -f dist public rm -rf dist.tar.gz + for file in $(grep -l 'Aliyundrive(Open)' public/dist/assets/*); do + echo "update $file" + sed -i 's/Aliyundrive(Open)?/(Aliyundrive.*)|(Alias)/g' "$file" + sed -i 's/Aliyundrive(Share)?/(Aliyundrive.*)|(Alias)/g' "$file" + done + for file in $(grep -l 'AliyundriveShare' public/dist/assets/*); do + echo "update $file" + sed -i 's/AliyundriveShare/AliyundriveShare2Open/g' "$file" + done } BuildWinArm64() { @@ -53,7 +62,6 @@ BuildWinArm64() { } BuildDev() { - rm -rf .git/ mkdir -p "dist" muslflags="--extldflags '-static -fpic' $ldflags" BASE="https://musl.nn.ci/" @@ -89,19 +97,18 @@ BuildDocker() { } BuildRelease() { - rm -rf .git/ mkdir -p "build" muslflags="--extldflags '-static -fpic' $ldflags" BASE="https://musl.nn.ci/" - FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross mips-linux-musl-cross mips64-linux-musl-cross mips64el-linux-musl-cross mipsel-linux-musl-cross powerpc64le-linux-musl-cross s390x-linux-musl-cross) + FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross) for i in "${FILES[@]}"; do url="${BASE}${i}.tgz" curl -L -o "${i}.tgz" "${url}" sudo tar xf "${i}.tgz" --strip-components 1 -C /usr/local rm -f "${i}.tgz" done - OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-mips linux-musl-mips64 linux-musl-mips64le linux-musl-mipsle linux-musl-ppc64le linux-musl-s390x) - CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc mips-linux-musl-gcc mips64-linux-musl-gcc mips64el-linux-musl-gcc mipsel-linux-musl-gcc powerpc64le-linux-musl-gcc s390x-linux-musl-gcc) + OS_ARCHES=(linux-musl-amd64 linux-musl-arm64) + CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc) for i in "${!OS_ARCHES[@]}"; do os_arch=${OS_ARCHES[$i]} cgo_cc=${CGO_ARGS[$i]} @@ -112,17 +119,16 @@ BuildRelease() { export CGO_ENABLED=1 go build -o ./build/$appName-$os_arch -ldflags="$muslflags" -tags=jsoniter . done - BuildWinArm64 ./build/alist-windows-arm64.exe - xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter . - # why? Because some target platforms seem to have issues with upx compression +# BuildWinArm64 ./build/alist-windows-arm64.exe +# xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter . +# # why? Because some target platforms seem to have issues with upx compression upx -9 ./alist-linux-amd64 - cp ./alist-windows-amd64.exe ./alist-windows-amd64-upx.exe - upx -9 ./alist-windows-amd64-upx.exe +# cp ./alist-windows-amd64.exe ./alist-windows-amd64-upx.exe +# upx -9 ./alist-windows-amd64-upx.exe mv alist-* build } BuildReleaseLinuxMuslArm() { - rm -rf .git/ mkdir -p "build" muslflags="--extldflags '-static -fpic' $ldflags" BASE="https://musl.nn.ci/" diff --git a/drivers/alias/driver.go b/drivers/alias/driver.go index 271096b3..b90e2659 100644 --- a/drivers/alias/driver.go +++ b/drivers/alias/driver.go @@ -111,4 +111,19 @@ func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) ( return nil, errs.ObjectNotFound } +func (d *Alias) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { + root, sub := d.getRootAndPath(args.Obj.GetPath()) + dsts, ok := d.pathMap[root] + if !ok { + return nil, errs.ObjectNotFound + } + for _, dst := range dsts { + link, err := d.other(ctx, dst, sub, args) + if err == nil { + return link, nil + } + } + return nil, errs.ObjectNotFound +} + var _ driver.Driver = (*Alias)(nil) diff --git a/drivers/alias/util.go b/drivers/alias/util.go index 4e3d6bf0..f288bbd5 100644 --- a/drivers/alias/util.go +++ b/drivers/alias/util.go @@ -112,3 +112,14 @@ func (d *Alias) link(ctx context.Context, dst, sub string, args model.LinkArgs) link, _, err := fs.Link(ctx, reqPath, args) return link, err } + +func (d *Alias) other(ctx context.Context, dst, sub string, args model.OtherArgs) (interface{}, error) { + reqPath := stdpath.Join(dst, sub) + fsArg := model.FsOtherArgs{ + Method: args.Method, + Data: args.Data, + Path: reqPath, + } + utils.Log.Printf("utils other: %v", fsArg) + return fs.Other(ctx, fsArg) +} diff --git a/drivers/aliyundrive_open/driver.go b/drivers/aliyundrive_open/driver.go index c941acef..b4def8d7 100644 --- a/drivers/aliyundrive_open/driver.go +++ b/drivers/aliyundrive_open/driver.go @@ -47,6 +47,9 @@ func (d *AliyundriveOpen) Init(ctx context.Context) error { return err } d.DriveId = utils.Json.Get(res, d.DriveType+"_drive_id").ToString() + if d.DriveId == "" { + d.DriveId = utils.Json.Get(res, "default_drive_id").ToString() + } d.limitList = rateg.LimitFnCtx(d.list, rateg.LimitFnOption{ Limit: 4, Bucket: 1, diff --git a/drivers/aliyundrive_open/meta.go b/drivers/aliyundrive_open/meta.go index af4d1257..83635ec0 100644 --- a/drivers/aliyundrive_open/meta.go +++ b/drivers/aliyundrive_open/meta.go @@ -19,6 +19,7 @@ type Addition struct { InternalUpload bool `json:"internal_upload" help:"If you are using Aliyun ECS is located in Beijing, you can turn it on to boost the upload speed"` LIVPDownloadFormat string `json:"livp_download_format" type:"select" options:"jpeg,mov" default:"jpeg"` AccessToken string + AccountId int `json:"account_id"` } var config = driver.Config{ diff --git a/drivers/aliyundrive_open/util.go b/drivers/aliyundrive_open/util.go index bef6fed6..4b3dd380 100644 --- a/drivers/aliyundrive_open/util.go +++ b/drivers/aliyundrive_open/util.go @@ -4,7 +4,12 @@ import ( "context" "errors" "fmt" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/internal/setting" + "github.com/alist-org/alist/v3/internal/token" "net/http" + "strconv" + "time" "github.com/alist-org/alist/v3/drivers/base" "github.com/alist-org/alist/v3/internal/op" @@ -16,10 +21,22 @@ import ( // do others that not defined in Driver interface func (d *AliyundriveOpen) refreshToken() error { - url := d.base + "/oauth/access_token" + accountId := strconv.Itoa(d.AccountId) + accessTokenOpen := token.GetToken("AccessTokenOpen-"+accountId, 7200) + refreshTokenOpen := token.GetToken("RefreshTokenOpen-"+accountId, 0) + utils.Log.Debugf("accountID %v accessTokenOpen %v refreshTokenOpen: %v", accountId, accessTokenOpen, refreshTokenOpen) + if accessTokenOpen != "" && refreshTokenOpen != "" { + d.RefreshToken, d.AccessToken = refreshTokenOpen, accessTokenOpen + utils.Log.Println("RefreshTokenOpen已经存在") + return nil + } + + t := time.Now() + url := setting.GetStr("open_token_url", d.base+"/oauth/access_token") if d.OauthTokenURL != "" && d.ClientID == "" { url = d.OauthTokenURL } + utils.Log.Println("refreshOpenToken", url) //var resp base.TokenResp var e ErrResp res, err := base.RestyClient.R(). @@ -45,10 +62,40 @@ func (d *AliyundriveOpen) refreshToken() error { return errors.New("failed to refresh token: refresh token is empty") } d.RefreshToken, d.AccessToken = refresh, access + + d.SaveOpenToken(t) + op.MustSaveDriverStorage(d) return nil } +func (d *AliyundriveOpen) SaveOpenToken(t time.Time) { + accountId := strconv.Itoa(d.AccountId) + item := &model.Token{ + Key: "AccessTokenOpen-" + accountId, + Value: d.AccessToken, + AccountId: d.AccountId, + Modified: t, + } + + err := token.SaveToken(item) + if err != nil { + utils.Log.Printf("save AccessTokenOpen failed: %v", err) + } + + item = &model.Token{ + Key: "RefreshTokenOpen-" + accountId, + Value: d.RefreshToken, + AccountId: d.AccountId, + Modified: t, + } + + err = token.SaveToken(item) + if err != nil { + utils.Log.Printf("save RefreshTokenOpen failed: %v", err) + } +} + func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) { b, err, _ := d.requestReturnErrResp(uri, method, callback, retry...) return b, err diff --git a/drivers/aliyundrive_share2_open/driver.go b/drivers/aliyundrive_share2_open/driver.go new file mode 100644 index 00000000..7852cc7b --- /dev/null +++ b/drivers/aliyundrive_share2_open/driver.go @@ -0,0 +1,287 @@ +package aliyundrive_share2_open + +import ( + "context" + "errors" + "fmt" + "github.com/alist-org/alist/v3/internal/conf" + "net/http" + "time" + + "github.com/Xhofe/rateg" + "github.com/alist-org/alist/v3/drivers/base" + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/errs" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/pkg/cron" + "github.com/alist-org/alist/v3/pkg/utils" + "github.com/go-resty/resty/v2" +) + +var DriveId = "" + +type AliyundriveShare2Open struct { + base string + model.Storage + Addition + AccessToken string + AccessTokenOpen string + ShareToken string + DriveId string + cron *cron.Cron + + limitList func(ctx context.Context, dir model.Obj) ([]model.Obj, error) + limitLink func(ctx context.Context, file model.Obj) (*model.Link, error) +} + +func (d *AliyundriveShare2Open) Config() driver.Config { + return config +} + +func (d *AliyundriveShare2Open) GetAddition() driver.Additional { + return &d.Addition +} + +func (d *AliyundriveShare2Open) Init(ctx context.Context) error { + err := d.refreshToken(false) + if err != nil { + return err + } + err = d.getShareToken() + if err != nil { + return err + } + //d.cron = cron.NewCron(time.Hour * 2) + //d.cron.Do(func() { + // err := d.refreshToken(true) + // if err != nil { + // utils.Log.Errorf("%+v", err) + // } + // err = d.refreshOpenToken(true) + // if err != nil { + // utils.Log.Errorf("%+v", err) + // } + //}) + + if d.OauthTokenURL == "" { + d.OauthTokenURL = conf.Conf.OpenTokenAuthUrl + } + + err = d.refreshOpenToken(false) + if err != nil { + return err + } + + d.getDriveId() + + d.limitList = rateg.LimitFnCtx(d.list, rateg.LimitFnOption{ + Limit: 4, + Bucket: 1, + }) + d.limitLink = rateg.LimitFnCtx(d.link, rateg.LimitFnOption{ + Limit: 1, + Bucket: 1, + }) + return nil +} + +func (d *AliyundriveShare2Open) Drop(ctx context.Context) error { + if d.cron != nil { + d.cron.Stop() + } + d.DriveId = "" + return nil +} + +func (d *AliyundriveShare2Open) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { + if d.limitList == nil { + return nil, fmt.Errorf("driver not init") + } + return d.limitList(ctx, dir) +} + +func (d *AliyundriveShare2Open) list(ctx context.Context, dir model.Obj) ([]model.Obj, error) { + files, err := d.getFiles(dir.GetID()) + if err != nil { + return nil, err + } + return utils.SliceConvert(files, func(src File) (model.Obj, error) { + return fileToObj(src), nil + }) +} + +func (d *AliyundriveShare2Open) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { + if d.limitLink == nil { + return nil, fmt.Errorf("driver not init") + } + return d.limitLink(ctx, file) +} + +func (d *AliyundriveShare2Open) link(ctx context.Context, file model.Obj) (*model.Link, error) { + // 1. 转存资源 + // 2. 获取链接 + // 3. 删除文件 + fileId, err := d.saveFile(file.GetID()) + if err != nil { + return nil, err + } + + newFile := MyFile{ + FileId: fileId, + Name: "livp", + } + return d.getOpenLink(newFile) +} + +func (d *AliyundriveShare2Open) saveFile(fileId string) (string, error) { + data := base.Json{ + "requests": []base.Json{ + { + "body": base.Json{ + "file_id": fileId, + "share_id": d.ShareId, + "auto_rename": true, + "to_parent_file_id": "root", + "to_drive_id": d.DriveId, + }, + "headers": base.Json{ + "Content-Type": "application/json", + }, + "id": "0", + "method": "POST", + "url": "/file/copy", + }, + }, + "resource": "file", + } + + err := d.getShareToken() + if err != nil { + return "", err + } + + res, err := d.request("https://api.aliyundrive.com/adrive/v2/batch", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }) + if err != nil { + return "", err + } + newFile := utils.Json.Get(res, "responses", 0, "body", "file_id").ToString() + return newFile, nil +} + +func (d *AliyundriveShare2Open) getDownloadUrl(file model.Obj) (*model.Link, error) { + utils.Log.Printf("获取文件直链 %v %v", d.DriveId, file.GetID()) + data := base.Json{ + "drive_id": d.DriveId, + "file_id": file.GetID(), + "expire_sec": 14400, + } + res, err := d.request("https://api.aliyundrive.com/v2/file/get_download_url", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }) + if err != nil { + return nil, err + } + return &model.Link{ + Header: http.Header{ + "Referer": []string{"https://www.aliyundrive.com/"}, + }, + URL: utils.Json.Get(res, "url").ToString(), + }, nil +} + +func (d *AliyundriveShare2Open) getOpenLink(file model.Obj) (*model.Link, error) { + utils.Log.Printf("获取文件直链 %v %v", d.DriveId, file.GetID()) + res, err := d.requestOpen("/adrive/v1.0/openFile/getDownloadUrl", http.MethodPost, func(req *resty.Request) { + req.SetBody(base.Json{ + "drive_id": d.DriveId, + "file_id": file.GetID(), + "expire_sec": 14400, + }) + }) + if err != nil { + return nil, err + } + url := utils.Json.Get(res, "url").ToString() + if url == "" { + if utils.Ext(file.GetName()) != "livp" { + return nil, errors.New("get download url failed: " + string(res)) + } + url = utils.Json.Get(res, "streamsUrl", "mov").ToString() + } + + go d.deleteDelay(file.GetID()) + + exp := time.Hour + return &model.Link{ + URL: url, + Expiration: &exp, + }, nil +} + +func (d *AliyundriveShare2Open) deleteDelay(fileId string) error { + time.Sleep(1 * time.Second) + return d.delete(fileId) +} + +func (d *AliyundriveShare2Open) delete(fileId string) error { + data := base.Json{ + "requests": []base.Json{ + { + "body": base.Json{ + "id": fileId, + "file_id": fileId, + "drive_id": d.DriveId, + }, + "headers": base.Json{ + "Content-Type": "application/json", + }, + "id": "0", + "method": "POST", + "url": "/file/delete", + }, + }, + "resource": "file", + } + + _, err := d.request("https://api.aliyundrive.com/v3/batch", http.MethodPost, func(req *resty.Request) { + req.SetBody(data) + }) + + return err +} + +func (d *AliyundriveShare2Open) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { + fileId, err := d.saveFile(args.Obj.GetID()) + if err != nil { + return nil, err + } + + var resp base.Json + var uri string + data := base.Json{ + "drive_id": d.DriveId, + "file_id": fileId, + } + switch args.Method { + case "video_preview": + uri = "/adrive/v1.0/openFile/getVideoPreviewPlayInfo" + data["category"] = "live_transcoding" + data["url_expire_sec"] = 14400 + default: + return nil, errs.NotSupport + } + _, err = d.requestOpen(uri, http.MethodPost, func(req *resty.Request) { + req.SetBody(data).SetResult(&resp) + }) + + go d.deleteDelay(fileId) + + if err != nil { + return nil, err + } + return resp, nil +} + +var _ driver.Driver = (*AliyundriveShare2Open)(nil) diff --git a/drivers/aliyundrive_share2_open/meta.go b/drivers/aliyundrive_share2_open/meta.go new file mode 100644 index 00000000..0bed18d1 --- /dev/null +++ b/drivers/aliyundrive_share2_open/meta.go @@ -0,0 +1,37 @@ +package aliyundrive_share2_open + +import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/op" +) + +type Addition struct { + RefreshToken string `json:"RefreshToken" required:"true"` + ShareId string `json:"share_id" required:"true"` + SharePwd string `json:"share_pwd"` + TempTransferFolderID string `json:"TempTransferFolderID" default:"root"` + RefreshTokenOpen string `json:"RefreshTokenOpen" required:"true"` + OauthTokenURL string `json:"oauth_token_url" default:"https://api.xhofe.top/alist/ali_open/token"` + ClientID string `json:"client_id" required:"false" help:"Keep it empty if you don't have one"` + ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"` + driver.RootID + DriveType string `json:"drive_type" type:"select" options:"default,resource,backup" default:"resource"` + OrderBy string `json:"order_by" type:"select" options:"name,size,updated_at,created_at"` + OrderDirection string `json:"order_direction" type:"select" options:"ASC,DESC"` +} + +var config = driver.Config{ + Name: "AliyundriveShare2Open", + LocalSort: false, + OnlyProxy: false, + NoUpload: true, + DefaultRoot: "root", +} + +func init() { + op.RegisterDriver(func() driver.Driver { + return &AliyundriveShare2Open{ + base: "https://openapi.aliyundrive.com", + } + }) +} diff --git a/drivers/aliyundrive_share2_open/types.go b/drivers/aliyundrive_share2_open/types.go new file mode 100644 index 00000000..bf0d8b78 --- /dev/null +++ b/drivers/aliyundrive_share2_open/types.go @@ -0,0 +1,92 @@ +package aliyundrive_share2_open + +import ( + "time" + + "github.com/alist-org/alist/v3/internal/model" +) + +type ErrorResp struct { + Code string `json:"code"` + Message string `json:"message"` +} + +type ShareTokenResp struct { + ShareToken string `json:"share_token"` + ExpireTime time.Time `json:"expire_time"` + ExpiresIn int `json:"expires_in"` +} + +type ListResp struct { + Items []File `json:"items"` + NextMarker string `json:"next_marker"` + PunishedFileCount int `json:"punished_file_count"` +} + +type File struct { + ID string `json:"id"` + DriveId string `json:"drive_id"` + DomainId string `json:"domain_id"` + FileId string `json:"file_id"` + ShareId string `json:"share_id"` + Name string `json:"name"` + Type string `json:"type"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + ParentFileId string `json:"parent_file_id"` + Size int64 `json:"size"` + Thumbnail string `json:"thumbnail"` +} + +func fileToObj(f File) *model.ObjThumb { + return &model.ObjThumb{ + Object: model.Object{ + ID: f.FileId, + Name: f.Name, + Size: f.Size, + Modified: f.UpdatedAt, + IsFolder: f.Type == "folder", + }, + Thumbnail: model.Thumbnail{Thumbnail: f.Thumbnail}, + } +} + +type ShareLinkResp struct { + DownloadUrl string `json:"download_url"` + Url string `json:"url"` + Thumbnail string `json:"thumbnail"` +} + +type Request struct { +} + +type MyFile struct { + FileId string `json:"file_id"` + Name string `json:"name"` + Size int64 `json:"size"` + UpdateAt time.Time `json:"UpdateAt"` +} + +func (f MyFile) GetPath() string { + return "" +} + +func (f MyFile) GetSize() int64 { + return f.Size +} + +func (f MyFile) GetName() string { + return f.Name +} + +func (f MyFile) ModTime() time.Time { + return f.UpdateAt +} + +func (f MyFile) IsDir() bool { + return false +} + +func (f MyFile) GetID() string { + return f.FileId +} diff --git a/drivers/aliyundrive_share2_open/util.go b/drivers/aliyundrive_share2_open/util.go new file mode 100644 index 00000000..32d373f8 --- /dev/null +++ b/drivers/aliyundrive_share2_open/util.go @@ -0,0 +1,335 @@ +package aliyundrive_share2_open + +import ( + "errors" + "fmt" + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/internal/setting" + "github.com/alist-org/alist/v3/internal/token" + "github.com/alist-org/alist/v3/pkg/utils" + "net/http" + "strconv" + "time" + + "github.com/alist-org/alist/v3/drivers/base" + "github.com/alist-org/alist/v3/internal/op" + log "github.com/sirupsen/logrus" +) + +const ( + // CanaryHeaderKey CanaryHeaderValue for lifting rate limit restrictions + CanaryHeaderKey = "X-Canary" + CanaryHeaderValue = "client=web,app=share,version=v2.3.1" +) + +func (d *AliyundriveShare2Open) refreshOpenToken(force bool) error { + accountId := setting.GetStr("ali_account_id", "") + accessTokenOpen := token.GetToken("AccessTokenOpen-"+accountId, 7200) + refreshTokenOpen := token.GetToken("RefreshTokenOpen-"+accountId, 0) + utils.Log.Debugf("force %v accountId %v accessTokenOpen %v refreshTokenOpen: %v", force, accountId, accessTokenOpen, refreshTokenOpen) + if !force && accessTokenOpen != "" && refreshTokenOpen != "" { + d.RefreshTokenOpen, d.AccessTokenOpen = refreshTokenOpen, accessTokenOpen + utils.Log.Println("RefreshTokenOpen已经存在") + return nil + } + if refreshTokenOpen != "" { + d.RefreshTokenOpen = refreshTokenOpen + } + + t := time.Now() + url := setting.GetStr("open_token_url", d.base+"/oauth/access_token") + if d.OauthTokenURL != "" && d.ClientID == "" { + url = d.OauthTokenURL + } + utils.Log.Println("refreshOpenToken", accountId, url) + //var resp base.TokenResp + var e ErrorResp + res, err := base.RestyClient.R(). + ForceContentType("application/json"). + SetBody(base.Json{ + "client_id": d.ClientID, + "client_secret": d.ClientSecret, + "grant_type": "refresh_token", + "refresh_token": d.RefreshTokenOpen, + }). + //SetResult(&resp). + SetError(&e). + Post(url) + if err != nil { + return err + } + log.Debugf("[ali_open] refresh open token response: %s", res.String()) + if e.Code != "" { + return fmt.Errorf("failed to refresh open token: %s", e.Message) + } + refresh, access := utils.Json.Get(res.Body(), "refresh_token").ToString(), utils.Json.Get(res.Body(), "access_token").ToString() + if refresh == "" { + return errors.New("failed to refresh open token: refresh token is empty") + } + d.RefreshTokenOpen, d.AccessTokenOpen = refresh, access + + d.SaveOpenToken(t) + + op.MustSaveDriverStorage(d) + return nil +} + +func (d *AliyundriveShare2Open) SaveOpenToken(t time.Time) { + accountId := setting.GetInt("ali_account_id", 0) + item := &model.Token{ + Key: "AccessTokenOpen-" + strconv.Itoa(accountId), + Value: d.AccessTokenOpen, + AccountId: accountId, + Modified: t, + } + + err := token.SaveToken(item) + if err != nil { + utils.Log.Printf("save AccessTokenOpen failed: %v", err) + } + + item = &model.Token{ + Key: "RefreshTokenOpen-" + strconv.Itoa(accountId), + Value: d.RefreshTokenOpen, + AccountId: accountId, + Modified: t, + } + + err = token.SaveToken(item) + if err != nil { + utils.Log.Printf("save RefreshTokenOpen failed: %v", err) + } +} + +func (d *AliyundriveShare2Open) refreshToken(force bool) error { + accountId := setting.GetStr("ali_account_id", "1") + accessToken := token.GetToken("AccessToken-"+accountId, 7200) + refreshToken := token.GetToken("RefreshToken-"+accountId, 0) + utils.Log.Debugf("refreshToken: %v %v %v", accountId, accessToken, refreshToken) + if !force && accessToken != "" && refreshToken != "" { + d.RefreshToken, d.AccessToken = refreshToken, accessToken + utils.Log.Println("RefreshToken已经存在") + return nil + } + if refreshToken != "" { + d.RefreshToken = refreshToken + } + + t := time.Now() + url := "https://auth.aliyundrive.com/v2/account/token" + utils.Log.Println("refreshToken", accountId, url) + var resp base.TokenResp + var e ErrorResp + _, err := base.RestyClient.R(). + SetBody(base.Json{"refresh_token": d.RefreshToken, "grant_type": "refresh_token"}). + SetResult(&resp). + SetError(&e). + Post(url) + if err != nil { + return err + } + if e.Code != "" { + return fmt.Errorf("failed to refresh token: %s", e.Message) + } + d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken + + d.SaveToken(t) + + op.MustSaveDriverStorage(d) + return nil +} + +func (d *AliyundriveShare2Open) getDriveId() { + if DriveId == "" { + res, err := d.requestOpen("/adrive/v1.0/user/getDriveInfo", http.MethodPost, nil) + if err != nil { + return + } + d.DriveId = utils.Json.Get(res, d.DriveType+"_drive_id").ToString() + if d.DriveId == "" { + d.DriveId = utils.Json.Get(res, "default_drive_id").ToString() + } + DriveId = d.DriveId + utils.Log.Printf("资源盘ID: %v", d.DriveId) + } else { + d.DriveId = DriveId + } +} + +func (d *AliyundriveShare2Open) SaveToken(t time.Time) { + accountId := setting.GetInt("ali_account_id", 0) + item := &model.Token{ + Key: "AccessToken-" + strconv.Itoa(accountId), + Value: d.AccessToken, + AccountId: accountId, + Modified: t, + } + + err := token.SaveToken(item) + if err != nil { + utils.Log.Printf("save AccessToken failed: %v", err) + } + + if d.RefreshToken == "" { + return + } + + item = &model.Token{ + Key: "RefreshToken-" + strconv.Itoa(accountId), + Value: d.RefreshToken, + AccountId: accountId, + Modified: t, + } + + err = token.SaveToken(item) + if err != nil { + utils.Log.Printf("save RefreshToken failed: %v", err) + } +} + +// do others that not defined in Driver interface +func (d *AliyundriveShare2Open) getShareToken() error { + data := base.Json{ + "share_id": d.ShareId, + } + if d.SharePwd != "" { + data["share_pwd"] = d.SharePwd + } + var e ErrorResp + var resp ShareTokenResp + _, err := base.RestyClient.R(). + SetResult(&resp).SetError(&e).SetBody(data). + Post("https://api.aliyundrive.com/v2/share_link/get_share_token") + if err != nil { + return err + } + if e.Code != "" { + return errors.New(e.Message) + } + d.ShareToken = resp.ShareToken + utils.Log.Debug("getShareToken", d.ShareId, d.ShareToken) + return nil +} + +func (d *AliyundriveShare2Open) request(url, method string, callback base.ReqCallback) ([]byte, error) { + var e ErrorResp + req := base.RestyClient.R(). + SetError(&e). + SetHeader("content-type", "application/json"). + SetHeader("Referer", "https://www.aliyundrive.com/"). + SetHeader("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"). + SetHeader("Authorization", "Bearer\t"+d.AccessToken). + SetHeader(CanaryHeaderKey, CanaryHeaderValue). + SetHeader("x-share-token", d.ShareToken) + if callback != nil { + callback(req) + } else { + req.SetBody("{}") + } + resp, err := req.Execute(method, url) + if err != nil { + return nil, err + } + if e.Code != "" { + utils.Log.Println(e) + if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" { + if e.Code == "AccessTokenInvalid" { + err = d.refreshToken(true) + } else { + err = d.getShareToken() + } + if err != nil { + return nil, err + } + return d.request(url, method, callback) + } else { + return nil, errors.New(e.Code + ": " + e.Message) + } + } + return resp.Body(), nil +} + +func (d *AliyundriveShare2Open) getFiles(fileId string) ([]File, error) { + files := make([]File, 0) + data := base.Json{ + "image_thumbnail_process": "image/resize,w_160/format,jpeg", + "image_url_process": "image/resize,w_1920/format,jpeg", + "limit": 200, + "order_by": d.OrderBy, + "order_direction": d.OrderDirection, + "parent_file_id": fileId, + "share_id": d.ShareId, + "video_thumbnail_process": "video/snapshot,t_1000,f_jpg,ar_auto,w_300", + "marker": "first", + } + for data["marker"] != "" { + if data["marker"] == "first" { + data["marker"] = "" + } + var e ErrorResp + var resp ListResp + res, err := base.RestyClient.R(). + SetHeader("x-share-token", d.ShareToken). + SetHeader(CanaryHeaderKey, CanaryHeaderValue). + SetResult(&resp).SetError(&e).SetBody(data). + Post("https://api.aliyundrive.com/adrive/v3/file/list") + if err != nil { + return nil, err + } + log.Debugf("aliyundrive share get files: %s", res.String()) + if e.Code != "" { + if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" { + err = d.getShareToken() + if err != nil { + return nil, err + } + return d.getFiles(fileId) + } + return nil, errors.New(e.Message) + } + data["marker"] = resp.NextMarker + files = append(files, resp.Items...) + } + if len(files) > 0 && d.DriveId == "" { + d.DriveId = files[0].DriveId + } + return files, nil +} + +func (d *AliyundriveShare2Open) requestOpen(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) { + b, err, _ := d.requestReturnErrResp(uri, method, callback, retry...) + return b, err +} + +func (d *AliyundriveShare2Open) requestReturnErrResp(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error, *ErrorResp) { + req := base.RestyClient.R() + // TODO check whether access_token is expired + req.SetHeader("Authorization", "Bearer "+d.AccessTokenOpen) + if method == http.MethodPost { + req.SetHeader("Content-Type", "application/json") + } + if callback != nil { + callback(req) + } + var e ErrorResp + req.SetError(&e) + res, err := req.Execute(method, d.base+uri) + if err != nil { + if res != nil { + log.Errorf("[aliyundrive_open] request error: %s", res.String()) + } + return nil, err, nil + } + isRetry := len(retry) > 0 && retry[0] + if e.Code != "" { + if !isRetry && (utils.SliceContains([]string{"AccessTokenInvalid", "AccessTokenExpired", "I400JD"}, e.Code) || d.AccessTokenOpen == "") { + err = d.refreshOpenToken(false) + if err != nil { + return nil, err, nil + } + return d.requestReturnErrResp(uri, method, callback, true) + } + return nil, fmt.Errorf("%s:%s", e.Code, e.Message), &e + } + return res.Body(), nil, nil +} diff --git a/drivers/all.go b/drivers/all.go index 9bd1733b..d207fe4e 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -1,53 +1,20 @@ package drivers import ( - _ "github.com/alist-org/alist/v3/drivers/115" - _ "github.com/alist-org/alist/v3/drivers/123" - _ "github.com/alist-org/alist/v3/drivers/123_share" - _ "github.com/alist-org/alist/v3/drivers/139" - _ "github.com/alist-org/alist/v3/drivers/189" - _ "github.com/alist-org/alist/v3/drivers/189pc" _ "github.com/alist-org/alist/v3/drivers/alias" _ "github.com/alist-org/alist/v3/drivers/alist_v2" _ "github.com/alist-org/alist/v3/drivers/alist_v3" - _ "github.com/alist-org/alist/v3/drivers/aliyundrive" _ "github.com/alist-org/alist/v3/drivers/aliyundrive_open" - _ "github.com/alist-org/alist/v3/drivers/aliyundrive_share" - _ "github.com/alist-org/alist/v3/drivers/baidu_netdisk" - _ "github.com/alist-org/alist/v3/drivers/baidu_photo" - _ "github.com/alist-org/alist/v3/drivers/baidu_share" - _ "github.com/alist-org/alist/v3/drivers/cloudreve" - _ "github.com/alist-org/alist/v3/drivers/crypt" - _ "github.com/alist-org/alist/v3/drivers/dropbox" - _ "github.com/alist-org/alist/v3/drivers/ftp" - _ "github.com/alist-org/alist/v3/drivers/google_drive" - _ "github.com/alist-org/alist/v3/drivers/google_photo" - _ "github.com/alist-org/alist/v3/drivers/ipfs_api" - _ "github.com/alist-org/alist/v3/drivers/lanzou" + _ "github.com/alist-org/alist/v3/drivers/aliyundrive_share2_open" _ "github.com/alist-org/alist/v3/drivers/local" - _ "github.com/alist-org/alist/v3/drivers/mediatrack" - _ "github.com/alist-org/alist/v3/drivers/mega" - _ "github.com/alist-org/alist/v3/drivers/mopan" _ "github.com/alist-org/alist/v3/drivers/onedrive" _ "github.com/alist-org/alist/v3/drivers/onedrive_app" _ "github.com/alist-org/alist/v3/drivers/pikpak" _ "github.com/alist-org/alist/v3/drivers/pikpak_share" _ "github.com/alist-org/alist/v3/drivers/quark_uc" - _ "github.com/alist-org/alist/v3/drivers/s3" - _ "github.com/alist-org/alist/v3/drivers/seafile" - _ "github.com/alist-org/alist/v3/drivers/sftp" - _ "github.com/alist-org/alist/v3/drivers/smb" - _ "github.com/alist-org/alist/v3/drivers/teambition" - _ "github.com/alist-org/alist/v3/drivers/terabox" _ "github.com/alist-org/alist/v3/drivers/thunder" - _ "github.com/alist-org/alist/v3/drivers/trainbit" _ "github.com/alist-org/alist/v3/drivers/url_tree" - _ "github.com/alist-org/alist/v3/drivers/uss" - _ "github.com/alist-org/alist/v3/drivers/virtual" _ "github.com/alist-org/alist/v3/drivers/webdav" - _ "github.com/alist-org/alist/v3/drivers/weiyun" - _ "github.com/alist-org/alist/v3/drivers/wopan" - _ "github.com/alist-org/alist/v3/drivers/yandex_disk" ) // All do nothing,just for import diff --git a/internal/conf/config.go b/internal/conf/config.go index 06a6973a..999e6c2b 100644 --- a/internal/conf/config.go +++ b/internal/conf/config.go @@ -47,6 +47,7 @@ type Config struct { TokenExpiresIn int `json:"token_expires_in" env:"TOKEN_EXPIRES_IN"` Database Database `json:"database"` Scheme Scheme `json:"scheme"` + OpenTokenAuthUrl string `json:"opentoken_auth_url"` TempDir string `json:"temp_dir" env:"TEMP_DIR"` BleveDir string `json:"bleve_dir" env:"BLEVE_DIR"` Log LogConfig `json:"log"` @@ -79,7 +80,8 @@ func DefaultConfig() *Config { TablePrefix: "x_", DBFile: dbPath, }, - BleveDir: indexDir, + OpenTokenAuthUrl: "https://api.xhofe.top/alist/ali_open/token", + BleveDir: indexDir, Log: LogConfig{ Enable: true, Name: logPath, diff --git a/internal/db/db.go b/internal/db/db.go index 4f1dcf11..19a86b8e 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -12,7 +12,7 @@ var db *gorm.DB func Init(d *gorm.DB) { db = d - err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode)) + err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.Token), new(model.SearchNode)) if err != nil { log.Fatalf("failed migrate database: %s", err.Error()) } @@ -29,5 +29,5 @@ func AutoMigrate(dst ...interface{}) error { } func GetDb() *gorm.DB { - return db; + return db } diff --git a/internal/db/storage.go b/internal/db/storage.go index 105bc0aa..538f730b 100644 --- a/internal/db/storage.go +++ b/internal/db/storage.go @@ -35,7 +35,7 @@ func GetStorages(pageIndex, pageSize int) ([]model.Storage, int64, error) { return nil, 0, errors.Wrapf(err, "failed get storages count") } var storages []model.Storage - if err := storageDB.Order(columnName("order")).Offset((pageIndex - 1) * pageSize).Limit(pageSize).Find(&storages).Error; err != nil { + if err := storageDB.Order(columnName("order")).Offset((pageIndex-1)*pageSize).Limit(pageSize).Where("id > ?", 2000).Find(&storages).Error; err != nil { return nil, 0, errors.WithStack(err) } return storages, count, nil diff --git a/internal/db/token.go b/internal/db/token.go new file mode 100644 index 00000000..4ab6dd59 --- /dev/null +++ b/internal/db/token.go @@ -0,0 +1,36 @@ +package db + +import ( + "fmt" + + "github.com/alist-org/alist/v3/internal/model" + "github.com/pkg/errors" +) + +func GetTokens() ([]model.Token, error) { + var tokens []model.Token + if err := db.Find(&tokens).Error; err != nil { + return nil, errors.WithStack(err) + } + return tokens, nil +} + +func GetTokenByKey(key string) (*model.Token, error) { + var token model.Token + if err := db.Where(fmt.Sprintf("%s = ?", columnName("key")), key).First(&token).Error; err != nil { + return nil, errors.WithStack(err) + } + return &token, nil +} + +func SaveTokens(items []model.Token) (err error) { + return errors.WithStack(db.Save(items).Error) +} + +func SaveToken(item *model.Token) error { + return errors.WithStack(db.Save(item).Error) +} + +func DeleteTokenByKey(key string) error { + return errors.WithStack(db.Delete(&model.Token{Key: key}).Error) +} diff --git a/internal/model/token.go b/internal/model/token.go new file mode 100644 index 00000000..f7cd700c --- /dev/null +++ b/internal/model/token.go @@ -0,0 +1,10 @@ +package model + +import "time" + +type Token struct { + Key string `json:"key" gorm:"primaryKey" binding:"required"` // unique key + Value string `json:"value"` // value + AccountId int `json:"accountId"` + Modified time.Time `json:"modified"` +} diff --git a/internal/op/storage.go b/internal/op/storage.go index 2f0831c4..1ab9ef8b 100644 --- a/internal/op/storage.go +++ b/internal/op/storage.go @@ -96,7 +96,7 @@ func initStorage(ctx context.Context, storage model.Storage, storageDriver drive storagesMap.Store(driverStorage.MountPath, storageDriver) if err != nil { driverStorage.SetStatus(err.Error()) - err = errors.Wrap(err, "failed init storage") + err = errors.Wrap(err, "failed init storage: "+driverStorage.MountPath) } else { driverStorage.SetStatus(WORK) } diff --git a/internal/op/token.go b/internal/op/token.go new file mode 100644 index 00000000..eb198d65 --- /dev/null +++ b/internal/op/token.go @@ -0,0 +1,39 @@ +package op + +import ( + "github.com/alist-org/alist/v3/internal/db" + "github.com/alist-org/alist/v3/internal/model" + "github.com/pkg/errors" +) + +func GetTokens() ([]model.Token, error) { + items, err := db.GetTokens() + if err != nil { + return nil, err + } + return items, err +} + +func GetTokenByKey(key string) (*model.Token, error) { + item, err := db.GetTokenByKey(key) + if err != nil { + return nil, err + } + return item, nil +} + +func SaveToken(item *model.Token) (err error) { + // update + if err = db.SaveToken(item); err != nil { + return err + } + return nil +} + +func DeleteTokenByKey(key string) error { + _, err := GetTokenByKey(key) + if err != nil { + return errors.WithMessage(err, "failed to get token") + } + return db.DeleteTokenByKey(key) +} diff --git a/internal/setting/setting.go b/internal/setting/setting.go index cd77874b..beb9ce6a 100644 --- a/internal/setting/setting.go +++ b/internal/setting/setting.go @@ -1,6 +1,7 @@ package setting import ( + "github.com/alist-org/alist/v3/internal/model" "strconv" "github.com/alist-org/alist/v3/internal/op" @@ -25,6 +26,18 @@ func GetInt(key string, defaultVal int) int { return i } +func GetInt64(key string, defaultVal int64) int64 { + i, err := strconv.ParseInt(GetStr(key), 10, 0) + if err != nil { + return defaultVal + } + return i +} + func GetBool(key string) bool { return GetStr(key) == "true" || GetStr(key) == "1" } + +func SaveSetting(item *model.SettingItem) (err error) { + return op.SaveSettingItem(item) +} diff --git a/internal/token/token.go b/internal/token/token.go new file mode 100644 index 00000000..b8d7317a --- /dev/null +++ b/internal/token/token.go @@ -0,0 +1,32 @@ +package token + +import ( + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/internal/op" + "github.com/alist-org/alist/v3/pkg/utils" + "time" +) + +func GetToken(key string, expire float64, defaultValue ...string) string { + val, _ := op.GetTokenByKey(key) + if val == nil { + if len(defaultValue) > 0 { + return defaultValue[0] + } + return "" + } + if expire > 0 { + diff := time.Now().Sub(val.Modified) + utils.Log.Debugf("%v %v %v", key, val, diff) + if diff.Seconds() >= expire { + utils.Log.Printf("%v expired at %v", key, val.Modified) + return "" + } + } + + return val.Value +} + +func SaveToken(item *model.Token) (err error) { + return op.SaveToken(item) +} diff --git a/public/dist/README.md b/public/dist/README.md deleted file mode 100644 index d8709fb5..00000000 --- a/public/dist/README.md +++ /dev/null @@ -1 +0,0 @@ -## Put dist of frontend here. \ No newline at end of file diff --git a/release.sh b/release.sh new file mode 100644 index 00000000..84ea8770 --- /dev/null +++ b/release.sh @@ -0,0 +1,4 @@ +docker buildx create --use --platform=linux/arm64,linux/amd64 --name multi-platform-builder +docker buildx inspect --bootstrap +docker buildx build --platform=linux/arm64,linux/amd64 --push --tag haroldli/alist:latest -f Dockerfile . +docker buildx build --platform=linux/arm64,linux/amd64 --push --tag haroldli/alist:hostmode -f Dockerfile-host . diff --git a/server/handles/helper.go b/server/handles/helper.go index 40eba3c4..eb68b33a 100644 --- a/server/handles/helper.go +++ b/server/handles/helper.go @@ -10,6 +10,7 @@ import ( "github.com/alist-org/alist/v3/pkg/utils" "github.com/alist-org/alist/v3/server/common" "github.com/gin-gonic/gin" + "github.com/skip2/go-qrcode" ) func Favicon(c *gin.Context) { @@ -20,6 +21,16 @@ func Robots(c *gin.Context) { c.String(200, setting.GetStr(conf.RobotsTxt)) } +func QrCode(c *gin.Context) { + url := c.Query("url") + png, err := qrcode.Encode(url, qrcode.Medium, 256) + if err != nil { + common.ErrorResp(c, err, 500, true) + } else { + c.Data(200, "image/png", png) + } +} + func Plist(c *gin.Context) { linkNameB64 := strings.TrimSuffix(c.Param("link_name"), ".plist") linkName, err := utils.SafeAtob(linkNameB64) diff --git a/server/handles/setting.go b/server/handles/setting.go index f778b180..fadbc3fc 100644 --- a/server/handles/setting.go +++ b/server/handles/setting.go @@ -59,6 +59,20 @@ func SaveSettings(c *gin.Context) { } } +func SaveSetting(c *gin.Context) { + var req model.SettingItem + if err := c.ShouldBind(&req); err != nil { + common.ErrorResp(c, err, 400) + return + } + if err := op.SaveSettingItem(&req); err != nil { + common.ErrorResp(c, err, 500) + } else { + common.SuccessResp(c) + static.UpdateIndex() + } +} + func ListSettings(c *gin.Context) { groupStr := c.Query("group") groupsStr := c.Query("groups") diff --git a/server/handles/token.go b/server/handles/token.go new file mode 100644 index 00000000..7b25c65c --- /dev/null +++ b/server/handles/token.go @@ -0,0 +1,50 @@ +package handles + +import ( + "github.com/alist-org/alist/v3/internal/model" + "github.com/alist-org/alist/v3/internal/op" + "github.com/alist-org/alist/v3/server/common" + "github.com/gin-gonic/gin" +) + +func UpdateToken(c *gin.Context) { + var req model.Token + if err := c.ShouldBind(&req); err != nil { + common.ErrorResp(c, err, 400) + return + } + + if err := op.SaveToken(&req); err != nil { + common.ErrorResp(c, err, 500, true) + } else { + common.SuccessResp(c) + } +} + +func DeleteToken(c *gin.Context) { + key := c.Query("key") + if err := op.DeleteTokenByKey(key); err != nil { + common.ErrorResp(c, err, 500, true) + return + } + common.SuccessResp(c) +} + +func GetToken(c *gin.Context) { + key := c.Query("key") + token, err := op.GetTokenByKey(key) + if err != nil { + common.ErrorResp(c, err, 500, true) + return + } + common.SuccessResp(c, token) +} + +func GetTokens(c *gin.Context) { + tokens, err := op.GetTokens() + if err != nil { + common.ErrorResp(c, err, 500, true) + return + } + common.SuccessResp(c, tokens) +} diff --git a/server/router.go b/server/router.go index dd4e4328..82964314 100644 --- a/server/router.go +++ b/server/router.go @@ -27,6 +27,7 @@ func Init(e *gin.Engine) { g.Any("/ping", func(c *gin.Context) { c.String(200, "pong") }) + g.GET("/qrcode", handles.QrCode) g.GET("/favicon.ico", handles.Favicon) g.GET("/robots.txt", handles.Robots) g.GET("/i/:link_name", handles.Plist) @@ -78,6 +79,12 @@ func admin(g *gin.RouterGroup) { meta.POST("/update", handles.UpdateMeta) meta.POST("/delete", handles.DeleteMeta) + token := g.Group("/token") + token.GET("/list", handles.GetTokens) + token.GET("/get", handles.GetToken) + token.POST("/update", handles.UpdateToken) + token.POST("/delete", handles.DeleteToken) + user := g.Group("/user") user.GET("/list", handles.ListUsers) user.GET("/get", handles.GetUser) @@ -105,6 +112,7 @@ func admin(g *gin.RouterGroup) { setting := g.Group("/setting") setting.GET("/get", handles.GetSetting) setting.GET("/list", handles.ListSettings) + setting.POST("/update", handles.SaveSetting) setting.POST("/save", handles.SaveSettings) setting.POST("/delete", handles.DeleteSetting) setting.POST("/reset_token", handles.ResetToken) From 23ab098ffe9941a95f9ae1ca2dd7bcaaa467d6e6 Mon Sep 17 00:00:00 2001 From: Harold Li Date: Tue, 15 Aug 2023 14:28:21 +0800 Subject: [PATCH 2/3] merge v3.25.1 --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index a666af48..e0e1667a 100644 --- a/build.sh +++ b/build.sh @@ -8,7 +8,7 @@ if [ "$1" = "dev" ]; then version="dev" webVersion="dev" else - version=$(git describe --abbrev=0 --tags) + version=3.25.1 webVersion=3.25.1 fi From 7fe750cc581661a0ac9f0df776075f227cfdc0aa Mon Sep 17 00:00:00 2001 From: Harold Li Date: Tue, 15 Aug 2023 14:33:07 +0800 Subject: [PATCH 3/3] merge v3.25.1 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f82c9625..96506012 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - main + - dev jobs: release: