Compare commits
133 Commits
Author | SHA1 | Date | |
---|---|---|---|
d988f98b81 | |||
10634c7b77 | |||
135d505192 | |||
3f2be8a6ca | |||
79bef09ee7 | |||
3534f6afac | |||
106c1d069c | |||
8ed0afe80d | |||
6a6e3944d5 | |||
94d5b5e47e | |||
e61b0f8e34 | |||
f7fbe1de6c | |||
01de01630e | |||
f9f92e2198 | |||
7d5f50b04a | |||
72b5d25e4c | |||
cae7f36531 | |||
aa79f49e25 | |||
b4ad301d53 | |||
00ed54c4c9 | |||
ffa52794db | |||
24058d0c36 | |||
641ca67671 | |||
52ee2e0a8b | |||
724fc7f37e | |||
9d279b104b | |||
eb61f70164 | |||
b3a8201768 | |||
185795954b | |||
cc62cc99d2 | |||
270349f37c | |||
977888070a | |||
192d0f2bf3 | |||
f2ec7884ec | |||
815975a4d2 | |||
f96a0238fc | |||
f695bd0959 | |||
efe8f46e17 | |||
515daa22a9 | |||
f11e22deaf | |||
5be976169f | |||
a6e08f3bf4 | |||
944e68a979 | |||
b3a6e33ce1 | |||
cb53ddc8e8 | |||
693417be4f | |||
5c3f91bb55 | |||
8a219d0732 | |||
146a544af3 | |||
48dccc6c0b | |||
ce1740cec4 | |||
d40dbeae3e | |||
5094b673c4 | |||
228e6d10e7 | |||
e90b979d15 | |||
fb05a6ca48 | |||
e055ed3afa | |||
4371c470b3 | |||
7bb237d0ef | |||
5c42354b01 | |||
387e8af422 | |||
5dca777caf | |||
0814778a14 | |||
6827af3997 | |||
435bdea8f7 | |||
4f81735af6 | |||
bef3d2f88d | |||
ba99c7dc03 | |||
f5c5162a9b | |||
a22903533e | |||
86cda58b22 | |||
7804cf9d5c | |||
2bb7036110 | |||
ba545555cf | |||
be55ca690c | |||
9013add749 | |||
3201b6da76 | |||
feb42f1f4b | |||
6f14d0eb5c | |||
7530d8f5b2 | |||
e25fe05a53 | |||
8e0ab8f780 | |||
cb2a3c2b42 | |||
1b6ec94f33 | |||
cb23edc1fe | |||
6fd05d7d72 | |||
f26ac57569 | |||
2434ac54d0 | |||
f25b557327 | |||
81a0706d01 | |||
5f6b576cbf | |||
549877f71e | |||
c6a5ba9b91 | |||
1a69d80489 | |||
b797f4302c | |||
bf9aa5c3d3 | |||
7390e19a7a | |||
b31a12a0cc | |||
26ce001782 | |||
a2c7ff3262 | |||
8fc7c716c0 | |||
c70fc3fc4b | |||
df513b7dc0 | |||
2a9598f4c6 | |||
224c20779c | |||
5d722298cb | |||
4bcc6359e3 | |||
4144afcc92 | |||
2ad27046fb | |||
9516ac6718 | |||
de638c7c36 | |||
c6b34a033b | |||
31de3399d2 | |||
0dc2ca019f | |||
04724f7f0f | |||
75a983a965 | |||
e12d8bb8ca | |||
68f1ccfed4 | |||
54272db59c | |||
6d34e88360 | |||
0a901a2eb0 | |||
e1671a0511 | |||
dcb4ec695f | |||
4a21b6fe1d | |||
96a237902b | |||
cfb51e9f80 | |||
e952f1c243 | |||
07d6ca27db | |||
8245da485a | |||
5c759217cf | |||
0648fdebc2 | |||
ed670e528f | |||
2473309a51 |
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -5,7 +5,7 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
Thanks for taking the time to fill out this bug report!
|
Thanks for taking the time to fill out this bug report, please confirm that your issue is not a duplicate issue
|
||||||
- type: input
|
- type: input
|
||||||
id: version
|
id: version
|
||||||
attributes:
|
attributes:
|
||||||
|
4
.github/ISSUE_TEMPLATE/config.yml
vendored
4
.github/ISSUE_TEMPLATE/config.yml
vendored
@ -1,5 +1,5 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: Questions & Discussions & Feature request
|
- name: Questions & Discussions
|
||||||
url: https://github.com/Xhofe/alist/discussions
|
url: https://github.com/Xhofe/alist/discussions
|
||||||
about: Use GitHub discussions for message-board style questions and discussions or feature request.
|
about: Use GitHub discussions for message-board style questions and discussions.
|
24
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
24
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
name: "Feature request"
|
||||||
|
description: Feature request
|
||||||
|
labels: ["enhancement: pending triage"]
|
||||||
|
body:
|
||||||
|
- type: textarea
|
||||||
|
id: feature-description
|
||||||
|
attributes:
|
||||||
|
label: Description of the feature / 需求描述
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: suggested-solution
|
||||||
|
attributes:
|
||||||
|
label: Suggested solution / 实现思路
|
||||||
|
description: |
|
||||||
|
Solutions to achieve this requirement.
|
||||||
|
实现此需求的解决思路。
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional context / 附件
|
||||||
|
description: |
|
||||||
|
Any other context or screenshots about the feature request here, or information you find helpful.
|
||||||
|
相关的任何其他上下文或截图,或者你觉得有帮助的信息
|
46
.github/workflows/build.yml
vendored
46
.github/workflows/build.yml
vendored
@ -2,9 +2,9 @@ name: build
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ v2 ]
|
branches: [ '**' ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ v2 ]
|
branches: [ '**' ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@ -28,10 +28,9 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
ref: v2
|
|
||||||
path: alist
|
path: alist
|
||||||
|
|
||||||
- name: Set up xgo
|
- name: Install upx
|
||||||
run: |
|
run: |
|
||||||
docker pull techknowlogick/xgo:latest
|
docker pull techknowlogick/xgo:latest
|
||||||
go install src.techknowlogick.com/xgo@latest
|
go install src.techknowlogick.com/xgo@latest
|
||||||
@ -48,41 +47,4 @@ jobs:
|
|||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: artifact
|
name: artifact
|
||||||
path: alist/build
|
path: alist/build
|
||||||
docker:
|
|
||||||
name: Docker
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: xhofe/alist
|
|
||||||
- name: Set up Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- name: Build web
|
|
||||||
run: |
|
|
||||||
bash build.sh web
|
|
||||||
mv dist/* public
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: xhofe
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
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
|
|
46
.github/workflows/build_docker.yml
vendored
Normal file
46
.github/workflows/build_docker.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
name: build_docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ v2 ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ v2 ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_docker:
|
||||||
|
name: Docker
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: xhofe/alist
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- name: Build web
|
||||||
|
run: |
|
||||||
|
bash build.sh web
|
||||||
|
mv dist/* public
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: xhofe
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
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
|
20
.github/workflows/issue_check_question.yml
vendored
Normal file
20
.github/workflows/issue_check_question.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
name: Check need info
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 */7 * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-need-info:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: close-issues
|
||||||
|
uses: actions-cool/issues-helper@v2
|
||||||
|
with:
|
||||||
|
actions: 'close-issues'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
labels: 'question'
|
||||||
|
inactive-day: 7
|
||||||
|
body: |
|
||||||
|
Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 7 days.
|
||||||
|
你好 @${{ github.event.issue.user.login }},此issue因超过7天未回复被关闭。
|
25
.github/workflows/issue_duplicate.yml
vendored
Normal file
25
.github/workflows/issue_duplicate.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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@v2
|
||||||
|
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@v2
|
||||||
|
with:
|
||||||
|
actions: 'close-issue'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
16
.github/workflows/issue_month_statistics.yml
vendored
Normal file
16
.github/workflows/issue_month_statistics.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
name: Issue Month Statistics
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 1 1 * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
month-statistics:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: month-statistics
|
||||||
|
uses: actions-cool/issues-month-statistics@v1
|
||||||
|
with:
|
||||||
|
count-lables: true
|
||||||
|
count-comments: true
|
||||||
|
emoji: 'eyes'
|
20
.github/workflows/issue_question.yml
vendored
Normal file
20
.github/workflows/issue_question.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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@v2.0.0
|
||||||
|
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 7 days.
|
||||||
|
你好 @${{ github.event.issue.user.login }},请按照issue模板填写, 并详细说明问题/复现步骤/实现思路或提供更多信息等, 7天内未回复issue自动关闭。
|
25
.github/workflows/issue_wontfix.yml
vendored
Normal file
25
.github/workflows/issue_wontfix.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
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@v2
|
||||||
|
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@v2
|
||||||
|
with:
|
||||||
|
actions: 'close-issue'
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
41
.github/workflows/release.yml
vendored
41
.github/workflows/release.yml
vendored
@ -27,7 +27,7 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up xgo
|
- name: Install upx
|
||||||
run: |
|
run: |
|
||||||
docker pull techknowlogick/xgo:latest
|
docker pull techknowlogick/xgo:latest
|
||||||
go install src.techknowlogick.com/xgo@latest
|
go install src.techknowlogick.com/xgo@latest
|
||||||
@ -43,41 +43,4 @@ jobs:
|
|||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: alist/build/compress/*
|
files: alist/build/compress/*
|
||||||
docker:
|
|
||||||
name: Docker
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: xhofe/alist
|
|
||||||
- name: Set up Node
|
|
||||||
uses: actions/setup-node@v2
|
|
||||||
with:
|
|
||||||
node-version: '16'
|
|
||||||
- name: Build web
|
|
||||||
run: |
|
|
||||||
bash build.sh cdn
|
|
||||||
mv dist/* public
|
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v1
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v1
|
|
||||||
- name: Login to DockerHub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: xhofe
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
- name: Build and push
|
|
||||||
id: docker_build
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
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
|
|
45
.github/workflows/release_docker.yml
vendored
Normal file
45
.github/workflows/release_docker.yml
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
name: release_docker
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker_release:
|
||||||
|
name: Docker
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Docker meta
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: xhofe/alist
|
||||||
|
- name: Set up Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: '16'
|
||||||
|
- name: Build web
|
||||||
|
run: |
|
||||||
|
bash build.sh cdn
|
||||||
|
mv dist/* public
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v1
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v1
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
username: xhofe
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Build and push
|
||||||
|
id: docker_build
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
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
|
@ -3,7 +3,7 @@ LABEL stage=go-builder
|
|||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
RUN apk add --no-cache bash git go gcc musl-dev; \
|
RUN apk add --no-cache bash git go gcc musl-dev; \
|
||||||
sh build.sh docker
|
bash build.sh docker
|
||||||
|
|
||||||
FROM alpine:edge
|
FROM alpine:edge
|
||||||
LABEL MAINTAINER="i@nn.ci"
|
LABEL MAINTAINER="i@nn.ci"
|
||||||
|
10
README.md
10
README.md
@ -1,5 +1,5 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h3><a href="https://alist.nn.ci">Alist</a></h3>
|
<a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/assets@main/logo.svg"/></a>
|
||||||
<p><em>🗂️Another file list program that supports multiple storage, powered by Gin and React.</em></p>
|
<p><em>🗂️Another file list program that supports multiple storage, powered by Gin and React.</em></p>
|
||||||
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
|
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
|
||||||
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
|
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
|
||||||
@ -12,6 +12,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
English | [中文](./README_cn.md)
|
English | [中文](./README_cn.md)
|
||||||
@ -35,6 +36,9 @@ English | [中文](./README_cn.md)
|
|||||||
- [x] Teambition([China](https://www.teambition.com/ ),[International](https://us.teambition.com/ ))
|
- [x] Teambition([China](https://www.teambition.com/ ),[International](https://us.teambition.com/ ))
|
||||||
- [x] [Mediatrack](https://www.mediatrack.cn/)
|
- [x] [Mediatrack](https://www.mediatrack.cn/)
|
||||||
- [x] [139yun](https://yun.139.com/) (Personal, Family)
|
- [x] [139yun](https://yun.139.com/) (Personal, Family)
|
||||||
|
- [x] [Yandex.Disk](https://disk.yandex.com/)
|
||||||
|
- [x] [Baidu Disk](http://pan.baidu.com/)
|
||||||
|
- [x] [Quark](https://pan.quark.cn)
|
||||||
- [x] Easy to deploy and out-of-the-box
|
- [x] Easy to deploy and out-of-the-box
|
||||||
- [x] File preview (PDF, markdown, code, plain text, ...)
|
- [x] File preview (PDF, markdown, code, plain text, ...)
|
||||||
- [x] Image preview in gallery mode
|
- [x] Image preview in gallery mode
|
||||||
@ -50,11 +54,11 @@ English | [中文](./README_cn.md)
|
|||||||
- [x] Cloudflare workers proxy
|
- [x] Cloudflare workers proxy
|
||||||
- [x] File/Folder package download
|
- [x] File/Folder package download
|
||||||
- [x] Support video list playback and subtitles(ass,srt,vtt)
|
- [x] Support video list playback and subtitles(ass,srt,vtt)
|
||||||
- [x] Web upload(Can allow visitors to upload) and delete
|
- [x] Web upload(Can allow visitors to upload), delete, mkdir, rename, move and copy
|
||||||
|
|
||||||
## Discussion
|
## Discussion
|
||||||
|
|
||||||
Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports only.**
|
Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature request only.**
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
|
10
README_cn.md
10
README_cn.md
@ -1,5 +1,5 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h3><a href="https://alist.nn.ci">Alist</a></h3>
|
<a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/assets@main/logo.svg"/></a>
|
||||||
<p><em>🗂️一个支持多存储的文件列表程序,使用 Gin 和 React 。</em></p>
|
<p><em>🗂️一个支持多存储的文件列表程序,使用 Gin 和 React 。</em></p>
|
||||||
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
|
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
|
||||||
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
|
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
|
||||||
@ -11,6 +11,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
[English](./README.md) | 中文
|
[English](./README.md) | 中文
|
||||||
@ -34,6 +35,9 @@
|
|||||||
- [x] Teambition([中国](https://www.teambition.com/ ),[国际](https://us.teambition.com/ ))
|
- [x] Teambition([中国](https://www.teambition.com/ ),[国际](https://us.teambition.com/ ))
|
||||||
- [x] [分秒帧](https://www.mediatrack.cn/)
|
- [x] [分秒帧](https://www.mediatrack.cn/)
|
||||||
- [x] [和彩云](https://yun.139.com/) (个人云, 家庭云)
|
- [x] [和彩云](https://yun.139.com/) (个人云, 家庭云)
|
||||||
|
- [x] [Yandex.Disk](https://disk.yandex.com/)
|
||||||
|
- [x] [百度网盘](http://pan.baidu.com/)
|
||||||
|
- [x] [夸克网盘](https://pan.quark.cn)
|
||||||
- [x] 部署方便,开箱即用
|
- [x] 部署方便,开箱即用
|
||||||
- [x] 文件预览(PDF、markdown、代码、纯文本……)
|
- [x] 文件预览(PDF、markdown、代码、纯文本……)
|
||||||
- [x] 画廊模式下的图像预览
|
- [x] 画廊模式下的图像预览
|
||||||
@ -49,11 +53,11 @@
|
|||||||
- [x] Cloudflare workers 中转
|
- [x] Cloudflare workers 中转
|
||||||
- [x] 文件/文件夹打包下载
|
- [x] 文件/文件夹打包下载
|
||||||
- [x] 支持视频列表播放和字幕(ass,srt,vtt)
|
- [x] 支持视频列表播放和字幕(ass,srt,vtt)
|
||||||
- [x] 网页上传(可以允许访客上传),删除
|
- [x] 网页上传(可以允许访客上传),删除,新建文件夹,重命名,移动,复制
|
||||||
|
|
||||||
## 讨论
|
## 讨论
|
||||||
|
|
||||||
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告。**
|
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告和功能请求。**
|
||||||
|
|
||||||
## 演示
|
## 演示
|
||||||
|
|
||||||
|
5
alist.go
5
alist.go
@ -22,7 +22,7 @@ func Init() bool {
|
|||||||
log.Errorf(err.Error())
|
log.Errorf(err.Error())
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
log.Infof("current password: %s", pass.Value)
|
fmt.Printf("your password: %s\n", pass.Value)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
server.InitIndex()
|
server.InitIndex()
|
||||||
@ -34,7 +34,8 @@ func Init() bool {
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if conf.Version {
|
if conf.Version {
|
||||||
fmt.Printf("Built At: %s\nGo Version: %s\nAuthor: %s\nCommit ID: %s\nVersion: %s\n", conf.BuiltAt, conf.GoVersion, conf.GitAuthor, conf.GitCommit, conf.GitTag)
|
fmt.Printf("Built At: %s\nGo Version: %s\nAuthor: %s\nCommit ID: %s\nVersion: %s\nWebVersion: %s\n",
|
||||||
|
conf.BuiltAt, conf.GoVersion, conf.GitAuthor, conf.GitCommit, conf.GitTag, conf.WebTag)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !Init() {
|
if !Init() {
|
||||||
|
@ -19,6 +19,7 @@ func InitAccounts() {
|
|||||||
if !ok {
|
if !ok {
|
||||||
log.Errorf("no [%s] driver", account.Type)
|
log.Errorf("no [%s] driver", account.Type)
|
||||||
} else {
|
} else {
|
||||||
|
log.Infof("start init account: [%s], type: [%s]", account.Name, account.Type)
|
||||||
err := driver.Save(&accounts[i], nil)
|
err := driver.Save(&accounts[i], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("init account [%s] error:[%s]", account.Name, err.Error())
|
log.Errorf("init account [%s] error:[%s]", account.Name, err.Error())
|
||||||
@ -27,4 +28,4 @@ func InitAccounts() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitConf init config
|
// InitConf init config
|
||||||
@ -20,25 +21,29 @@ func InitConf() {
|
|||||||
if !utils.WriteToJson(conf.ConfigFile, conf.Conf) {
|
if !utils.WriteToJson(conf.ConfigFile, conf.Conf) {
|
||||||
log.Fatalf("failed to create default config file")
|
log.Fatalf("failed to create default config file")
|
||||||
}
|
}
|
||||||
return
|
} else {
|
||||||
|
config, err := ioutil.ReadFile(conf.ConfigFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("reading config file error:%s", err.Error())
|
||||||
|
}
|
||||||
|
conf.Conf = conf.DefaultConfig()
|
||||||
|
err = utils.Json.Unmarshal(config, conf.Conf)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("load config error: %s", err.Error())
|
||||||
|
}
|
||||||
|
log.Debugf("config:%+v", conf.Conf)
|
||||||
|
// update config.json struct
|
||||||
|
confBody, err := utils.Json.MarshalIndent(conf.Conf, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("marshal config error:%s", err.Error())
|
||||||
|
}
|
||||||
|
err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("update config struct error: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
config, err := ioutil.ReadFile(conf.ConfigFile)
|
err := os.MkdirAll(conf.Conf.TempDir, 0700)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("reading config file error:%s", err.Error())
|
log.Fatalf("create temp dir error: %s", err.Error())
|
||||||
}
|
|
||||||
conf.Conf = new(conf.Config)
|
|
||||||
err = utils.Json.Unmarshal(config, conf.Conf)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("load config error: %s", err.Error())
|
|
||||||
}
|
|
||||||
log.Debugf("config:%+v", conf.Conf)
|
|
||||||
// update config.json struct
|
|
||||||
confBody, err := utils.Json.MarshalIndent(conf.Conf, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("marshal config error:%s", err.Error())
|
|
||||||
}
|
|
||||||
err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("update config struct error: %s", err.Error())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@ func InitLog() {
|
|||||||
log.SetLevel(log.DebugLevel)
|
log.SetLevel(log.DebugLevel)
|
||||||
log.SetReportCaller(true)
|
log.SetReportCaller(true)
|
||||||
}
|
}
|
||||||
|
if conf.Password || conf.Version {
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
}
|
||||||
log.SetFormatter(&log.TextFormatter{
|
log.SetFormatter(&log.TextFormatter{
|
||||||
//DisableColors: true,
|
//DisableColors: true,
|
||||||
ForceColors: true,
|
ForceColors: true,
|
||||||
@ -29,4 +32,4 @@ func init() {
|
|||||||
flag.BoolVar(&conf.Password, "password", false, "print current password")
|
flag.BoolVar(&conf.Password, "password", false, "print current password")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
InitLog()
|
InitLog()
|
||||||
}
|
}
|
||||||
|
@ -60,14 +60,13 @@ func InitModel() {
|
|||||||
}
|
}
|
||||||
case "postgres":
|
case "postgres":
|
||||||
{
|
{
|
||||||
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
|
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=Asia/Shanghai",
|
||||||
databaseConfig.Host, databaseConfig.User, databaseConfig.Password, databaseConfig.Name, databaseConfig.Port)
|
databaseConfig.Host, databaseConfig.User, databaseConfig.Password, databaseConfig.Name, databaseConfig.Port, databaseConfig.SslMode)
|
||||||
db, err := gorm.Open(postgres.Open(dsn), gormConfig)
|
db, err := gorm.Open(postgres.Open(dsn), gormConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to connect database:%s", err.Error())
|
log.Errorf("failed to connect database:%s", err.Error())
|
||||||
}
|
}
|
||||||
conf.DB = db
|
conf.DB = db
|
||||||
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Fatalf("not supported database type: %s", databaseConfig.Type)
|
log.Fatalf("not supported database type: %s", databaseConfig.Type)
|
||||||
@ -80,6 +79,6 @@ func InitModel() {
|
|||||||
err = conf.DB.AutoMigrate(&model.SettingItem{}, &model.Account{}, &model.Meta{})
|
err = conf.DB.AutoMigrate(&model.SettingItem{}, &model.Account{}, &model.Meta{})
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to auto migrate")
|
log.Fatalf("failed to auto migrate: %s", err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package bootstrap
|
|||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"strings"
|
"strings"
|
||||||
@ -27,7 +28,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "password",
|
Key: "password",
|
||||||
Value: "alist",
|
Value: utils.RandomStr(8),
|
||||||
Description: "password",
|
Description: "password",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PRIVATE,
|
Access: model.PRIVATE,
|
||||||
@ -35,7 +36,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "logo",
|
Key: "logo",
|
||||||
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
|
Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/can_circle.svg",
|
||||||
Description: "logo",
|
Description: "logo",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PUBLIC,
|
Access: model.PUBLIC,
|
||||||
@ -43,7 +44,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "favicon",
|
Key: "favicon",
|
||||||
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png",
|
Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg",
|
||||||
Description: "favicon",
|
Description: "favicon",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PUBLIC,
|
Access: model.PUBLIC,
|
||||||
@ -65,15 +66,23 @@ func InitSettings() {
|
|||||||
Group: model.FRONT,
|
Group: model.FRONT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "hide readme file",
|
Key: "d_proxy types",
|
||||||
Value: "true",
|
Value: strings.Join(conf.DProxyTypes, ","),
|
||||||
Type: "bool",
|
Type: "string",
|
||||||
Description: "hide readme file? ",
|
Description: "/d but proxy",
|
||||||
|
Access: model.PRIVATE,
|
||||||
|
Group: model.BACK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "hide files",
|
||||||
|
Value: "/\\/README.md/i",
|
||||||
|
Type: "text",
|
||||||
|
Description: "hide files, support RegExp, one per line",
|
||||||
Group: model.FRONT,
|
Group: model.FRONT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "music cover",
|
Key: "music cover",
|
||||||
Value: "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png",
|
Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/circle_center.svg",
|
||||||
Description: "music cover image",
|
Description: "music cover image",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PUBLIC,
|
Access: model.PUBLIC,
|
||||||
@ -157,7 +166,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "WebDAV username",
|
Key: "WebDAV username",
|
||||||
Value: "alist_admin",
|
Value: "admin",
|
||||||
Description: "WebDAV username",
|
Description: "WebDAV username",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PRIVATE,
|
Access: model.PRIVATE,
|
||||||
@ -165,7 +174,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "WebDAV password",
|
Key: "WebDAV password",
|
||||||
Value: "alist_admin",
|
Value: utils.RandomStr(8),
|
||||||
Description: "WebDAV password",
|
Description: "WebDAV password",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PRIVATE,
|
Access: model.PRIVATE,
|
||||||
@ -189,7 +198,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "Visitor WebDAV username",
|
Key: "Visitor WebDAV username",
|
||||||
Value: "alist_visitor",
|
Value: "guest",
|
||||||
Description: "Visitor WebDAV username",
|
Description: "Visitor WebDAV username",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PRIVATE,
|
Access: model.PRIVATE,
|
||||||
@ -197,7 +206,7 @@ func InitSettings() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Key: "Visitor WebDAV password",
|
Key: "Visitor WebDAV password",
|
||||||
Value: "alist_visitor",
|
Value: "guest",
|
||||||
Description: "Visitor WebDAV password",
|
Description: "Visitor WebDAV password",
|
||||||
Type: "string",
|
Type: "string",
|
||||||
Access: model.PRIVATE,
|
Access: model.PRIVATE,
|
||||||
@ -219,6 +228,14 @@ func InitSettings() {
|
|||||||
Access: model.PUBLIC,
|
Access: model.PUBLIC,
|
||||||
Group: model.FRONT,
|
Group: model.FRONT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Key: "ocr api",
|
||||||
|
Value: "https://api.xhofe.top/ocr/file/json",
|
||||||
|
Description: "Used to identify verification codes",
|
||||||
|
Type: "string",
|
||||||
|
Access: model.PRIVATE,
|
||||||
|
Group: model.BACK,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for i, _ := range settings {
|
for i, _ := range settings {
|
||||||
v := settings[i]
|
v := settings[i]
|
||||||
@ -227,6 +244,9 @@ func InitSettings() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if err == gorm.ErrRecordNotFound {
|
if err == gorm.ErrRecordNotFound {
|
||||||
err = model.SaveSetting(v)
|
err = model.SaveSetting(v)
|
||||||
|
if v.Key == "password" {
|
||||||
|
log.Infof("Initial password: %s", v.Value)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed write setting: %s", err.Error())
|
log.Fatalf("failed write setting: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
71
build.sh
71
build.sh
@ -25,6 +25,7 @@ BUILD_DOCKER() {
|
|||||||
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
|
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
|
||||||
gitCommit=$(git log --pretty=format:"%h" -1)
|
gitCommit=$(git log --pretty=format:"%h" -1)
|
||||||
gitTag=$(git describe --long --tags --dirty --always)
|
gitTag=$(git describe --long --tags --dirty --always)
|
||||||
|
webTag=$(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')
|
||||||
ldflags="\
|
ldflags="\
|
||||||
-w -s \
|
-w -s \
|
||||||
-X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \
|
-X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \
|
||||||
@ -32,6 +33,7 @@ BUILD_DOCKER() {
|
|||||||
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
|
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
|
||||||
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
|
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
|
||||||
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
|
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \
|
||||||
"
|
"
|
||||||
go build -o ./bin/alist -ldflags="$ldflags" -tags=jsoniter alist.go
|
go build -o ./bin/alist -ldflags="$ldflags" -tags=jsoniter alist.go
|
||||||
}
|
}
|
||||||
@ -44,6 +46,7 @@ BUILD() {
|
|||||||
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
|
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
|
||||||
gitCommit=$(git log --pretty=format:"%h" -1)
|
gitCommit=$(git log --pretty=format:"%h" -1)
|
||||||
gitTag=$(git describe --long --tags --dirty --always)
|
gitTag=$(git describe --long --tags --dirty --always)
|
||||||
|
webTag=$(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')
|
||||||
echo "build version: $gitTag"
|
echo "build version: $gitTag"
|
||||||
ldflags="\
|
ldflags="\
|
||||||
-w -s \
|
-w -s \
|
||||||
@ -52,24 +55,73 @@ BUILD() {
|
|||||||
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
|
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
|
||||||
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
|
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
|
||||||
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
|
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \
|
||||||
"
|
"
|
||||||
|
|
||||||
if [ "$1" == "release" ]; then
|
if [ "$1" == "release" ]; then
|
||||||
xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
|
xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
|
||||||
else
|
else
|
||||||
xgo -targets=linux/amd64,windows/amd64 -out alist -ldflags="$ldflags" -tags=jsoniter .
|
xgo -targets=linux/amd64,windows/amd64,darwin/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
|
||||||
fi
|
fi
|
||||||
mkdir "build"
|
mkdir -p "build"
|
||||||
mv alist-* build
|
mv alist-* build
|
||||||
cd build
|
if [ "$1" != "release" ]; then
|
||||||
upx -9 ./*
|
cd build
|
||||||
find . -type f -print0 | xargs -0 md5sum >md5.txt
|
upx -9 ./alist-linux*
|
||||||
cat md5.txt
|
upx -9 ./alist-windows*
|
||||||
cd ../..
|
find . -type f -print0 | xargs -0 md5sum >md5.txt
|
||||||
|
cat md5.txt
|
||||||
|
cd ..
|
||||||
|
fi
|
||||||
|
cd ..
|
||||||
|
}
|
||||||
|
|
||||||
|
BUILD_MUSL() {
|
||||||
|
BASE="https://musl.cc/"
|
||||||
|
FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross arm-linux-musleabihf-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)
|
||||||
|
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
|
||||||
|
done
|
||||||
|
cd alist
|
||||||
|
appName="alist"
|
||||||
|
builtAt="$(date +'%F %T %z')"
|
||||||
|
goVersion=$(go version | sed 's/go version //')
|
||||||
|
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
|
||||||
|
gitCommit=$(git log --pretty=format:"%h" -1)
|
||||||
|
gitTag=$(git describe --long --tags --dirty --always)
|
||||||
|
webTag=$(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')
|
||||||
|
ldflags="\
|
||||||
|
-w -s \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.GoVersion=$goVersion' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
|
||||||
|
-X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \
|
||||||
|
"
|
||||||
|
OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-arm 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 arm-linux-musleabihf-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)
|
||||||
|
for i in "${!OS_ARCHES[@]}"; do
|
||||||
|
os_arch=${OS_ARCHES[$i]}
|
||||||
|
cgo_cc=${CGO_ARGS[$i]}
|
||||||
|
echo building for ${os_arch}
|
||||||
|
export GOOS=${os_arch%%-*}
|
||||||
|
export GOARCH=${os_arch##*-}
|
||||||
|
export CC=${cgo_cc}
|
||||||
|
export CGO_ENABLED=1
|
||||||
|
go build -o ./build/$appName-$os_arch -ldflags="$ldflags" -tags=jsoniter alist.go
|
||||||
|
done
|
||||||
|
cd ..
|
||||||
}
|
}
|
||||||
|
|
||||||
RELEASE() {
|
RELEASE() {
|
||||||
cd alist/build
|
cd alist/build
|
||||||
|
upx -9 ./alist-linux*
|
||||||
|
upx -9 ./alist-windows*
|
||||||
|
find . -type f -print0 | xargs -0 md5sum >md5.txt
|
||||||
|
cat md5.txt
|
||||||
mkdir compress
|
mkdir compress
|
||||||
mv md5.txt compress
|
mv md5.txt compress
|
||||||
for i in $(find . -type f -name "$appName-linux-*"); do
|
for i in $(find . -type f -name "$appName-linux-*"); do
|
||||||
@ -94,7 +146,8 @@ elif [ "$1" = "build" ]; then
|
|||||||
BUILD build
|
BUILD build
|
||||||
elif [ "$1" = "release" ]; then
|
elif [ "$1" = "release" ]; then
|
||||||
BUILD release
|
BUILD release
|
||||||
|
BUILD_MUSL
|
||||||
RELEASE
|
RELEASE
|
||||||
else
|
else
|
||||||
echo -e "${RED_COLOR} 错误的命令${RES}"
|
echo -e "${RED_COLOR} Parameter error ${RES}"
|
||||||
fi
|
fi
|
@ -9,6 +9,7 @@ type Database struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
TablePrefix string `json:"table_prefix"`
|
TablePrefix string `json:"table_prefix"`
|
||||||
DBFile string `json:"db_file"`
|
DBFile string `json:"db_file"`
|
||||||
|
SslMode string `json:"ssl_mode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Scheme struct {
|
type Scheme struct {
|
||||||
@ -29,18 +30,21 @@ type Config struct {
|
|||||||
Database Database `json:"database"`
|
Database Database `json:"database"`
|
||||||
Scheme Scheme `json:"scheme"`
|
Scheme Scheme `json:"scheme"`
|
||||||
Cache CacheConfig `json:"cache"`
|
Cache CacheConfig `json:"cache"`
|
||||||
|
TempDir string `json:"temp_dir"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func DefaultConfig() *Config {
|
func DefaultConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
Address: "0.0.0.0",
|
Address: "0.0.0.0",
|
||||||
Port: 5244,
|
Port: 5244,
|
||||||
Assets: "zhimg",
|
Assets: "https://npm.elemecdn.com/alist-web@$version/dist",
|
||||||
|
TempDir: "data/temp",
|
||||||
Database: Database{
|
Database: Database{
|
||||||
Type: "sqlite3",
|
Type: "sqlite3",
|
||||||
Port: 0,
|
Port: 0,
|
||||||
TablePrefix: "x_",
|
TablePrefix: "x_",
|
||||||
DBFile: "data/data.db",
|
DBFile: "data/data.db",
|
||||||
|
SslMode: "disable",
|
||||||
},
|
},
|
||||||
Cache: CacheConfig{
|
Cache: CacheConfig{
|
||||||
Expiration: 60,
|
Expiration: 60,
|
||||||
|
@ -14,6 +14,7 @@ var (
|
|||||||
GitAuthor string
|
GitAuthor string
|
||||||
GitCommit string
|
GitCommit string
|
||||||
GitTag string = "dev"
|
GitTag string = "dev"
|
||||||
|
WebTag string
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -33,13 +34,14 @@ var (
|
|||||||
TextTypes = []string{"txt", "htm", "html", "xml", "java", "properties", "sql",
|
TextTypes = []string{"txt", "htm", "html", "xml", "java", "properties", "sql",
|
||||||
"js", "md", "json", "conf", "ini", "vue", "php", "py", "bat", "gitignore", "yml",
|
"js", "md", "json", "conf", "ini", "vue", "php", "py", "bat", "gitignore", "yml",
|
||||||
"go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"}
|
"go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"}
|
||||||
|
DProxyTypes = []string{"m3u8"}
|
||||||
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
|
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
|
||||||
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv"}
|
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv"}
|
||||||
AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav"}
|
AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav"}
|
||||||
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico"}
|
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico", "swf", "webp"}
|
||||||
)
|
)
|
||||||
|
|
||||||
var settingsMap = make(map[string]string, 0)
|
var settingsMap = make(map[string]string)
|
||||||
|
|
||||||
func Set(key string, value string) {
|
func Set(key string, value string) {
|
||||||
settingsMap[key] = value
|
settingsMap[key] = value
|
||||||
@ -78,6 +80,7 @@ var (
|
|||||||
"check parent folder", "check down link", "WebDAV username", "WebDAV password",
|
"check parent folder", "check down link", "WebDAV username", "WebDAV password",
|
||||||
"Visitor WebDAV username", "Visitor WebDAV password",
|
"Visitor WebDAV username", "Visitor WebDAV password",
|
||||||
"default page size", "load type",
|
"default page size", "load type",
|
||||||
|
"ocr api", "favicon",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package _23
|
package _23
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
@ -10,9 +12,11 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
@ -53,6 +57,7 @@ func (driver Pan123) Items() []base.Item {
|
|||||||
Type: base.TypeSelect,
|
Type: base.TypeSelect,
|
||||||
Values: "name,fileId,updateAt,createAt",
|
Values: "name,fileId,updateAt,createAt",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
Default: "name",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
@ -60,11 +65,15 @@ func (driver Pan123) Items() []base.Item {
|
|||||||
Type: base.TypeSelect,
|
Type: base.TypeSelect,
|
||||||
Values: "asc,desc",
|
Values: "asc,desc",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
Default: "asc",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Save(account *model.Account, old *model.Account) error {
|
func (driver Pan123) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if account.RootFolder == "" {
|
if account.RootFolder == "" {
|
||||||
account.RootFolder = "0"
|
account.RootFolder = "0"
|
||||||
}
|
}
|
||||||
@ -189,9 +198,9 @@ func (driver Pan123) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Pan123) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Pan123) Proxy(r *http.Request, account *model.Account) {
|
||||||
c.Request.Header.Del("origin")
|
// r.Header.Del("origin")
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
@ -234,7 +243,7 @@ func (driver Pan123) Move(src string, dst string, account *model.Account) error
|
|||||||
}
|
}
|
||||||
parentFileId, _ := strconv.Atoi(dstDirFile.Id)
|
parentFileId, _ := strconv.Atoi(dstDirFile.Id)
|
||||||
data := base.Json{
|
data := base.Json{
|
||||||
"fileId": fileId,
|
"fileIdList": []base.Json{{"FileId": fileId}},
|
||||||
"parentFileId": parentFileId,
|
"parentFileId": parentFileId,
|
||||||
}
|
}
|
||||||
_, err = driver.Request("https://www.123pan.com/api/file/mod_pid",
|
_, err = driver.Request("https://www.123pan.com/api/file/mod_pid",
|
||||||
@ -291,10 +300,37 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
|
|||||||
return base.ErrNotFolder
|
return base.ErrNotFolder
|
||||||
}
|
}
|
||||||
parentFileId, _ := strconv.Atoi(parentFile.Id)
|
parentFileId, _ := strconv.Atoi(parentFile.Id)
|
||||||
|
tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = tempFile.Close()
|
||||||
|
_ = os.Remove(tempFile.Name())
|
||||||
|
}()
|
||||||
|
_, err = io.Copy(tempFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h := md5.New()
|
||||||
|
_, err = io.Copy(h, tempFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
etag := hex.EncodeToString(h.Sum(nil))
|
||||||
|
log.Debugln("md5:", etag)
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
data := base.Json{
|
data := base.Json{
|
||||||
"driveId": 0,
|
"driveId": 0,
|
||||||
"duplicate": true,
|
"duplicate": true,
|
||||||
"etag": "836aae6cac845e17fce51919594737d0", //maybe file's md5
|
"etag": etag,
|
||||||
"fileName": file.GetFileName(),
|
"fileName": file.GetFileName(),
|
||||||
"parentFileId": parentFileId,
|
"parentFileId": parentFileId,
|
||||||
"size": file.GetSize(),
|
"size": file.GetSize(),
|
||||||
@ -307,6 +343,9 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if resp.Data.Key == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
Credentials: credentials.NewStaticCredentials(resp.Data.AccessKeyId, resp.Data.SecretAccessKey, resp.Data.SessionToken),
|
Credentials: credentials.NewStaticCredentials(resp.Data.AccessKeyId, resp.Data.SecretAccessKey, resp.Data.SessionToken),
|
||||||
Region: aws.String("123pan"),
|
Region: aws.String("123pan"),
|
||||||
@ -321,7 +360,7 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
|
|||||||
input := &s3manager.UploadInput{
|
input := &s3manager.UploadInput{
|
||||||
Bucket: &resp.Data.Bucket,
|
Bucket: &resp.Data.Bucket,
|
||||||
Key: &resp.Data.Key,
|
Key: &resp.Data.Key,
|
||||||
Body: file,
|
Body: tempFile,
|
||||||
}
|
}
|
||||||
_, err = uploader.Upload(input)
|
_, err = uploader.Upload(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,7 +17,7 @@ import (
|
|||||||
func (driver Cloud139) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
func (driver Cloud139) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
url := "https://yun.139.com" + pathname
|
url := "https://yun.139.com" + pathname
|
||||||
req := base.RestyClient.R()
|
req := base.RestyClient.R()
|
||||||
randStr := randomStr(16)
|
randStr := utils.RandomStr(16)
|
||||||
ts := time.Now().Format("2006-01-02 15:04:05")
|
ts := time.Now().Format("2006-01-02 15:04:05")
|
||||||
log.Debugf("%+v", data)
|
log.Debugf("%+v", data)
|
||||||
body, err := utils.Json.Marshal(data)
|
body, err := utils.Json.Marshal(data)
|
||||||
@ -136,7 +136,7 @@ func (driver Cloud139) GetFiles(catalogID string, account *model.Account) ([]mod
|
|||||||
f := model.File{
|
f := model.File{
|
||||||
Id: content.ContentID,
|
Id: content.ContentID,
|
||||||
Name: content.ContentName,
|
Name: content.ContentName,
|
||||||
Size: int64(content.ContentSize),
|
Size: content.ContentSize,
|
||||||
Type: utils.GetFileType(path.Ext(content.ContentName)),
|
Type: utils.GetFileType(path.Ext(content.ContentName)),
|
||||||
Driver: driver.Config().Name,
|
Driver: driver.Config().Name,
|
||||||
UpdatedAt: getTime(content.UpdateTime),
|
UpdatedAt: getTime(content.UpdateTime),
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
@ -64,6 +63,9 @@ func (driver Cloud139) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud139) Save(account *model.Account, old *model.Account) error {
|
func (driver Cloud139) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err := driver.Request("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Post, nil, nil, nil, base.Json{
|
_, err := driver.Request("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Post, nil, nil, nil, base.Json{
|
||||||
"qryUserExternInfoReq": base.Json{
|
"qryUserExternInfoReq": base.Json{
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
@ -160,9 +162,9 @@ func (driver Cloud139) Path(path string, account *model.Account) (*model.File, [
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud139) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Cloud139) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Cloud139) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Cloud139) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
@ -229,7 +231,7 @@ func (driver Cloud139) Move(src string, dst string, account *model.Account) erro
|
|||||||
"newCatalogID": dstParentFile.Id,
|
"newCatalogID": dstParentFile.Id,
|
||||||
},
|
},
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -254,7 +256,7 @@ func (driver Cloud139) Rename(src string, dst string, account *model.Account) er
|
|||||||
"catalogID": srcFile.Id,
|
"catalogID": srcFile.Id,
|
||||||
"catalogName": utils.Base(dst),
|
"catalogName": utils.Base(dst),
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -264,7 +266,7 @@ func (driver Cloud139) Rename(src string, dst string, account *model.Account) er
|
|||||||
"contentID": srcFile.Id,
|
"contentID": srcFile.Id,
|
||||||
"contentName": utils.Base(dst),
|
"contentName": utils.Base(dst),
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -303,7 +305,7 @@ func (driver Cloud139) Copy(src string, dst string, account *model.Account) erro
|
|||||||
"newCatalogID": dstParentFile.Id,
|
"newCatalogID": dstParentFile.Id,
|
||||||
},
|
},
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -332,10 +334,10 @@ func (driver Cloud139) Delete(path string, account *model.Account) error {
|
|||||||
"taskInfo": base.Json{
|
"taskInfo": base.Json{
|
||||||
"newCatalogID": "",
|
"newCatalogID": "",
|
||||||
"contentInfoList": contentInfoList,
|
"contentInfoList": contentInfoList,
|
||||||
"catalogInfoList": contentInfoList,
|
"catalogInfoList": catalogInfoList,
|
||||||
},
|
},
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -346,7 +348,7 @@ func (driver Cloud139) Delete(path string, account *model.Account) error {
|
|||||||
"catalogList": catalogInfoList,
|
"catalogList": catalogInfoList,
|
||||||
"contentList": contentInfoList,
|
"contentList": contentInfoList,
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
"sourceCatalogType": 1002,
|
"sourceCatalogType": 1002,
|
||||||
@ -382,7 +384,7 @@ func (driver Cloud139) Upload(file *model.FileStream, account *model.Account) er
|
|||||||
"parentCatalogID": parentFile.Id,
|
"parentCatalogID": parentFile.Id,
|
||||||
"newCatalogName": "",
|
"newCatalogName": "",
|
||||||
"commonAccountInfo": base.Json{
|
"commonAccountInfo": base.Json{
|
||||||
"account": "18627147660",
|
"account": account.Username,
|
||||||
"accountType": 1,
|
"accountType": 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ func (driver Cloud139) familyGetFiles(catalogID string, account *model.Account)
|
|||||||
f := model.File{
|
f := model.File{
|
||||||
Id: content.ContentID,
|
Id: content.ContentID,
|
||||||
Name: content.ContentName,
|
Name: content.ContentName,
|
||||||
Size: int64(content.ContentSize),
|
Size: content.ContentSize,
|
||||||
Type: utils.GetFileType(path.Ext(content.ContentName)),
|
Type: utils.GetFileType(path.Ext(content.ContentName)),
|
||||||
Driver: driver.Config().Name,
|
Driver: driver.Config().Name,
|
||||||
UpdatedAt: getTime(content.LastUpdateTime),
|
UpdatedAt: getTime(content.LastUpdateTime),
|
||||||
|
@ -40,7 +40,7 @@ type Content struct {
|
|||||||
ContentID string `json:"contentID"`
|
ContentID string `json:"contentID"`
|
||||||
ContentName string `json:"contentName"`
|
ContentName string `json:"contentName"`
|
||||||
//ContentSuffix string `json:"contentSuffix"`
|
//ContentSuffix string `json:"contentSuffix"`
|
||||||
ContentSize int `json:"contentSize"`
|
ContentSize int64 `json:"contentSize"`
|
||||||
//ContentDesc string `json:"contentDesc"`
|
//ContentDesc string `json:"contentDesc"`
|
||||||
//ContentType int `json:"contentType"`
|
//ContentType int `json:"contentType"`
|
||||||
//ContentOrigin int `json:"contentOrigin"`
|
//ContentOrigin int `json:"contentOrigin"`
|
||||||
@ -132,43 +132,43 @@ type UploadResp struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CloudContent struct {
|
type CloudContent struct {
|
||||||
ContentID string `json:"contentID"`
|
ContentID string `json:"contentID"`
|
||||||
Modifier string `json:"modifier"`
|
//Modifier string `json:"modifier"`
|
||||||
Nickname string `json:"nickname"`
|
//Nickname string `json:"nickname"`
|
||||||
CloudNickName string `json:"cloudNickName"`
|
//CloudNickName string `json:"cloudNickName"`
|
||||||
ContentName string `json:"contentName"`
|
ContentName string `json:"contentName"`
|
||||||
ContentType int `json:"contentType"`
|
//ContentType int `json:"contentType"`
|
||||||
ContentSuffix string `json:"contentSuffix"`
|
//ContentSuffix string `json:"contentSuffix"`
|
||||||
ContentSize int `json:"contentSize"`
|
ContentSize int64 `json:"contentSize"`
|
||||||
ContentDesc string `json:"contentDesc"`
|
//ContentDesc string `json:"contentDesc"`
|
||||||
CreateTime string `json:"createTime"`
|
//CreateTime string `json:"createTime"`
|
||||||
Shottime interface{} `json:"shottime"`
|
//Shottime interface{} `json:"shottime"`
|
||||||
LastUpdateTime string `json:"lastUpdateTime"`
|
LastUpdateTime string `json:"lastUpdateTime"`
|
||||||
ThumbnailURL string `json:"thumbnailURL"`
|
ThumbnailURL string `json:"thumbnailURL"`
|
||||||
MidthumbnailURL string `json:"midthumbnailURL"`
|
//MidthumbnailURL string `json:"midthumbnailURL"`
|
||||||
BigthumbnailURL string `json:"bigthumbnailURL"`
|
//BigthumbnailURL string `json:"bigthumbnailURL"`
|
||||||
PresentURL string `json:"presentURL"`
|
//PresentURL string `json:"presentURL"`
|
||||||
PresentLURL string `json:"presentLURL"`
|
//PresentLURL string `json:"presentLURL"`
|
||||||
PresentHURL string `json:"presentHURL"`
|
//PresentHURL string `json:"presentHURL"`
|
||||||
ParentCatalogID string `json:"parentCatalogID"`
|
//ParentCatalogID string `json:"parentCatalogID"`
|
||||||
Uploader string `json:"uploader"`
|
//Uploader string `json:"uploader"`
|
||||||
UploaderNickName string `json:"uploaderNickName"`
|
//UploaderNickName string `json:"uploaderNickName"`
|
||||||
TreeInfo interface{} `json:"treeInfo"`
|
//TreeInfo interface{} `json:"treeInfo"`
|
||||||
UpdateTime interface{} `json:"updateTime"`
|
//UpdateTime interface{} `json:"updateTime"`
|
||||||
ExtInfo struct {
|
//ExtInfo struct {
|
||||||
Uploader string `json:"uploader"`
|
// Uploader string `json:"uploader"`
|
||||||
} `json:"extInfo"`
|
//} `json:"extInfo"`
|
||||||
EtagOprType interface{} `json:"etagOprType"`
|
//EtagOprType interface{} `json:"etagOprType"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CloudCatalog struct {
|
type CloudCatalog struct {
|
||||||
CatalogID string `json:"catalogID"`
|
CatalogID string `json:"catalogID"`
|
||||||
CatalogName string `json:"catalogName"`
|
CatalogName string `json:"catalogName"`
|
||||||
CloudID string `json:"cloudID"`
|
//CloudID string `json:"cloudID"`
|
||||||
CreateTime string `json:"createTime"`
|
//CreateTime string `json:"createTime"`
|
||||||
LastUpdateTime string `json:"lastUpdateTime"`
|
LastUpdateTime string `json:"lastUpdateTime"`
|
||||||
Creator string `json:"creator"`
|
//Creator string `json:"creator"`
|
||||||
CreatorNickname string `json:"creatorNickname"`
|
//CreatorNickname string `json:"creatorNickname"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryContentListResp struct {
|
type QueryContentListResp struct {
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"math/rand"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -13,16 +12,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func randomStr(n int) string {
|
|
||||||
builder := strings.Builder{}
|
|
||||||
t := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
r := rand.Intn(len(t))
|
|
||||||
builder.WriteString(t[r : r+1])
|
|
||||||
}
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func encodeURIComponent(str string) string {
|
func encodeURIComponent(str string) string {
|
||||||
r := url.QueryEscape(str)
|
r := url.QueryEscape(str)
|
||||||
r = strings.Replace(r, "+", "%20", -1)
|
r = strings.Replace(r, "+", "%20", -1)
|
||||||
|
@ -2,16 +2,9 @@ package _89
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
|
||||||
"crypto/hmac"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/pem"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
@ -19,14 +12,11 @@ import (
|
|||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/google/uuid"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
mathRand "math/rand"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -35,6 +25,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var client189Map map[string]*resty.Client
|
var client189Map map[string]*resty.Client
|
||||||
|
var infoMap = make(map[string]Rsa)
|
||||||
|
|
||||||
|
func (driver Cloud189) getClient(account *model.Account) (*resty.Client, error) {
|
||||||
|
client, ok := client189Map[account.Name]
|
||||||
|
if ok {
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
err := driver.Login(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
client, ok = client189Map[account.Name]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("can't find [%s] client", account.Name)
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
|
func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
|
||||||
f := &model.File{
|
f := &model.File{
|
||||||
@ -100,6 +107,7 @@ func (driver Cloud189) Login(account *model.Account) error {
|
|||||||
//cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
//cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
|
||||||
client = resty.New()
|
client = resty.New()
|
||||||
//client.SetCookieJar(cookieJar)
|
//client.SetCookieJar(cookieJar)
|
||||||
|
client.SetTimeout(base.DefaultTimeout)
|
||||||
client.SetRetryCount(3)
|
client.SetRetryCount(3)
|
||||||
client.SetHeader("Referer", "https://cloud.189.cn/")
|
client.SetHeader("Referer", "https://cloud.189.cn/")
|
||||||
}
|
}
|
||||||
@ -137,9 +145,33 @@ func (driver Cloud189) Login(account *model.Account) error {
|
|||||||
vCodeRS := ""
|
vCodeRS := ""
|
||||||
if vCodeID != "" {
|
if vCodeID != "" {
|
||||||
// need ValidateCode
|
// need ValidateCode
|
||||||
|
log.Debugf("try to identify verification codes")
|
||||||
|
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
|
||||||
|
u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp
|
||||||
|
imgRes, err := client.R().SetHeaders(map[string]string{
|
||||||
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0",
|
||||||
|
"Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
|
||||||
|
"Sec-Fetch-Dest": "image",
|
||||||
|
"Sec-Fetch-Mode": "no-cors",
|
||||||
|
"Sec-Fetch-Site": "same-origin",
|
||||||
|
}).Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vRes, err := client.R().SetMultipartField(
|
||||||
|
"image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
|
||||||
|
Post(conf.GetStr("ocr api"))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||||
|
return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
|
||||||
|
}
|
||||||
|
vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
|
||||||
|
log.Debugln("code: ", vCodeRS)
|
||||||
}
|
}
|
||||||
userRsa := RsaEncode([]byte(account.Username), jRsakey)
|
userRsa := RsaEncode([]byte(account.Username), jRsakey, true)
|
||||||
passwordRsa := RsaEncode([]byte(account.Password), jRsakey)
|
passwordRsa := RsaEncode([]byte(account.Password), jRsakey, true)
|
||||||
url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
|
url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
|
||||||
var loginResp LoginResp
|
var loginResp LoginResp
|
||||||
res, err := client.R().
|
res, err := client.R().
|
||||||
@ -183,39 +215,6 @@ func (driver Cloud189) Login(account *model.Account) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cloud189Error struct {
|
|
||||||
ErrorCode string `json:"errorCode"`
|
|
||||||
ErrorMsg string `json:"errorMsg"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cloud189File struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
LastOpTime string `json:"lastOpTime"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Size int64 `json:"size"`
|
|
||||||
Icon struct {
|
|
||||||
SmallUrl string `json:"smallUrl"`
|
|
||||||
//LargeUrl string `json:"largeUrl"`
|
|
||||||
} `json:"icon"`
|
|
||||||
Url string `json:"url"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cloud189Folder struct {
|
|
||||||
Id int64 `json:"id"`
|
|
||||||
LastOpTime string `json:"lastOpTime"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Cloud189Files struct {
|
|
||||||
ResCode int `json:"res_code"`
|
|
||||||
ResMessage string `json:"res_message"`
|
|
||||||
FileListAO struct {
|
|
||||||
Count int `json:"count"`
|
|
||||||
FileList []Cloud189File `json:"fileList"`
|
|
||||||
FolderList []Cloud189Folder `json:"folderList"`
|
|
||||||
} `json:"fileListAO"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver Cloud189) isFamily(account *model.Account) bool {
|
func (driver Cloud189) isFamily(account *model.Account) bool {
|
||||||
return account.InternalType == "Family"
|
return account.InternalType == "Family"
|
||||||
}
|
}
|
||||||
@ -233,8 +232,8 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
|
|||||||
"mediaType": "0",
|
"mediaType": "0",
|
||||||
"folderId": fileId,
|
"folderId": fileId,
|
||||||
"iconOption": "5",
|
"iconOption": "5",
|
||||||
"orderBy": account.OrderBy,
|
"orderBy": "lastOpTime", //account.OrderBy
|
||||||
"descending": account.OrderDirection,
|
"descending": "true", //account.OrderDirection
|
||||||
}, nil, nil, account)
|
}, nil, nil, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -264,9 +263,9 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Request(url string, method int, query, form map[string]string, headers map[string]string, account *model.Account) ([]byte, error) {
|
func (driver Cloud189) Request(url string, method int, query, form map[string]string, headers map[string]string, account *model.Account) ([]byte, error) {
|
||||||
client, ok := client189Map[account.Name]
|
client, err := driver.getClient(account)
|
||||||
if !ok {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't find [%s] client", account.Name)
|
return nil, err
|
||||||
}
|
}
|
||||||
//var resp base.Json
|
//var resp base.Json
|
||||||
if driver.isFamily(account) {
|
if driver.isFamily(account) {
|
||||||
@ -293,7 +292,6 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
|
|||||||
if headers != nil {
|
if headers != nil {
|
||||||
req = req.SetHeaders(headers)
|
req = req.SetHeaders(headers)
|
||||||
}
|
}
|
||||||
var err error
|
|
||||||
var res *resty.Response
|
var res *resty.Response
|
||||||
switch method {
|
switch method {
|
||||||
case base.Get:
|
case base.Get:
|
||||||
@ -306,7 +304,7 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debug(res.String())
|
//log.Debug(res.String())
|
||||||
if e.ErrorCode != "" {
|
if e.ErrorCode != "" {
|
||||||
if e.ErrorCode == "InvalidSessionKey" {
|
if e.ErrorCode == "InvalidSessionKey" {
|
||||||
err = driver.Login(account)
|
err = driver.Login(account)
|
||||||
@ -323,61 +321,198 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) GetSessionKey(account *model.Account) (string, error) {
|
func (driver Cloud189) GetSessionKey(account *model.Account) (string, error) {
|
||||||
|
//info, ok := infoMap[account.Name]
|
||||||
|
//if !ok {
|
||||||
|
// info = Info{}
|
||||||
|
// infoMap[account.Name] = info
|
||||||
|
//} else {
|
||||||
|
// log.Debugf("hit")
|
||||||
|
//}
|
||||||
|
//if info.SessionKey != "" {
|
||||||
|
// return info.SessionKey, nil
|
||||||
|
//}
|
||||||
resp, err := driver.Request("https://cloud.189.cn/v2/getUserBriefInfo.action", base.Get, nil, nil, nil, account)
|
resp, err := driver.Request("https://cloud.189.cn/v2/getUserBriefInfo.action", base.Get, nil, nil, nil, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return jsoniter.Get(resp, "sessionKey").ToString(), nil
|
sessionKey := jsoniter.Get(resp, "sessionKey").ToString()
|
||||||
|
//info.SessionKey = sessionKey
|
||||||
|
return sessionKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) GetResKey(account *model.Account) (string, string, error) {
|
func (driver Cloud189) GetResKey(account *model.Account) (string, string, error) {
|
||||||
|
rsa, ok := infoMap[account.Name]
|
||||||
|
if !ok {
|
||||||
|
rsa = Rsa{}
|
||||||
|
infoMap[account.Name] = rsa
|
||||||
|
}
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
if rsa.Expire > now {
|
||||||
|
return rsa.PubKey, rsa.PkId, nil
|
||||||
|
}
|
||||||
resp, err := driver.Request("https://cloud.189.cn/api/security/generateRsaKey.action", base.Get, nil, nil, nil, account)
|
resp, err := driver.Request("https://cloud.189.cn/api/security/generateRsaKey.action", base.Get, nil, nil, nil, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString(), nil
|
pubKey, pkId := jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString()
|
||||||
|
rsa.PubKey, rsa.PkId = pubKey, pkId
|
||||||
|
rsa.Expire = jsoniter.Get(resp, "expire").ToInt64()
|
||||||
|
return pubKey, pkId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) UploadRequest(url string, form map[string]string, account *model.Account) ([]byte, error) {
|
//func (driver Cloud189) UploadRequest1(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
|
||||||
sessionKey, err := driver.GetSessionKey(account)
|
// //sessionKey, err := driver.GetSessionKey(account)
|
||||||
if err != nil {
|
// //if err != nil {
|
||||||
return nil, err
|
// // return nil, err
|
||||||
}
|
// //}
|
||||||
|
// sessionKey := account.DriveId
|
||||||
|
// pubKey, pkId, err := driver.GetResKey(account)
|
||||||
|
// log.Debugln(sessionKey, pubKey, pkId)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// xRId := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
|
||||||
|
// pkey := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")[0 : 16+int(16*mathRand.Float32())]
|
||||||
|
// params := hex.EncodeToString(AesEncrypt([]byte(qs(form)), []byte(pkey[0:16])))
|
||||||
|
// date := strconv.FormatInt(time.Now().Unix(), 10)
|
||||||
|
// a := make(url.Values)
|
||||||
|
// a.Set("SessionKey", sessionKey)
|
||||||
|
// a.Set("Operate", http.MethodGet)
|
||||||
|
// a.Set("RequestURI", uri)
|
||||||
|
// a.Set("Date", date)
|
||||||
|
// a.Set("params", params)
|
||||||
|
// signature := hex.EncodeToString(SHA1(EncodeParam(a), pkey))
|
||||||
|
// encryptionText := RsaEncode([]byte(pkey), pubKey, false)
|
||||||
|
// headers := map[string]string{
|
||||||
|
// "signature": signature,
|
||||||
|
// "sessionKey": sessionKey,
|
||||||
|
// "encryptionText": encryptionText,
|
||||||
|
// "pkId": pkId,
|
||||||
|
// "x-request-id": xRId,
|
||||||
|
// "x-request-date": date,
|
||||||
|
// }
|
||||||
|
// req := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params)
|
||||||
|
// if resp != nil {
|
||||||
|
// req.SetResult(resp)
|
||||||
|
// }
|
||||||
|
// res, err := req.Get("https://upload.cloud.189.cn" + uri)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// //log.Debug(res.String())
|
||||||
|
// data := res.Body()
|
||||||
|
// if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
|
||||||
|
// return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
|
||||||
|
// }
|
||||||
|
// return data, nil
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func (driver Cloud189) UploadRequest2(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
|
||||||
|
// c := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||||
|
// r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
|
||||||
|
// l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
|
||||||
|
// l = l[0 : 16+int(16*mathRand.Float32())]
|
||||||
|
//
|
||||||
|
// e := qs(form)
|
||||||
|
// data := AesEncrypt([]byte(e), []byte(l[0:16]))
|
||||||
|
// h := hex.EncodeToString(data)
|
||||||
|
//
|
||||||
|
// sessionKey := account.DriveId
|
||||||
|
// a := make(url.Values)
|
||||||
|
// a.Set("SessionKey", sessionKey)
|
||||||
|
// a.Set("Operate", http.MethodGet)
|
||||||
|
// a.Set("RequestURI", uri)
|
||||||
|
// a.Set("Date", c)
|
||||||
|
// a.Set("params", h)
|
||||||
|
// g := SHA1(EncodeParam(a), l)
|
||||||
|
//
|
||||||
|
// pubKey, pkId, err := driver.GetResKey(account)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// b := RsaEncode([]byte(l), pubKey, false)
|
||||||
|
// client, err := driver.getClient(account)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// req := client.R()
|
||||||
|
// req.Header.Set("accept", "application/json;charset=UTF-8")
|
||||||
|
// req.Header.Set("SessionKey", sessionKey)
|
||||||
|
// req.Header.Set("Signature", hex.EncodeToString(g))
|
||||||
|
// req.Header.Set("X-Request-Date", c)
|
||||||
|
// req.Header.Set("X-Request-ID", r)
|
||||||
|
// req.Header.Set("EncryptionText", b)
|
||||||
|
// req.Header.Set("PkId", pkId)
|
||||||
|
// if resp != nil {
|
||||||
|
// req.SetResult(resp)
|
||||||
|
// }
|
||||||
|
// res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
// }
|
||||||
|
// //log.Debug(res.String())
|
||||||
|
// data = res.Body()
|
||||||
|
// if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
|
||||||
|
// return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
|
||||||
|
// }
|
||||||
|
// return data, nil
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (driver Cloud189) UploadRequest(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
|
||||||
|
c := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||||
|
r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
|
||||||
|
l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
|
||||||
|
l = l[0 : 16+int(16*utils.Rand.Float32())]
|
||||||
|
|
||||||
|
e := qs(form)
|
||||||
|
data := AesEncrypt([]byte(e), []byte(l[0:16]))
|
||||||
|
h := hex.EncodeToString(data)
|
||||||
|
|
||||||
|
sessionKey := account.DriveId
|
||||||
|
signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s¶ms=%s", sessionKey, uri, c, h), l)
|
||||||
|
|
||||||
pubKey, pkId, err := driver.GetResKey(account)
|
pubKey, pkId, err := driver.GetResKey(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
xRId := uuid.New().String()
|
b := RsaEncode([]byte(l), pubKey, false)
|
||||||
pkey := strings.ReplaceAll(xRId, "-", "")[:mathRand.Intn(16)+16]
|
client, err := driver.getClient(account)
|
||||||
params := aesEncrypt(qs(form), pkey[:16])
|
|
||||||
date := strconv.FormatInt(time.Now().Unix(), 10)
|
|
||||||
signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s¶ms=%s", sessionKey, url, date, params), pkey)
|
|
||||||
encryptionText := RsaEncode([]byte(pkey), pubKey)
|
|
||||||
headers := map[string]string{
|
|
||||||
"signature": signature,
|
|
||||||
"sessionKey": sessionKey,
|
|
||||||
"encryptionText": encryptionText,
|
|
||||||
"pkId": pkId,
|
|
||||||
"x-request-id": xRId,
|
|
||||||
"x-request-date": date,
|
|
||||||
"origin": "https://cloud.189.cn",
|
|
||||||
"referer": "https://cloud.189.cn/",
|
|
||||||
}
|
|
||||||
log.Debugf("%+v\n%s", headers, params)
|
|
||||||
res, err := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params).Get("https://upload.cloud.189.cn" + url)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debug(res.String())
|
req := client.R()
|
||||||
data := res.Body()
|
req.Header.Set("accept", "application/json;charset=UTF-8")
|
||||||
|
req.Header.Set("SessionKey", sessionKey)
|
||||||
|
req.Header.Set("Signature", signature)
|
||||||
|
req.Header.Set("X-Request-Date", c)
|
||||||
|
req.Header.Set("X-Request-ID", r)
|
||||||
|
req.Header.Set("EncryptionText", b)
|
||||||
|
req.Header.Set("PkId", pkId)
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//log.Debug(res.String())
|
||||||
|
data = res.Body()
|
||||||
if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
|
if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
|
||||||
return nil, errors.New(jsoniter.Get(data, "msg").ToString())
|
return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
|
||||||
}
|
}
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upload Error: decrypt encryptionText failed
|
// NewUpload Error: signature check false
|
||||||
func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account) error {
|
func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account) error {
|
||||||
|
sessionKey, err := driver.GetSessionKey(account)
|
||||||
|
if err != nil {
|
||||||
|
account.Status = err.Error()
|
||||||
|
} else {
|
||||||
|
account.Status = "work"
|
||||||
|
account.DriveId = sessionKey
|
||||||
|
}
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
const DEFAULT uint64 = 10485760
|
const DEFAULT uint64 = 10485760
|
||||||
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
|
||||||
var finish uint64 = 0
|
var finish uint64 = 0
|
||||||
@ -390,15 +525,18 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
|
|||||||
}
|
}
|
||||||
res, err := driver.UploadRequest("/person/initMultiUpload", map[string]string{
|
res, err := driver.UploadRequest("/person/initMultiUpload", map[string]string{
|
||||||
"parentFolderId": parentFile.Id,
|
"parentFolderId": parentFile.Id,
|
||||||
"fileName": file.Name,
|
"fileName": encode(file.Name),
|
||||||
"fileSize": strconv.FormatInt(int64(file.Size), 10),
|
"fileSize": strconv.FormatInt(int64(file.Size), 10),
|
||||||
"sliceSize": strconv.FormatInt(int64(DEFAULT), 10),
|
"sliceSize": strconv.FormatInt(int64(DEFAULT), 10),
|
||||||
"lazyCheck": "1",
|
"lazyCheck": "1",
|
||||||
}, account)
|
}, account, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
uploadFileId := jsoniter.Get(res, "data.uploadFileId").ToString()
|
uploadFileId := jsoniter.Get(res, "data", "uploadFileId").ToString()
|
||||||
|
//_, err = driver.UploadRequest("/person/getUploadedPartsInfo", map[string]string{
|
||||||
|
// "uploadFileId": uploadFileId,
|
||||||
|
//}, account, nil)
|
||||||
var i int64
|
var i int64
|
||||||
var byteSize uint64
|
var byteSize uint64
|
||||||
md5s := make([]string, 0)
|
md5s := make([]string, 0)
|
||||||
@ -408,180 +546,82 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
|
|||||||
if DEFAULT < byteSize {
|
if DEFAULT < byteSize {
|
||||||
byteSize = DEFAULT
|
byteSize = DEFAULT
|
||||||
}
|
}
|
||||||
log.Debugf("%d,%d", byteSize, finish)
|
//log.Debugf("%d,%d", byteSize, finish)
|
||||||
byteData := make([]byte, byteSize)
|
byteData := make([]byte, byteSize)
|
||||||
n, err := io.ReadFull(file, byteData)
|
n, err := io.ReadFull(file, byteData)
|
||||||
log.Debug(err, n)
|
//log.Debug(err, n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
finish += uint64(n)
|
finish += uint64(n)
|
||||||
md5Bytes := getMd5(byteData)
|
md5Bytes := getMd5(byteData)
|
||||||
md5Str := hex.EncodeToString(md5Bytes)
|
md5Hex := hex.EncodeToString(md5Bytes)
|
||||||
md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes)
|
md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes)
|
||||||
md5s = append(md5s, md5Str)
|
md5s = append(md5s, strings.ToUpper(md5Hex))
|
||||||
md5Sum.Write(byteData)
|
md5Sum.Write(byteData)
|
||||||
|
//log.Debugf("md5Bytes: %+v,md5Str:%s,md5Base64:%s", md5Bytes, md5Hex, md5Base64)
|
||||||
|
var resp UploadUrlsResp
|
||||||
res, err = driver.UploadRequest("/person/getMultiUploadUrls", map[string]string{
|
res, err = driver.UploadRequest("/person/getMultiUploadUrls", map[string]string{
|
||||||
"partInfo": fmt.Sprintf("%s-%s", strconv.FormatInt(i, 10), md5Base64),
|
"partInfo": fmt.Sprintf("%s-%s", strconv.FormatInt(i, 10), md5Base64),
|
||||||
"uploadFileId": uploadFileId,
|
"uploadFileId": uploadFileId,
|
||||||
}, account)
|
}, account, &resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
uploadData := jsoniter.Get(res, "uploadUrls.partNumber_"+strconv.FormatInt(i, 10))
|
uploadData := resp.UploadUrls["partNumber_"+strconv.FormatInt(i, 10)]
|
||||||
headers := strings.Split(uploadData.Get("requestHeader").ToString(), "&")
|
log.Debugf("uploadData: %+v", uploadData)
|
||||||
req, err := http.NewRequest("PUT", uploadData.Get("requestURL").ToString(), bytes.NewBuffer(byteData))
|
requestURL := uploadData.RequestURL
|
||||||
|
uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&")
|
||||||
|
req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
|
||||||
|
for _, v := range uploadHeaders {
|
||||||
|
i := strings.Index(v, "=")
|
||||||
|
req.Header.Set(v[0:i], v[i+1:])
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := base.HttpClient.Do(req)
|
||||||
|
log.Debugf("%+v %+v", r, r.Request.Header)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, header := range headers {
|
|
||||||
kv := strings.Split(header, "=")
|
|
||||||
req.Header.Set(kv[0], strings.Join(kv[1:], "="))
|
|
||||||
}
|
|
||||||
res, err := base.HttpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
log.Debugf("%+v", res)
|
|
||||||
}
|
}
|
||||||
id := md5Sum.Sum(nil)
|
fileMd5 := hex.EncodeToString(md5Sum.Sum(nil))
|
||||||
|
sliceMd5 := fileMd5
|
||||||
|
if file.GetSize() > DEFAULT {
|
||||||
|
sliceMd5 = utils.GetMD5Encode(strings.Join(md5s, "\n"))
|
||||||
|
}
|
||||||
res, err = driver.UploadRequest("/person/commitMultiUploadFile", map[string]string{
|
res, err = driver.UploadRequest("/person/commitMultiUploadFile", map[string]string{
|
||||||
"uploadFileId": uploadFileId,
|
"uploadFileId": uploadFileId,
|
||||||
"fileMd5": hex.EncodeToString(id),
|
"fileMd5": fileMd5,
|
||||||
"sliceMd5": utils.GetMD5Encode(strings.Join(md5s, "\n")),
|
"sliceMd5": sliceMd5,
|
||||||
"lazyCheck": "1",
|
"lazyCheck": "1",
|
||||||
}, account)
|
}, account, nil)
|
||||||
|
account.DriveId, _ = driver.GetSessionKey(account)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func random() string {
|
func (driver Cloud189) OldUpload(file *model.FileStream, account *model.Account) error {
|
||||||
return fmt.Sprintf("0.%17v", mathRand.New(mathRand.NewSource(time.Now().UnixNano())).Int63n(100000000000000000))
|
//return base.ErrNotImplement
|
||||||
}
|
client, err := driver.getClient(account)
|
||||||
|
|
||||||
func RsaEncode(origData []byte, j_rsakey string) string {
|
|
||||||
publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
|
|
||||||
block, _ := pem.Decode(publicKey)
|
|
||||||
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
pub := pubInterface.(*rsa.PublicKey)
|
|
||||||
b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("err: %s", err.Error())
|
return err
|
||||||
}
|
}
|
||||||
return b64tohex(base64.StdEncoding.EncodeToString(b))
|
parentFile, err := driver.File(file.ParentPath, account)
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
|
||||||
|
|
||||||
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
|
|
||||||
|
|
||||||
func int2char(a int) string {
|
|
||||||
return strings.Split(BI_RM, "")[a]
|
|
||||||
}
|
|
||||||
|
|
||||||
func b64tohex(a string) string {
|
|
||||||
d := ""
|
|
||||||
e := 0
|
|
||||||
c := 0
|
|
||||||
for i := 0; i < len(a); i++ {
|
|
||||||
m := strings.Split(a, "")[i]
|
|
||||||
if m != "=" {
|
|
||||||
v := strings.Index(b64map, m)
|
|
||||||
if 0 == e {
|
|
||||||
e = 1
|
|
||||||
d += int2char(v >> 2)
|
|
||||||
c = 3 & v
|
|
||||||
} else if 1 == e {
|
|
||||||
e = 2
|
|
||||||
d += int2char(c<<2 | v>>4)
|
|
||||||
c = 15 & v
|
|
||||||
} else if 2 == e {
|
|
||||||
e = 3
|
|
||||||
d += int2char(c)
|
|
||||||
d += int2char(v >> 2)
|
|
||||||
c = 3 & v
|
|
||||||
} else {
|
|
||||||
e = 0
|
|
||||||
d += int2char(c<<2 | v>>4)
|
|
||||||
d += int2char(15 & v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if e == 1 {
|
// api refer to PanIndex
|
||||||
d += int2char(c << 2)
|
res, err := client.R().SetMultipartFormData(map[string]string{
|
||||||
|
"parentId": parentFile.Id,
|
||||||
|
"sessionKey": account.DriveId,
|
||||||
|
"opertype": "1",
|
||||||
|
"fname": file.GetFileName(),
|
||||||
|
}).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return d
|
if jsoniter.Get(res.Body(), "MD5").ToString() != "" {
|
||||||
}
|
return nil
|
||||||
|
|
||||||
func qs(form map[string]string) string {
|
|
||||||
strList := make([]string, 0)
|
|
||||||
for k, v := range form {
|
|
||||||
strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
|
|
||||||
}
|
}
|
||||||
return strings.Join(strList, "&")
|
log.Debugf(res.String())
|
||||||
}
|
return errors.New(res.String())
|
||||||
|
|
||||||
func aesEncrypt(data, key string) string {
|
|
||||||
encrypted := AesEncryptECB([]byte(data), []byte(key))
|
|
||||||
//return string(encrypted)
|
|
||||||
return hex.EncodeToString(encrypted)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hmacSha1(data string, secret string) string {
|
|
||||||
h := hmac.New(sha1.New, []byte(secret))
|
|
||||||
h.Write([]byte(data))
|
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
|
|
||||||
cipher, _ := aes.NewCipher(generateKey(key))
|
|
||||||
length := (len(origData) + aes.BlockSize) / aes.BlockSize
|
|
||||||
plain := make([]byte, length*aes.BlockSize)
|
|
||||||
copy(plain, origData)
|
|
||||||
pad := byte(len(plain) - len(origData))
|
|
||||||
for i := len(origData); i < len(plain); i++ {
|
|
||||||
plain[i] = pad
|
|
||||||
}
|
|
||||||
encrypted = make([]byte, len(plain))
|
|
||||||
// 分组分块加密
|
|
||||||
for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
|
||||||
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
|
|
||||||
}
|
|
||||||
|
|
||||||
return encrypted
|
|
||||||
}
|
|
||||||
func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
|
|
||||||
cipher, _ := aes.NewCipher(generateKey(key))
|
|
||||||
decrypted = make([]byte, len(encrypted))
|
|
||||||
//
|
|
||||||
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
|
|
||||||
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
|
|
||||||
}
|
|
||||||
|
|
||||||
trim := 0
|
|
||||||
if len(decrypted) > 0 {
|
|
||||||
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return decrypted[:trim]
|
|
||||||
}
|
|
||||||
func generateKey(key []byte) (genKey []byte) {
|
|
||||||
genKey = make([]byte, 16)
|
|
||||||
copy(genKey, key)
|
|
||||||
for i := 16; i < len(key); {
|
|
||||||
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
|
|
||||||
genKey[j] ^= key[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return genKey
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMd5(data []byte) []byte {
|
|
||||||
h := md5.New()
|
|
||||||
h.Write(data)
|
|
||||||
return h.Sum(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
base.RegisterDriver(&Cloud189{})
|
|
||||||
client189Map = make(map[string]*resty.Client, 0)
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
package _89
|
package _89
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -17,7 +14,8 @@ type Cloud189 struct{}
|
|||||||
|
|
||||||
func (driver Cloud189) Config() base.DriverConfig {
|
func (driver Cloud189) Config() base.DriverConfig {
|
||||||
return base.DriverConfig{
|
return base.DriverConfig{
|
||||||
Name: "189Cloud",
|
Name: "189Cloud",
|
||||||
|
LocalSort: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,27 +53,30 @@ func (driver Cloud189) Items() []base.Item {
|
|||||||
// Label: "family id",
|
// Label: "family id",
|
||||||
// Type: base.TypeString,
|
// Type: base.TypeString,
|
||||||
//},
|
//},
|
||||||
{
|
//{
|
||||||
Name: "order_by",
|
// Name: "order_by",
|
||||||
Label: "order_by",
|
// Label: "order_by",
|
||||||
Type: base.TypeSelect,
|
// Type: base.TypeSelect,
|
||||||
Values: "name,size,lastOpTime,createdDate",
|
// Values: "name,size,lastOpTime,createdDate",
|
||||||
Required: true,
|
// Required: true,
|
||||||
},
|
//},
|
||||||
{
|
//{
|
||||||
Name: "order_direction",
|
// Name: "order_direction",
|
||||||
Label: "desc",
|
// Label: "desc",
|
||||||
Type: base.TypeSelect,
|
// Type: base.TypeSelect,
|
||||||
Values: "true,false",
|
// Values: "true,false",
|
||||||
Required: true,
|
// Required: true,
|
||||||
},
|
//},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
|
func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
|
||||||
if old != nil && old.Name != account.Name {
|
if old != nil {
|
||||||
delete(client189Map, old.Name)
|
delete(client189Map, old.Name)
|
||||||
}
|
}
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if err := driver.Login(account); err != nil {
|
if err := driver.Login(account); err != nil {
|
||||||
account.Status = err.Error()
|
account.Status = err.Error()
|
||||||
_ = model.SaveAccount(account)
|
_ = model.SaveAccount(account)
|
||||||
@ -170,7 +171,12 @@ func (driver Cloud189) Link(args base.Args, account *model.Account) (*base.Link,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
link := base.Link{}
|
link := base.Link{
|
||||||
|
Headers: []base.Header{
|
||||||
|
{Name: "User-Agent", Value: base.UserAgent},
|
||||||
|
{Name: "Authorization", Value: ""},
|
||||||
|
},
|
||||||
|
}
|
||||||
if res.StatusCode() == 302 {
|
if res.StatusCode() == 302 {
|
||||||
link.Url = res.Header().Get("location")
|
link.Url = res.Header().Get("location")
|
||||||
} else {
|
} else {
|
||||||
@ -196,9 +202,9 @@ func (driver Cloud189) Path(path string, account *model.Account) (*model.File, [
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Cloud189) Proxy(ctx *gin.Context, account *model.Account) {
|
//func (driver Cloud189) Proxy(r *http.Request, account *model.Account) {
|
||||||
ctx.Request.Header.Del("Origin")
|
// r.Header.Del("Origin")
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
@ -346,29 +352,8 @@ func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) er
|
|||||||
if file == nil {
|
if file == nil {
|
||||||
return base.ErrEmptyFile
|
return base.ErrEmptyFile
|
||||||
}
|
}
|
||||||
client, ok := client189Map[account.Name]
|
return driver.NewUpload(file, account)
|
||||||
if !ok {
|
//return driver.OldUpload(file, account)
|
||||||
return fmt.Errorf("can't find [%s] client", account.Name)
|
|
||||||
}
|
|
||||||
parentFile, err := driver.File(file.ParentPath, account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// api refer to PanIndex
|
|
||||||
res, err := client.R().SetMultipartFormData(map[string]string{
|
|
||||||
"parentId": parentFile.Id,
|
|
||||||
"sessionKey": account.DriveId,
|
|
||||||
"opertype": "1",
|
|
||||||
"fname": file.GetFileName(),
|
|
||||||
}).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if jsoniter.Get(res.Body(), "MD5").ToString() != "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
log.Debugf(res.String())
|
|
||||||
return errors.New(res.String())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ base.Driver = (*Cloud189)(nil)
|
var _ base.Driver = (*Cloud189)(nil)
|
||||||
|
55
drivers/189/types.go
Normal file
55
drivers/189/types.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package _89
|
||||||
|
|
||||||
|
type Cloud189Error struct {
|
||||||
|
ErrorCode string `json:"errorCode"`
|
||||||
|
ErrorMsg string `json:"errorMsg"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cloud189File struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
LastOpTime string `json:"lastOpTime"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
Icon struct {
|
||||||
|
SmallUrl string `json:"smallUrl"`
|
||||||
|
//LargeUrl string `json:"largeUrl"`
|
||||||
|
} `json:"icon"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cloud189Folder struct {
|
||||||
|
Id int64 `json:"id"`
|
||||||
|
LastOpTime string `json:"lastOpTime"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Cloud189Files struct {
|
||||||
|
ResCode int `json:"res_code"`
|
||||||
|
ResMessage string `json:"res_message"`
|
||||||
|
FileListAO struct {
|
||||||
|
Count int `json:"count"`
|
||||||
|
FileList []Cloud189File `json:"fileList"`
|
||||||
|
FolderList []Cloud189Folder `json:"folderList"`
|
||||||
|
} `json:"fileListAO"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadUrlsResp struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
UploadUrls map[string]Part `json:"uploadUrls"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Part struct {
|
||||||
|
RequestURL string `json:"requestURL"`
|
||||||
|
RequestHeader string `json:"requestHeader"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//type Info struct {
|
||||||
|
// SessionKey string
|
||||||
|
// Rsa Rsa
|
||||||
|
//}
|
||||||
|
|
||||||
|
type Rsa struct {
|
||||||
|
Expire int64 `json:"expire"`
|
||||||
|
PkId string `json:"pkId"`
|
||||||
|
PubKey string `json:"pubKey"`
|
||||||
|
}
|
199
drivers/189/util.go
Normal file
199
drivers/189/util.go
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package _89
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func random() string {
|
||||||
|
return fmt.Sprintf("0.%17v", utils.Rand.Int63n(100000000000000000))
|
||||||
|
}
|
||||||
|
|
||||||
|
func RsaEncode(origData []byte, j_rsakey string, hex bool) string {
|
||||||
|
publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
|
||||||
|
block, _ := pem.Decode(publicKey)
|
||||||
|
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
pub := pubInterface.(*rsa.PublicKey)
|
||||||
|
b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("err: %s", err.Error())
|
||||||
|
}
|
||||||
|
res := base64.StdEncoding.EncodeToString(b)
|
||||||
|
if hex {
|
||||||
|
return b64tohex(res)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
|
||||||
|
|
||||||
|
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
|
||||||
|
|
||||||
|
func int2char(a int) string {
|
||||||
|
return strings.Split(BI_RM, "")[a]
|
||||||
|
}
|
||||||
|
|
||||||
|
func b64tohex(a string) string {
|
||||||
|
d := ""
|
||||||
|
e := 0
|
||||||
|
c := 0
|
||||||
|
for i := 0; i < len(a); i++ {
|
||||||
|
m := strings.Split(a, "")[i]
|
||||||
|
if m != "=" {
|
||||||
|
v := strings.Index(b64map, m)
|
||||||
|
if 0 == e {
|
||||||
|
e = 1
|
||||||
|
d += int2char(v >> 2)
|
||||||
|
c = 3 & v
|
||||||
|
} else if 1 == e {
|
||||||
|
e = 2
|
||||||
|
d += int2char(c<<2 | v>>4)
|
||||||
|
c = 15 & v
|
||||||
|
} else if 2 == e {
|
||||||
|
e = 3
|
||||||
|
d += int2char(c)
|
||||||
|
d += int2char(v >> 2)
|
||||||
|
c = 3 & v
|
||||||
|
} else {
|
||||||
|
e = 0
|
||||||
|
d += int2char(c<<2 | v>>4)
|
||||||
|
d += int2char(15 & v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if e == 1 {
|
||||||
|
d += int2char(c << 2)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func qs(form map[string]string) string {
|
||||||
|
f := make(url.Values)
|
||||||
|
for k, v := range form {
|
||||||
|
f.Set(k, v)
|
||||||
|
}
|
||||||
|
return EncodeParam(f)
|
||||||
|
//strList := make([]string, 0)
|
||||||
|
//for k, v := range form {
|
||||||
|
// strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
|
||||||
|
//}
|
||||||
|
//return strings.Join(strList, "&")
|
||||||
|
}
|
||||||
|
|
||||||
|
func EncodeParam(v url.Values) string {
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf strings.Builder
|
||||||
|
keys := make([]string, 0, len(v))
|
||||||
|
for k := range v {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
vs := v[k]
|
||||||
|
for _, v := range vs {
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
buf.WriteByte('&')
|
||||||
|
}
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteByte('=')
|
||||||
|
//if k == "fileName" {
|
||||||
|
// buf.WriteString(encode(v))
|
||||||
|
//} else {
|
||||||
|
buf.WriteString(v)
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func encode(str string) string {
|
||||||
|
//str = strings.ReplaceAll(str, "%", "%25")
|
||||||
|
//str = strings.ReplaceAll(str, "&", "%26")
|
||||||
|
//str = strings.ReplaceAll(str, "+", "%2B")
|
||||||
|
//return str
|
||||||
|
return url.QueryEscape(str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AesEncrypt(data, key []byte) []byte {
|
||||||
|
block, _ := aes.NewCipher(key)
|
||||||
|
if block == nil {
|
||||||
|
return []byte{}
|
||||||
|
}
|
||||||
|
data = PKCS7Padding(data, block.BlockSize())
|
||||||
|
decrypted := make([]byte, len(data))
|
||||||
|
size := block.BlockSize()
|
||||||
|
for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
|
||||||
|
block.Encrypt(decrypted[bs:be], data[bs:be])
|
||||||
|
}
|
||||||
|
return decrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
|
||||||
|
padding := blockSize - len(ciphertext)%blockSize
|
||||||
|
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||||
|
return append(ciphertext, padtext...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hmacSha1(data string, secret string) string {
|
||||||
|
h := hmac.New(sha1.New, []byte(secret))
|
||||||
|
h.Write([]byte(data))
|
||||||
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func getMd5(data []byte) []byte {
|
||||||
|
h := md5.New()
|
||||||
|
h.Write(data)
|
||||||
|
return h.Sum(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
base.RegisterDriver(&Cloud189{})
|
||||||
|
client189Map = make(map[string]*resty.Client)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeURIComponent(str string) string {
|
||||||
|
r, _ := url.PathUnescape(str)
|
||||||
|
//r = strings.ReplaceAll(r, " ", "+")
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func Random(v string) string {
|
||||||
|
reg := regexp.MustCompilePOSIX("[xy]")
|
||||||
|
data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte {
|
||||||
|
var i int64
|
||||||
|
t := int64(16 * utils.Rand.Float32())
|
||||||
|
if msg[0] == 120 {
|
||||||
|
i = t
|
||||||
|
} else {
|
||||||
|
i = 3&t | 8
|
||||||
|
}
|
||||||
|
return []byte(strconv.FormatInt(i, 16))
|
||||||
|
})
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
//func SHA1(v, l string) []byte {
|
||||||
|
// key := []byte(l)
|
||||||
|
// mac := hmac.New(sha1.New, key)
|
||||||
|
// mac.Write([]byte(v))
|
||||||
|
// return mac.Sum(nil)
|
||||||
|
//}
|
@ -8,9 +8,9 @@ import (
|
|||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ func (driver AliDrive) rename(fileId, name string, account *model.Account) error
|
|||||||
return fmt.Errorf("%+v", resp)
|
return fmt.Errorf("%+v", resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error {
|
func (driver AliDrive) batch(srcId, dstId string, url string, account *model.Account) error {
|
||||||
var e AliRespError
|
var e AliRespError
|
||||||
res, err := aliClient.R().SetError(&e).
|
res, err := aliClient.R().SetError(&e).
|
||||||
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
SetHeader("authorization", "Bearer\t"+account.AccessToken).
|
||||||
@ -208,6 +208,7 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
|
|||||||
"to_drive_id": account.DriveId,
|
"to_drive_id": account.DriveId,
|
||||||
"to_parent_file_id": dstId,
|
"to_parent_file_id": dstId,
|
||||||
},
|
},
|
||||||
|
"url": url,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"resource": "file",
|
"resource": "file",
|
||||||
@ -222,12 +223,13 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
|
|||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
_ = model.SaveAccount(account)
|
_ = model.SaveAccount(account)
|
||||||
return driver.batch(srcId, dstId, account)
|
return driver.batch(srcId, dstId, url, account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("%s", e.Message)
|
return fmt.Errorf("%s", e.Message)
|
||||||
}
|
}
|
||||||
if strings.Contains(res.String(), `"status":200`) {
|
status := jsoniter.Get(res.Body(), "status").ToInt()
|
||||||
|
if status < 400 && status >= 100 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New(res.String())
|
return errors.New(res.String())
|
||||||
@ -236,6 +238,7 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
|
|||||||
func init() {
|
func init() {
|
||||||
base.RegisterDriver(&AliDrive{})
|
base.RegisterDriver(&AliDrive{})
|
||||||
aliClient.
|
aliClient.
|
||||||
|
SetTimeout(base.DefaultTimeout).
|
||||||
SetRetryCount(3).
|
SetRetryCount(3).
|
||||||
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
|
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
|
||||||
SetHeader("content-type", "application/json").
|
SetHeader("content-type", "application/json").
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
@ -67,6 +66,9 @@ func (driver AliDrive) Save(account *model.Account, old *model.Account) error {
|
|||||||
if old != nil {
|
if old != nil {
|
||||||
conf.Cron.Remove(cron.EntryID(old.CronId))
|
conf.Cron.Remove(cron.EntryID(old.CronId))
|
||||||
}
|
}
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if account.RootFolder == "" {
|
if account.RootFolder == "" {
|
||||||
account.RootFolder = "root"
|
account.RootFolder = "root"
|
||||||
}
|
}
|
||||||
@ -188,6 +190,12 @@ func (driver AliDrive) Link(args base.Args, account *model.Account) (*base.Link,
|
|||||||
return nil, fmt.Errorf("%s", e.Message)
|
return nil, fmt.Errorf("%s", e.Message)
|
||||||
}
|
}
|
||||||
return &base.Link{
|
return &base.Link{
|
||||||
|
Headers: []base.Header{
|
||||||
|
{
|
||||||
|
Name: "Referer",
|
||||||
|
Value: "https://www.aliyundrive.com/",
|
||||||
|
},
|
||||||
|
},
|
||||||
Url: resp["url"].(string),
|
Url: resp["url"].(string),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
@ -209,10 +217,10 @@ func (driver AliDrive) Path(path string, account *model.Account) (*model.File, [
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver AliDrive) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver AliDrive) Proxy(r *http.Request, account *model.Account) {
|
||||||
c.Request.Header.Del("Origin")
|
// r.Header.Del("Origin")
|
||||||
c.Request.Header.Set("Referer", "https://www.aliyundrive.com/")
|
// r.Header.Set("Referer", "https://www.aliyundrive.com/")
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
file, err := driver.GetFile(path, account)
|
file, err := driver.GetFile(path, account)
|
||||||
@ -301,7 +309,7 @@ func (driver AliDrive) Move(src string, dst string, account *model.Account) erro
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = driver.batch(srcFile.Id, dstDirFile.Id, account)
|
err = driver.batch(srcFile.Id, dstDirFile.Id, "/file/move", account)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +324,17 @@ func (driver AliDrive) Rename(src string, dst string, account *model.Account) er
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver AliDrive) Copy(src string, dst string, account *model.Account) error {
|
func (driver AliDrive) Copy(src string, dst string, account *model.Account) error {
|
||||||
return base.ErrNotSupport
|
dstDir, _ := filepath.Split(dst)
|
||||||
|
srcFile, err := driver.File(src, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstDirFile, err := driver.File(dstDir, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = driver.batch(srcFile.Id, dstDirFile.Id, "/file/copy", account)
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver AliDrive) Delete(path string, account *model.Account) error {
|
func (driver AliDrive) Delete(path string, account *model.Account) error {
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -48,6 +47,9 @@ func (driver Alist) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Alist) Save(account *model.Account, old *model.Account) error {
|
func (driver Alist) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
account.SiteUrl = strings.TrimRight(account.SiteUrl, "/")
|
account.SiteUrl = strings.TrimRight(account.SiteUrl, "/")
|
||||||
if account.RootFolder == "" {
|
if account.RootFolder == "" {
|
||||||
account.RootFolder = "/"
|
account.RootFolder = "/"
|
||||||
@ -145,7 +147,7 @@ func (driver Alist) Path(path string, account *model.Account) (*model.File, []mo
|
|||||||
return nil, resp.Data.Files, nil
|
return nil, resp.Data.Files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Alist) Proxy(c *gin.Context, account *model.Account) {}
|
//func (driver Alist) Proxy(r *http.Request, account *model.Account) {}
|
||||||
|
|
||||||
func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
var resp PathResp
|
var resp PathResp
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
_ "github.com/Xhofe/alist/drivers/189"
|
_ "github.com/Xhofe/alist/drivers/189"
|
||||||
_ "github.com/Xhofe/alist/drivers/alidrive"
|
_ "github.com/Xhofe/alist/drivers/alidrive"
|
||||||
_ "github.com/Xhofe/alist/drivers/alist"
|
_ "github.com/Xhofe/alist/drivers/alist"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/baidu"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
_ "github.com/Xhofe/alist/drivers/ftp"
|
_ "github.com/Xhofe/alist/drivers/ftp"
|
||||||
_ "github.com/Xhofe/alist/drivers/google"
|
_ "github.com/Xhofe/alist/drivers/google"
|
||||||
@ -14,10 +15,12 @@ import (
|
|||||||
_ "github.com/Xhofe/alist/drivers/native"
|
_ "github.com/Xhofe/alist/drivers/native"
|
||||||
_ "github.com/Xhofe/alist/drivers/onedrive"
|
_ "github.com/Xhofe/alist/drivers/onedrive"
|
||||||
_ "github.com/Xhofe/alist/drivers/pikpak"
|
_ "github.com/Xhofe/alist/drivers/pikpak"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/quark"
|
||||||
_ "github.com/Xhofe/alist/drivers/s3"
|
_ "github.com/Xhofe/alist/drivers/s3"
|
||||||
_ "github.com/Xhofe/alist/drivers/shandian"
|
_ "github.com/Xhofe/alist/drivers/shandian"
|
||||||
_ "github.com/Xhofe/alist/drivers/teambition"
|
_ "github.com/Xhofe/alist/drivers/teambition"
|
||||||
_ "github.com/Xhofe/alist/drivers/webdav"
|
_ "github.com/Xhofe/alist/drivers/webdav"
|
||||||
|
_ "github.com/Xhofe/alist/drivers/yandex"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
186
drivers/baidu/baidu.go
Normal file
186
drivers/baidu/baidu.go
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
package baidu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (driver Baidu) RefreshToken(account *model.Account) error {
|
||||||
|
err := driver.refreshToken(account)
|
||||||
|
if err != nil && err == base.ErrEmptyToken {
|
||||||
|
err = driver.refreshToken(account)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
account.Status = err.Error()
|
||||||
|
}
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) refreshToken(account *model.Account) error {
|
||||||
|
u := "https://openapi.baidu.com/oauth/2.0/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e TokenErrResp
|
||||||
|
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetQueryParams(map[string]string{
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
"refresh_token": account.RefreshToken,
|
||||||
|
"client_id": account.ClientId,
|
||||||
|
"client_secret": account.ClientSecret,
|
||||||
|
}).Get(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Error != "" {
|
||||||
|
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||||
|
}
|
||||||
|
if resp.RefreshToken == "" {
|
||||||
|
return base.ErrEmptyToken
|
||||||
|
}
|
||||||
|
account.Status = "work"
|
||||||
|
account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
u := "https://pan.baidu.com/rest/2.0" + pathname
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
req.SetQueryParam("access_token", account.AccessToken)
|
||||||
|
if headers != nil {
|
||||||
|
req.SetHeaders(headers)
|
||||||
|
}
|
||||||
|
if query != nil {
|
||||||
|
req.SetQueryParams(query)
|
||||||
|
}
|
||||||
|
if form != nil {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}
|
||||||
|
if data != nil {
|
||||||
|
req.SetBody(data)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
var res *resty.Response
|
||||||
|
var err error
|
||||||
|
switch method {
|
||||||
|
case base.Get:
|
||||||
|
res, err = req.Get(u)
|
||||||
|
case base.Post:
|
||||||
|
res, err = req.Post(u)
|
||||||
|
case base.Patch:
|
||||||
|
res, err = req.Patch(u)
|
||||||
|
case base.Delete:
|
||||||
|
res, err = req.Delete(u)
|
||||||
|
case base.Put:
|
||||||
|
res, err = req.Put(u)
|
||||||
|
default:
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//log.Debug(res.String())
|
||||||
|
errno := jsoniter.Get(res.Body(), "errno").ToInt()
|
||||||
|
if errno != 0 {
|
||||||
|
if errno == -6 {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return driver.Request(pathname, method, headers, query, form, data, resp, account)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("errno: %d, refer to https://pan.baidu.com/union/doc/", errno)
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Get(pathname string, params map[string]string, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
return driver.Request(pathname, base.Get, nil, params, nil, nil, resp, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Post(pathname string, params map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
return driver.Request(pathname, base.Post, nil, params, nil, data, resp, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) manage(opera string, filelist interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
params := map[string]string{
|
||||||
|
"method": "filemanager",
|
||||||
|
"opera": opera,
|
||||||
|
}
|
||||||
|
marshal, err := utils.Json.Marshal(filelist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", string(marshal))
|
||||||
|
return driver.Post("/xpan/file", params, data, nil, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) GetFiles(dir string, account *model.Account) ([]model.File, error) {
|
||||||
|
dir = utils.Join(account.RootFolder, dir)
|
||||||
|
start := 0
|
||||||
|
limit := 200
|
||||||
|
params := map[string]string{
|
||||||
|
"method": "list",
|
||||||
|
"dir": dir,
|
||||||
|
"web": "web",
|
||||||
|
}
|
||||||
|
if account.OrderBy != "" {
|
||||||
|
params["order"] = account.OrderBy
|
||||||
|
if account.OrderDirection == "desc" {
|
||||||
|
params["desc"] = "1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res := make([]model.File, 0)
|
||||||
|
for {
|
||||||
|
params["start"] = strconv.Itoa(start)
|
||||||
|
params["limit"] = strconv.Itoa(limit)
|
||||||
|
start += limit
|
||||||
|
var resp ListResp
|
||||||
|
_, err := driver.Get("/xpan/file", params, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(resp.List) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for _, f := range resp.List {
|
||||||
|
file := model.File{
|
||||||
|
Id: strconv.FormatInt(f.FsId, 10),
|
||||||
|
Name: f.ServerFilename,
|
||||||
|
Size: f.Size,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: getTime(f.ServerMtime),
|
||||||
|
Thumbnail: f.Thumbs.Url3,
|
||||||
|
}
|
||||||
|
if f.Isdir == 1 {
|
||||||
|
file.Type = conf.FOLDER
|
||||||
|
} else {
|
||||||
|
file.Type = utils.GetFileType(path.Ext(f.ServerFilename))
|
||||||
|
}
|
||||||
|
res = append(res, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) create(path string, size uint64, isdir int, uploadid, block_list string, account *model.Account) ([]byte, error) {
|
||||||
|
params := map[string]string{
|
||||||
|
"method": "create",
|
||||||
|
}
|
||||||
|
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir)
|
||||||
|
if uploadid != "" {
|
||||||
|
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
|
||||||
|
}
|
||||||
|
return driver.Post("/xpan/file", params, data, nil, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
base.RegisterDriver(&Baidu{})
|
||||||
|
}
|
355
drivers/baidu/driver.go
Normal file
355
drivers/baidu/driver.go
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
package baidu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Baidu struct{}
|
||||||
|
|
||||||
|
func (driver Baidu) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
|
Name: "Baidu.Disk",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Items() []base.Item {
|
||||||
|
return []base.Item{
|
||||||
|
{
|
||||||
|
Name: "refresh_token",
|
||||||
|
Label: "refresh token",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder path",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Default: "/",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Default: "name",
|
||||||
|
Values: "name,time,size",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "order_direction",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "asc,desc",
|
||||||
|
Default: "asc",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "client_id",
|
||||||
|
Label: "client id",
|
||||||
|
Default: "iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "client_secret",
|
||||||
|
Label: "client secret",
|
||||||
|
Default: "jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Save(account *model.Account, old *model.Account) error {
|
||||||
|
return driver.RefreshToken(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
if path == "/" {
|
||||||
|
return &model.File{
|
||||||
|
Id: account.RootFolder,
|
||||||
|
Name: account.Name,
|
||||||
|
Size: 0,
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: account.UpdatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
files, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == name {
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
cache, err := base.GetCache(path, account)
|
||||||
|
if err == nil {
|
||||||
|
files, _ := cache.([]model.File)
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
files, err := driver.GetFiles(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(files) > 0 {
|
||||||
|
_ = base.SetCache(path, files, account)
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||||
|
file, err := driver.File(args.Path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if file.IsDir() {
|
||||||
|
return nil, base.ErrNotFile
|
||||||
|
}
|
||||||
|
var resp DownloadResp
|
||||||
|
params := map[string]string{
|
||||||
|
"method": "filemetas",
|
||||||
|
"fsids": fmt.Sprintf("[%s]", file.Id),
|
||||||
|
"dlink": "1",
|
||||||
|
}
|
||||||
|
_, err = driver.Get("/xpan/multimedia", params, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("%s&access_token=%s", resp.List[0].Dlink, account.AccessToken)
|
||||||
|
res, err := base.NoRedirectClient.R().SetHeader("User-Agent", "pan.baidu.com").Head(u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//if res.StatusCode() == 302 {
|
||||||
|
u = res.Header().Get("location")
|
||||||
|
//}
|
||||||
|
return &base.Link{
|
||||||
|
Url: u,
|
||||||
|
Headers: []base.Header{
|
||||||
|
{Name: "User-Agent", Value: "pan.baidu.com"},
|
||||||
|
}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !file.IsDir() {
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (driver Baidu) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
// r.Header.Set("User-Agent", "pan.baidu.com")
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (driver Baidu) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) MakeDir(path string, account *model.Account) error {
|
||||||
|
_, err := driver.create(utils.Join(account.RootFolder, path), 0, 1, "", "", account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Move(src string, dst string, account *model.Account) error {
|
||||||
|
path := utils.Join(account.RootFolder, src)
|
||||||
|
dest, newname := utils.Split(utils.Join(account.RootFolder, dst))
|
||||||
|
data := []base.Json{
|
||||||
|
{
|
||||||
|
"path": path,
|
||||||
|
"dest": dest,
|
||||||
|
"newname": newname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := driver.manage("move", data, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Rename(src string, dst string, account *model.Account) error {
|
||||||
|
path := utils.Join(account.RootFolder, src)
|
||||||
|
newname := utils.Base(dst)
|
||||||
|
data := []base.Json{
|
||||||
|
{
|
||||||
|
"path": path,
|
||||||
|
"newname": newname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := driver.manage("rename", data, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
path := utils.Join(account.RootFolder, src)
|
||||||
|
dest, newname := utils.Split(utils.Join(account.RootFolder, dst))
|
||||||
|
data := []base.Json{
|
||||||
|
{
|
||||||
|
"path": path,
|
||||||
|
"dest": dest,
|
||||||
|
"newname": newname,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := driver.manage("copy", data, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Delete(path string, account *model.Account) error {
|
||||||
|
path = utils.Join(account.RootFolder, path)
|
||||||
|
data := []string{path}
|
||||||
|
_, err := driver.manage("delete", data, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
if file == nil {
|
||||||
|
return base.ErrEmptyFile
|
||||||
|
}
|
||||||
|
tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = tempFile.Close()
|
||||||
|
_ = os.Remove(tempFile.Name())
|
||||||
|
}()
|
||||||
|
_, err = io.Copy(tempFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var Default uint64 = 4 * 1024 * 1024
|
||||||
|
defaultByteData := make([]byte, Default)
|
||||||
|
count := int(math.Ceil(float64(file.GetSize()) / float64(Default)))
|
||||||
|
var SliceSize uint64 = 256 * 1024
|
||||||
|
// cal md5
|
||||||
|
h1 := md5.New()
|
||||||
|
h2 := md5.New()
|
||||||
|
block_list := make([]string, 0)
|
||||||
|
content_md5 := ""
|
||||||
|
slice_md5 := ""
|
||||||
|
left := file.GetSize()
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
byteSize := Default
|
||||||
|
var byteData []byte
|
||||||
|
if left < Default {
|
||||||
|
byteSize = left
|
||||||
|
byteData = make([]byte, byteSize)
|
||||||
|
} else {
|
||||||
|
byteData = defaultByteData
|
||||||
|
}
|
||||||
|
left -= byteSize
|
||||||
|
_, err = io.ReadFull(tempFile, byteData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h1.Write(byteData)
|
||||||
|
h2.Write(byteData)
|
||||||
|
block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil))))
|
||||||
|
h2.Reset()
|
||||||
|
}
|
||||||
|
content_md5 = hex.EncodeToString(h1.Sum(nil))
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if file.GetSize() <= SliceSize {
|
||||||
|
slice_md5 = content_md5
|
||||||
|
} else {
|
||||||
|
sliceData := make([]byte, SliceSize)
|
||||||
|
_, err = io.ReadFull(tempFile, sliceData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
h2.Write(sliceData)
|
||||||
|
slice_md5 = hex.EncodeToString(h2.Sum(nil))
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path := encodeURIComponent(utils.Join(account.RootFolder, file.ParentPath, file.Name))
|
||||||
|
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
|
||||||
|
data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s&content-md5=%s&slice-md5=%s",
|
||||||
|
path, file.GetSize(),
|
||||||
|
block_list_str,
|
||||||
|
content_md5, slice_md5)
|
||||||
|
params := map[string]string{
|
||||||
|
"method": "precreate",
|
||||||
|
}
|
||||||
|
var precreateResp PrecreateResp
|
||||||
|
_, err = driver.Post("/xpan/file", params, data, &precreateResp, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("%+v", precreateResp)
|
||||||
|
if precreateResp.ReturnType == 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
params = map[string]string{
|
||||||
|
"method": "upload",
|
||||||
|
"access_token": account.AccessToken,
|
||||||
|
"type": "tmpfile",
|
||||||
|
"path": path,
|
||||||
|
"uploadid": precreateResp.Uploadid,
|
||||||
|
}
|
||||||
|
left = file.GetSize()
|
||||||
|
for _, partseq := range precreateResp.BlockList {
|
||||||
|
byteSize := Default
|
||||||
|
var byteData []byte
|
||||||
|
if left < Default {
|
||||||
|
byteSize = left
|
||||||
|
byteData = make([]byte, byteSize)
|
||||||
|
} else {
|
||||||
|
byteData = defaultByteData
|
||||||
|
}
|
||||||
|
left -= byteSize
|
||||||
|
_, err = io.ReadFull(tempFile, byteData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
|
||||||
|
params["partseq"] = strconv.Itoa(partseq)
|
||||||
|
res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", file.Name, bytes.NewReader(byteData)).Post(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugln(res.String())
|
||||||
|
}
|
||||||
|
_, err = driver.create(path, file.GetSize(), 0, precreateResp.Uploadid, block_list_str, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Baidu)(nil)
|
84
drivers/baidu/types.go
Normal file
84
drivers/baidu/types.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package baidu
|
||||||
|
|
||||||
|
type TokenErrResp struct {
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
//TkbindId int `json:"tkbind_id"`
|
||||||
|
//OwnerType int `json:"owner_type"`
|
||||||
|
//Category int `json:"category"`
|
||||||
|
//RealCategory string `json:"real_category"`
|
||||||
|
FsId int64 `json:"fs_id"`
|
||||||
|
ServerMtime int64 `json:"server_mtime"`
|
||||||
|
//OperId int `json:"oper_id"`
|
||||||
|
//ServerCtime int `json:"server_ctime"`
|
||||||
|
Thumbs struct {
|
||||||
|
//Icon string `json:"icon"`
|
||||||
|
Url3 string `json:"url3"`
|
||||||
|
//Url2 string `json:"url2"`
|
||||||
|
//Url1 string `json:"url1"`
|
||||||
|
} `json:"thumbs"`
|
||||||
|
//Wpfile int `json:"wpfile"`
|
||||||
|
//LocalMtime int `json:"local_mtime"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
//ExtentTinyint7 int `json:"extent_tinyint7"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
//Share int `json:"share"`
|
||||||
|
//ServerAtime int `json:"server_atime"`
|
||||||
|
//Pl int `json:"pl"`
|
||||||
|
//LocalCtime int `json:"local_ctime"`
|
||||||
|
ServerFilename string `json:"server_filename"`
|
||||||
|
//Md5 string `json:"md5"`
|
||||||
|
//OwnerId int `json:"owner_id"`
|
||||||
|
//Unlist int `json:"unlist"`
|
||||||
|
Isdir int `json:"isdir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListResp struct {
|
||||||
|
Errno int `json:"errno"`
|
||||||
|
GuidInfo string `json:"guid_info"`
|
||||||
|
List []File `json:"list"`
|
||||||
|
RequestId int64 `json:"request_id"`
|
||||||
|
Guid int `json:"guid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownloadResp struct {
|
||||||
|
Errmsg string `json:"errmsg"`
|
||||||
|
Errno int `json:"errno"`
|
||||||
|
List []struct {
|
||||||
|
//Category int `json:"category"`
|
||||||
|
//DateTaken int `json:"date_taken,omitempty"`
|
||||||
|
Dlink string `json:"dlink"`
|
||||||
|
//Filename string `json:"filename"`
|
||||||
|
//FsId int64 `json:"fs_id"`
|
||||||
|
//Height int `json:"height,omitempty"`
|
||||||
|
//Isdir int `json:"isdir"`
|
||||||
|
//Md5 string `json:"md5"`
|
||||||
|
//OperId int `json:"oper_id"`
|
||||||
|
//Path string `json:"path"`
|
||||||
|
//ServerCtime int `json:"server_ctime"`
|
||||||
|
//ServerMtime int `json:"server_mtime"`
|
||||||
|
//Size int `json:"size"`
|
||||||
|
//Thumbs struct {
|
||||||
|
// Icon string `json:"icon,omitempty"`
|
||||||
|
// Url1 string `json:"url1,omitempty"`
|
||||||
|
// Url2 string `json:"url2,omitempty"`
|
||||||
|
// Url3 string `json:"url3,omitempty"`
|
||||||
|
//} `json:"thumbs"`
|
||||||
|
//Width int `json:"width,omitempty"`
|
||||||
|
} `json:"list"`
|
||||||
|
//Names struct {
|
||||||
|
//} `json:"names"`
|
||||||
|
RequestId string `json:"request_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrecreateResp struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Uploadid string `json:"uploadid"`
|
||||||
|
ReturnType int `json:"return_type"`
|
||||||
|
BlockList []int `json:"block_list"`
|
||||||
|
Errno int `json:"errno"`
|
||||||
|
RequestId int64 `json:"request_id"`
|
||||||
|
}
|
18
drivers/baidu/util.go
Normal file
18
drivers/baidu/util.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package baidu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTime(t int64) *time.Time {
|
||||||
|
tm := time.Unix(t, 0)
|
||||||
|
return &tm
|
||||||
|
}
|
||||||
|
|
||||||
|
func encodeURIComponent(str string) string {
|
||||||
|
r := url.QueryEscape(str)
|
||||||
|
r = strings.ReplaceAll(r, "+", "%20")
|
||||||
|
return r
|
||||||
|
}
|
@ -2,10 +2,10 @@ package base
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DriverConfig struct {
|
type DriverConfig struct {
|
||||||
@ -38,8 +38,8 @@ type Driver interface {
|
|||||||
Link(args Args, account *model.Account) (*Link, error)
|
Link(args Args, account *model.Account) (*Link, error)
|
||||||
// Path 取路径(文件或文件夹)
|
// Path 取路径(文件或文件夹)
|
||||||
Path(path string, account *model.Account) (*model.File, []model.File, error)
|
Path(path string, account *model.Account) (*model.File, []model.File, error)
|
||||||
// Proxy 代理处理
|
// Deprecated Proxy 代理处理
|
||||||
Proxy(c *gin.Context, account *model.Account)
|
//Proxy(r *http.Request, account *model.Account)
|
||||||
// Preview 预览
|
// Preview 预览
|
||||||
Preview(path string, account *model.Account) (interface{}, error)
|
Preview(path string, account *model.Account) (interface{}, error)
|
||||||
// MakeDir 创建文件夹
|
// MakeDir 创建文件夹
|
||||||
@ -62,6 +62,7 @@ type Item struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
|
Default string `json:"default"`
|
||||||
Values string `json:"values"`
|
Values string `json:"values"`
|
||||||
Required bool `json:"required"`
|
Required bool `json:"required"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
@ -84,19 +85,21 @@ func GetDriversMap() map[string]Driver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetDrivers() map[string][]Item {
|
func GetDrivers() map[string][]Item {
|
||||||
res := make(map[string][]Item, 0)
|
res := make(map[string][]Item)
|
||||||
for k, v := range driversMap {
|
for k, v := range driversMap {
|
||||||
|
webdavDirect := Item{
|
||||||
|
Name: "webdav_direct",
|
||||||
|
Label: "webdav direct",
|
||||||
|
Type: TypeBool,
|
||||||
|
Required: true,
|
||||||
|
Description: "Transfer the WebDAV of this account through the native",
|
||||||
|
}
|
||||||
if v.Config().OnlyProxy {
|
if v.Config().OnlyProxy {
|
||||||
res[k] = v.Items()
|
res[k] = append([]Item{
|
||||||
|
webdavDirect,
|
||||||
|
}, v.Items()...)
|
||||||
} else {
|
} else {
|
||||||
res[k] = append([]Item{
|
res[k] = append([]Item{
|
||||||
//{
|
|
||||||
// Name: "allow_proxy",
|
|
||||||
// Label: "allow_proxy",
|
|
||||||
// Type: TypeBool,
|
|
||||||
// Required: true,
|
|
||||||
// Description: "allow proxy",
|
|
||||||
//},
|
|
||||||
{
|
{
|
||||||
Name: "proxy",
|
Name: "proxy",
|
||||||
Label: "proxy",
|
Label: "proxy",
|
||||||
@ -111,6 +114,7 @@ func GetDrivers() map[string][]Item {
|
|||||||
Required: true,
|
Required: true,
|
||||||
Description: "Transfer the WebDAV of this account through the server",
|
Description: "Transfer the WebDAV of this account through the server",
|
||||||
},
|
},
|
||||||
|
webdavDirect,
|
||||||
}, v.Items()...)
|
}, v.Items()...)
|
||||||
}
|
}
|
||||||
res[k] = append([]Item{
|
res[k] = append([]Item{
|
||||||
@ -119,6 +123,12 @@ func GetDrivers() map[string][]Item {
|
|||||||
Label: "down_proxy_url",
|
Label: "down_proxy_url",
|
||||||
Type: TypeString,
|
Type: TypeString,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "extract_folder",
|
||||||
|
Label: "extract_folder",
|
||||||
|
Values: "front,back",
|
||||||
|
Type: TypeSelect,
|
||||||
|
},
|
||||||
}, res[k]...)
|
}, res[k]...)
|
||||||
if v.Config().ApiProxy {
|
if v.Config().ApiProxy {
|
||||||
res[k] = append([]Item{
|
res[k] = append([]Item{
|
||||||
@ -154,6 +164,8 @@ func GetDrivers() map[string][]Item {
|
|||||||
var NoRedirectClient *resty.Client
|
var NoRedirectClient *resty.Client
|
||||||
var RestyClient = resty.New()
|
var RestyClient = resty.New()
|
||||||
var HttpClient = &http.Client{}
|
var HttpClient = &http.Client{}
|
||||||
|
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
||||||
|
var DefaultTimeout = time.Second * 20
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
NoRedirectClient = resty.New().SetRedirectPolicy(
|
NoRedirectClient = resty.New().SetRedirectPolicy(
|
||||||
@ -161,8 +173,8 @@ func init() {
|
|||||||
return http.ErrUseLastResponse
|
return http.ErrUseLastResponse
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
userAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
|
NoRedirectClient.SetHeader("user-agent", UserAgent)
|
||||||
NoRedirectClient.SetHeader("user-agent", userAgent)
|
RestyClient.SetHeader("user-agent", UserAgent)
|
||||||
RestyClient.SetHeader("user-agent", userAgent)
|
|
||||||
RestyClient.SetRetryCount(3)
|
RestyClient.SetRetryCount(3)
|
||||||
|
RestyClient.SetTimeout(DefaultTimeout)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,8 @@ var (
|
|||||||
ErrNotSupport = errors.New("not support")
|
ErrNotSupport = errors.New("not support")
|
||||||
ErrNotFolder = errors.New("not a folder")
|
ErrNotFolder = errors.New("not a folder")
|
||||||
ErrEmptyFile = errors.New("empty file")
|
ErrEmptyFile = errors.New("empty file")
|
||||||
|
ErrRelativePath = errors.New("access using relative path is not allowed")
|
||||||
|
ErrEmptyToken = errors.New("empty token")
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -42,7 +44,8 @@ type Header struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Link struct {
|
type Link struct {
|
||||||
Url string `json:"url"`
|
Url string `json:"url"`
|
||||||
Headers []Header `json:"headers"`
|
Headers []Header `json:"headers"`
|
||||||
Data io.ReadCloser
|
Data io.ReadCloser
|
||||||
|
FilePath string `json:"path"` // for native
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/jlaffaye/ftp"
|
"github.com/jlaffaye/ftp"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -61,6 +60,9 @@ func (driver FTP) Save(account *model.Account, old *model.Account) error {
|
|||||||
delete(connMap, old.Name)
|
delete(connMap, old.Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if account.RootFolder == "" {
|
if account.RootFolder == "" {
|
||||||
account.RootFolder = "/"
|
account.RootFolder = "/"
|
||||||
}
|
}
|
||||||
@ -183,9 +185,9 @@ func (driver FTP) Path(path string, account *model.Account) (*model.File, []mode
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver FTP) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver FTP) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver FTP) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver FTP) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
@ -11,7 +11,12 @@ var connMap map[string]*ftp.ServerConn
|
|||||||
func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
|
func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
|
||||||
conn, ok := connMap[account.Name]
|
conn, ok := connMap[account.Name]
|
||||||
if ok {
|
if ok {
|
||||||
return conn, nil
|
_, err := conn.CurrentDir()
|
||||||
|
if err == nil {
|
||||||
|
return conn, nil
|
||||||
|
} else {
|
||||||
|
delete(connMap, account.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
conn, err := ftp.Connect(account.SiteUrl)
|
conn, err := ftp.Connect(account.SiteUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -21,9 +26,11 @@ func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
connMap[account.Name] = conn
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
base.RegisterDriver(&FTP{})
|
base.RegisterDriver(&FTP{})
|
||||||
|
connMap = make(map[string]*ftp.ServerConn)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -67,6 +66,9 @@ func (driver GoogleDrive) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Save(account *model.Account, old *model.Account) error {
|
func (driver GoogleDrive) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
account.Proxy = true
|
account.Proxy = true
|
||||||
err := driver.RefreshToken(account)
|
err := driver.RefreshToken(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -175,9 +177,9 @@ func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver GoogleDrive) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver GoogleDrive) Proxy(r *http.Request, account *model.Account) {
|
||||||
c.Request.Header.Add("Authorization", "Bearer "+account.AccessToken)
|
// r.Header.Add("Authorization", "Bearer "+account.AccessToken)
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -33,17 +32,19 @@ func (driver Lanzou) Items() []base.Item {
|
|||||||
Label: "cookie",
|
Label: "cookie",
|
||||||
Type: base.TypeString,
|
Type: base.TypeString,
|
||||||
Description: "about 15 days valid",
|
Description: "about 15 days valid",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "site_url",
|
||||||
|
Label: "share url",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "root_folder",
|
Name: "root_folder",
|
||||||
Label: "root folder file_id",
|
Label: "root folder file_id",
|
||||||
Type: base.TypeString,
|
Type: base.TypeString,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "site_url",
|
|
||||||
Label: "share url",
|
|
||||||
Type: base.TypeString,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "password",
|
Name: "password",
|
||||||
Label: "share password",
|
Label: "share password",
|
||||||
@ -53,6 +54,9 @@ func (driver Lanzou) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Save(account *model.Account, old *model.Account) error {
|
func (driver Lanzou) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if account.InternalType == "cookie" {
|
if account.InternalType == "cookie" {
|
||||||
if account.RootFolder == "" {
|
if account.RootFolder == "" {
|
||||||
account.RootFolder = "-1"
|
account.RootFolder = "-1"
|
||||||
@ -127,12 +131,15 @@ func (driver Lanzou) Link(args base.Args, account *model.Account) (*base.Link, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
url, err := driver.GetLink(downId)
|
url, err := driver.GetLink(downId, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
link := base.Link{
|
link := base.Link{
|
||||||
Url: url,
|
Url: url,
|
||||||
|
Headers: []base.Header{
|
||||||
|
{Name: "User-Agent", Value: base.UserAgent},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return &link, nil
|
return &link, nil
|
||||||
}
|
}
|
||||||
@ -154,9 +161,9 @@ func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Lanzou) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Lanzou) Proxy(r *http.Request, account *model.Account) {
|
||||||
c.Request.Header.Del("Origin")
|
// r.Header.Del("Origin")
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
@ -6,16 +6,14 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var lanzouClient = resty.New()
|
|
||||||
|
|
||||||
type LanZouFile struct {
|
type LanZouFile struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
NameAll string `json:"name_all"`
|
NameAll string `json:"name_all"`
|
||||||
@ -57,7 +55,7 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
|
|||||||
files := make([]LanZouFile, 0)
|
files := make([]LanZouFile, 0)
|
||||||
var resp LanZouFilesResp
|
var resp LanZouFilesResp
|
||||||
// folders
|
// folders
|
||||||
res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
|
res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
|
||||||
SetFormData(map[string]string{
|
SetFormData(map[string]string{
|
||||||
"task": "47",
|
"task": "47",
|
||||||
"folder_id": folderId,
|
"folder_id": folderId,
|
||||||
@ -76,7 +74,7 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
|
|||||||
// files
|
// files
|
||||||
pg := 1
|
pg := 1
|
||||||
for {
|
for {
|
||||||
_, err = lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
|
_, err = base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
|
||||||
SetFormData(map[string]string{
|
SetFormData(map[string]string{
|
||||||
"task": "5",
|
"task": "5",
|
||||||
"folder_id": folderId,
|
"folder_id": folderId,
|
||||||
@ -103,7 +101,11 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
|
|||||||
func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error) {
|
func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error) {
|
||||||
files := make([]LanZouFile, 0)
|
files := make([]LanZouFile, 0)
|
||||||
shareUrl := account.SiteUrl
|
shareUrl := account.SiteUrl
|
||||||
res, err := lanzouClient.R().Get(shareUrl)
|
u, err := url.Parse(shareUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res, err := base.RestyClient.R().Get(shareUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -116,7 +118,10 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
|
|||||||
uid := regexp.MustCompile(`'uid':'(.+?)',`).FindStringSubmatch(res.String())[1]
|
uid := regexp.MustCompile(`'uid':'(.+?)',`).FindStringSubmatch(res.String())[1]
|
||||||
rep := regexp.MustCompile(`'rep':'(.+?)',`).FindStringSubmatch(res.String())[1]
|
rep := regexp.MustCompile(`'rep':'(.+?)',`).FindStringSubmatch(res.String())[1]
|
||||||
up := regexp.MustCompile(`'up':(.+?),`).FindStringSubmatch(res.String())[1]
|
up := regexp.MustCompile(`'up':(.+?),`).FindStringSubmatch(res.String())[1]
|
||||||
ls := regexp.MustCompile(`'ls':(.+?),`).FindStringSubmatch(res.String())[1]
|
ls := ""
|
||||||
|
if account.Password != "" {
|
||||||
|
ls = regexp.MustCompile(`'ls':(.+?),`).FindStringSubmatch(res.String())[1]
|
||||||
|
}
|
||||||
tName := regexp.MustCompile(`'t':(.+?),`).FindStringSubmatch(res.String())[1]
|
tName := regexp.MustCompile(`'t':(.+?),`).FindStringSubmatch(res.String())[1]
|
||||||
kName := regexp.MustCompile(`'k':(.+?),`).FindStringSubmatch(res.String())[1]
|
kName := regexp.MustCompile(`'k':(.+?),`).FindStringSubmatch(res.String())[1]
|
||||||
t := regexp.MustCompile(`var ` + tName + ` = '(.+?)';`).FindStringSubmatch(res.String())[1]
|
t := regexp.MustCompile(`var ` + tName + ` = '(.+?)';`).FindStringSubmatch(res.String())[1]
|
||||||
@ -124,7 +129,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
|
|||||||
pg := 1
|
pg := 1
|
||||||
for {
|
for {
|
||||||
var resp LanZouFilesResp
|
var resp LanZouFilesResp
|
||||||
res, err = lanzouClient.R().SetResult(&resp).SetFormData(map[string]string{
|
res, err = base.RestyClient.R().SetResult(&resp).SetFormData(map[string]string{
|
||||||
"lx": lx,
|
"lx": lx,
|
||||||
"fid": fid,
|
"fid": fid,
|
||||||
"uid": uid,
|
"uid": uid,
|
||||||
@ -135,7 +140,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
|
|||||||
"up": up,
|
"up": up,
|
||||||
"ls": ls,
|
"ls": ls,
|
||||||
"pwd": account.Password,
|
"pwd": account.Password,
|
||||||
}).Post("https://wwa.lanzouo.com/filemoreajax.php")
|
}).Post(fmt.Sprintf("https://%s/filemoreajax.php", u.Host))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
break
|
break
|
||||||
@ -158,10 +163,10 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
|
|||||||
// IsNewd string `json:"is_newd"`
|
// IsNewd string `json:"is_newd"`
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// 获取下载页面的ID
|
// GetDownPageId 获取下载页面的ID
|
||||||
func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, error) {
|
func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, error) {
|
||||||
var resp LanZouFilesResp
|
var resp LanZouFilesResp
|
||||||
res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
|
res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
|
||||||
SetFormData(map[string]string{
|
SetFormData(map[string]string{
|
||||||
"task": "22",
|
"task": "22",
|
||||||
"file_id": fileId,
|
"file_id": fileId,
|
||||||
@ -190,8 +195,13 @@ type LanzouLinkResp struct {
|
|||||||
Zt int `json:"zt"`
|
Zt int `json:"zt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver *Lanzou) GetLink(downId string) (string, error) {
|
func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, error) {
|
||||||
res, err := lanzouClient.R().Get("https://wwa.lanzouo.com/" + downId)
|
shareUrl := account.SiteUrl
|
||||||
|
u, err := url.Parse(shareUrl)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
res, err := base.RestyClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -199,32 +209,42 @@ func (driver *Lanzou) GetLink(downId string) (string, error) {
|
|||||||
if len(iframe) == 0 {
|
if len(iframe) == 0 {
|
||||||
return "", fmt.Errorf("get down empty page")
|
return "", fmt.Errorf("get down empty page")
|
||||||
}
|
}
|
||||||
iframeUrl := "https://wwa.lanzouo.com" + iframe[1]
|
iframeUrl := fmt.Sprintf("https://%s%s", u.Host, iframe[1])
|
||||||
res, err = lanzouClient.R().Get(iframeUrl)
|
res, err = base.RestyClient.R().Get(iframeUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
log.Debugln(res.String())
|
||||||
ajaxdata := regexp.MustCompile(`var ajaxdata = '(.+?)'`).FindStringSubmatch(res.String())
|
ajaxdata := regexp.MustCompile(`var ajaxdata = '(.+?)'`).FindStringSubmatch(res.String())
|
||||||
if len(ajaxdata) == 0 {
|
if len(ajaxdata) == 0 {
|
||||||
return "", fmt.Errorf("get iframe empty page")
|
return "", fmt.Errorf("get iframe empty page")
|
||||||
}
|
}
|
||||||
signs := ajaxdata[1]
|
signs := ajaxdata[1]
|
||||||
sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1]
|
//sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1]
|
||||||
websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1]
|
sign := regexp.MustCompile(`'sign':'(.+?)',`).FindStringSubmatch(res.String())[1]
|
||||||
|
//websign := regexp.MustCompile(`'websign':'(.+?)'`).FindStringSubmatch(res.String())[1]
|
||||||
|
//websign := regexp.MustCompile(`var websign = '(.+?)'`).FindStringSubmatch(res.String())[1]
|
||||||
|
websign := ""
|
||||||
|
//websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1]
|
||||||
|
websignkey := regexp.MustCompile(`var websignkey = '(.+?)';`).FindStringSubmatch(res.String())[1]
|
||||||
var resp LanzouLinkResp
|
var resp LanzouLinkResp
|
||||||
form := map[string]string{
|
form := map[string]string{
|
||||||
"action": "downprocess",
|
"action": "downprocess",
|
||||||
"signs": signs,
|
"signs": signs,
|
||||||
"sign": sign,
|
"sign": sign,
|
||||||
"ves": "1",
|
"ves": "1",
|
||||||
"websign": "",
|
"websign": websign,
|
||||||
"websignkey": websignkey,
|
"websignkey": websignkey,
|
||||||
}
|
}
|
||||||
log.Debugf("form: %+v", form)
|
log.Debugf("form: %+v", form)
|
||||||
_, err = lanzouClient.R().SetResult(&resp).
|
res, err = base.RestyClient.R().SetResult(&resp).
|
||||||
SetHeader("origin", "https://wwa.lanzouo.com").
|
SetHeader("origin", "https://"+u.Host).
|
||||||
SetHeader("referer", iframeUrl).
|
SetHeader("referer", iframeUrl).
|
||||||
SetFormData(form).Post("https://wwa.lanzouo.com/ajaxm.php")
|
SetFormData(form).Post(fmt.Sprintf("https://%s/ajaxm.php", u.Host))
|
||||||
|
log.Debug(res.String())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
if resp.Zt == 1 {
|
if resp.Zt == 1 {
|
||||||
return resp.Dom + "/file/" + resp.Url, nil
|
return resp.Dom + "/file/" + resp.Url, nil
|
||||||
}
|
}
|
||||||
@ -233,7 +253,4 @@ func (driver *Lanzou) GetLink(downId string) (string, error) {
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
base.RegisterDriver(&Lanzou{})
|
base.RegisterDriver(&Lanzou{})
|
||||||
lanzouClient.
|
|
||||||
SetRetryCount(3).
|
|
||||||
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package mediatrack
|
package mediatrack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -13,11 +12,12 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,11 +45,12 @@ func (driver MediaTrack) Items() []base.Item {
|
|||||||
Required: true,
|
Required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_by",
|
Name: "order_by",
|
||||||
Label: "order_by",
|
Label: "order_by",
|
||||||
Type: base.TypeSelect,
|
Type: base.TypeSelect,
|
||||||
Values: "updated_at,title,size",
|
Values: "updated_at,title,size",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
Description: "title",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
@ -57,11 +58,15 @@ func (driver MediaTrack) Items() []base.Item {
|
|||||||
Type: base.TypeSelect,
|
Type: base.TypeSelect,
|
||||||
Values: "true,false",
|
Values: "true,false",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
Default: "false",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver MediaTrack) Save(account *model.Account, old *model.Account) error {
|
func (driver MediaTrack) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,15 +149,19 @@ func (driver MediaTrack) Path(path string, account *model.Account) (*model.File,
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver MediaTrack) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver MediaTrack) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver MediaTrack) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver MediaTrack) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotImplement
|
return nil, base.ErrNotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver MediaTrack) MakeDir(path string, account *model.Account) error {
|
func (driver MediaTrack) MakeDir(path string, account *model.Account) error {
|
||||||
|
_, err := driver.File(path, account)
|
||||||
|
if err != base.ErrPathNotFound {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
parentFile, err := driver.File(utils.Dir(path), account)
|
parentFile, err := driver.File(utils.Dir(path), account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -258,21 +267,39 @@ func (driver MediaTrack) Upload(file *model.FileStream, account *model.Account)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var buf bytes.Buffer
|
tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
|
||||||
read := io.TeeReader(file, &buf)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = tempFile.Close()
|
||||||
|
_ = os.Remove(tempFile.Name())
|
||||||
|
}()
|
||||||
|
_, err = io.Copy(tempFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
uploader := s3manager.NewUploader(s)
|
uploader := s3manager.NewUploader(s)
|
||||||
input := &s3manager.UploadInput{
|
input := &s3manager.UploadInput{
|
||||||
Bucket: &resp.Data.Bucket,
|
Bucket: &resp.Data.Bucket,
|
||||||
Key: &resp.Data.Object,
|
Key: &resp.Data.Object,
|
||||||
Body: read,
|
Body: tempFile,
|
||||||
}
|
}
|
||||||
_, err = uploader.Upload(input)
|
_, err = uploader.Upload(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", parentFile.Id)
|
url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", parentFile.Id)
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
_, err = io.Copy(h, &buf)
|
_, err = io.Copy(h, tempFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@ -39,6 +38,9 @@ func (driver Native) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Save(account *model.Account, old *model.Account) error {
|
func (driver Native) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
log.Debugf("save a account: [%s]", account.Name)
|
log.Debugf("save a account: [%s]", account.Name)
|
||||||
if !utils.Exists(account.RootFolder) {
|
if !utils.Exists(account.RootFolder) {
|
||||||
account.Status = fmt.Sprintf("[%s] not exist", account.RootFolder)
|
account.Status = fmt.Sprintf("[%s] not exist", account.RootFolder)
|
||||||
@ -55,6 +57,9 @@ func (driver Native) Save(account *model.Account, old *model.Account) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) File(path string, account *model.Account) (*model.File, error) {
|
func (driver Native) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
if utils.IsContain(strings.Split(path, "/"), "..") {
|
||||||
|
return nil, base.ErrRelativePath
|
||||||
|
}
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
if !utils.Exists(fullPath) {
|
if !utils.Exists(fullPath) {
|
||||||
return nil, base.ErrPathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
@ -79,6 +84,9 @@ func (driver Native) File(path string, account *model.Account) (*model.File, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Files(path string, account *model.Account) ([]model.File, error) {
|
func (driver Native) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
if utils.IsContain(strings.Split(path, "/"), "..") {
|
||||||
|
return nil, base.ErrRelativePath
|
||||||
|
}
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
if !utils.Exists(fullPath) {
|
if !utils.Exists(fullPath) {
|
||||||
return nil, base.ErrPathNotFound
|
return nil, base.ErrPathNotFound
|
||||||
@ -107,11 +115,14 @@ func (driver Native) Files(path string, account *model.Account) ([]model.File, e
|
|||||||
}
|
}
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
model.SortFiles(files, account)
|
|
||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||||
|
_, err := driver.File(args.Path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
fullPath := filepath.Join(account.RootFolder, args.Path)
|
fullPath := filepath.Join(account.RootFolder, args.Path)
|
||||||
s, err := os.Stat(fullPath)
|
s, err := os.Stat(fullPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -121,7 +132,7 @@ func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, e
|
|||||||
return nil, base.ErrNotFile
|
return nil, base.ErrNotFile
|
||||||
}
|
}
|
||||||
link := base.Link{
|
link := base.Link{
|
||||||
Url: fullPath,
|
FilePath: fullPath,
|
||||||
}
|
}
|
||||||
return &link, nil
|
return &link, nil
|
||||||
}
|
}
|
||||||
@ -144,21 +155,27 @@ func (driver Native) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Native) Proxy(r *http.Request, account *model.Account) {
|
||||||
// unnecessary
|
// // unnecessary
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Native) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Native) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) MakeDir(path string, account *model.Account) error {
|
func (driver Native) MakeDir(path string, account *model.Account) error {
|
||||||
|
if utils.IsContain(strings.Split(path, "/"), "..") {
|
||||||
|
return base.ErrRelativePath
|
||||||
|
}
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
err := os.MkdirAll(fullPath, 0700)
|
err := os.MkdirAll(fullPath, 0700)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Move(src string, dst string, account *model.Account) error {
|
func (driver Native) Move(src string, dst string, account *model.Account) error {
|
||||||
|
if utils.IsContain(strings.Split(src+"/"+dst, "/"), "..") {
|
||||||
|
return base.ErrRelativePath
|
||||||
|
}
|
||||||
fullSrc := filepath.Join(account.RootFolder, src)
|
fullSrc := filepath.Join(account.RootFolder, src)
|
||||||
fullDst := filepath.Join(account.RootFolder, dst)
|
fullDst := filepath.Join(account.RootFolder, dst)
|
||||||
return os.Rename(fullSrc, fullDst)
|
return os.Rename(fullSrc, fullDst)
|
||||||
@ -169,6 +186,9 @@ func (driver Native) Rename(src string, dst string, account *model.Account) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Copy(src string, dst string, account *model.Account) error {
|
func (driver Native) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
if utils.IsContain(strings.Split(src+"/"+dst, "/"), "..") {
|
||||||
|
return base.ErrRelativePath
|
||||||
|
}
|
||||||
fullSrc := filepath.Join(account.RootFolder, src)
|
fullSrc := filepath.Join(account.RootFolder, src)
|
||||||
fullDst := filepath.Join(account.RootFolder, dst)
|
fullDst := filepath.Join(account.RootFolder, dst)
|
||||||
srcFile, err := driver.File(src, account)
|
srcFile, err := driver.File(src, account)
|
||||||
@ -188,6 +208,9 @@ func (driver Native) Copy(src string, dst string, account *model.Account) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Native) Delete(path string, account *model.Account) error {
|
func (driver Native) Delete(path string, account *model.Account) error {
|
||||||
|
if utils.IsContain(strings.Split(path, "/"), "..") {
|
||||||
|
return base.ErrRelativePath
|
||||||
|
}
|
||||||
fullPath := filepath.Join(account.RootFolder, path)
|
fullPath := filepath.Join(account.RootFolder, path)
|
||||||
file, err := driver.File(path, account)
|
file, err := driver.File(path, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -203,6 +226,9 @@ func (driver Native) Upload(file *model.FileStream, account *model.Account) erro
|
|||||||
if file == nil {
|
if file == nil {
|
||||||
return base.ErrEmptyFile
|
return base.ErrEmptyFile
|
||||||
}
|
}
|
||||||
|
if utils.IsContain(strings.Split(file.ParentPath, "/"), "..") {
|
||||||
|
return base.ErrRelativePath
|
||||||
|
}
|
||||||
fullPath := filepath.Join(account.RootFolder, file.ParentPath, file.Name)
|
fullPath := filepath.Join(account.RootFolder, file.ParentPath, file.Name)
|
||||||
_, err := driver.File(filepath.Join(file.ParentPath, file.Name), account)
|
_, err := driver.File(filepath.Join(file.ParentPath, file.Name), account)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -222,6 +248,16 @@ func (driver Native) Upload(file *model.FileStream, account *model.Account) erro
|
|||||||
defer func() {
|
defer func() {
|
||||||
_ = out.Close()
|
_ = out.Close()
|
||||||
}()
|
}()
|
||||||
|
//var buf bytes.Buffer
|
||||||
|
//reader := io.TeeReader(file, &buf)
|
||||||
|
//h := md5.New()
|
||||||
|
//_, err = io.Copy(h, reader)
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
//hash := hex.EncodeToString(h.Sum(nil))
|
||||||
|
//log.Debugln("md5:", hash)
|
||||||
|
//_, err = io.Copy(out, &buf)
|
||||||
_, err = io.Copy(out, file)
|
_, err = io.Copy(out, file)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/robfig/cron/v3"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -92,37 +90,37 @@ func (driver Onedrive) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
|
func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
|
||||||
|
//if old != nil {
|
||||||
|
// conf.Cron.Remove(cron.EntryID(old.CronId))
|
||||||
|
//}
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, ok := onedriveHostMap[account.Zone]
|
_, ok := onedriveHostMap[account.Zone]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("no [%s] zone", account.Zone)
|
return fmt.Errorf("no [%s] zone", account.Zone)
|
||||||
}
|
}
|
||||||
if old != nil {
|
|
||||||
conf.Cron.Remove(cron.EntryID(old.CronId))
|
|
||||||
}
|
|
||||||
account.RootFolder = utils.ParsePath(account.RootFolder)
|
account.RootFolder = utils.ParsePath(account.RootFolder)
|
||||||
err := driver.RefreshToken(account)
|
err := driver.RefreshToken(account)
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cronId, err := conf.Cron.AddFunc("@every 1h", func() {
|
//cronId, err := conf.Cron.AddFunc("@every 1h", func() {
|
||||||
name := account.Name
|
// name := account.Name
|
||||||
log.Debugf("onedrive account name: %s", name)
|
// log.Debugf("onedrive account name: %s", name)
|
||||||
newAccount, ok := model.GetAccount(name)
|
// newAccount, ok := model.GetAccount(name)
|
||||||
log.Debugf("onedrive account: %+v", newAccount)
|
// log.Debugf("onedrive account: %+v", newAccount)
|
||||||
if !ok {
|
// if !ok {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
err = driver.RefreshToken(&newAccount)
|
// err = driver.RefreshToken(&newAccount)
|
||||||
_ = model.SaveAccount(&newAccount)
|
// _ = model.SaveAccount(&newAccount)
|
||||||
})
|
//})
|
||||||
if err != nil {
|
//if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
//}
|
||||||
account.CronId = int(cronId)
|
//account.CronId = int(cronId)
|
||||||
err = model.SaveAccount(account)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,9 +200,9 @@ func (driver Onedrive) Path(path string, account *model.Account) (*model.File, [
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Onedrive) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Onedrive) Proxy(r *http.Request, account *model.Account) {
|
||||||
c.Request.Header.Del("Origin")
|
// r.Header.Del("Origin")
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
@ -19,8 +19,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var oneClient = resty.New()
|
|
||||||
|
|
||||||
type Host struct {
|
type Host struct {
|
||||||
Oauth string
|
Oauth string
|
||||||
Api string
|
Api string
|
||||||
@ -47,7 +45,7 @@ var onedriveHostMap = map[string]Host{
|
|||||||
|
|
||||||
func (driver Onedrive) GetMetaUrl(account *model.Account, auth bool, path string) string {
|
func (driver Onedrive) GetMetaUrl(account *model.Account, auth bool, path string) string {
|
||||||
path = filepath.Join(account.RootFolder, path)
|
path = filepath.Join(account.RootFolder, path)
|
||||||
log.Debugf(path)
|
//log.Debugf(path)
|
||||||
host, _ := onedriveHostMap[account.Zone]
|
host, _ := onedriveHostMap[account.Zone]
|
||||||
if auth {
|
if auth {
|
||||||
return host.Oauth
|
return host.Oauth
|
||||||
@ -81,7 +79,7 @@ type OneTokenErr struct {
|
|||||||
|
|
||||||
func (driver Onedrive) RefreshToken(account *model.Account) error {
|
func (driver Onedrive) RefreshToken(account *model.Account) error {
|
||||||
err := driver.refreshToken(account)
|
err := driver.refreshToken(account)
|
||||||
if err != nil && err.Error() == "empty refresh_token" {
|
if err != nil && err == base.ErrEmptyToken {
|
||||||
return driver.refreshToken(account)
|
return driver.refreshToken(account)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
@ -91,7 +89,7 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
|
|||||||
url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token"
|
url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token"
|
||||||
var resp base.TokenResp
|
var resp base.TokenResp
|
||||||
var e OneTokenErr
|
var e OneTokenErr
|
||||||
_, err := oneClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
|
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
|
||||||
"grant_type": "refresh_token",
|
"grant_type": "refresh_token",
|
||||||
"client_id": account.ClientId,
|
"client_id": account.ClientId,
|
||||||
"client_secret": account.ClientSecret,
|
"client_secret": account.ClientSecret,
|
||||||
@ -109,8 +107,8 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
|
|||||||
account.Status = "work"
|
account.Status = "work"
|
||||||
}
|
}
|
||||||
if resp.RefreshToken == "" {
|
if resp.RefreshToken == "" {
|
||||||
account.Status = "empty refresh_token"
|
account.Status = base.ErrEmptyToken.Error()
|
||||||
return errors.New("empty refresh_token")
|
return base.ErrEmptyToken
|
||||||
}
|
}
|
||||||
account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken
|
account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken
|
||||||
return nil
|
return nil
|
||||||
@ -178,16 +176,17 @@ func (driver Onedrive) GetFiles(account *model.Account, path string) ([]OneFile,
|
|||||||
}
|
}
|
||||||
for nextLink != "" {
|
for nextLink != "" {
|
||||||
var files OneFiles
|
var files OneFiles
|
||||||
var e OneRespErr
|
_, err := driver.Request(nextLink, base.Get, nil, nil, nil, nil, &files, account)
|
||||||
_, err := oneClient.R().SetResult(&files).SetError(&e).
|
//var e OneRespErr
|
||||||
SetHeader("Authorization", "Bearer "+account.AccessToken).
|
//_, err := oneClient.R().SetResult(&files).SetError(&e).
|
||||||
Get(nextLink)
|
// SetHeader("Authorization", "Bearer "+account.AccessToken).
|
||||||
|
// Get(nextLink)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if e.Error.Code != "" {
|
//if e.Error.Code != "" {
|
||||||
return nil, fmt.Errorf("%s", e.Error.Message)
|
// return nil, fmt.Errorf("%s", e.Error.Message)
|
||||||
}
|
//}
|
||||||
res = append(res, files.Value...)
|
res = append(res, files.Value...)
|
||||||
nextLink = files.NextLink
|
nextLink = files.NextLink
|
||||||
}
|
}
|
||||||
@ -196,16 +195,18 @@ func (driver Onedrive) GetFiles(account *model.Account, path string) ([]OneFile,
|
|||||||
|
|
||||||
func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, error) {
|
func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, error) {
|
||||||
var file OneFile
|
var file OneFile
|
||||||
var e OneRespErr
|
//var e OneRespErr
|
||||||
_, err := oneClient.R().SetResult(&file).SetError(&e).
|
u := driver.GetMetaUrl(account, false, path)
|
||||||
SetHeader("Authorization", "Bearer "+account.AccessToken).
|
_, err := driver.Request(u, base.Get, nil, nil, nil, nil, &file, account)
|
||||||
Get(driver.GetMetaUrl(account, false, path))
|
//_, err := oneClient.R().SetResult(&file).SetError(&e).
|
||||||
|
// SetHeader("Authorization", "Bearer "+account.AccessToken).
|
||||||
|
// Get(driver.GetMetaUrl(account, false, path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if e.Error.Code != "" {
|
//if e.Error.Code != "" {
|
||||||
return nil, fmt.Errorf("%s", e.Error.Message)
|
// return nil, fmt.Errorf("%s", e.Error.Message)
|
||||||
}
|
//}
|
||||||
return &file, nil
|
return &file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -252,7 +253,7 @@ func (driver Onedrive) Request(url string, method int, headers, query, form map[
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
log.Debug(res.String())
|
//log.Debug(res.String())
|
||||||
if e.Error.Code != "" {
|
if e.Error.Code != "" {
|
||||||
if e.Error.Code == "InvalidAuthenticationToken" {
|
if e.Error.Code == "InvalidAuthenticationToken" {
|
||||||
err = driver.RefreshToken(account)
|
err = driver.RefreshToken(account)
|
||||||
@ -314,5 +315,4 @@ func (driver Onedrive) UploadBig(file *model.FileStream, account *model.Account)
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
base.RegisterDriver(&Onedrive{})
|
base.RegisterDriver(&Onedrive{})
|
||||||
oneClient.SetRetryCount(3)
|
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,30 @@ import (
|
|||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"runtime/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func Path(driver base.Driver, account *model.Account, path string) (*model.File, []model.File, error) {
|
||||||
|
return driver.Path(path, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Files(driver base.Driver, account *model.Account, path string) ([]model.File, error) {
|
||||||
|
_, files, err := Path(driver, account, path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if files == nil {
|
||||||
|
return nil, base.ErrNotFolder
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func File(driver base.Driver, account *model.Account, path string) (*model.File, error) {
|
||||||
|
return driver.File(path, account)
|
||||||
|
}
|
||||||
|
|
||||||
func MakeDir(driver base.Driver, account *model.Account, path string, clearCache bool) error {
|
func MakeDir(driver base.Driver, account *model.Account, path string, clearCache bool) error {
|
||||||
|
log.Debugf("mkdir: %s", path)
|
||||||
err := driver.MakeDir(path, account)
|
err := driver.MakeDir(path, account)
|
||||||
if err == nil && clearCache {
|
if err == nil && clearCache {
|
||||||
_ = base.DeleteCache(utils.Dir(path), account)
|
_ = base.DeleteCache(utils.Dir(path), account)
|
||||||
@ -19,6 +40,7 @@ func MakeDir(driver base.Driver, account *model.Account, path string, clearCache
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Move(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
|
func Move(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
|
||||||
|
log.Debugf("move %s to %s", src, dst)
|
||||||
rename := false
|
rename := false
|
||||||
if utils.Dir(src) == utils.Dir(dst) {
|
if utils.Dir(src) == utils.Dir(dst) {
|
||||||
rename = true
|
rename = true
|
||||||
@ -42,6 +64,7 @@ func Move(driver base.Driver, account *model.Account, src, dst string, clearCach
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Copy(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
|
func Copy(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
|
||||||
|
log.Debugf("copy %s to %s", src, dst)
|
||||||
err := driver.Copy(src, dst, account)
|
err := driver.Copy(src, dst, account)
|
||||||
if err == nil && clearCache {
|
if err == nil && clearCache {
|
||||||
_ = base.DeleteCache(utils.Dir(dst), account)
|
_ = base.DeleteCache(utils.Dir(dst), account)
|
||||||
@ -53,6 +76,7 @@ func Copy(driver base.Driver, account *model.Account, src, dst string, clearCach
|
|||||||
}
|
}
|
||||||
|
|
||||||
func Delete(driver base.Driver, account *model.Account, path string, clearCache bool) error {
|
func Delete(driver base.Driver, account *model.Account, path string, clearCache bool) error {
|
||||||
|
log.Debugf("delete %s", path)
|
||||||
err := driver.Delete(path, account)
|
err := driver.Delete(path, account)
|
||||||
if err == nil && clearCache {
|
if err == nil && clearCache {
|
||||||
_ = base.DeleteCache(utils.Dir(path), account)
|
_ = base.DeleteCache(utils.Dir(path), account)
|
||||||
@ -74,5 +98,6 @@ func Upload(driver base.Driver, account *model.Account, file *model.FileStream,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("upload error: %s", err.Error())
|
log.Errorf("upload error: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
debug.FreeOSMemory()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import (
|
|||||||
"github.com/aws/aws-sdk-go/aws/credentials"
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
||||||
"github.com/aws/aws-sdk-go/aws/session"
|
"github.com/aws/aws-sdk-go/aws/session"
|
||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -51,6 +50,9 @@ func (driver PikPak) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver PikPak) Save(account *model.Account, old *model.Account) error {
|
func (driver PikPak) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
err := driver.Login(account)
|
err := driver.Login(account)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -139,9 +141,9 @@ func (driver PikPak) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver PikPak) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver PikPak) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver PikPak) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver PikPak) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
320
drivers/quark/driver.go
Normal file
320
drivers/quark/driver.go
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
package quark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Quark struct{}
|
||||||
|
|
||||||
|
func (driver Quark) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
|
Name: "Quark",
|
||||||
|
OnlyProxy: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Items() []base.Item {
|
||||||
|
return []base.Item{
|
||||||
|
{
|
||||||
|
Name: "access_token",
|
||||||
|
Label: "Cookie",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
Description: "Unknown expiration time",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder file_id",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
Default: "0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "file_type,file_name,updated_at",
|
||||||
|
Required: true,
|
||||||
|
Default: "file_name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "order_direction",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "asc,desc",
|
||||||
|
Required: true,
|
||||||
|
Default: "asc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
_, err := driver.Get("/config", nil, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
if path == "/" {
|
||||||
|
return &model.File{
|
||||||
|
Id: account.RootFolder,
|
||||||
|
Name: account.Name,
|
||||||
|
Size: 0,
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: account.UpdatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
files, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == name {
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
var files []model.File
|
||||||
|
cache, err := base.GetCache(path, account)
|
||||||
|
if err == nil {
|
||||||
|
files, _ = cache.([]model.File)
|
||||||
|
} else {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files, err = driver.GetFiles(file.Id, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(files) > 0 {
|
||||||
|
_ = base.SetCache(path, files, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||||
|
path := args.Path
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"fids": []string{file.Id},
|
||||||
|
}
|
||||||
|
var resp DownResp
|
||||||
|
_, err = driver.Post("/file/download", data, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &base.Link{
|
||||||
|
Url: resp.Data[0].DownloadUrl,
|
||||||
|
Headers: []base.Header{
|
||||||
|
{Name: "Cookie", Value: account.AccessToken},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
log.Debugf("quark path: %s", path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !file.IsDir() {
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) MakeDir(path string, account *model.Account) error {
|
||||||
|
parentFile, err := driver.File(utils.Dir(path), account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"dir_init_lock": false,
|
||||||
|
"dir_path": "",
|
||||||
|
"file_name": utils.Base(path),
|
||||||
|
"pdir_fid": parentFile.Id,
|
||||||
|
}
|
||||||
|
_, err = driver.Post("/file", data, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Move(src string, dst string, account *model.Account) error {
|
||||||
|
srcFile, err := driver.File(src, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstParentFile, err := driver.File(utils.Dir(dst), account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"action_type": 1,
|
||||||
|
"exclude_fids": []string{},
|
||||||
|
"filelist": []string{srcFile.Id},
|
||||||
|
"to_pdir_fid": dstParentFile.Id,
|
||||||
|
}
|
||||||
|
_, err = driver.Post("/file/move", data, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Rename(src string, dst string, account *model.Account) error {
|
||||||
|
srcFile, err := driver.File(src, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"fid": srcFile.Id,
|
||||||
|
"file_name": utils.Base(dst),
|
||||||
|
}
|
||||||
|
_, err = driver.Post("/file/rename", data, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
return base.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Delete(path string, account *model.Account) error {
|
||||||
|
srcFile, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
data := base.Json{
|
||||||
|
"action_type": 1,
|
||||||
|
"exclude_fids": []string{},
|
||||||
|
"filelist": []string{srcFile.Id},
|
||||||
|
}
|
||||||
|
_, err = driver.Post("/file/delete", data, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
if file == nil {
|
||||||
|
return base.ErrEmptyFile
|
||||||
|
}
|
||||||
|
parentFile, err := driver.File(file.ParentPath, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = tempFile.Close()
|
||||||
|
_ = os.Remove(tempFile.Name())
|
||||||
|
}()
|
||||||
|
_, err = io.Copy(tempFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
m := md5.New()
|
||||||
|
_, err = io.Copy(m, tempFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
md5Str := hex.EncodeToString(m.Sum(nil))
|
||||||
|
s := sha1.New()
|
||||||
|
_, err = io.Copy(s, tempFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = tempFile.Seek(0, io.SeekStart)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sha1Str := hex.EncodeToString(s.Sum(nil))
|
||||||
|
// pre
|
||||||
|
pre, err := driver.UpPre(file, parentFile.Id, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugln("hash: ", md5Str, sha1Str)
|
||||||
|
// hash
|
||||||
|
finish, err := driver.UpHash(md5Str, sha1Str, pre.Data.TaskId, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if finish {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// part up
|
||||||
|
partSize := pre.Metadata.PartSize
|
||||||
|
var bytes []byte
|
||||||
|
md5s := make([]string, 0)
|
||||||
|
defaultBytes := make([]byte, partSize)
|
||||||
|
left := int64(file.GetSize())
|
||||||
|
partNumber := 1
|
||||||
|
for left > 0 {
|
||||||
|
if left > int64(partSize) {
|
||||||
|
bytes = defaultBytes
|
||||||
|
} else {
|
||||||
|
bytes = make([]byte, left)
|
||||||
|
}
|
||||||
|
_, err := io.ReadFull(tempFile, bytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
left -= int64(partSize)
|
||||||
|
log.Debugf("left: %d", left)
|
||||||
|
m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account)
|
||||||
|
//m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if m == "finish" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
md5s = append(md5s, m)
|
||||||
|
partNumber++
|
||||||
|
}
|
||||||
|
err = driver.UpCommit(pre, md5s, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return driver.UpFinish(pre, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Quark)(nil)
|
266
drivers/quark/quark.go
Normal file
266
drivers/quark/quark.go
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
package quark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (driver Quark) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
u := "https://drive.quark.cn/1/clouddrive" + pathname
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
req.SetHeaders(map[string]string{
|
||||||
|
"Cookie": account.AccessToken,
|
||||||
|
"Accept": "application/json, text/plain, */*",
|
||||||
|
"Referer": "https://pan.quark.cn/",
|
||||||
|
})
|
||||||
|
req.SetQueryParam("pr", "ucpro")
|
||||||
|
req.SetQueryParam("fr", "pc")
|
||||||
|
if headers != nil {
|
||||||
|
req.SetHeaders(headers)
|
||||||
|
}
|
||||||
|
if query != nil {
|
||||||
|
req.SetQueryParams(query)
|
||||||
|
}
|
||||||
|
if form != nil {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}
|
||||||
|
if data != nil {
|
||||||
|
req.SetBody(data)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
var e Resp
|
||||||
|
var err error
|
||||||
|
var res *resty.Response
|
||||||
|
req.SetError(&e)
|
||||||
|
switch method {
|
||||||
|
case base.Get:
|
||||||
|
res, err = req.Get(u)
|
||||||
|
case base.Post:
|
||||||
|
res, err = req.Post(u)
|
||||||
|
case base.Delete:
|
||||||
|
res, err = req.Delete(u)
|
||||||
|
case base.Patch:
|
||||||
|
res, err = req.Patch(u)
|
||||||
|
case base.Put:
|
||||||
|
res, err = req.Put(u)
|
||||||
|
default:
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("%s response: %s", pathname, res.String())
|
||||||
|
if e.Status >= 400 || e.Code != 0 {
|
||||||
|
return nil, errors.New(e.Message)
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Get(pathname string, query map[string]string, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
return driver.Request(pathname, base.Get, nil, query, nil, nil, resp, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) Post(pathname string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
return driver.Request(pathname, base.Post, nil, nil, nil, data, resp, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) GetFiles(parent string, account *model.Account) ([]model.File, error) {
|
||||||
|
files := make([]model.File, 0)
|
||||||
|
page := 1
|
||||||
|
size := 100
|
||||||
|
query := map[string]string{
|
||||||
|
"pdir_fid": parent,
|
||||||
|
"_size": strconv.Itoa(size),
|
||||||
|
"_fetch_total": "1",
|
||||||
|
"_sort": "file_type:asc," + account.OrderBy + ":" + account.OrderDirection,
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
query["_page"] = strconv.Itoa(page)
|
||||||
|
var resp SortResp
|
||||||
|
_, err := driver.Get("/file/sort", query, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, f := range resp.Data.List {
|
||||||
|
files = append(files, *driver.formatFile(&f))
|
||||||
|
}
|
||||||
|
if page*size >= resp.Metadata.Count {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) UpPre(file *model.FileStream, parentId string, account *model.Account) (UpPreResp, error) {
|
||||||
|
now := time.Now()
|
||||||
|
data := base.Json{
|
||||||
|
"ccp_hash_update": true,
|
||||||
|
"dir_name": "",
|
||||||
|
"file_name": file.Name,
|
||||||
|
"format_type": file.MIMEType,
|
||||||
|
"l_created_at": now.UnixMilli(),
|
||||||
|
"l_updated_at": now.UnixMilli(),
|
||||||
|
"pdir_fid": parentId,
|
||||||
|
"size": file.Size,
|
||||||
|
}
|
||||||
|
log.Debugf("uppre data: %+v", data)
|
||||||
|
var resp UpPreResp
|
||||||
|
_, err := driver.Post("/file/upload/pre", data, &resp, account)
|
||||||
|
return resp, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) UpHash(md5, sha1, taskId string, account *model.Account) (bool, error) {
|
||||||
|
data := base.Json{
|
||||||
|
"md5": md5,
|
||||||
|
"sha1": sha1,
|
||||||
|
"task_id": taskId,
|
||||||
|
}
|
||||||
|
log.Debugf("hash: %+v", data)
|
||||||
|
var resp HashResp
|
||||||
|
_, err := driver.Post("/file/update/hash", data, &resp, account)
|
||||||
|
return resp.Data.Finish, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account) (string, error) {
|
||||||
|
//func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
|
||||||
|
timeStr := time.Now().UTC().Format(http.TimeFormat)
|
||||||
|
data := base.Json{
|
||||||
|
"auth_info": pre.Data.AuthInfo,
|
||||||
|
"auth_meta": fmt.Sprintf(`PUT
|
||||||
|
|
||||||
|
%s
|
||||||
|
%s
|
||||||
|
x-oss-date:%s
|
||||||
|
x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
|
||||||
|
/%s/%s?partNumber=%d&uploadId=%s`,
|
||||||
|
mineType, timeStr, timeStr, pre.Data.Bucket, pre.Data.ObjKey, partNumber, pre.Data.UploadId),
|
||||||
|
"task_id": pre.Data.TaskId,
|
||||||
|
}
|
||||||
|
var resp UpAuthResp
|
||||||
|
_, err := driver.Post("/file/upload/auth", data, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
//if partNumber == 1 {
|
||||||
|
// finish, err := driver.UpHash(md5Str, sha1Str, pre.Data.TaskId, account)
|
||||||
|
// if err != nil {
|
||||||
|
// return "", err
|
||||||
|
// }
|
||||||
|
// if finish {
|
||||||
|
// return "finish", nil
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
|
||||||
|
res, err := base.RestyClient.R().
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"Authorization": resp.Data.AuthKey,
|
||||||
|
"Content-Type": mineType,
|
||||||
|
"Referer": "https://pan.quark.cn/",
|
||||||
|
"x-oss-date": timeStr,
|
||||||
|
"x-oss-user-agent": "aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit",
|
||||||
|
}).
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"partNumber": strconv.Itoa(partNumber),
|
||||||
|
"uploadId": pre.Data.UploadId,
|
||||||
|
}).SetBody(bytes).Put(u)
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return "", fmt.Errorf("up status: %d, error: %s", res.StatusCode(), res.String())
|
||||||
|
}
|
||||||
|
return res.Header().Get("ETag"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) UpCommit(pre UpPreResp, md5s []string, account *model.Account) error {
|
||||||
|
timeStr := time.Now().UTC().Format(http.TimeFormat)
|
||||||
|
log.Debugf("md5s: %+v", md5s)
|
||||||
|
bodyBuilder := strings.Builder{}
|
||||||
|
bodyBuilder.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<CompleteMultipartUpload>
|
||||||
|
`)
|
||||||
|
for i, m := range md5s {
|
||||||
|
bodyBuilder.WriteString(fmt.Sprintf(`<Part>
|
||||||
|
<PartNumber>%d</PartNumber>
|
||||||
|
<ETag>%s</ETag>
|
||||||
|
</Part>
|
||||||
|
`, i+1, m))
|
||||||
|
}
|
||||||
|
bodyBuilder.WriteString("</CompleteMultipartUpload>")
|
||||||
|
body := bodyBuilder.String()
|
||||||
|
m := md5.New()
|
||||||
|
m.Write([]byte(body))
|
||||||
|
contentMd5 := base64.StdEncoding.EncodeToString(m.Sum(nil))
|
||||||
|
callbackBytes, err := utils.Json.Marshal(pre.Data.Callback)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
callbackBase64 := base64.StdEncoding.EncodeToString(callbackBytes)
|
||||||
|
data := base.Json{
|
||||||
|
"auth_info": pre.Data.AuthInfo,
|
||||||
|
"auth_meta": fmt.Sprintf(`POST
|
||||||
|
%s
|
||||||
|
application/xml
|
||||||
|
%s
|
||||||
|
x-oss-callback:%s
|
||||||
|
x-oss-date:%s
|
||||||
|
x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
|
||||||
|
/%s/%s?uploadId=%s`,
|
||||||
|
contentMd5, timeStr, callbackBase64, timeStr,
|
||||||
|
pre.Data.Bucket, pre.Data.ObjKey, pre.Data.UploadId),
|
||||||
|
"task_id": pre.Data.TaskId,
|
||||||
|
}
|
||||||
|
log.Debugf("xml: %s", body)
|
||||||
|
log.Debugf("auth data: %+v", data)
|
||||||
|
var resp UpAuthResp
|
||||||
|
_, err = driver.Post("/file/upload/auth", data, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
|
||||||
|
res, err := base.RestyClient.R().
|
||||||
|
SetHeaders(map[string]string{
|
||||||
|
"Authorization": resp.Data.AuthKey,
|
||||||
|
"Content-MD5": contentMd5,
|
||||||
|
"Content-Type": "application/xml",
|
||||||
|
"Referer": "https://pan.quark.cn/",
|
||||||
|
"x-oss-callback": callbackBase64,
|
||||||
|
"x-oss-date": timeStr,
|
||||||
|
"x-oss-user-agent": "aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit",
|
||||||
|
}).
|
||||||
|
SetQueryParams(map[string]string{
|
||||||
|
"uploadId": pre.Data.UploadId,
|
||||||
|
}).SetBody(body).Post(u)
|
||||||
|
if res.StatusCode() != 200 {
|
||||||
|
return fmt.Errorf("up status: %d, error: %s", res.StatusCode(), res.String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) UpFinish(pre UpPreResp, account *model.Account) error {
|
||||||
|
data := base.Json{
|
||||||
|
"obj_key": pre.Data.ObjKey,
|
||||||
|
"task_id": pre.Data.TaskId,
|
||||||
|
}
|
||||||
|
_, err := driver.Post("/file/upload/finish", data, nil, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
base.RegisterDriver(&Quark{})
|
||||||
|
}
|
134
drivers/quark/types.go
Normal file
134
drivers/quark/types.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package quark
|
||||||
|
|
||||||
|
type Resp struct {
|
||||||
|
Status int `json:"status"`
|
||||||
|
Code int `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
//ReqId string `json:"req_id"`
|
||||||
|
//Timestamp int `json:"timestamp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Fid string `json:"fid"`
|
||||||
|
FileName string `json:"file_name"`
|
||||||
|
//PdirFid string `json:"pdir_fid"`
|
||||||
|
//Category int `json:"category"`
|
||||||
|
//FileType int `json:"file_type"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
//FormatType string `json:"format_type"`
|
||||||
|
//Status int `json:"status"`
|
||||||
|
//Tags string `json:"tags,omitempty"`
|
||||||
|
//LCreatedAt int64 `json:"l_created_at"`
|
||||||
|
LUpdatedAt int64 `json:"l_updated_at"`
|
||||||
|
//NameSpace int `json:"name_space"`
|
||||||
|
//IncludeItems int `json:"include_items,omitempty"`
|
||||||
|
//RiskType int `json:"risk_type"`
|
||||||
|
//BackupSign int `json:"backup_sign"`
|
||||||
|
//Duration int `json:"duration"`
|
||||||
|
//FileSource string `json:"file_source"`
|
||||||
|
File bool `json:"file"`
|
||||||
|
//CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
//PrivateExtra struct {} `json:"_private_extra"`
|
||||||
|
//ObjCategory string `json:"obj_category,omitempty"`
|
||||||
|
//Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortResp struct {
|
||||||
|
Resp
|
||||||
|
Data struct {
|
||||||
|
List []File `json:"list"`
|
||||||
|
} `json:"data"`
|
||||||
|
Metadata struct {
|
||||||
|
Size int `json:"_size"`
|
||||||
|
Page int `json:"_page"`
|
||||||
|
Count int `json:"_count"`
|
||||||
|
Total int `json:"_total"`
|
||||||
|
Way string `json:"way"`
|
||||||
|
} `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownResp struct {
|
||||||
|
Resp
|
||||||
|
Data []struct {
|
||||||
|
Fid string `json:"fid"`
|
||||||
|
FileName string `json:"file_name"`
|
||||||
|
PdirFid string `json:"pdir_fid"`
|
||||||
|
Category int `json:"category"`
|
||||||
|
FileType int `json:"file_type"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
FormatType string `json:"format_type"`
|
||||||
|
Status int `json:"status"`
|
||||||
|
Tags string `json:"tags"`
|
||||||
|
LCreatedAt int64 `json:"l_created_at"`
|
||||||
|
LUpdatedAt int64 `json:"l_updated_at"`
|
||||||
|
NameSpace int `json:"name_space"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
DownloadUrl string `json:"download_url"`
|
||||||
|
Md5 string `json:"md5"`
|
||||||
|
RiskType int `json:"risk_type"`
|
||||||
|
RangeSize int `json:"range_size"`
|
||||||
|
BackupSign int `json:"backup_sign"`
|
||||||
|
ObjCategory string `json:"obj_category"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
|
FileSource string `json:"file_source"`
|
||||||
|
File bool `json:"file"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
|
PrivateExtra struct {
|
||||||
|
} `json:"_private_extra"`
|
||||||
|
} `json:"data"`
|
||||||
|
Metadata struct {
|
||||||
|
Acc2 string `json:"acc2"`
|
||||||
|
Acc1 string `json:"acc1"`
|
||||||
|
} `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpPreResp struct {
|
||||||
|
Resp
|
||||||
|
Data struct {
|
||||||
|
TaskId string `json:"task_id"`
|
||||||
|
Finish bool `json:"finish"`
|
||||||
|
UploadId string `json:"upload_id"`
|
||||||
|
ObjKey string `json:"obj_key"`
|
||||||
|
UploadUrl string `json:"upload_url"`
|
||||||
|
Fid string `json:"fid"`
|
||||||
|
Bucket string `json:"bucket"`
|
||||||
|
Callback struct {
|
||||||
|
CallbackUrl string `json:"callbackUrl"`
|
||||||
|
CallbackBody string `json:"callbackBody"`
|
||||||
|
} `json:"callback"`
|
||||||
|
FormatType string `json:"format_type"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
AuthInfo string `json:"auth_info"`
|
||||||
|
} `json:"data"`
|
||||||
|
Metadata struct {
|
||||||
|
PartThread int `json:"part_thread"`
|
||||||
|
Acc2 string `json:"acc2"`
|
||||||
|
Acc1 string `json:"acc1"`
|
||||||
|
PartSize int `json:"part_size"` // 分片大小
|
||||||
|
} `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HashResp struct {
|
||||||
|
Resp
|
||||||
|
Data struct {
|
||||||
|
Finish bool `json:"finish"`
|
||||||
|
Fid string `json:"fid"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
FormatType string `json:"format_type"`
|
||||||
|
} `json:"data"`
|
||||||
|
Metadata struct {
|
||||||
|
} `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpAuthResp struct {
|
||||||
|
Resp
|
||||||
|
Data struct {
|
||||||
|
AuthKey string `json:"auth_key"`
|
||||||
|
Speed int `json:"speed"`
|
||||||
|
Headers []interface{} `json:"headers"`
|
||||||
|
} `json:"data"`
|
||||||
|
Metadata struct {
|
||||||
|
} `json:"metadata"`
|
||||||
|
}
|
31
drivers/quark/util.go
Normal file
31
drivers/quark/util.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package quark
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"path"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getTime(t int64) *time.Time {
|
||||||
|
tm := time.UnixMilli(t)
|
||||||
|
//log.Debugln(tm)
|
||||||
|
return &tm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Quark) formatFile(f *File) *model.File {
|
||||||
|
file := model.File{
|
||||||
|
Id: f.Fid,
|
||||||
|
Name: f.FileName,
|
||||||
|
Size: f.Size,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: getTime(f.UpdatedAt),
|
||||||
|
}
|
||||||
|
if f.File {
|
||||||
|
file.Type = utils.GetFileType(path.Ext(f.FileName))
|
||||||
|
} else {
|
||||||
|
file.Type = conf.FOLDER
|
||||||
|
}
|
||||||
|
return &file
|
||||||
|
}
|
@ -8,11 +8,9 @@ import (
|
|||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/aws/aws-sdk-go/service/s3"
|
"github.com/aws/aws-sdk-go/service/s3"
|
||||||
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -71,14 +69,35 @@ func (driver S3) Items() []base.Item {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "limit",
|
Name: "limit",
|
||||||
Label: "url expire time(hours)",
|
Label: "Sign url expire time(hours)",
|
||||||
Type: base.TypeNumber,
|
Type: base.TypeNumber,
|
||||||
Description: "default 4 hours",
|
Description: "default 4 hours",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "zone",
|
||||||
|
Label: "placeholder filename",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Description: "default empty string",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bool_1",
|
||||||
|
Label: "S3ForcePathStyle",
|
||||||
|
Type: base.TypeBool,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "internal_type",
|
||||||
|
Label: "ListObject Version",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "v1,v2",
|
||||||
|
Default: "v1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver S3) Save(account *model.Account, old *model.Account) error {
|
func (driver S3) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if account.Limit == 0 {
|
if account.Limit == 0 {
|
||||||
account.Limit = 4
|
account.Limit = 4
|
||||||
}
|
}
|
||||||
@ -125,7 +144,11 @@ func (driver S3) Files(path string, account *model.Account) ([]model.File, error
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
files, _ = cache.([]model.File)
|
files, _ = cache.([]model.File)
|
||||||
} else {
|
} else {
|
||||||
files, err = driver.List(path, account)
|
if account.InternalType == "v2" {
|
||||||
|
files, err = driver.ListV2(path, account)
|
||||||
|
} else {
|
||||||
|
files, err = driver.List(path, account)
|
||||||
|
}
|
||||||
if err == nil && len(files) > 0 {
|
if err == nil && len(files) > 0 {
|
||||||
_ = base.SetCache(path, files, account)
|
_ = base.SetCache(path, files, account)
|
||||||
}
|
}
|
||||||
@ -134,19 +157,28 @@ func (driver S3) Files(path string, account *model.Account) ([]model.File, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||||
client, err := driver.GetClient(account)
|
client, err := driver.GetClient(account, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
path := strings.TrimPrefix(args.Path, "/")
|
path := driver.GetKey(args.Path, account, false)
|
||||||
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(utils.Base(path)))
|
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(utils.Base(path)))
|
||||||
input := &s3.GetObjectInput{
|
input := &s3.GetObjectInput{
|
||||||
Bucket: &account.Bucket,
|
Bucket: &account.Bucket,
|
||||||
Key: &path,
|
Key: &path,
|
||||||
ResponseContentDisposition: &disposition,
|
//ResponseContentDisposition: &disposition,
|
||||||
|
}
|
||||||
|
if account.CustomHost == "" {
|
||||||
|
input.ResponseContentDisposition = &disposition
|
||||||
}
|
}
|
||||||
req, _ := client.GetObjectRequest(input)
|
req, _ := client.GetObjectRequest(input)
|
||||||
link, err := req.Presign(time.Hour * time.Duration(account.Limit))
|
var link string
|
||||||
|
if account.CustomHost != "" {
|
||||||
|
err = req.Build()
|
||||||
|
link = req.HTTPRequest.URL.String()
|
||||||
|
} else {
|
||||||
|
link, err = req.Presign(time.Hour * time.Duration(account.Limit))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -172,9 +204,9 @@ func (driver S3) Path(path string, account *model.Account) (*model.File, []model
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver S3) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver S3) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver S3) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver S3) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
@ -198,7 +230,7 @@ func (driver S3) Rename(src string, dst string, account *model.Account) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver S3) Copy(src string, dst string, account *model.Account) error {
|
func (driver S3) Copy(src string, dst string, account *model.Account) error {
|
||||||
client, err := driver.GetClient(account)
|
client, err := driver.GetClient(account, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -218,7 +250,7 @@ func (driver S3) Copy(src string, dst string, account *model.Account) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver S3) Delete(path string, account *model.Account) error {
|
func (driver S3) Delete(path string, account *model.Account) error {
|
||||||
client, err := driver.GetClient(account)
|
client, err := driver.GetClient(account, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package s3
|
package s3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
@ -22,20 +23,21 @@ var sessionsMap map[string]*session.Session
|
|||||||
|
|
||||||
func (driver S3) NewSession(account *model.Account) (*session.Session, error) {
|
func (driver S3) NewSession(account *model.Account) (*session.Session, error) {
|
||||||
cfg := &aws.Config{
|
cfg := &aws.Config{
|
||||||
Credentials: credentials.NewStaticCredentials(account.AccessKey, account.AccessSecret, ""),
|
Credentials: credentials.NewStaticCredentials(account.AccessKey, account.AccessSecret, ""),
|
||||||
Region: &account.Region,
|
Region: &account.Region,
|
||||||
Endpoint: &account.Endpoint,
|
Endpoint: &account.Endpoint,
|
||||||
|
S3ForcePathStyle: aws.Bool(account.Bool1),
|
||||||
}
|
}
|
||||||
return session.NewSession(cfg)
|
return session.NewSession(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver S3) GetClient(account *model.Account) (*s3.S3, error) {
|
func (driver S3) GetClient(account *model.Account, link bool) (*s3.S3, error) {
|
||||||
s, ok := sessionsMap[account.Name]
|
s, ok := sessionsMap[account.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("can't find [%s] session", account.Name)
|
return nil, fmt.Errorf("can't find [%s] session", account.Name)
|
||||||
}
|
}
|
||||||
client := s3.New(s)
|
client := s3.New(s)
|
||||||
if account.CustomHost != "" {
|
if link && account.CustomHost != "" {
|
||||||
cURL, err := url.Parse(account.CustomHost)
|
cURL, err := url.Parse(account.CustomHost)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -54,7 +56,7 @@ func (driver S3) GetClient(account *model.Account) (*s3.S3, error) {
|
|||||||
func (driver S3) List(prefix string, account *model.Account) ([]model.File, error) {
|
func (driver S3) List(prefix string, account *model.Account) ([]model.File, error) {
|
||||||
prefix = driver.GetKey(prefix, account, true)
|
prefix = driver.GetKey(prefix, account, true)
|
||||||
log.Debugf("list: %s", prefix)
|
log.Debugf("list: %s", prefix)
|
||||||
client, err := driver.GetClient(account)
|
client, err := driver.GetClient(account, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -73,9 +75,10 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, object := range listObjectsResult.CommonPrefixes {
|
for _, object := range listObjectsResult.CommonPrefixes {
|
||||||
|
name := utils.Base(strings.Trim(*object.Prefix, "/"))
|
||||||
file := model.File{
|
file := model.File{
|
||||||
//Id: *object.Key,
|
//Id: *object.Key,
|
||||||
Name: utils.Base(strings.Trim(*object.Prefix, "/")),
|
Name: name,
|
||||||
Driver: driver.Config().Name,
|
Driver: driver.Config().Name,
|
||||||
UpdatedAt: account.UpdatedAt,
|
UpdatedAt: account.UpdatedAt,
|
||||||
TimeStr: "-",
|
TimeStr: "-",
|
||||||
@ -84,9 +87,13 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
|
|||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
for _, object := range listObjectsResult.Contents {
|
for _, object := range listObjectsResult.Contents {
|
||||||
|
name := utils.Base(*object.Key)
|
||||||
|
if name == account.Zone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
file := model.File{
|
file := model.File{
|
||||||
//Id: *object.Key,
|
//Id: *object.Key,
|
||||||
Name: utils.Base(*object.Key),
|
Name: name,
|
||||||
Size: *object.Size,
|
Size: *object.Size,
|
||||||
Driver: driver.Config().Name,
|
Driver: driver.Config().Name,
|
||||||
UpdatedAt: object.LastModified,
|
UpdatedAt: object.LastModified,
|
||||||
@ -94,6 +101,9 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
|
|||||||
}
|
}
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
}
|
}
|
||||||
|
if listObjectsResult.IsTruncated == nil {
|
||||||
|
return nil, errors.New("IsTruncated nil")
|
||||||
|
}
|
||||||
if *listObjectsResult.IsTruncated {
|
if *listObjectsResult.IsTruncated {
|
||||||
marker = *listObjectsResult.NextMarker
|
marker = *listObjectsResult.NextMarker
|
||||||
} else {
|
} else {
|
||||||
@ -103,6 +113,73 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (driver S3) ListV2(prefix string, account *model.Account) ([]model.File, error) {
|
||||||
|
prefix = driver.GetKey(prefix, account, true)
|
||||||
|
//if prefix == "" {
|
||||||
|
// prefix = "/"
|
||||||
|
//}
|
||||||
|
log.Debugf("list: %s", prefix)
|
||||||
|
client, err := driver.GetClient(account, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
files := make([]model.File, 0)
|
||||||
|
var continuationToken, startAfter *string
|
||||||
|
for {
|
||||||
|
input := &s3.ListObjectsV2Input{
|
||||||
|
Bucket: &account.Bucket,
|
||||||
|
ContinuationToken: continuationToken,
|
||||||
|
Prefix: &prefix,
|
||||||
|
Delimiter: aws.String("/"),
|
||||||
|
StartAfter: startAfter,
|
||||||
|
}
|
||||||
|
listObjectsResult, err := client.ListObjectsV2(input)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debugf("resp: %+v", listObjectsResult)
|
||||||
|
for _, object := range listObjectsResult.CommonPrefixes {
|
||||||
|
name := utils.Base(strings.Trim(*object.Prefix, "/"))
|
||||||
|
file := model.File{
|
||||||
|
//Id: *object.Key,
|
||||||
|
Name: name,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: account.UpdatedAt,
|
||||||
|
TimeStr: "-",
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
for _, object := range listObjectsResult.Contents {
|
||||||
|
name := utils.Base(*object.Key)
|
||||||
|
if name == account.Zone {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
file := model.File{
|
||||||
|
//Id: *object.Key,
|
||||||
|
Name: name,
|
||||||
|
Size: *object.Size,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: object.LastModified,
|
||||||
|
Type: utils.GetFileType(path.Ext(*object.Key)),
|
||||||
|
}
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
if !aws.BoolValue(listObjectsResult.IsTruncated) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if listObjectsResult.NextContinuationToken != nil {
|
||||||
|
continuationToken = listObjectsResult.NextContinuationToken
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(listObjectsResult.Contents) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
startAfter = listObjectsResult.Contents[len(listObjectsResult.Contents)-1].Key
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
|
func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
|
||||||
path = utils.Join(account.RootFolder, path)
|
path = utils.Join(account.RootFolder, path)
|
||||||
path = strings.TrimPrefix(path, "/")
|
path = strings.TrimPrefix(path, "/")
|
||||||
@ -113,6 +190,6 @@ func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
sessionsMap = make(map[string]*session.Session, 0)
|
sessionsMap = make(map[string]*session.Session)
|
||||||
base.RegisterDriver(&S3{})
|
base.RegisterDriver(&S3{})
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -48,6 +47,9 @@ func (driver Shandian) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Shandian) Save(account *model.Account, old *model.Account) error {
|
func (driver Shandian) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if account.RootFolder == "" {
|
if account.RootFolder == "" {
|
||||||
account.RootFolder = "0"
|
account.RootFolder = "0"
|
||||||
}
|
}
|
||||||
@ -151,9 +153,9 @@ func (driver Shandian) Path(path string, account *model.Account) (*model.File, [
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Shandian) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Shandian) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
@ -52,6 +51,7 @@ func (driver Teambition) Items() []base.Item {
|
|||||||
Type: base.TypeSelect,
|
Type: base.TypeSelect,
|
||||||
Values: "fileName,fileSize,updated,created",
|
Values: "fileName,fileSize,updated,created",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
Default: "fileName",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "order_direction",
|
Name: "order_direction",
|
||||||
@ -59,11 +59,15 @@ func (driver Teambition) Items() []base.Item {
|
|||||||
Type: base.TypeSelect,
|
Type: base.TypeSelect,
|
||||||
Values: "Asc,Desc",
|
Values: "Asc,Desc",
|
||||||
Required: true,
|
Required: true,
|
||||||
|
Default: "Asc",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Teambition) Save(account *model.Account, old *model.Account) error {
|
func (driver Teambition) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
_, err := driver.Request("/api/v2/roles", base.Get, nil, nil, nil, nil, nil, account)
|
_, err := driver.Request("/api/v2/roles", base.Get, nil, nil, nil, nil, nil, account)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -146,9 +150,9 @@ func (driver Teambition) Path(path string, account *model.Account) (*model.File,
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver Teambition) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver Teambition) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver Teambition) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver Teambition) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
@ -249,7 +253,34 @@ func (driver Teambition) Delete(path string, account *model.Account) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver Teambition) Upload(file *model.FileStream, account *model.Account) error {
|
func (driver Teambition) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
return base.ErrNotImplement
|
if file == nil {
|
||||||
|
return base.ErrEmptyFile
|
||||||
|
}
|
||||||
|
parentFile, err := driver.File(file.ParentPath, account)
|
||||||
|
if !parentFile.IsDir() {
|
||||||
|
return base.ErrNotFolder
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := driver.Request("/projects", base.Get, nil, nil, nil, nil, nil, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
token := GetBetweenStr(string(res), "strikerAuth":"", "","phoneForLogin")
|
||||||
|
var newFile *FileUpload
|
||||||
|
if file.Size <= 20971520 {
|
||||||
|
// post upload
|
||||||
|
newFile, err = driver.upload(file, token, account)
|
||||||
|
} else {
|
||||||
|
// chunk upload
|
||||||
|
//err = base.ErrNotImplement
|
||||||
|
newFile, err = driver.chunkUpload(file, token, account)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return driver.finishUpload(newFile, parentFile.Id, account)
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ base.Driver = (*Teambition)(nil)
|
var _ base.Driver = (*Teambition)(nil)
|
||||||
|
@ -2,11 +2,14 @@ package teambition
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
@ -66,25 +69,6 @@ func (driver Teambition) Request(pathname string, method int, headers, query, fo
|
|||||||
return res.Body(), nil
|
return res.Body(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Collection struct {
|
|
||||||
ID string `json:"_id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Updated time.Time `json:"updated"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Work struct {
|
|
||||||
ID string `json:"_id"`
|
|
||||||
FileName string `json:"fileName"`
|
|
||||||
FileSize int64 `json:"fileSize"`
|
|
||||||
FileKey string `json:"fileKey"`
|
|
||||||
FileCategory string `json:"fileCategory"`
|
|
||||||
DownloadURL string `json:"downloadUrl"`
|
|
||||||
ThumbnailURL string `json:"thumbnailUrl"`
|
|
||||||
Thumbnail string `json:"thumbnail"`
|
|
||||||
Updated time.Time `json:"updated"`
|
|
||||||
PreviewURL string `json:"previewUrl"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]model.File, error) {
|
func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]model.File, error) {
|
||||||
files := make([]model.File, 0)
|
files := make([]model.File, 0)
|
||||||
page := 1
|
page := 1
|
||||||
@ -114,7 +98,7 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo
|
|||||||
Size: 0,
|
Size: 0,
|
||||||
Type: conf.FOLDER,
|
Type: conf.FOLDER,
|
||||||
Driver: driver.Config().Name,
|
Driver: driver.Config().Name,
|
||||||
UpdatedAt: &collection.Updated,
|
UpdatedAt: collection.Updated,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +126,7 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo
|
|||||||
Size: work.FileSize,
|
Size: work.FileSize,
|
||||||
Type: utils.GetFileType(path.Ext(work.FileName)),
|
Type: utils.GetFileType(path.Ext(work.FileName)),
|
||||||
Driver: driver.Config().Name,
|
Driver: driver.Config().Name,
|
||||||
UpdatedAt: &work.Updated,
|
UpdatedAt: work.Updated,
|
||||||
Thumbnail: work.Thumbnail,
|
Thumbnail: work.Thumbnail,
|
||||||
Url: work.DownloadURL,
|
Url: work.DownloadURL,
|
||||||
})
|
})
|
||||||
@ -151,6 +135,103 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo
|
|||||||
return files, nil
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (driver Teambition) upload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) {
|
||||||
|
prefix := "tcs"
|
||||||
|
if account.InternalType == "International" {
|
||||||
|
prefix = "us-tcs"
|
||||||
|
}
|
||||||
|
var newFile FileUpload
|
||||||
|
_, err := base.RestyClient.R().SetResult(&newFile).SetHeader("Authorization", token).
|
||||||
|
SetMultipartFormData(map[string]string{
|
||||||
|
"name": file.GetFileName(),
|
||||||
|
"type": file.GetMIMEType(),
|
||||||
|
"size": strconv.FormatUint(file.GetSize(), 10),
|
||||||
|
//"lastModifiedDate": "",
|
||||||
|
}).SetMultipartField("file", file.GetFileName(), file.GetMIMEType(), file).
|
||||||
|
Post(fmt.Sprintf("https://%s.teambition.net/upload", prefix))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &newFile, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Teambition) chunkUpload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) {
|
||||||
|
prefix := "tcs"
|
||||||
|
referer := "https://www.teambition.com/"
|
||||||
|
if account.InternalType == "International" {
|
||||||
|
prefix = "us-tcs"
|
||||||
|
referer = "https://us.teambition.com/"
|
||||||
|
}
|
||||||
|
var newChunk ChunkUpload
|
||||||
|
_, err := base.RestyClient.R().SetResult(&newChunk).SetHeader("Authorization", token).
|
||||||
|
SetBody(base.Json{
|
||||||
|
"fileName": file.GetFileName(),
|
||||||
|
"fileSize": file.GetSize(),
|
||||||
|
"lastUpdated": time.Now(),
|
||||||
|
}).Post(fmt.Sprintf("https://%s.teambition.net/upload/chunk", prefix))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for i := 0; i < newChunk.Chunks; i++ {
|
||||||
|
chunkSize := newChunk.ChunkSize
|
||||||
|
if i == newChunk.Chunks-1 {
|
||||||
|
chunkSize = int(file.GetSize()) - i*chunkSize
|
||||||
|
}
|
||||||
|
log.Debugf("%d : %d", i, chunkSize)
|
||||||
|
chunkData := make([]byte, chunkSize)
|
||||||
|
_, err = io.ReadFull(file, chunkData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("https://%s.teambition.net/upload/chunk/%s?chunk=%d&chunks=%d",
|
||||||
|
prefix, newChunk.FileKey, i+1, newChunk.Chunks)
|
||||||
|
log.Debugf("url: %s", u)
|
||||||
|
res, err := base.RestyClient.R().SetHeaders(map[string]string{
|
||||||
|
"Authorization": token,
|
||||||
|
"Content-Type": "application/octet-stream",
|
||||||
|
"Referer": referer,
|
||||||
|
}).SetBody(chunkData).Post(u)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
log.Debug(res.Status(), res.String())
|
||||||
|
//req, err := http.NewRequest("POST",
|
||||||
|
// u,
|
||||||
|
// bytes.NewBuffer(chunkData))
|
||||||
|
//if err != nil {
|
||||||
|
// return nil, err
|
||||||
|
//}
|
||||||
|
//req.Header.Set("Authorization", token)
|
||||||
|
//req.Header.Set("Content-Type", "application/octet-stream")
|
||||||
|
//req.Header.Set("Referer", "https://www.teambition.com/")
|
||||||
|
//resp, err := base.HttpClient.Do(req)
|
||||||
|
//res, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
//log.Debugf("chunk upload status: %s, res: %s", resp.Status, string(res))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := base.RestyClient.R().SetHeader("Authorization", token).Post(
|
||||||
|
fmt.Sprintf("https://%s.teambition.net/upload/chunk/%s",
|
||||||
|
prefix, newChunk.FileKey))
|
||||||
|
log.Debug(res.Status(), res.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &newChunk.FileUpload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Teambition) finishUpload(file *FileUpload, parentId string, account *model.Account) error {
|
||||||
|
file.InvolveMembers = []interface{}{}
|
||||||
|
file.Visible = "members"
|
||||||
|
file.ParentId = parentId
|
||||||
|
_, err := driver.Request("/api/works", base.Post, nil, nil, nil, base.Json{
|
||||||
|
"works": []FileUpload{*file},
|
||||||
|
"_parentId": parentId,
|
||||||
|
}, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
base.RegisterDriver(&Teambition{})
|
base.RegisterDriver(&Teambition{})
|
||||||
}
|
}
|
||||||
|
63
drivers/teambition/types.go
Normal file
63
drivers/teambition/types.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
package teambition
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Collection struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
Updated *time.Time `json:"updated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Work struct {
|
||||||
|
ID string `json:"_id"`
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
FileSize int64 `json:"fileSize"`
|
||||||
|
FileKey string `json:"fileKey"`
|
||||||
|
FileCategory string `json:"fileCategory"`
|
||||||
|
DownloadURL string `json:"downloadUrl"`
|
||||||
|
ThumbnailURL string `json:"thumbnailUrl"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
Updated *time.Time `json:"updated"`
|
||||||
|
PreviewURL string `json:"previewUrl"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileUpload struct {
|
||||||
|
FileKey string `json:"fileKey"`
|
||||||
|
FileName string `json:"fileName"`
|
||||||
|
FileType string `json:"fileType"`
|
||||||
|
FileSize int `json:"fileSize"`
|
||||||
|
FileCategory string `json:"fileCategory"`
|
||||||
|
ImageWidth int `json:"imageWidth"`
|
||||||
|
ImageHeight int `json:"imageHeight"`
|
||||||
|
InvolveMembers []interface{} `json:"involveMembers"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Visible string `json:"visible"`
|
||||||
|
ParentId string `json:"_parentId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChunkUpload struct {
|
||||||
|
FileUpload
|
||||||
|
Storage string `json:"storage"`
|
||||||
|
MimeType string `json:"mimeType"`
|
||||||
|
Chunks int `json:"chunks"`
|
||||||
|
ChunkSize int `json:"chunkSize"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
FileMD5 string `json:"fileMD5"`
|
||||||
|
LastUpdated time.Time `json:"lastUpdated"`
|
||||||
|
UploadedChunks []interface{} `json:"uploadedChunks"`
|
||||||
|
Token struct {
|
||||||
|
AppID string `json:"AppID"`
|
||||||
|
OrganizationID string `json:"OrganizationID"`
|
||||||
|
UserID string `json:"UserID"`
|
||||||
|
Exp time.Time `json:"Exp"`
|
||||||
|
Storage string `json:"Storage"`
|
||||||
|
Resource string `json:"Resource"`
|
||||||
|
Speed int `json:"Speed"`
|
||||||
|
} `json:"token"`
|
||||||
|
DownloadUrl string `json:"downloadUrl"`
|
||||||
|
ThumbnailUrl string `json:"thumbnailUrl"`
|
||||||
|
PreviewUrl string `json:"previewUrl"`
|
||||||
|
ImmPreviewUrl string `json:"immPreviewUrl"`
|
||||||
|
PreviewExt string `json:"previewExt"`
|
||||||
|
LastUploadTime interface{} `json:"lastUploadTime"`
|
||||||
|
}
|
18
drivers/teambition/util.go
Normal file
18
drivers/teambition/util.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package teambition
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func GetBetweenStr(str, start, end string) string {
|
||||||
|
n := strings.Index(str, start)
|
||||||
|
if n == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
n = n + len(start)
|
||||||
|
str = string([]byte(str)[n:])
|
||||||
|
m := strings.Index(str, end)
|
||||||
|
if m == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
str = string([]byte(str)[:m])
|
||||||
|
return str
|
||||||
|
}
|
237
drivers/uss/driver.go
Normal file
237
drivers/uss/driver.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package uss
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/upyun/go-sdk/v3/upyun"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type USS struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
|
Name: "USS",
|
||||||
|
LocalSort: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Items() []base.Item {
|
||||||
|
return []base.Item{
|
||||||
|
{
|
||||||
|
Name: "bucket",
|
||||||
|
Label: "Bucket",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "endpoint",
|
||||||
|
Label: "Endpoint",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "access_key",
|
||||||
|
Label: "Operator Name",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "access_secret",
|
||||||
|
Label: "Operator Password",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder path",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "custom_host",
|
||||||
|
Label: "Custom Host",
|
||||||
|
Type: base.TypeString,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "limit",
|
||||||
|
Label: "Sign url expire time(hours)",
|
||||||
|
Type: base.TypeNumber,
|
||||||
|
Default: "4",
|
||||||
|
Description: "default 4 hours",
|
||||||
|
},
|
||||||
|
//{
|
||||||
|
// Name: "zone",
|
||||||
|
// Label: "placeholder filename",
|
||||||
|
// Type: base.TypeString,
|
||||||
|
// Description: "default empty string",
|
||||||
|
//},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if account.Limit == 0 {
|
||||||
|
account.Limit = 4
|
||||||
|
}
|
||||||
|
client, err := driver.NewUpYun(account)
|
||||||
|
if err != nil {
|
||||||
|
account.Status = err.Error()
|
||||||
|
} else {
|
||||||
|
clientsMap[account.Name] = client
|
||||||
|
account.Status = "work"
|
||||||
|
}
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
if path == "/" {
|
||||||
|
return &model.File{
|
||||||
|
Id: account.RootFolder,
|
||||||
|
Name: account.Name,
|
||||||
|
Size: 0,
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: account.UpdatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
files, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == name {
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
var files []model.File
|
||||||
|
cache, err := base.GetCache(path, account)
|
||||||
|
if err == nil {
|
||||||
|
files, _ = cache.([]model.File)
|
||||||
|
} else {
|
||||||
|
files, err = driver.List(path, account)
|
||||||
|
if err == nil && len(files) > 0 {
|
||||||
|
_ = base.SetCache(path, files, account)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||||
|
key := driver.GetKey(args.Path, account, false)
|
||||||
|
host := account.CustomHost
|
||||||
|
if host == "" {
|
||||||
|
host = account.Endpoint
|
||||||
|
}
|
||||||
|
if strings.Contains(host, "://") {
|
||||||
|
host = "https://" + host
|
||||||
|
}
|
||||||
|
u := fmt.Sprintf("%s/%s", host, key)
|
||||||
|
downExp := time.Hour * time.Duration(account.Limit)
|
||||||
|
expireAt := time.Now().Add(downExp).Unix()
|
||||||
|
upd := url.QueryEscape(utils.Base(args.Path))
|
||||||
|
signStr := strings.Join([]string{account.AccessSecret, fmt.Sprint(expireAt), fmt.Sprintf("/%s", key)}, "&")
|
||||||
|
upt := utils.GetMD5Encode(signStr)[12:20] + fmt.Sprint(expireAt)
|
||||||
|
link := fmt.Sprintf("%s?_upd=%s&_upt=%s", u, upd, upt)
|
||||||
|
return &base.Link{Url: link}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
log.Debugf("s3 path: %s", path)
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !file.IsDir() {
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) MakeDir(path string, account *model.Account) error {
|
||||||
|
client, err := driver.GetClient(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.Mkdir(driver.GetKey(path, account, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Move(src string, dst string, account *model.Account) error {
|
||||||
|
client, err := driver.GetClient(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.Move(&upyun.MoveObjectConfig{
|
||||||
|
SrcPath: driver.GetKey(src, account, false),
|
||||||
|
DestPath: driver.GetKey(dst, account, false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Rename(src string, dst string, account *model.Account) error {
|
||||||
|
return driver.Move(src, dst, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
client, err := driver.GetClient(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.Copy(&upyun.CopyObjectConfig{
|
||||||
|
SrcPath: driver.GetKey(src, account, false),
|
||||||
|
DestPath: driver.GetKey(dst, account, false),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Delete(path string, account *model.Account) error {
|
||||||
|
client, err := driver.GetClient(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.Delete(&upyun.DeleteObjectConfig{
|
||||||
|
Path: driver.GetKey(path, account, false),
|
||||||
|
Async: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
if file == nil {
|
||||||
|
return base.ErrEmptyFile
|
||||||
|
}
|
||||||
|
client, err := driver.GetClient(account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return client.Put(&upyun.PutObjectConfig{
|
||||||
|
Path: driver.GetKey(utils.Join(file.ParentPath, file.GetFileName()), account, false),
|
||||||
|
Reader: file,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*USS)(nil)
|
82
drivers/uss/uss.go
Normal file
82
drivers/uss/uss.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package uss
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/upyun/go-sdk/v3/upyun"
|
||||||
|
"path"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var clientsMap map[string]*upyun.UpYun
|
||||||
|
|
||||||
|
func (driver USS) NewUpYun(account *model.Account) (*upyun.UpYun, error) {
|
||||||
|
return upyun.NewUpYun(&upyun.UpYunConfig{
|
||||||
|
Bucket: account.Bucket,
|
||||||
|
Operator: account.AccessKey,
|
||||||
|
Password: account.AccessToken,
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) GetClient(account *model.Account) (*upyun.UpYun, error) {
|
||||||
|
client, ok := clientsMap[account.Name]
|
||||||
|
if ok {
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("can't get client")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) List(prefix string, account *model.Account) ([]model.File, error) {
|
||||||
|
prefix = driver.GetKey(prefix, account, true)
|
||||||
|
client, err := driver.GetClient(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objsChan := make(chan *upyun.FileInfo, 10)
|
||||||
|
defer close(objsChan)
|
||||||
|
go func() {
|
||||||
|
err = client.List(&upyun.GetObjectsConfig{
|
||||||
|
Path: prefix,
|
||||||
|
ObjectsChan: objsChan,
|
||||||
|
MaxListObjects: 0,
|
||||||
|
MaxListLevel: 1,
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res := make([]model.File, 0)
|
||||||
|
for obj := range objsChan {
|
||||||
|
t := obj.Time
|
||||||
|
f := model.File{
|
||||||
|
Name: obj.Name,
|
||||||
|
Size: obj.Size,
|
||||||
|
UpdatedAt: &t,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
}
|
||||||
|
if obj.IsDir {
|
||||||
|
f.Type = conf.FOLDER
|
||||||
|
} else {
|
||||||
|
f.Type = utils.GetFileType(path.Ext(obj.Name))
|
||||||
|
}
|
||||||
|
res = append(res, f)
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver USS) GetKey(path string, account *model.Account, dir bool) string {
|
||||||
|
path = utils.Join(account.RootFolder, path)
|
||||||
|
path = strings.TrimPrefix(path, "/")
|
||||||
|
if dir {
|
||||||
|
path += "/"
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
clientsMap = make(map[string]*upyun.UpYun)
|
||||||
|
base.RegisterDriver(&USS{})
|
||||||
|
}
|
@ -5,7 +5,6 @@ import (
|
|||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,6 +44,9 @@ func (driver WebDav) Items() []base.Item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (driver WebDav) Save(account *model.Account, old *model.Account) error {
|
func (driver WebDav) Save(account *model.Account, old *model.Account) error {
|
||||||
|
if account == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
account.Status = "work"
|
account.Status = "work"
|
||||||
_ = model.SaveAccount(account)
|
_ = model.SaveAccount(account)
|
||||||
return nil
|
return nil
|
||||||
@ -134,9 +136,9 @@ func (driver WebDav) Path(path string, account *model.Account) (*model.File, []m
|
|||||||
return nil, files, nil
|
return nil, files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (driver WebDav) Proxy(c *gin.Context, account *model.Account) {
|
//func (driver WebDav) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
}
|
//}
|
||||||
|
|
||||||
func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) {
|
func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
return nil, base.ErrNotSupport
|
return nil, base.ErrNotSupport
|
||||||
|
224
drivers/yandex/driver.go
Normal file
224
drivers/yandex/driver.go
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package yandex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Yandex struct{}
|
||||||
|
|
||||||
|
func (driver Yandex) Config() base.DriverConfig {
|
||||||
|
return base.DriverConfig{
|
||||||
|
Name: "Yandex.Disk",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Items() []base.Item {
|
||||||
|
return []base.Item{
|
||||||
|
{
|
||||||
|
Name: "refresh_token",
|
||||||
|
Label: "refresh token",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "root_folder",
|
||||||
|
Label: "root folder path",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Default: "/",
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_by",
|
||||||
|
Label: "order_by",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Default: "name",
|
||||||
|
Values: "name,path,created,modified,size",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "order_direction",
|
||||||
|
Label: "order_direction",
|
||||||
|
Type: base.TypeSelect,
|
||||||
|
Values: "asc,desc",
|
||||||
|
Default: "asc",
|
||||||
|
Required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "client_id",
|
||||||
|
Label: "client id",
|
||||||
|
Default: "a78d5a69054042fa936f6c77f9a0ae8b",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "client_secret",
|
||||||
|
Label: "client secret",
|
||||||
|
Default: "9c119bbb04b346d2a52aa64401936b2b",
|
||||||
|
Type: base.TypeString,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Save(account *model.Account, old *model.Account) error {
|
||||||
|
return driver.RefreshToken(account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) File(path string, account *model.Account) (*model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
if path == "/" {
|
||||||
|
return &model.File{
|
||||||
|
Id: account.RootFolder,
|
||||||
|
Name: account.Name,
|
||||||
|
Size: 0,
|
||||||
|
Type: conf.FOLDER,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: account.UpdatedAt,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
files, err := driver.Files(dir, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == name {
|
||||||
|
return &file, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, base.ErrPathNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Files(path string, account *model.Account) ([]model.File, error) {
|
||||||
|
path = utils.ParsePath(path)
|
||||||
|
cache, err := base.GetCache(path, account)
|
||||||
|
if err == nil {
|
||||||
|
files, _ := cache.([]model.File)
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
files, err := driver.GetFiles(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(files) > 0 {
|
||||||
|
_ = base.SetCache(path, files, account)
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Link(args base.Args, account *model.Account) (*base.Link, error) {
|
||||||
|
path := utils.Join(account.RootFolder, args.Path)
|
||||||
|
log.Debugln("down path:", path)
|
||||||
|
var resp DownResp
|
||||||
|
_, err := driver.Request("/download", base.Get, nil, map[string]string{
|
||||||
|
"path": path,
|
||||||
|
}, nil, nil, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
link := base.Link{
|
||||||
|
Url: resp.Href,
|
||||||
|
}
|
||||||
|
return &link, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Path(path string, account *model.Account) (*model.File, []model.File, error) {
|
||||||
|
file, err := driver.File(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !file.IsDir() {
|
||||||
|
return file, nil, nil
|
||||||
|
}
|
||||||
|
files, err := driver.Files(path, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return nil, files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (driver Yandex) Proxy(r *http.Request, account *model.Account) {
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
func (driver Yandex) Preview(path string, account *model.Account) (interface{}, error) {
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) MakeDir(path string, account *model.Account) error {
|
||||||
|
path = utils.Join(account.RootFolder, path)
|
||||||
|
_, err := driver.Request("", base.Put, nil, map[string]string{
|
||||||
|
"path": path,
|
||||||
|
}, nil, nil, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Move(src string, dst string, account *model.Account) error {
|
||||||
|
from := utils.Join(account.RootFolder, src)
|
||||||
|
path := utils.Join(account.RootFolder, dst)
|
||||||
|
_, err := driver.Request("/move", base.Post, nil, map[string]string{
|
||||||
|
"from": from,
|
||||||
|
"path": path,
|
||||||
|
"overwrite": "true",
|
||||||
|
}, nil, nil, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Rename(src string, dst string, account *model.Account) error {
|
||||||
|
return driver.Move(src, dst, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Copy(src string, dst string, account *model.Account) error {
|
||||||
|
from := utils.Join(account.RootFolder, src)
|
||||||
|
path := utils.Join(account.RootFolder, dst)
|
||||||
|
_, err := driver.Request("/copy", base.Post, nil, map[string]string{
|
||||||
|
"from": from,
|
||||||
|
"path": path,
|
||||||
|
"overwrite": "true",
|
||||||
|
}, nil, nil, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Delete(path string, account *model.Account) error {
|
||||||
|
path = utils.Join(account.RootFolder, path)
|
||||||
|
_, err := driver.Request("", base.Delete, nil, map[string]string{
|
||||||
|
"path": path,
|
||||||
|
}, nil, nil, nil, account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Upload(file *model.FileStream, account *model.Account) error {
|
||||||
|
if file == nil {
|
||||||
|
return base.ErrEmptyFile
|
||||||
|
}
|
||||||
|
path := utils.Join(account.RootFolder, file.ParentPath, file.Name)
|
||||||
|
var resp UploadResp
|
||||||
|
_, err := driver.Request("/upload", base.Get, nil, map[string]string{
|
||||||
|
"path": path,
|
||||||
|
"overwrite": "true",
|
||||||
|
}, nil, nil, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(resp.Method, resp.Href, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Length", strconv.FormatUint(file.Size, 10))
|
||||||
|
req.Header.Set("Content-Type", "application/octet-stream")
|
||||||
|
_, err = base.HttpClient.Do(req)
|
||||||
|
//res, err := base.RestyClient.R().
|
||||||
|
// SetHeader("Content-Length", strconv.FormatUint(file.Size, 10)).
|
||||||
|
// SetBody(file).Put(resp.Href)
|
||||||
|
//log.Debugln(res.Status(), res.String())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ base.Driver = (*Yandex)(nil)
|
74
drivers/yandex/types.go
Normal file
74
drivers/yandex/types.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package yandex
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type TokenErrResp struct {
|
||||||
|
ErrorDescription string `json:"error_description"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrResp struct {
|
||||||
|
Message string `json:"message"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
//AntivirusStatus string `json:"antivirus_status"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
//CommentIds struct {
|
||||||
|
// PrivateResource string `json:"private_resource"`
|
||||||
|
// PublicResource string `json:"public_resource"`
|
||||||
|
//} `json:"comment_ids"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
//Exif struct {
|
||||||
|
// DateTime time.Time `json:"date_time"`
|
||||||
|
//} `json:"exif"`
|
||||||
|
//Created time.Time `json:"created"`
|
||||||
|
//ResourceId string `json:"resource_id"`
|
||||||
|
Modified *time.Time `json:"modified"`
|
||||||
|
//MimeType string `json:"mime_type"`
|
||||||
|
File string `json:"file"`
|
||||||
|
//MediaType string `json:"media_type"`
|
||||||
|
Preview string `json:"preview"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
//Sha256 string `json:"sha256"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
//Md5 string `json:"md5"`
|
||||||
|
//Revision int64 `json:"revision"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilesResp struct {
|
||||||
|
Embedded struct {
|
||||||
|
Sort string `json:"sort"`
|
||||||
|
Items []File `json:"items"`
|
||||||
|
Limit int `json:"limit"`
|
||||||
|
Offset int `json:"offset"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
} `json:"_embedded"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Exif struct {
|
||||||
|
} `json:"exif"`
|
||||||
|
ResourceId string `json:"resource_id"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Modified time.Time `json:"modified"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
CommentIds struct {
|
||||||
|
} `json:"comment_ids"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Revision int64 `json:"revision"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DownResp struct {
|
||||||
|
Href string `json:"href"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Templated bool `json:"templated"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UploadResp struct {
|
||||||
|
OperationId string `json:"operation_id"`
|
||||||
|
Href string `json:"href"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Templated bool `json:"templated"`
|
||||||
|
}
|
1
drivers/yandex/util.go
Normal file
1
drivers/yandex/util.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package yandex
|
154
drivers/yandex/yandex.go
Normal file
154
drivers/yandex/yandex.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package yandex
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (driver Yandex) RefreshToken(account *model.Account) error {
|
||||||
|
err := driver.refreshToken(account)
|
||||||
|
if err != nil && err == base.ErrEmptyToken {
|
||||||
|
err = driver.refreshToken(account)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
account.Status = err.Error()
|
||||||
|
}
|
||||||
|
_ = model.SaveAccount(account)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) refreshToken(account *model.Account) error {
|
||||||
|
u := "https://oauth.yandex.com/token"
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e TokenErrResp
|
||||||
|
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
"refresh_token": account.RefreshToken,
|
||||||
|
"client_id": account.ClientId,
|
||||||
|
"client_secret": account.ClientSecret,
|
||||||
|
}).Post(u)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Error != "" {
|
||||||
|
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||||
|
}
|
||||||
|
if resp.RefreshToken == "" {
|
||||||
|
return base.ErrEmptyToken
|
||||||
|
}
|
||||||
|
account.Status = "work"
|
||||||
|
account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
|
||||||
|
u := "https://cloud-api.yandex.net/v1/disk/resources" + pathname
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
req.SetHeader("Authorization", "OAuth "+account.AccessToken)
|
||||||
|
if headers != nil {
|
||||||
|
req.SetHeaders(headers)
|
||||||
|
}
|
||||||
|
if query != nil {
|
||||||
|
req.SetQueryParams(query)
|
||||||
|
}
|
||||||
|
if form != nil {
|
||||||
|
req.SetFormData(form)
|
||||||
|
}
|
||||||
|
if data != nil {
|
||||||
|
req.SetBody(data)
|
||||||
|
}
|
||||||
|
if resp != nil {
|
||||||
|
req.SetResult(resp)
|
||||||
|
}
|
||||||
|
var res *resty.Response
|
||||||
|
var err error
|
||||||
|
var e ErrResp
|
||||||
|
req.SetError(&e)
|
||||||
|
switch method {
|
||||||
|
case base.Get:
|
||||||
|
res, err = req.Get(u)
|
||||||
|
case base.Post:
|
||||||
|
res, err = req.Post(u)
|
||||||
|
case base.Patch:
|
||||||
|
res, err = req.Patch(u)
|
||||||
|
case base.Delete:
|
||||||
|
res, err = req.Delete(u)
|
||||||
|
case base.Put:
|
||||||
|
res, err = req.Put(u)
|
||||||
|
default:
|
||||||
|
return nil, base.ErrNotSupport
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
//log.Debug(res.String())
|
||||||
|
if e.Error != "" {
|
||||||
|
if e.Error == "UnauthorizedError" {
|
||||||
|
err = driver.RefreshToken(account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return driver.Request(pathname, method, headers, query, form, data, resp, account)
|
||||||
|
}
|
||||||
|
return nil, errors.New(e.Description)
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (driver Yandex) GetFiles(rawPath string, account *model.Account) ([]model.File, error) {
|
||||||
|
path_ := utils.Join(account.RootFolder, rawPath)
|
||||||
|
limit := 100
|
||||||
|
page := 1
|
||||||
|
res := make([]model.File, 0)
|
||||||
|
for {
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
query := map[string]string{
|
||||||
|
"path": path_,
|
||||||
|
"limit": strconv.Itoa(limit),
|
||||||
|
"offset": strconv.Itoa(offset),
|
||||||
|
}
|
||||||
|
if account.OrderBy != "" {
|
||||||
|
if account.OrderDirection == "desc" {
|
||||||
|
query["sort"] = "-" + account.OrderBy
|
||||||
|
} else {
|
||||||
|
query["sort"] = account.OrderBy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var resp FilesResp
|
||||||
|
_, err := driver.Request("", base.Get, nil, query, nil, nil, &resp, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
for _, file := range resp.Embedded.Items {
|
||||||
|
f := model.File{
|
||||||
|
Name: file.Name,
|
||||||
|
Size: file.Size,
|
||||||
|
Driver: driver.Config().Name,
|
||||||
|
UpdatedAt: file.Modified,
|
||||||
|
Thumbnail: file.Preview,
|
||||||
|
Url: file.File,
|
||||||
|
}
|
||||||
|
if file.Type == "dir" {
|
||||||
|
f.Type = conf.FOLDER
|
||||||
|
} else {
|
||||||
|
f.Type = utils.GetFileType(path.Ext(file.Name))
|
||||||
|
}
|
||||||
|
res = append(res, f)
|
||||||
|
}
|
||||||
|
if resp.Embedded.Total <= offset+limit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
base.RegisterDriver(&Yandex{})
|
||||||
|
}
|
25
go.mod
25
go.mod
@ -8,17 +8,19 @@ require (
|
|||||||
github.com/gin-contrib/cors v1.3.1
|
github.com/gin-contrib/cors v1.3.1
|
||||||
github.com/gin-gonic/gin v1.7.4
|
github.com/gin-gonic/gin v1.7.4
|
||||||
github.com/go-resty/resty/v2 v2.6.0
|
github.com/go-resty/resty/v2 v2.6.0
|
||||||
|
github.com/google/uuid v1.3.0
|
||||||
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b
|
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||||
github.com/robfig/cron/v3 v3.0.0
|
github.com/robfig/cron/v3 v3.0.0
|
||||||
github.com/sirupsen/logrus v1.8.1
|
github.com/sirupsen/logrus v1.8.1
|
||||||
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f
|
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f
|
||||||
|
github.com/upyun/go-sdk/v3 v3.0.2
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gorm.io/driver/mysql v1.1.2
|
gorm.io/driver/mysql v1.3.2
|
||||||
gorm.io/driver/postgres v1.1.2
|
gorm.io/driver/postgres v1.3.1
|
||||||
gorm.io/driver/sqlite v1.1.6
|
gorm.io/driver/sqlite v1.3.1
|
||||||
gorm.io/gorm v1.21.16
|
gorm.io/gorm v1.23.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@ -35,21 +37,20 @@ require (
|
|||||||
github.com/go-redis/redis/v8 v8.9.0 // indirect
|
github.com/go-redis/redis/v8 v8.9.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.2 // indirect
|
github.com/golang/protobuf v1.5.2 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||||
github.com/jackc/pgconn v1.10.0 // indirect
|
github.com/jackc/pgconn v1.11.0 // indirect
|
||||||
github.com/jackc/pgio v1.0.0 // indirect
|
github.com/jackc/pgio v1.0.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
|
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||||
github.com/jackc/pgtype v1.8.1 // indirect
|
github.com/jackc/pgtype v1.10.0 // indirect
|
||||||
github.com/jackc/pgx/v4 v4.13.0 // indirect
|
github.com/jackc/pgx/v4 v4.15.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.2 // indirect
|
github.com/jinzhu/now v1.1.4 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
|
||||||
github.com/leodido/go-urn v1.2.1 // indirect
|
github.com/leodido/go-urn v1.2.1 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.9 // indirect
|
github.com/mattn/go-sqlite3 v1.14.11 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
@ -63,7 +64,7 @@ require (
|
|||||||
go.opentelemetry.io/otel v0.20.0 // indirect
|
go.opentelemetry.io/otel v0.20.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v0.20.0 // indirect
|
go.opentelemetry.io/otel/metric v0.20.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v0.20.0 // indirect
|
go.opentelemetry.io/otel/trace v0.20.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
|
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
|
||||||
google.golang.org/protobuf v1.27.1 // indirect
|
google.golang.org/protobuf v1.27.1 // indirect
|
||||||
|
65
go.sum
65
go.sum
@ -229,8 +229,10 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
|
|||||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
||||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
||||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
|
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
|
||||||
github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
|
github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=
|
||||||
|
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
||||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
||||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||||
@ -246,29 +248,40 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod
|
|||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
|
||||||
|
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
||||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
||||||
github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
|
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0=
|
||||||
|
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
|
github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
|
||||||
|
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
||||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||||
github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
|
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
|
||||||
github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=
|
github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU=
|
||||||
|
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
|
||||||
|
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
|
||||||
|
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
|
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
|
|
||||||
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
|
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
|
||||||
|
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b h1:Ur6QAxsHCK99Quj9PaWafoV4unb0DO/HWiKExD+TN5g=
|
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b h1:Ur6QAxsHCK99Quj9PaWafoV4unb0DO/HWiKExD+TN5g=
|
||||||
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
|
||||||
@ -328,9 +341,9 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
|
|||||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
@ -497,6 +510,8 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
|
|||||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
|
||||||
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
|
||||||
|
github.com/upyun/go-sdk/v3 v3.0.2 h1:Ke+iOipK5CT0xzMwsgJsi7faJV7ID4lAs+wrH1RH0dA=
|
||||||
|
github.com/upyun/go-sdk/v3 v3.0.2/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
@ -539,8 +554,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
|||||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
|
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
|
||||||
|
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
|
||||||
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
@ -575,6 +593,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||||||
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
||||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
@ -729,16 +748,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M=
|
gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y=
|
||||||
gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM=
|
gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
|
||||||
gorm.io/driver/postgres v1.1.2 h1:Amy3hCvLqM+/ICzjCnQr8wKFLVJTeOTdlMT7kCP+J1Q=
|
gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
|
||||||
gorm.io/driver/postgres v1.1.2/go.mod h1:/AGV0zvqF3mt9ZtzLzQmXWQ/5vr+1V1TyHZGZVjzmwI=
|
gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
|
||||||
gorm.io/driver/sqlite v1.1.6 h1:p3U8WXkVFTOLPED4JjrZExfndjOtya3db8w9/vEMNyI=
|
gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
|
||||||
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
|
gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
|
||||||
gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g=
|
||||||
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU=
|
||||||
gorm.io/gorm v1.21.16 h1:YBIQLtP5PLfZQz59qfrq7xbrK7KWQ+JsXXCH/THlMqs=
|
gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
|
||||||
gorm.io/gorm v1.21.16/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
|
||||||
|
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
|
||||||
|
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
|
||||||
|
gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0=
|
||||||
|
gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -2,7 +2,9 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/robfig/cron/v3"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,21 +33,26 @@ type Account struct {
|
|||||||
SiteUrl string `json:"site_url"`
|
SiteUrl string `json:"site_url"`
|
||||||
SiteId string `json:"site_id"`
|
SiteId string `json:"site_id"`
|
||||||
InternalType string `json:"internal_type"`
|
InternalType string `json:"internal_type"`
|
||||||
WebdavProxy bool `json:"webdav_proxy"` // 开启之后只会webdav走中转
|
WebdavProxy bool `json:"webdav_proxy"` // 开启之后只会webdav走中转
|
||||||
Proxy bool `json:"proxy"` // 是否中转,开启之后web和webdav都会走中转
|
Proxy bool `json:"proxy"` // 是否中转,开启之后web和webdav都会走中转
|
||||||
|
WebdavDirect bool `json:"webdav_direct"` // webdav 下载不跳转
|
||||||
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
|
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
|
||||||
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
|
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
|
||||||
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
|
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
|
||||||
// for s3
|
// for s3
|
||||||
Bucket string `json:"bucket"`
|
Bucket string `json:"bucket"`
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
AccessKey string `json:"access_key"`
|
AccessKey string `json:"access_key"`
|
||||||
AccessSecret string `json:"access_secret"`
|
AccessSecret string `json:"access_secret"`
|
||||||
CustomHost string `json:"custom_host"`
|
CustomHost string `json:"custom_host"`
|
||||||
|
ExtractFolder string `json:"extract_folder"`
|
||||||
|
Bool1 bool `json:"bool_1"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var accountsMap = map[string]Account{}
|
var accountsMap = make(map[string]Account)
|
||||||
|
|
||||||
|
var balance = ".balance"
|
||||||
|
|
||||||
// SaveAccount save account to database
|
// SaveAccount save account to database
|
||||||
func SaveAccount(account *Account) error {
|
func SaveAccount(account *Account) error {
|
||||||
@ -64,19 +71,18 @@ func CreateAccount(account *Account) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteAccount(id uint) error {
|
func DeleteAccount(id uint) (*Account, error) {
|
||||||
var account Account
|
var account Account
|
||||||
account.ID = id
|
account.ID = id
|
||||||
if err := conf.DB.First(&account).Error; err != nil {
|
if err := conf.DB.First(&account).Error; err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
name := account.Name
|
name := account.Name
|
||||||
conf.Cron.Remove(cron.EntryID(account.CronId))
|
|
||||||
if err := conf.DB.Delete(&account).Error; err != nil {
|
if err := conf.DB.Delete(&account).Error; err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
delete(accountsMap, name)
|
delete(accountsMap, name)
|
||||||
return nil
|
return &account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteAccountFromMap(name string) {
|
func DeleteAccountFromMap(name string) {
|
||||||
@ -101,6 +107,46 @@ func GetAccount(name string) (Account, bool) {
|
|||||||
return account, ok
|
return account, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAccountsByName(name string) []Account {
|
||||||
|
accounts := make([]Account, 0)
|
||||||
|
if AccountsCount() == 1 {
|
||||||
|
account, _ := GetAccount("")
|
||||||
|
accounts = append(accounts, account)
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
for _, v := range accountsMap {
|
||||||
|
if v.Name == name || strings.HasPrefix(v.Name, name+balance) {
|
||||||
|
accounts = append(accounts, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accounts
|
||||||
|
}
|
||||||
|
|
||||||
|
var balanceMap sync.Map
|
||||||
|
|
||||||
|
func GetBalancedAccount(name string) (Account, bool) {
|
||||||
|
accounts := GetAccountsByName(name)
|
||||||
|
accountNum := len(accounts)
|
||||||
|
switch accountNum {
|
||||||
|
case 0:
|
||||||
|
return Account{}, false
|
||||||
|
case 1:
|
||||||
|
return accounts[0], true
|
||||||
|
default:
|
||||||
|
cur, ok := balanceMap.Load(name)
|
||||||
|
if ok {
|
||||||
|
i := cur.(int)
|
||||||
|
i = (i + 1) % accountNum
|
||||||
|
balanceMap.Store(name, i)
|
||||||
|
log.Debugln("use: ", i)
|
||||||
|
return accounts[i], true
|
||||||
|
} else {
|
||||||
|
balanceMap.Store(name, 0)
|
||||||
|
return accounts[0], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetAccountById(id uint) (*Account, error) {
|
func GetAccountById(id uint) (*Account, error) {
|
||||||
var account Account
|
var account Account
|
||||||
account.ID = id
|
account.ID = id
|
||||||
@ -117,6 +163,9 @@ func GetAccountFiles() ([]File, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
for _, v := range accounts {
|
for _, v := range accounts {
|
||||||
|
if strings.Contains(v.Name, balance) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
files = append(files, File{
|
files = append(files, File{
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
Size: 0,
|
Size: 0,
|
||||||
|
@ -25,14 +25,6 @@ func SortFiles(files []File, account *Account) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
sort.Slice(files, func(i, j int) bool {
|
sort.Slice(files, func(i, j int) bool {
|
||||||
if files[i].IsDir() || files[j].IsDir() {
|
|
||||||
if !files[i].IsDir() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if !files[j].IsDir() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch account.OrderBy {
|
switch account.OrderBy {
|
||||||
case "name":
|
case "name":
|
||||||
{
|
{
|
||||||
@ -59,6 +51,24 @@ func SortFiles(files []File, account *Account) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExtractFolder(files []File, account *Account) {
|
||||||
|
if account.ExtractFolder == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
front := account.ExtractFolder == "front"
|
||||||
|
sort.Slice(files, func(i, j int) bool {
|
||||||
|
if files[i].IsDir() || files[j].IsDir() {
|
||||||
|
if !files[i].IsDir() {
|
||||||
|
return !front
|
||||||
|
}
|
||||||
|
if !files[j].IsDir() {
|
||||||
|
return front
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (f File) GetSize() uint64 {
|
func (f File) GetSize() uint64 {
|
||||||
return uint64(f.Size)
|
return uint64(f.Size)
|
||||||
}
|
}
|
||||||
|
@ -93,11 +93,15 @@ func LoadSettings() {
|
|||||||
if err == nil {
|
if err == nil {
|
||||||
conf.TextTypes = strings.Split(textTypes.Value, ",")
|
conf.TextTypes = strings.Split(textTypes.Value, ",")
|
||||||
}
|
}
|
||||||
|
dProxyTypes, err := GetSettingByKey("d_proxy types")
|
||||||
|
if err == nil {
|
||||||
|
conf.DProxyTypes = strings.Split(dProxyTypes.Value, ",")
|
||||||
|
}
|
||||||
// html
|
// html
|
||||||
favicon, err := GetSettingByKey("favicon")
|
favicon, err := GetSettingByKey("favicon")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
//conf.Favicon = favicon.Value
|
//conf.Favicon = favicon.Value
|
||||||
conf.ManageHtml = strings.Replace(conf.RawIndexHtml, "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png", favicon.Value, 1)
|
conf.ManageHtml = strings.Replace(conf.RawIndexHtml, "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg", favicon.Value, 1)
|
||||||
}
|
}
|
||||||
title, err := GetSettingByKey("title")
|
title, err := GetSettingByKey("title")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -114,7 +118,11 @@ func LoadSettings() {
|
|||||||
// token
|
// token
|
||||||
adminPassword, err := GetSettingByKey("password")
|
adminPassword, err := GetSettingByKey("password")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value))
|
if adminPassword.Value != "" {
|
||||||
|
conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value))
|
||||||
|
} else {
|
||||||
|
conf.Token = ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// load settings
|
// load settings
|
||||||
for _, key := range conf.LoadSettings {
|
for _, key := range conf.LoadSettings {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"strings"
|
"strings"
|
||||||
@ -39,7 +40,7 @@ func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
|
|||||||
path = "/" + strings.Join(paths[2:], "/")
|
path = "/" + strings.Join(paths[2:], "/")
|
||||||
name = paths[1]
|
name = paths[1]
|
||||||
}
|
}
|
||||||
account, ok := model.GetAccount(name)
|
account, ok := model.GetBalancedAccount(name)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
return nil, "", nil, fmt.Errorf("no [%s] account", name)
|
||||||
}
|
}
|
||||||
@ -85,3 +86,18 @@ func SuccessResp(c *gin.Context, data ...interface{}) {
|
|||||||
Data: data[0],
|
Data: data[0],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Hide(meta *model.Meta, files []model.File) []model.File {
|
||||||
|
//meta, _ := model.GetMetaByPath(path)
|
||||||
|
if meta != nil && meta.Hide != "" {
|
||||||
|
tmpFiles := make([]model.File, 0)
|
||||||
|
hideFiles := strings.Split(meta.Hide, ",")
|
||||||
|
for _, item := range files {
|
||||||
|
if !utils.IsContain(hideFiles, item.Name) {
|
||||||
|
tmpFiles = append(tmpFiles, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
files = tmpFiles
|
||||||
|
}
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
89
server/common/proxy.go
Normal file
89
server/common/proxy.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
var HttpClient = &http.Client{}
|
||||||
|
|
||||||
|
func Proxy(w http.ResponseWriter, r *http.Request, link *base.Link, file *model.File) error {
|
||||||
|
// 本机读取数据
|
||||||
|
var err error
|
||||||
|
if link.Data != nil {
|
||||||
|
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
|
||||||
|
defer func() {
|
||||||
|
_ = link.Data.Close()
|
||||||
|
}()
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
|
||||||
|
w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10))
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
_, err = io.Copy(w, link.Data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 本机文件直接返回文件
|
||||||
|
if link.FilePath != "" {
|
||||||
|
f, err := os.Open(link.FilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
fileStat, err := os.Stat(link.FilePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
|
||||||
|
http.ServeContent(w, r, file.Name, fileStat.ModTime(), f)
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
req, err := http.NewRequest(r.Method, link.Url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for h, val := range r.Header {
|
||||||
|
req.Header[h] = val
|
||||||
|
}
|
||||||
|
for _, header := range link.Headers {
|
||||||
|
req.Header.Set(header.Name, header.Value)
|
||||||
|
}
|
||||||
|
res, err := HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = res.Body.Close()
|
||||||
|
}()
|
||||||
|
log.Debugf("proxy status: %d", res.StatusCode)
|
||||||
|
for h, v := range res.Header {
|
||||||
|
w.Header()[h] = v
|
||||||
|
}
|
||||||
|
w.WriteHeader(res.StatusCode)
|
||||||
|
if res.StatusCode >= 400 {
|
||||||
|
all, _ := ioutil.ReadAll(res.Body)
|
||||||
|
msg := string(all)
|
||||||
|
log.Debugln(msg)
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(w, res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
@ -87,9 +87,16 @@ func DeleteAccount(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 400)
|
common.ErrorResp(c, err, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := model.DeleteAccount(uint(id)); err != nil {
|
if account, err := model.DeleteAccount(uint(id)); err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
|
} else {
|
||||||
|
driver, ok := base.GetDriver(account.Type)
|
||||||
|
if ok {
|
||||||
|
_ = driver.Save(nil, account)
|
||||||
|
} else {
|
||||||
|
log.Errorf("no driver: %s", account.Type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,29 @@
|
|||||||
package controllers
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
"github.com/Xhofe/alist/server/common"
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Down(c *gin.Context) {
|
func Down(c *gin.Context) {
|
||||||
rawPath := c.Param("path")
|
rawPath := c.Param("path")
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
log.Debugf("down: %s", rawPath)
|
log.Debugf("down: %s", rawPath)
|
||||||
account, path, driver, err := common.ParsePath(rawPath)
|
account, path_, driver, err := common.ParsePath(rawPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if driver.Config().OnlyProxy || account.Proxy {
|
if driver.Config().OnlyProxy || account.Proxy || utils.IsContain(conf.DProxyTypes, path.Ext(rawPath)) {
|
||||||
Proxy(c)
|
Proxy(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
link, err := driver.Link(base.Args{Path: path, IP: c.ClientIP()}, account)
|
link, err := driver.Link(base.Args{Path: path_, IP: c.ClientIP()}, account)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
|
61
server/controllers/file/copy.go
Normal file
61
server/controllers/file/copy.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Copy(c *gin.Context) {
|
||||||
|
var req MoveCopyReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(req.Names) == 0 {
|
||||||
|
common.ErrorStrResp(c, "Empty file names", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if model.AccountsCount() > 1 && (req.SrcDir == "/" || req.DstDir == "/") {
|
||||||
|
common.ErrorStrResp(c, "Can't operate root folder", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcAccount, srcPath, srcDriver, err := common.ParsePath(utils.Join(req.SrcDir, req.Names[0]))
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dstAccount, dstPath, _, err := common.ParsePath(utils.Join(req.DstDir, req.Names[0]))
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if srcAccount.Name != dstAccount.Name {
|
||||||
|
common.ErrorStrResp(c, "Can't copy files between two accounts", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if srcPath == "/" || dstPath == "/" {
|
||||||
|
common.ErrorStrResp(c, "Can't copy root folder", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcDir, dstDir := utils.Dir(srcPath), utils.Dir(dstPath)
|
||||||
|
for i, name := range req.Names {
|
||||||
|
clearCache := false
|
||||||
|
if i == len(req.Names)-1 {
|
||||||
|
clearCache = true
|
||||||
|
}
|
||||||
|
err := operate.Copy(srcDriver, srcAccount, utils.Join(srcDir, name), utils.Join(dstDir, name), clearCache)
|
||||||
|
if err != nil {
|
||||||
|
if i == 0 {
|
||||||
|
_ = base.DeleteCache(srcDir, srcAccount)
|
||||||
|
_ = base.DeleteCache(dstDir, dstAccount)
|
||||||
|
}
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
53
server/controllers/file/folder.go
Normal file
53
server/controllers/file/folder.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FolderReq struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Folder(c *gin.Context) {
|
||||||
|
var req FolderReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var files = make([]model.File, 0)
|
||||||
|
var err error
|
||||||
|
if model.AccountsCount() > 1 && (req.Path == "/" || req.Path == "") {
|
||||||
|
files, err = model.GetAccountFiles()
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
account, path, driver, err := common.ParsePath(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
file, rawFiles, err := operate.Path(driver, account, path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if file != nil {
|
||||||
|
common.ErrorStrResp(c, "Not folder", 400)
|
||||||
|
}
|
||||||
|
for _, file := range rawFiles {
|
||||||
|
if file.IsDir() {
|
||||||
|
files = append(files, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.JSON(200, common.Resp{
|
||||||
|
Code: 200,
|
||||||
|
Message: "success",
|
||||||
|
Data: files,
|
||||||
|
})
|
||||||
|
}
|
34
server/controllers/file/mkdir.go
Normal file
34
server/controllers/file/mkdir.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MkdirReq struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mkdir(c *gin.Context) {
|
||||||
|
var req MkdirReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
account, path_, driver, err := common.ParsePath(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if path_ == "/" {
|
||||||
|
common.ErrorStrResp(c, "Folder name can't be empty", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = operate.MakeDir(driver, account, path_, true)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
67
server/controllers/file/move.go
Normal file
67
server/controllers/file/move.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
|
"github.com/Xhofe/alist/model"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MoveCopyReq struct {
|
||||||
|
SrcDir string `json:"src_dir"`
|
||||||
|
DstDir string `json:"dst_dir"`
|
||||||
|
Names []string `json:"names"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Move(c *gin.Context) {
|
||||||
|
var req MoveCopyReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(req.Names) == 0 {
|
||||||
|
common.ErrorStrResp(c, "Empty file names", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if model.AccountsCount() > 1 && (req.SrcDir == "/" || req.DstDir == "/") {
|
||||||
|
common.ErrorStrResp(c, "Can't operate root folder", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcAccount, srcPath, srcDriver, err := common.ParsePath(utils.Join(req.SrcDir, req.Names[0]))
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dstAccount, dstPath, _, err := common.ParsePath(utils.Join(req.DstDir, req.Names[0]))
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if srcAccount.Name != dstAccount.Name {
|
||||||
|
common.ErrorStrResp(c, "Can't move files between two accounts", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if srcPath == "/" || dstPath == "/" {
|
||||||
|
common.ErrorStrResp(c, "Can't move root folder", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcDir, dstDir := utils.Dir(srcPath), utils.Dir(dstPath)
|
||||||
|
for i, name := range req.Names {
|
||||||
|
clearCache := false
|
||||||
|
if i == len(req.Names)-1 {
|
||||||
|
clearCache = true
|
||||||
|
}
|
||||||
|
err := operate.Move(srcDriver, srcAccount, utils.Join(srcDir, name), utils.Join(dstDir, name), clearCache)
|
||||||
|
if err != nil {
|
||||||
|
if i == 0 {
|
||||||
|
_ = base.DeleteCache(srcDir, srcAccount)
|
||||||
|
_ = base.DeleteCache(dstDir, dstAccount)
|
||||||
|
}
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
36
server/controllers/file/raname.go
Normal file
36
server/controllers/file/raname.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RenameReq struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Rename(c *gin.Context) {
|
||||||
|
var req RenameReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
account, path_, driver, err := common.ParsePath(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if path_ == "/" {
|
||||||
|
common.ErrorStrResp(c, "Can't edit account name here", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = operate.Move(driver, account, path_, utils.Join(utils.Dir(path_), req.Name), true)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
30
server/controllers/file/refresh.go
Normal file
30
server/controllers/file/refresh.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RefreshReq struct {
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func RefreshFolder(c *gin.Context) {
|
||||||
|
var req RefreshReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
account, path_, _, err := common.ParsePath(req.Path)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = base.DeleteCache(path_, account)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
65
server/controllers/other.go
Normal file
65
server/controllers/other.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"github.com/Xhofe/alist/conf"
|
||||||
|
"github.com/Xhofe/alist/server/common"
|
||||||
|
"github.com/Xhofe/alist/utils"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Favicon(c *gin.Context) {
|
||||||
|
c.Redirect(302, conf.GetStr("favicon"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Plist(c *gin.Context) {
|
||||||
|
data := c.Param("data")
|
||||||
|
data = strings.ReplaceAll(data, "_", "/")
|
||||||
|
data = strings.ReplaceAll(data, "-", "=")
|
||||||
|
bytes, err := base64.StdEncoding.DecodeString(data)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
u := string(bytes)
|
||||||
|
name := utils.Base(u)
|
||||||
|
ipaIndex := strings.Index(name, ".ipa")
|
||||||
|
if ipaIndex != -1 {
|
||||||
|
name = name[:ipaIndex]
|
||||||
|
}
|
||||||
|
plist := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>items</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>assets</key>
|
||||||
|
<array>
|
||||||
|
<dict>
|
||||||
|
<key>kind</key>
|
||||||
|
<string>software-package</string>
|
||||||
|
<key>url</key>
|
||||||
|
<string>%s</string>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
<key>metadata</key>
|
||||||
|
<dict>
|
||||||
|
<key>bundle-identifier</key>
|
||||||
|
<string>ci.nn.%s</string>
|
||||||
|
<key>bundle-version</key>
|
||||||
|
<string>4.4</string>
|
||||||
|
<key>kind</key>
|
||||||
|
<string>software</string>
|
||||||
|
<key>title</key>
|
||||||
|
<string>%s</string>
|
||||||
|
</dict>
|
||||||
|
</dict>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>`, u, name, name)
|
||||||
|
c.Header("Content-Type", "application/xml;charset=utf-8")
|
||||||
|
c.Status(200)
|
||||||
|
_, _ = c.Writer.WriteString(plist)
|
||||||
|
}
|
@ -5,29 +5,14 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
"github.com/Xhofe/alist/model"
|
"github.com/Xhofe/alist/model"
|
||||||
"github.com/Xhofe/alist/server/common"
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Hide(meta *model.Meta, files []model.File) []model.File {
|
|
||||||
//meta, _ := model.GetMetaByPath(path)
|
|
||||||
if meta != nil && meta.Hide != "" {
|
|
||||||
tmpFiles := make([]model.File, 0)
|
|
||||||
hideFiles := strings.Split(meta.Hide, ",")
|
|
||||||
for _, item := range files {
|
|
||||||
if !utils.IsContain(hideFiles, item.Name) {
|
|
||||||
tmpFiles = append(tmpFiles, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
files = tmpFiles
|
|
||||||
}
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
func Pagination(files []model.File, req *common.PathReq) (int, []model.File) {
|
func Pagination(files []model.File, req *common.PathReq) (int, []model.File) {
|
||||||
pageNum, pageSize := req.PageNum, req.PageSize
|
pageNum, pageSize := req.PageNum, req.PageSize
|
||||||
total := len(files)
|
total := len(files)
|
||||||
@ -93,14 +78,14 @@ func Path(c *gin.Context) {
|
|||||||
if meta != nil && meta.Upload {
|
if meta != nil && meta.Upload {
|
||||||
upload = true
|
upload = true
|
||||||
}
|
}
|
||||||
if model.AccountsCount() > 1 && req.Path == "/" {
|
if model.AccountsCount() > 1 && (req.Path == "/" || req.Path == "") {
|
||||||
files, err := model.GetAccountFiles()
|
files, err := model.GetAccountFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
files = Hide(meta, files)
|
files = common.Hide(meta, files)
|
||||||
}
|
}
|
||||||
c.JSON(200, common.Resp{
|
c.JSON(200, common.Resp{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
@ -125,7 +110,7 @@ func Path(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file, files, err := driver.Path(path, account)
|
file, files, err := operate.Path(driver, account, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
@ -159,11 +144,12 @@ func Path(c *gin.Context) {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if !ok {
|
if !ok {
|
||||||
files = Hide(meta, files)
|
files = common.Hide(meta, files)
|
||||||
}
|
}
|
||||||
if driver.Config().LocalSort {
|
if driver.Config().LocalSort {
|
||||||
model.SortFiles(files, account)
|
model.SortFiles(files, account)
|
||||||
}
|
}
|
||||||
|
model.ExtractFolder(files, account)
|
||||||
total, files := Pagination(files, &req)
|
total, files := Pagination(files, &req)
|
||||||
c.JSON(200, common.Resp{
|
c.JSON(200, common.Resp{
|
||||||
Code: 200,
|
Code: 200,
|
||||||
|
@ -4,17 +4,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/drivers/base"
|
"github.com/Xhofe/alist/drivers/base"
|
||||||
|
"github.com/Xhofe/alist/drivers/operate"
|
||||||
"github.com/Xhofe/alist/server/common"
|
"github.com/Xhofe/alist/server/common"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Proxy(c *gin.Context) {
|
func Proxy(c *gin.Context) {
|
||||||
@ -31,7 +27,9 @@ func Proxy(c *gin.Context) {
|
|||||||
// 2. driver只能中转
|
// 2. driver只能中转
|
||||||
// 3. 是文本类型文件
|
// 3. 是文本类型文件
|
||||||
// 4. 开启webdav中转(需要验证sign)
|
// 4. 开启webdav中转(需要验证sign)
|
||||||
if !account.Proxy && !driver.Config().OnlyProxy && utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT {
|
if !account.Proxy && !driver.Config().OnlyProxy &&
|
||||||
|
utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT &&
|
||||||
|
!utils.IsContain(conf.DProxyTypes, filepath.Ext(rawPath)) {
|
||||||
// 只开启了webdav中转,验证sign
|
// 只开启了webdav中转,验证sign
|
||||||
ok := false
|
ok := false
|
||||||
if account.WebdavProxy {
|
if account.WebdavProxy {
|
||||||
@ -50,7 +48,7 @@ func Proxy(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 检查文件
|
// 检查文件
|
||||||
file, err := driver.File(path, account)
|
file, err := operate.File(driver, account, path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
@ -61,92 +59,27 @@ func Proxy(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 本机读取数据
|
err = common.Proxy(c.Writer, c.Request, link, file)
|
||||||
if link.Data != nil {
|
if err != nil {
|
||||||
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
|
common.ErrorResp(c, err, 500)
|
||||||
defer func() {
|
|
||||||
_ = link.Data.Close()
|
|
||||||
}()
|
|
||||||
c.Status(http.StatusOK)
|
|
||||||
c.Header("Content-Type", "application/octet-stream")
|
|
||||||
c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
|
|
||||||
c.Header("Content-Length", strconv.FormatInt(file.Size, 10))
|
|
||||||
_, err = io.Copy(c.Writer, link.Data)
|
|
||||||
if err != nil {
|
|
||||||
_, _ = c.Writer.WriteString(err.Error())
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 本机文件直接返回文件
|
|
||||||
if account.Type == "Native" {
|
|
||||||
// 对于名称为index.html的文件需要特殊处理
|
|
||||||
if utils.Base(rawPath) == "index.html" {
|
|
||||||
file, err := os.Open(link.Url)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = file.Close()
|
|
||||||
}()
|
|
||||||
fileStat, err := os.Stat(link.Url)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
c.File(link.Url)
|
|
||||||
return
|
|
||||||
} else {
|
|
||||||
if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
|
|
||||||
Text(c, link)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
driver.Proxy(c, account)
|
|
||||||
r := c.Request
|
|
||||||
w := c.Writer
|
|
||||||
//target, err := url.Parse(link.Url)
|
|
||||||
//if err != nil {
|
|
||||||
// common.ErrorResp(c, err, 500)
|
|
||||||
// return
|
|
||||||
//}
|
|
||||||
req, err := http.NewRequest("GET", link.Url, nil)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for h, val := range r.Header {
|
|
||||||
req.Header[h] = val
|
|
||||||
}
|
|
||||||
res, err := HttpClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.WriteHeader(res.StatusCode)
|
|
||||||
for h, v := range res.Header {
|
|
||||||
w.Header()[h] = v
|
|
||||||
}
|
|
||||||
_, err = io.Copy(w, res.Body)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 500)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var client *resty.Client
|
var client *resty.Client
|
||||||
var HttpClient = &http.Client{}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
client = resty.New()
|
client = resty.New()
|
||||||
client.SetRetryCount(3)
|
client.SetRetryCount(3).SetTimeout(base.DefaultTimeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Text(c *gin.Context, link *base.Link) {
|
func Text(c *gin.Context, link *base.Link) {
|
||||||
res, err := client.R().Get(link.Url)
|
req := client.R()
|
||||||
|
if link.Headers != nil {
|
||||||
|
for _, header := range link.Headers {
|
||||||
|
req.SetHeader(header.Name, header.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res, err := req.Get(link.Url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
|
@ -19,7 +19,7 @@ func Auth(c *gin.Context) {
|
|||||||
//}
|
//}
|
||||||
//if token != utils.GetMD5Encode(password.Value) {
|
//if token != utils.GetMD5Encode(password.Value) {
|
||||||
if token != conf.Token {
|
if token != conf.Token {
|
||||||
common.ErrorStrResp(c, "Wrong password", 401)
|
common.ErrorStrResp(c, "Invalid token", 401)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Next()
|
c.Next()
|
||||||
|
@ -15,6 +15,8 @@ func InitApiRouter(r *gin.Engine) {
|
|||||||
Cors(r)
|
Cors(r)
|
||||||
r.GET("/d/*path", middlewares.DownCheck, controllers.Down)
|
r.GET("/d/*path", middlewares.DownCheck, controllers.Down)
|
||||||
r.GET("/p/*path", middlewares.DownCheck, controllers.Proxy)
|
r.GET("/p/*path", middlewares.DownCheck, controllers.Proxy)
|
||||||
|
r.GET("/favicon.ico", controllers.Favicon)
|
||||||
|
r.GET("/i/:data/ipa.plist", controllers.Plist)
|
||||||
|
|
||||||
api := r.Group("/api")
|
api := r.Group("/api")
|
||||||
public := api.Group("/public")
|
public := api.Group("/public")
|
||||||
@ -32,7 +34,7 @@ func InitApiRouter(r *gin.Engine) {
|
|||||||
admin := api.Group("/admin")
|
admin := api.Group("/admin")
|
||||||
{
|
{
|
||||||
admin.Use(middlewares.Auth)
|
admin.Use(middlewares.Auth)
|
||||||
admin.GET("/login", common.Login)
|
admin.Any("/login", common.Login)
|
||||||
admin.GET("/settings", controllers.GetSettings)
|
admin.GET("/settings", controllers.GetSettings)
|
||||||
admin.POST("/settings", controllers.SaveSettings)
|
admin.POST("/settings", controllers.SaveSettings)
|
||||||
admin.DELETE("/setting", controllers.DeleteSetting)
|
admin.DELETE("/setting", controllers.DeleteSetting)
|
||||||
@ -51,6 +53,12 @@ func InitApiRouter(r *gin.Engine) {
|
|||||||
|
|
||||||
admin.POST("/link", controllers.Link)
|
admin.POST("/link", controllers.Link)
|
||||||
admin.DELETE("/files", file.DeleteFiles)
|
admin.DELETE("/files", file.DeleteFiles)
|
||||||
|
admin.POST("/mkdir", file.Mkdir)
|
||||||
|
admin.POST("/rename", file.Rename)
|
||||||
|
admin.POST("/move", file.Move)
|
||||||
|
admin.POST("/copy", file.Copy)
|
||||||
|
admin.POST("/folder", file.Folder)
|
||||||
|
admin.POST("/refresh", file.RefreshFolder)
|
||||||
}
|
}
|
||||||
WebDav(r)
|
WebDav(r)
|
||||||
Static(r)
|
Static(r)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/public"
|
"github.com/Xhofe/alist/public"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
@ -15,21 +14,21 @@ import (
|
|||||||
func InitIndex() {
|
func InitIndex() {
|
||||||
var index fs.File
|
var index fs.File
|
||||||
var err error
|
var err error
|
||||||
//if conf.Conf.Local {
|
if !strings.Contains(conf.Conf.Assets, "/") {
|
||||||
// index, err = public.Public.Open("local.html")
|
|
||||||
//} else {
|
|
||||||
// index, err = public.Public.Open("index.html")
|
|
||||||
//}
|
|
||||||
if conf.Conf.Assets == "" {
|
|
||||||
conf.Conf.Assets = conf.DefaultConfig().Assets
|
conf.Conf.Assets = conf.DefaultConfig().Assets
|
||||||
}
|
}
|
||||||
index, err = public.Public.Open(fmt.Sprintf("%s.html", conf.Conf.Assets))
|
index, err = public.Public.Open("index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf(err.Error())
|
log.Fatal(err.Error())
|
||||||
return
|
|
||||||
}
|
}
|
||||||
data, _ := ioutil.ReadAll(index)
|
data, _ := ioutil.ReadAll(index)
|
||||||
|
cdnUrl := strings.ReplaceAll(conf.Conf.Assets, "$version", conf.WebTag)
|
||||||
|
cdnUrl = strings.TrimRight(cdnUrl, "/")
|
||||||
conf.RawIndexHtml = string(data)
|
conf.RawIndexHtml = string(data)
|
||||||
|
if strings.Contains(conf.RawIndexHtml, "CDN_URL") {
|
||||||
|
conf.RawIndexHtml = strings.ReplaceAll(conf.RawIndexHtml, "/CDN_URL", cdnUrl)
|
||||||
|
conf.RawIndexHtml = strings.ReplaceAll(conf.RawIndexHtml, "assets/", cdnUrl+"/assets/")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Static(r *gin.Engine) {
|
func Static(r *gin.Engine) {
|
||||||
@ -45,8 +44,8 @@ func Static(r *gin.Engine) {
|
|||||||
r.StaticFS("/assets/", http.FS(assets))
|
r.StaticFS("/assets/", http.FS(assets))
|
||||||
r.StaticFS("/public/", http.FS(pub))
|
r.StaticFS("/public/", http.FS(pub))
|
||||||
r.NoRoute(func(c *gin.Context) {
|
r.NoRoute(func(c *gin.Context) {
|
||||||
c.Status(200)
|
|
||||||
c.Header("Content-Type", "text/html")
|
c.Header("Content-Type", "text/html")
|
||||||
|
c.Status(200)
|
||||||
if strings.HasPrefix(c.Request.URL.Path, "/@manage") {
|
if strings.HasPrefix(c.Request.URL.Path, "/@manage") {
|
||||||
_, _ = c.Writer.WriteString(conf.ManageHtml)
|
_, _ = c.Writer.WriteString(conf.ManageHtml)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"github.com/Xhofe/alist/conf"
|
"github.com/Xhofe/alist/conf"
|
||||||
"github.com/Xhofe/alist/server/webdav"
|
"github.com/Xhofe/alist/server/webdav"
|
||||||
"github.com/Xhofe/alist/utils"
|
"github.com/Xhofe/alist/utils"
|
||||||
@ -58,6 +59,8 @@ func WebDAVAuth(c *gin.Context) {
|
|||||||
(conf.GetStr("Visitor WebDAV username") == "" &&
|
(conf.GetStr("Visitor WebDAV username") == "" &&
|
||||||
conf.GetStr("Visitor WebDAV password") == "") {
|
conf.GetStr("Visitor WebDAV password") == "") {
|
||||||
if !utils.IsContain([]string{"PUT", "DELETE", "PROPPATCH", "MKCOL", "COPY", "MOVE"}, c.Request.Method) {
|
if !utils.IsContain([]string{"PUT", "DELETE", "PROPPATCH", "MKCOL", "COPY", "MOVE"}, c.Request.Method) {
|
||||||
|
ctx := context.WithValue(c.Request.Context(), "visitor", true)
|
||||||
|
c.Request = c.Request.WithContext(ctx)
|
||||||
c.Next()
|
c.Next()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,13 @@ import (
|
|||||||
|
|
||||||
type FileSystem struct{}
|
type FileSystem struct{}
|
||||||
|
|
||||||
|
var upFileMap = make(map[string]*model.File)
|
||||||
|
|
||||||
func (fs *FileSystem) File(rawPath string) (*model.File, error) {
|
func (fs *FileSystem) File(rawPath string) (*model.File, error) {
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
if f, ok := upFileMap[rawPath]; ok {
|
||||||
|
return f, nil
|
||||||
|
}
|
||||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
return &model.File{
|
return &model.File{
|
||||||
@ -40,23 +45,35 @@ func (fs *FileSystem) File(rawPath string) (*model.File, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return driver.File(path_, account)
|
return operate.File(driver, account, path_)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) Files(rawPath string) ([]model.File, error) {
|
func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) {
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
|
var files []model.File
|
||||||
|
var err error
|
||||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||||
files, err := model.GetAccountFiles()
|
files, err = model.GetAccountFiles()
|
||||||
|
} else {
|
||||||
|
account, path_, driver, err := common.ParsePath(rawPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return files, nil
|
files, err = operate.Files(driver, account, path_)
|
||||||
}
|
}
|
||||||
account, path_, driver, err := common.ParsePath(rawPath)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return driver.Files(path_, account)
|
meta, _ := model.GetMetaByPath(rawPath)
|
||||||
|
if visitor := ctx.Value("visitor"); visitor != nil {
|
||||||
|
if visitor.(bool) {
|
||||||
|
log.Debug("visitor")
|
||||||
|
files = common.Hide(meta, files)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Debug("admin")
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ClientIP(r *http.Request) string {
|
func ClientIP(r *http.Request) string {
|
||||||
@ -78,7 +95,7 @@ func ClientIP(r *http.Request) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
func (fs *FileSystem) Link(w http.ResponseWriter, r *http.Request, rawPath string) (string, error) {
|
||||||
rawPath = utils.ParsePath(rawPath)
|
rawPath = utils.ParsePath(rawPath)
|
||||||
log.Debugf("get link path: %s", rawPath)
|
log.Debugf("get link path: %s", rawPath)
|
||||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||||
@ -93,11 +110,24 @@ func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
|
|||||||
if r.TLS != nil {
|
if r.TLS != nil {
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
}
|
}
|
||||||
|
// 直接返回
|
||||||
|
if account.WebdavDirect {
|
||||||
|
file, err := fs.File(rawPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
link_, err := driver.Link(base.Args{Path: path_}, account)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = common.Proxy(w, r, link_, file)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
if driver.Config().OnlyProxy || account.WebdavProxy {
|
if driver.Config().OnlyProxy || account.WebdavProxy {
|
||||||
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
|
||||||
if conf.GetBool("check down link") {
|
if conf.GetBool("check down link") {
|
||||||
sign := utils.SignWithToken(utils.Base(rawPath), conf.Token)
|
sign := utils.SignWithToken(utils.Base(rawPath), conf.Token)
|
||||||
link += "?sign" + sign
|
link += "?sign=" + sign
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
link_, err := driver.Link(base.Args{Path: path_, IP: ClientIP(r)}, account)
|
link_, err := driver.Link(base.Args{Path: path_, IP: ClientIP(r)}, account)
|
||||||
@ -141,6 +171,17 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
|
|||||||
// return err
|
// return err
|
||||||
//}
|
//}
|
||||||
filePath, fileName := filepath.Split(path_)
|
filePath, fileName := filepath.Split(path_)
|
||||||
|
now := time.Now()
|
||||||
|
if fileSize == 0 {
|
||||||
|
upFileMap[rawPath] = &model.File{
|
||||||
|
Name: fileName,
|
||||||
|
Size: 0,
|
||||||
|
UpdatedAt: &now,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
delete(upFileMap, rawPath)
|
||||||
|
}
|
||||||
fileData := model.FileStream{
|
fileData := model.FileStream{
|
||||||
MIMEType: r.Header.Get("Content-Type"),
|
MIMEType: r.Header.Get("Content-Type"),
|
||||||
File: r.Body,
|
File: r.Body,
|
||||||
@ -260,7 +301,7 @@ func walkFS(
|
|||||||
depth = 0
|
depth = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
files, err := fs.Files(name)
|
files, err := fs.Files(ctx, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ func (h *Handler) stripPrefix(p string) (string, int, error) {
|
|||||||
func isPathExist(ctx context.Context, fs *FileSystem, path string) (bool, FileInfo) {
|
func isPathExist(ctx context.Context, fs *FileSystem, path string) (bool, FileInfo) {
|
||||||
file, err := fs.File(path)
|
file, err := fs.File(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
log.Debugln(path, err)
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
return true, file
|
return true, file
|
||||||
@ -237,11 +237,14 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
|
|||||||
}
|
}
|
||||||
w.Header().Set("ETag", etag)
|
w.Header().Set("ETag", etag)
|
||||||
log.Debugf("url: %+v", r.URL)
|
log.Debugf("url: %+v", r.URL)
|
||||||
link, err := fs.Link(r, reqPath)
|
link, err := fs.Link(w, r, reqPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Debugf("webdav link error: %s", err.Error())
|
||||||
return http.StatusInternalServerError, err
|
return http.StatusInternalServerError, err
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, link, 302)
|
if link != "" {
|
||||||
|
http.Redirect(w, r, link, 302)
|
||||||
|
}
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,3 +123,15 @@ func Join(elem ...string) string {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Split(p string) (string, string) {
|
||||||
|
return path.Split(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatName TODO
|
||||||
|
func FormatName(name string) string {
|
||||||
|
name = strings.ReplaceAll(name, "/", " ")
|
||||||
|
name = strings.ReplaceAll(name, "#", " ")
|
||||||
|
name = strings.ReplaceAll(name, "?", " ")
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
25
utils/random.go
Normal file
25
utils/random.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var Rand *rand.Rand
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
s := rand.NewSource(time.Now().UnixNano())
|
||||||
|
Rand = rand.New(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomStr(n int) string {
|
||||||
|
|
||||||
|
builder := strings.Builder{}
|
||||||
|
t := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
r := Rand.Intn(len(t))
|
||||||
|
builder.WriteString(t[r : r+1])
|
||||||
|
}
|
||||||
|
return builder.String()
|
||||||
|
}
|
Reference in New Issue
Block a user