Compare commits
91 Commits
Author | SHA1 | Date | |
---|---|---|---|
6c91cfeb90 | |||
bfd1f25972 | |||
8c0defce09 | |||
a1e88cfa05 | |||
443f5ffbcc | |||
b8bc94306d | |||
d9795ff22f | |||
c4108007cd | |||
f3db23a41e | |||
4741a75c92 | |||
301756ba03 | |||
3b2703a5e5 | |||
2a601f06cb | |||
adc3a56552 | |||
4d9a29bddd | |||
666e02f0c3 | |||
6aaec19c1c | |||
1091e1b740 | |||
d06c605421 | |||
43de823058 | |||
02d0aef611 | |||
5596661ce8 | |||
2379cb8d67 | |||
8c0ebe0841 | |||
fd868bac84 | |||
ebcbb29a0f | |||
00ff0a43a7 | |||
3d3f23ec9e | |||
d484219c48 | |||
dd4c97393e | |||
07b8ff25a7 | |||
0d5c3c5080 | |||
75b4429f73 | |||
34ef6bd18d | |||
c915313ec9 | |||
12a095a1d6 | |||
dc000f640a | |||
aa1c5b2be3 | |||
1d4ec3c50d | |||
ebfeef52f4 | |||
c595fd7f94 | |||
421052f88a | |||
603681fbe6 | |||
f442185aa5 | |||
ca9e739465 | |||
53a1c4283b | |||
93dd768234 | |||
c9c4d6bc7e | |||
81e10f8939 | |||
4dd753de52 | |||
79df63d319 | |||
ec54831162 | |||
c8f3e8ab4d | |||
4be8524d80 | |||
0d3146b51d | |||
f95d843969 | |||
28aee8c493 | |||
de3ea82eb9 | |||
268ba3d069 | |||
309d6558fb | |||
c08fdfc868 | |||
1b28e6af3e | |||
8655e33e60 | |||
50579fef84 | |||
e39299bfe2 | |||
d1ab2443f1 | |||
658cf368bb | |||
fd36ce59f6 | |||
95b3b87672 | |||
0d07d81802 | |||
923937b530 | |||
09492193c4 | |||
40b26a81a0 | |||
4293a0ba8c | |||
6c2f3486fc | |||
3c7512f64a | |||
84219d3d70 | |||
05d3727335 | |||
ee77c3b113 | |||
fcaf485e0b | |||
bd83469bb1 | |||
90f111b24f | |||
7d1034c569 | |||
236c17176c | |||
6ee4c10e8f | |||
3798634028 | |||
567ba5ccd4 | |||
ae2ee1821a | |||
805b1e4fa3 | |||
d92c10da56 | |||
6659f6d367 |
19
.github/stale.yml
vendored
Normal file
19
.github/stale.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Number of days of inactivity before an issue becomes stale
|
||||||
|
daysUntilStale: 44
|
||||||
|
# Number of days of inactivity before a stale issue is closed
|
||||||
|
daysUntilClose: 8
|
||||||
|
# Issues with these labels will never be considered stale
|
||||||
|
exemptLabels:
|
||||||
|
- accepted
|
||||||
|
- security
|
||||||
|
# Label to use when marking an issue as stale
|
||||||
|
staleLabel: stale
|
||||||
|
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||||
|
markComment: >
|
||||||
|
This issue has been automatically marked as stale because it has not had
|
||||||
|
recent activity. It will be closed if no further activity occurs. Thank you
|
||||||
|
for your contributions.
|
||||||
|
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||||
|
closeComment: >
|
||||||
|
This issue was closed due to inactive more than 52 days. You can reopen or
|
||||||
|
recreate it if you think it should continue. Thank you for your contributions again.
|
4
.github/workflows/auto_lang.yml
vendored
4
.github/workflows/auto_lang.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup go
|
- name: Setup go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ jobs:
|
|||||||
cd alist-web
|
cd alist-web
|
||||||
git add .
|
git add .
|
||||||
git config --local user.email "i@nn.ci"
|
git config --local user.email "i@nn.ci"
|
||||||
git config --local user.name "Noah Hsu"
|
git config --local user.name "Andy Hsu"
|
||||||
git commit -m "chore: auto update i18n file" -a 2>/dev/null || :
|
git commit -m "chore: auto update i18n file" -a 2>/dev/null || :
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
|
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
|
19
.github/workflows/changelog.yml
vendored
Normal file
19
.github/workflows/changelog.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: auto changelog
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changelog:
|
||||||
|
name: Create Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{secrets.MY_TOKEN}}
|
17
.github/workflows/issue_check_inactive.yml
vendored
17
.github/workflows/issue_check_inactive.yml
vendored
@ -1,17 +0,0 @@
|
|||||||
name: Check inactive
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 1 * *"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
check-inactive:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: check-inactive
|
|
||||||
uses: actions-cool/issues-helper@v3
|
|
||||||
with:
|
|
||||||
actions: 'check-inactive'
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
inactive-day: 30
|
|
||||||
body: Hello, this issue has been inactive for more than 30 days and will be closed if inactive for another 30 days.
|
|
21
.github/workflows/issue_close_inactive.yml
vendored
21
.github/workflows/issue_close_inactive.yml
vendored
@ -1,21 +0,0 @@
|
|||||||
name: Close inactive
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 0 */7 * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
close-inactive:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: close-issues
|
|
||||||
uses: actions-cool/issues-helper@v3
|
|
||||||
with:
|
|
||||||
actions: 'close-issues'
|
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
labels: 'inactive'
|
|
||||||
inactive-day: 30
|
|
||||||
close-reason: 'not_planned'
|
|
||||||
body: |
|
|
||||||
Hello @${{ github.event.issue.user.login }}, this issue was closed due to inactive more than 60 days. You can reopen or recreate it if you think it should continue.
|
|
66
.github/workflows/release.yml
vendored
66
.github/workflows/release.yml
vendored
@ -1,33 +1,27 @@
|
|||||||
name: release
|
name: release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
release:
|
||||||
tags:
|
types: [ published ]
|
||||||
- '*'
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
changelog:
|
|
||||||
name: Create Release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{secrets.MY_TOKEN}}
|
|
||||||
release:
|
release:
|
||||||
needs: changelog
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
platform: [ubuntu-latest]
|
platform: [ ubuntu-latest ]
|
||||||
go-version: [1.19]
|
go-version: [ 1.19 ]
|
||||||
name: Release
|
name: Release
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
steps:
|
steps:
|
||||||
|
- name: prerelease
|
||||||
|
uses: irongut/EditRelease@v1.2.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.MY_TOKEN }}
|
||||||
|
id: ${{ github.event.release.id }}
|
||||||
|
prerelease: true
|
||||||
|
|
||||||
- name: Setup Go
|
- name: Setup Go
|
||||||
uses: actions/setup-go@v3
|
uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go-version }}
|
go-version: ${{ matrix.go-version }}
|
||||||
|
|
||||||
@ -47,7 +41,41 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
bash build.sh release
|
bash build.sh release
|
||||||
|
|
||||||
|
- name: prerelease
|
||||||
|
uses: irongut/EditRelease@v1.2.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.MY_TOKEN }}
|
||||||
|
id: ${{ github.event.release.id }}
|
||||||
|
prerelease: false
|
||||||
|
|
||||||
- name: Release
|
- name: Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
with:
|
with:
|
||||||
files: build/compress/*
|
files: build/compress/*
|
||||||
|
|
||||||
|
release_desktop:
|
||||||
|
needs: release
|
||||||
|
name: Release desktop
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: alist-org/desktop-release
|
||||||
|
ref: main
|
||||||
|
persist-credentials: false
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Add tag
|
||||||
|
run: |
|
||||||
|
git config --local user.email "i@nn.ci"
|
||||||
|
git config --local user.name "Andy Hsu"
|
||||||
|
version=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
|
||||||
|
git tag -a $version -m "release $version"
|
||||||
|
|
||||||
|
- name: Push tags
|
||||||
|
uses: ad-m/github-push-action@master
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.MY_TOKEN }}
|
||||||
|
branch: main
|
||||||
|
repository: alist-org/desktop-release
|
2
.github/workflows/release_docker.yml
vendored
2
.github/workflows/release_docker.yml
vendored
@ -57,7 +57,7 @@ jobs:
|
|||||||
- name: Add tag
|
- name: Add tag
|
||||||
run: |
|
run: |
|
||||||
git config --local user.email "i@nn.ci"
|
git config --local user.email "i@nn.ci"
|
||||||
git config --local user.name "Noah Hsu"
|
git config --local user.name "Andy Hsu"
|
||||||
git tag -a ${{ github.ref_name }} -m "release ${{ github.ref_name }}"
|
git tag -a ${{ github.ref_name }} -m "release ${{ github.ref_name }}"
|
||||||
|
|
||||||
- name: Push tags
|
- name: Push tags
|
||||||
|
@ -15,4 +15,4 @@ RUN apk add --no-cache bash ca-certificates su-exec tzdata; \
|
|||||||
chmod +x /entrypoint.sh
|
chmod +x /entrypoint.sh
|
||||||
ENV PUID=0 PGID=0 UMASK=022
|
ENV PUID=0 PGID=0 UMASK=022
|
||||||
EXPOSE 5244
|
EXPOSE 5244
|
||||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
CMD [ "/entrypoint.sh" ]
|
||||||
|
14
README_cn.md
14
README_cn.md
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
[English](./README.md) | 中文 | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
|
[English](./README.md) | 中文 | [Contributing](./CONTRIBUTING.md) | [CODE_OF_CONDUCT](./CODE_OF_CONDUCT.md)
|
||||||
|
|
||||||
## Features
|
## 功能
|
||||||
|
|
||||||
- [x] 多种存储
|
- [x] 多种存储
|
||||||
- [x] 本地存储
|
- [x] 本地存储
|
||||||
@ -89,7 +89,7 @@
|
|||||||
- [x] 离线下载
|
- [x] 离线下载
|
||||||
- [x] 跨存储复制文件
|
- [x] 跨存储复制文件
|
||||||
|
|
||||||
## Document
|
## 文档
|
||||||
|
|
||||||
<https://alist.nn.ci/zh/>
|
<https://alist.nn.ci/zh/>
|
||||||
|
|
||||||
@ -97,21 +97,21 @@
|
|||||||
|
|
||||||
<https://al.nn.ci>
|
<https://al.nn.ci>
|
||||||
|
|
||||||
## Discussion
|
## 讨论
|
||||||
|
|
||||||
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告和功能请求。**
|
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) ,**issue仅针对错误报告和功能请求。**
|
||||||
|
|
||||||
## Sponsor
|
## 赞助
|
||||||
|
|
||||||
AList 是一个开源软件,如果你碰巧喜欢这个项目,并希望我继续下去,请考虑赞助我或提供一个单一的捐款!感谢所有的爱和支持:https://alist.nn.ci/zh/guide/sponsor.html
|
AList 是一个开源软件,如果你碰巧喜欢这个项目,并希望我继续下去,请考虑赞助我或提供一个单一的捐款!感谢所有的爱和支持:https://alist.nn.ci/zh/guide/sponsor.html
|
||||||
|
|
||||||
### Special sponsors
|
### 特别赞助
|
||||||
|
|
||||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||||
- [KinhDown 百度云盘不限速下载!永久免费!已稳定运行3年!非常可靠!Q群 -> 786799372](https://kinhdown.com)
|
- [KinhDown 百度云盘不限速下载!永久免费!已稳定运行3年!非常可靠!Q群 -> 786799372](https://kinhdown.com)
|
||||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||||
|
|
||||||
## Contributors
|
## 贡献者
|
||||||
|
|
||||||
Thanks goes to these wonderful people:
|
Thanks goes to these wonderful people:
|
||||||
|
|
||||||
@ -130,4 +130,4 @@ Thanks goes to these wonderful people:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
> [@博客](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@Telegram群](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2)
|
> [@博客](https://nn.ci/) · [@GitHub](https://github.com/Xhofe) · [@Telegram群](https://t.me/alist_chat) · [@Discord](https://discord.gg/F4ymsH4xv2)
|
||||||
|
2
build.sh
2
build.sh
@ -43,6 +43,8 @@ FetchWebRelease() {
|
|||||||
|
|
||||||
BuildWinArm64() {
|
BuildWinArm64() {
|
||||||
echo building for windows-arm64
|
echo building for windows-arm64
|
||||||
|
chmod +x ./wrapper/zcc-arm64
|
||||||
|
chmod +x ./wrapper/zcxx-arm64
|
||||||
export GOOS=windows
|
export GOOS=windows
|
||||||
export GOARCH=arm64
|
export GOARCH=arm64
|
||||||
export CC=$(pwd)/wrapper/zcc-arm64
|
export CC=$(pwd)/wrapper/zcc-arm64
|
||||||
|
@ -6,4 +6,5 @@ var (
|
|||||||
NoPrefix bool
|
NoPrefix bool
|
||||||
Dev bool
|
Dev bool
|
||||||
ForceBinDir bool
|
ForceBinDir bool
|
||||||
|
LogStd bool
|
||||||
)
|
)
|
||||||
|
@ -78,10 +78,19 @@ func writeFile(name string, data interface{}) {
|
|||||||
func generateDriversJson() {
|
func generateDriversJson() {
|
||||||
drivers := make(Drivers)
|
drivers := make(Drivers)
|
||||||
drivers["drivers"] = make(KV[interface{}])
|
drivers["drivers"] = make(KV[interface{}])
|
||||||
|
drivers["config"] = make(KV[interface{}])
|
||||||
driverInfoMap := op.GetDriverInfoMap()
|
driverInfoMap := op.GetDriverInfoMap()
|
||||||
for k, v := range driverInfoMap {
|
for k, v := range driverInfoMap {
|
||||||
drivers["drivers"][k] = convert(k)
|
drivers["drivers"][k] = convert(k)
|
||||||
items := make(KV[interface{}])
|
items := make(KV[interface{}])
|
||||||
|
config := map[string]string{}
|
||||||
|
if v.Config.Alert != "" {
|
||||||
|
alert := strings.SplitN(v.Config.Alert, "|", 2)
|
||||||
|
if len(alert) > 1 {
|
||||||
|
config["alert"] = alert[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drivers["config"][k] = config
|
||||||
for i := range v.Additional {
|
for i := range v.Additional {
|
||||||
item := v.Additional[i]
|
item := v.Additional[i]
|
||||||
items[item.Name] = convert(item.Name)
|
items[item.Name] = convert(item.Name)
|
||||||
|
@ -29,4 +29,5 @@ func init() {
|
|||||||
RootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
|
RootCmd.PersistentFlags().BoolVar(&flags.NoPrefix, "no-prefix", false, "disable env prefix")
|
||||||
RootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
|
RootCmd.PersistentFlags().BoolVar(&flags.Dev, "dev", false, "start with dev mode")
|
||||||
RootCmd.PersistentFlags().BoolVar(&flags.ForceBinDir, "force-bin-dir", false, "Force to use the directory where the binary file is located as data directory")
|
RootCmd.PersistentFlags().BoolVar(&flags.ForceBinDir, "force-bin-dir", false, "Force to use the directory where the binary file is located as data directory")
|
||||||
|
RootCmd.PersistentFlags().BoolVar(&flags.LogStd, "log-std", false, "Force to log to std")
|
||||||
}
|
}
|
||||||
|
52
cmd/storage.go
Normal file
52
cmd/storage.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2023 NAME HERE <EMAIL ADDRESS>
|
||||||
|
*/
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// storageCmd represents the storage command
|
||||||
|
var storageCmd = &cobra.Command{
|
||||||
|
Use: "storage",
|
||||||
|
Short: "Manage storage",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
var mountPath string
|
||||||
|
var disable = &cobra.Command{
|
||||||
|
Use: "disable",
|
||||||
|
Short: "Disable a storage",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
Init()
|
||||||
|
storage, err := db.GetStorageByMountPath(mountPath)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log.Errorf("failed to query storage: %+v", err)
|
||||||
|
} else {
|
||||||
|
storage.Disabled = true
|
||||||
|
err = db.UpdateStorage(storage)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log.Errorf("failed to update storage: %+v", err)
|
||||||
|
} else {
|
||||||
|
utils.Log.Infof("Storage with mount path [%s] have been disabled", mountPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
disable.Flags().StringVarP(&mountPath, "mount-path", "m", "", "The mountPath of storage")
|
||||||
|
RootCmd.AddCommand(storageCmd)
|
||||||
|
storageCmd.AddCommand(disable)
|
||||||
|
|
||||||
|
// Here you will define your flags and configuration settings.
|
||||||
|
|
||||||
|
// Cobra supports Persistent Flags which will work for this command
|
||||||
|
// and all subcommands, e.g.:
|
||||||
|
// storageCmd.PersistentFlags().String("foo", "", "A help for foo")
|
||||||
|
|
||||||
|
// Cobra supports local flags which will only run when this command
|
||||||
|
// is called directly, e.g.:
|
||||||
|
// storageCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
}
|
@ -6,16 +6,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
Cookie string `json:"cookie"`
|
Cookie string `json:"cookie" type:"text" help:"one of QR code token and cookie required"`
|
||||||
QRCodeToken string `json:"qrcode_token"`
|
QRCodeToken string `json:"qrcode_token" type:"text" help:"one of QR code token and cookie required"`
|
||||||
|
PageSize int64 `json:"page_size" type:"number" default:"56" help:"list api per page size of 115 driver"`
|
||||||
driver.RootID
|
driver.RootID
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
Name: "115 Cloud",
|
Name: "115 Cloud",
|
||||||
DefaultRoot: "0",
|
DefaultRoot: "0",
|
||||||
OnlyProxy: true,
|
OnlyProxy: true,
|
||||||
OnlyLocal: true,
|
OnlyLocal: true,
|
||||||
|
NoOverwriteUpload: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 115Browser/23.9.3.2 115disk/30.1.0"
|
var UserAgent = driver.UA115Desktop
|
||||||
|
|
||||||
func (d *Pan115) login() error {
|
func (d *Pan115) login() error {
|
||||||
var err error
|
var err error
|
||||||
@ -38,7 +38,10 @@ func (d *Pan115) login() error {
|
|||||||
|
|
||||||
func (d *Pan115) getFiles(fileId string) ([]driver.File, error) {
|
func (d *Pan115) getFiles(fileId string) ([]driver.File, error) {
|
||||||
res := make([]driver.File, 0)
|
res := make([]driver.File, 0)
|
||||||
files, err := d.client.List(fileId)
|
if d.PageSize <= 0 {
|
||||||
|
d.PageSize = driver.FileListLimit
|
||||||
|
}
|
||||||
|
files, err := d.client.ListWithLimit(fileId, d.PageSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,6 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
//type BaseResp struct {
|
|
||||||
// Code interface{} `json:"code"`
|
|
||||||
// Message string `json:"message"`
|
|
||||||
//}
|
|
||||||
|
|
||||||
type TokenResp struct {
|
|
||||||
//BaseResp
|
|
||||||
Data struct {
|
|
||||||
Token string `json:"token"`
|
|
||||||
} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type File struct {
|
type File struct {
|
||||||
FileName string `json:"FileName"`
|
FileName string `json:"FileName"`
|
||||||
Size int64 `json:"Size"`
|
Size int64 `json:"Size"`
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
@ -28,9 +29,7 @@ func (d *Pan123) login() error {
|
|||||||
"password": d.Password,
|
"password": d.Password,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var resp TokenResp
|
|
||||||
res, err := base.RestyClient.R().
|
res, err := base.RestyClient.R().
|
||||||
SetResult(&resp).
|
|
||||||
SetBody(body).Post(url)
|
SetBody(body).Post(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -38,7 +37,7 @@ func (d *Pan123) login() error {
|
|||||||
if utils.Json.Get(res.Body(), "code").ToInt() != 200 {
|
if utils.Json.Get(res.Body(), "code").ToInt() != 200 {
|
||||||
err = fmt.Errorf(utils.Json.Get(res.Body(), "message").ToString())
|
err = fmt.Errorf(utils.Json.Get(res.Body(), "message").ToString())
|
||||||
} else {
|
} else {
|
||||||
d.AccessToken = resp.Data.Token
|
d.AccessToken = utils.Json.Get(res.Body(), "data", "token").ToString()
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -77,18 +76,19 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Pan123) getFiles(parentId string) ([]File, error) {
|
func (d *Pan123) getFiles(parentId string) ([]File, error) {
|
||||||
next := "0"
|
page := 1
|
||||||
res := make([]File, 0)
|
res := make([]File, 0)
|
||||||
for next != "-1" {
|
for {
|
||||||
var resp Files
|
var resp Files
|
||||||
query := map[string]string{
|
query := map[string]string{
|
||||||
"driveId": "0",
|
"driveId": "0",
|
||||||
"limit": "100",
|
"limit": "100",
|
||||||
"next": next,
|
"next": "0",
|
||||||
"orderBy": d.OrderBy,
|
"orderBy": d.OrderBy,
|
||||||
"orderDirection": d.OrderDirection,
|
"orderDirection": d.OrderDirection,
|
||||||
"parentFileId": parentId,
|
"parentFileId": parentId,
|
||||||
"trashed": "false",
|
"trashed": "false",
|
||||||
|
"Page": strconv.Itoa(page),
|
||||||
}
|
}
|
||||||
_, err := d.request("https://www.123pan.com/api/file/list/new", http.MethodGet, func(req *resty.Request) {
|
_, err := d.request("https://www.123pan.com/api/file/list/new", http.MethodGet, func(req *resty.Request) {
|
||||||
req.SetQueryParams(query)
|
req.SetQueryParams(query)
|
||||||
@ -96,8 +96,11 @@ func (d *Pan123) getFiles(parentId string) ([]File, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
next = resp.Data.Next
|
page++
|
||||||
res = append(res, resp.Data.InfoList...)
|
res = append(res, resp.Data.InfoList...)
|
||||||
|
if len(resp.Data.InfoList) == 0 || resp.Data.Next == "-1" {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
114
drivers/alias/driver.go
Normal file
114
drivers/alias/driver.go
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
package alias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Alias struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
pathMap map[string][]string
|
||||||
|
autoFlatten bool
|
||||||
|
oneKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) Init(ctx context.Context) error {
|
||||||
|
if d.Paths == "" {
|
||||||
|
return errors.New("paths is required")
|
||||||
|
}
|
||||||
|
d.pathMap = make(map[string][]string)
|
||||||
|
for _, path := range strings.Split(d.Paths, "\n") {
|
||||||
|
path = strings.TrimSpace(path)
|
||||||
|
if path == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
k, v := getPair(path)
|
||||||
|
d.pathMap[k] = append(d.pathMap[k], v)
|
||||||
|
}
|
||||||
|
if len(d.pathMap) == 1 {
|
||||||
|
for k := range d.pathMap {
|
||||||
|
d.oneKey = k
|
||||||
|
}
|
||||||
|
d.autoFlatten = true
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) Drop(ctx context.Context) error {
|
||||||
|
d.pathMap = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) Get(ctx context.Context, path string) (model.Obj, error) {
|
||||||
|
if utils.PathEqual(path, "/") {
|
||||||
|
return &model.Object{
|
||||||
|
Name: "Root",
|
||||||
|
IsFolder: true,
|
||||||
|
Path: "/",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
root, sub := d.getRootAndPath(path)
|
||||||
|
dsts, ok := d.pathMap[root]
|
||||||
|
if !ok {
|
||||||
|
return nil, errs.ObjectNotFound
|
||||||
|
}
|
||||||
|
for _, dst := range dsts {
|
||||||
|
obj, err := d.get(ctx, path, dst, sub)
|
||||||
|
if err == nil {
|
||||||
|
return obj, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errs.ObjectNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
path := dir.GetPath()
|
||||||
|
if utils.PathEqual(path, "/") && !d.autoFlatten {
|
||||||
|
return d.listRoot(), nil
|
||||||
|
}
|
||||||
|
root, sub := d.getRootAndPath(path)
|
||||||
|
dsts, ok := d.pathMap[root]
|
||||||
|
if !ok {
|
||||||
|
return nil, errs.ObjectNotFound
|
||||||
|
}
|
||||||
|
var objs []model.Obj
|
||||||
|
for _, dst := range dsts {
|
||||||
|
tmp, err := d.list(ctx, dst, sub)
|
||||||
|
if err == nil {
|
||||||
|
objs = append(objs, tmp...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return objs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
root, sub := d.getRootAndPath(file.GetPath())
|
||||||
|
dsts, ok := d.pathMap[root]
|
||||||
|
if !ok {
|
||||||
|
return nil, errs.ObjectNotFound
|
||||||
|
}
|
||||||
|
for _, dst := range dsts {
|
||||||
|
link, err := d.link(ctx, dst, sub, args)
|
||||||
|
if err == nil {
|
||||||
|
return link, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, errs.ObjectNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*Alias)(nil)
|
27
drivers/alias/meta.go
Normal file
27
drivers/alias/meta.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package alias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
// Usually one of two
|
||||||
|
// driver.RootPath
|
||||||
|
// define other
|
||||||
|
Paths string `json:"paths" required:"true" type:"text"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "Alias",
|
||||||
|
LocalSort: true,
|
||||||
|
NoCache: true,
|
||||||
|
NoUpload: true,
|
||||||
|
DefaultRoot: "/",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &Alias{}
|
||||||
|
})
|
||||||
|
}
|
1
drivers/alias/types.go
Normal file
1
drivers/alias/types.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package alias
|
103
drivers/alias/util.go
Normal file
103
drivers/alias/util.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
package alias
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
stdpath "path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/fs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/internal/sign"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Alias) listRoot() []model.Obj {
|
||||||
|
var objs []model.Obj
|
||||||
|
for k, _ := range d.pathMap {
|
||||||
|
obj := model.Object{
|
||||||
|
Name: k,
|
||||||
|
IsFolder: true,
|
||||||
|
Modified: d.Modified,
|
||||||
|
}
|
||||||
|
objs = append(objs, &obj)
|
||||||
|
}
|
||||||
|
return objs
|
||||||
|
}
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
||||||
|
func getPair(path string) (string, string) {
|
||||||
|
//path = strings.TrimSpace(path)
|
||||||
|
if strings.Contains(path, ":") {
|
||||||
|
pair := strings.SplitN(path, ":", 2)
|
||||||
|
if !strings.Contains(pair[0], "/") {
|
||||||
|
return pair[0], pair[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stdpath.Base(path), path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) getRootAndPath(path string) (string, string) {
|
||||||
|
if d.autoFlatten {
|
||||||
|
return d.oneKey, path
|
||||||
|
}
|
||||||
|
path = strings.TrimPrefix(path, "/")
|
||||||
|
parts := strings.SplitN(path, "/", 2)
|
||||||
|
if len(parts) == 1 {
|
||||||
|
return parts[0], ""
|
||||||
|
}
|
||||||
|
return parts[0], parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) get(ctx context.Context, path string, dst, sub string) (model.Obj, error) {
|
||||||
|
obj, err := fs.Get(ctx, stdpath.Join(dst, sub))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Object{
|
||||||
|
Path: path,
|
||||||
|
Name: obj.GetName(),
|
||||||
|
Size: obj.GetSize(),
|
||||||
|
Modified: obj.ModTime(),
|
||||||
|
IsFolder: obj.IsDir(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) list(ctx context.Context, dst, sub string) ([]model.Obj, error) {
|
||||||
|
objs, err := fs.List(ctx, stdpath.Join(dst, sub))
|
||||||
|
// the obj must implement the model.SetPath interface
|
||||||
|
// return objs, err
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utils.SliceConvert(objs, func(obj model.Obj) (model.Obj, error) {
|
||||||
|
return &model.Object{
|
||||||
|
Name: obj.GetName(),
|
||||||
|
Size: obj.GetSize(),
|
||||||
|
Modified: obj.ModTime(),
|
||||||
|
IsFolder: obj.IsDir(),
|
||||||
|
}, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Alias) link(ctx context.Context, dst, sub string, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
reqPath := stdpath.Join(dst, sub)
|
||||||
|
storage, err := fs.GetStorage(reqPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = fs.Get(ctx, reqPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if common.ShouldProxy(storage, stdpath.Base(sub)) {
|
||||||
|
return &model.Link{
|
||||||
|
URL: fmt.Sprintf("/p%s?sign=%s",
|
||||||
|
utils.EncodePath(reqPath, true),
|
||||||
|
sign.Sign(reqPath)),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
link, _, err := fs.Link(ctx, reqPath, args)
|
||||||
|
return link, err
|
||||||
|
}
|
@ -2,6 +2,7 @@ package alist_v3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -55,6 +56,9 @@ func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
return nil, errors.New(resp.Message)
|
||||||
|
}
|
||||||
var files []model.Obj
|
var files []model.Obj
|
||||||
for _, f := range resp.Data.Content {
|
for _, f := range resp.Data.Content {
|
||||||
file := model.ObjThumb{
|
file := model.ObjThumb{
|
||||||
@ -84,6 +88,9 @@ func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if resp.Code != 200 {
|
||||||
|
return nil, errors.New(resp.Message)
|
||||||
|
}
|
||||||
return &model.Link{
|
return &model.Link{
|
||||||
URL: resp.Data.RawURL,
|
URL: resp.Data.RawURL,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -10,7 +10,7 @@ func checkResp(resp common.Resp[interface{}], err error) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if resp.Message == "success" {
|
if resp.Code == 200 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.New(resp.Message)
|
return errors.New(resp.Message)
|
||||||
|
@ -71,14 +71,14 @@ func (d *AliDrive) Init(ctx context.Context) error {
|
|||||||
// init privateKey
|
// init privateKey
|
||||||
privateKey, _ := NewPrivateKeyFromHex(deviceID)
|
privateKey, _ := NewPrivateKeyFromHex(deviceID)
|
||||||
state := State{
|
state := State{
|
||||||
nonce: -1,
|
|
||||||
privateKey: privateKey,
|
privateKey: privateKey,
|
||||||
deviceID: deviceID,
|
deviceID: deviceID,
|
||||||
}
|
}
|
||||||
// store state
|
// store state
|
||||||
global.Store(d.UserID, &state)
|
global.Store(d.UserID, &state)
|
||||||
// init signature
|
// init signature
|
||||||
return d.reSign()
|
d.sign()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliDrive) Drop(ctx context.Context) error {
|
func (d *AliDrive) Drop(ctx context.Context) error {
|
||||||
@ -226,7 +226,7 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
delete(reqBody, "pre_hash")
|
delete(reqBody, "pre_hash")
|
||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
if localFile != nil {
|
if localFile != nil {
|
||||||
if _, err = io.Copy(h, localFile); err != nil {
|
if err = utils.CopyWithCtx(ctx, h, localFile, 0, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err = localFile.Seek(0, io.SeekStart); err != nil {
|
if _, err = localFile.Seek(0, io.SeekStart); err != nil {
|
||||||
@ -241,7 +241,7 @@ func (d *AliDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileS
|
|||||||
_ = tempFile.Close()
|
_ = tempFile.Close()
|
||||||
_ = os.Remove(tempFile.Name())
|
_ = os.Remove(tempFile.Name())
|
||||||
}()
|
}()
|
||||||
if _, err = io.Copy(io.MultiWriter(tempFile, h), file); err != nil {
|
if err = utils.CopyWithCtx(ctx, io.MultiWriter(tempFile, h), file, 0, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
localFile = tempFile
|
localFile = tempFile
|
||||||
@ -337,6 +337,7 @@ func (d *AliDrive) Other(ctx context.Context, args model.OtherArgs) (interface{}
|
|||||||
case "video_preview":
|
case "video_preview":
|
||||||
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
||||||
data["category"] = "live_transcoding"
|
data["category"] = "live_transcoding"
|
||||||
|
data["url_expire_sec"] = 14400
|
||||||
default:
|
default:
|
||||||
return nil, errs.NotSupport
|
return nil, errs.NotSupport
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
type State struct {
|
type State struct {
|
||||||
deviceID string
|
deviceID string
|
||||||
signature string
|
signature string
|
||||||
nonce int
|
retry int
|
||||||
privateKey *ecdsa.PrivateKey
|
privateKey *ecdsa.PrivateKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +18,9 @@ type Addition struct {
|
|||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
Name: "Aliyundrive",
|
Name: "Aliyundrive",
|
||||||
DefaultRoot: "root",
|
DefaultRoot: "root",
|
||||||
|
Alert: `warning|There may be an infinite loop bug in this driver.
|
||||||
|
Deprecated, no longer maintained and will be removed in a future version.
|
||||||
|
We recommend using the official driver AliyundriveOpen.`,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -13,7 +13,6 @@ import (
|
|||||||
"github.com/dustinxie/ecc"
|
"github.com/dustinxie/ecc"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *AliDrive) createSession() error {
|
func (d *AliDrive) createSession() error {
|
||||||
@ -21,56 +20,39 @@ func (d *AliDrive) createSession() error {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("can't load user state, user_id: %s", d.UserID)
|
return fmt.Errorf("can't load user state, user_id: %s", d.UserID)
|
||||||
}
|
}
|
||||||
|
d.sign()
|
||||||
|
state.retry++
|
||||||
|
if state.retry > 3 {
|
||||||
|
state.retry = 0
|
||||||
|
return fmt.Errorf("createSession failed after three retries")
|
||||||
|
}
|
||||||
_, err, _ := d.request("https://api.aliyundrive.com/users/v1/users/device/create_session", http.MethodPost, func(req *resty.Request) {
|
_, err, _ := d.request("https://api.aliyundrive.com/users/v1/users/device/create_session", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetBody(base.Json{
|
req.SetBody(base.Json{
|
||||||
"deviceName": "samsung",
|
"deviceName": "samsung",
|
||||||
"modelName": "SM-G9810",
|
"modelName": "SM-G9810",
|
||||||
"nonce": state.nonce,
|
"nonce": 0,
|
||||||
"pubKey": PublicKeyToHex(&state.privateKey.PublicKey),
|
"pubKey": PublicKeyToHex(&state.privateKey.PublicKey),
|
||||||
"refreshToken": d.RefreshToken,
|
"refreshToken": d.RefreshToken,
|
||||||
})
|
})
|
||||||
}, nil)
|
}, nil)
|
||||||
|
if err == nil{
|
||||||
|
state.retry = 0
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliDrive) renewSession() error {
|
// func (d *AliDrive) renewSession() error {
|
||||||
_, err, _ := d.request("https://api.aliyundrive.com/users/v1/users/device/renew_session", http.MethodPost, nil, nil)
|
// _, err, _ := d.request("https://api.aliyundrive.com/users/v1/users/device/renew_session", http.MethodPost, nil, nil)
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (d *AliDrive) sign() {
|
func (d *AliDrive) sign() {
|
||||||
state, ok := global.Load(d.UserID)
|
state, _ := global.Load(d.UserID)
|
||||||
if !ok {
|
|
||||||
log.Errorf("can't load user state, user_id: %s", d.UserID)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
secpAppID := "5dde4e1bdf9e4966b387ba58f4b3fdc3"
|
secpAppID := "5dde4e1bdf9e4966b387ba58f4b3fdc3"
|
||||||
singdata := fmt.Sprintf("%s:%s:%s:%d", secpAppID, state.deviceID, d.UserID, state.nonce)
|
singdata := fmt.Sprintf("%s:%s:%s:%d", secpAppID, state.deviceID, d.UserID, 0)
|
||||||
hash := sha256.Sum256([]byte(singdata))
|
hash := sha256.Sum256([]byte(singdata))
|
||||||
data, _ := ecc.SignBytes(state.privateKey, hash[:], ecc.RecID|ecc.LowerS)
|
data, _ := ecc.SignBytes(state.privateKey, hash[:], ecc.RecID|ecc.LowerS)
|
||||||
state.signature = hex.EncodeToString(data)
|
state.signature = hex.EncodeToString(data) //strconv.Itoa(state.nonce)
|
||||||
}
|
|
||||||
|
|
||||||
func (d *AliDrive) reSign() error {
|
|
||||||
state, ok := global.Load(d.UserID)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("can't load user state, user_id: %s", d.UserID)
|
|
||||||
}
|
|
||||||
state.nonce++
|
|
||||||
if state.nonce >= 1073741823 {
|
|
||||||
state.nonce = 0
|
|
||||||
}
|
|
||||||
d.sign()
|
|
||||||
if state.nonce == 0 {
|
|
||||||
return d.createSession()
|
|
||||||
}
|
|
||||||
err := d.renewSession()
|
|
||||||
if err != nil && err.Error() == "device session signature error" {
|
|
||||||
state.nonce = 0
|
|
||||||
d.sign()
|
|
||||||
return d.createSession()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// do others that not defined in Driver interface
|
// do others that not defined in Driver interface
|
||||||
@ -141,7 +123,7 @@ func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp i
|
|||||||
return nil, err, e
|
return nil, err, e
|
||||||
}
|
}
|
||||||
case "DeviceSessionSignatureInvalid":
|
case "DeviceSessionSignatureInvalid":
|
||||||
err = d.reSign()
|
err = d.createSession()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err, e
|
return nil, err, e
|
||||||
}
|
}
|
||||||
|
228
drivers/aliyundrive_open/driver.go
Normal file
228
drivers/aliyundrive_open/driver.go
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
package aliyundrive_open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AliyundriveOpen struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
base string
|
||||||
|
|
||||||
|
AccessToken string
|
||||||
|
DriveId string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Init(ctx context.Context) error {
|
||||||
|
err := d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res, err := d.request("/adrive/v1.0/user/getDriveInfo", http.MethodPost, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.DriveId = utils.Json.Get(res, "default_drive_id").ToString()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
files, err := d.getFiles(dir.GetID())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
|
||||||
|
return fileToObj(src), nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
res, err := d.request("/adrive/v1.0/openFile/getDownloadUrl", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": file.GetID(),
|
||||||
|
"expire_sec": 14400,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
url := utils.Json.Get(res, "url").ToString()
|
||||||
|
return &model.Link{
|
||||||
|
URL: url,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"parent_file_id": parentDir.GetID(),
|
||||||
|
"name": dirName,
|
||||||
|
"type": "folder",
|
||||||
|
"check_name_mode": "refuse",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/move", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": srcObj.GetID(),
|
||||||
|
"to_parent_file_id": dstDir.GetID(),
|
||||||
|
"check_name_mode": "refuse", // optional:ignore,auto_rename,refuse
|
||||||
|
//"new_name": "newName", // The new name to use when a file of the same name exists
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/update", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": srcObj.GetID(),
|
||||||
|
"name": newName,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/copy", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": srcObj.GetID(),
|
||||||
|
"to_parent_file_id": dstDir.GetID(),
|
||||||
|
"auto_rename": true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
uri := "/adrive/v1.0/openFile/recyclebin/trash"
|
||||||
|
if d.RemoveWay == "delete" {
|
||||||
|
uri = "/adrive/v1.0/openFile/delete"
|
||||||
|
}
|
||||||
|
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": obj.GetID(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
// rapid_upload is not currently supported
|
||||||
|
// 1. create
|
||||||
|
const DEFAULT int64 = 20971520
|
||||||
|
createData := base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"parent_file_id": dstDir.GetID(),
|
||||||
|
"name": stream.GetName(),
|
||||||
|
"type": "file",
|
||||||
|
"check_name_mode": "ignore",
|
||||||
|
}
|
||||||
|
count := 1
|
||||||
|
if stream.GetSize() > DEFAULT {
|
||||||
|
count = int(math.Ceil(float64(stream.GetSize()) / float64(DEFAULT)))
|
||||||
|
partInfoList := make([]base.Json, 0, count)
|
||||||
|
for i := 1; i <= count; i++ {
|
||||||
|
partInfoList = append(partInfoList, base.Json{"part_number": i})
|
||||||
|
}
|
||||||
|
createData["part_info_list"] = partInfoList
|
||||||
|
}
|
||||||
|
var createResp CreateResp
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(createData).SetResult(&createResp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 2. upload
|
||||||
|
for i, partInfo := range createResp.PartInfoList {
|
||||||
|
if utils.IsCanceled(ctx) {
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
uploadUrl := partInfo.UploadUrl
|
||||||
|
if d.InternalUpload {
|
||||||
|
//Replace a known public Host with an internal Host
|
||||||
|
uploadUrl = strings.ReplaceAll(uploadUrl, "https://cn-beijing-data.aliyundrive.net/", "http://ccp-bj29-bj-1592982087.oss-cn-beijing-internal.aliyuncs.com/")
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest("PUT", uploadUrl, io.LimitReader(stream, DEFAULT))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req = req.WithContext(ctx)
|
||||||
|
res, err := base.HttpClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res.Body.Close()
|
||||||
|
if count > 0 {
|
||||||
|
up(i * 100 / count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 3. complete
|
||||||
|
_, err = d.request("/adrive/v1.0/openFile/complete", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": createResp.FileId,
|
||||||
|
"upload_id": createResp.UploadId,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
var resp base.Json
|
||||||
|
var uri string
|
||||||
|
data := base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"file_id": args.Obj.GetID(),
|
||||||
|
}
|
||||||
|
switch args.Method {
|
||||||
|
case "video_preview":
|
||||||
|
uri = "/adrive/v1.0/openFile/getVideoPreviewPlayInfo"
|
||||||
|
data["category"] = "live_transcoding"
|
||||||
|
data["url_expire_sec"] = 14400
|
||||||
|
default:
|
||||||
|
return nil, errs.NotSupport
|
||||||
|
}
|
||||||
|
_, err := d.request(uri, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(data).SetResult(&resp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*AliyundriveOpen)(nil)
|
38
drivers/aliyundrive_open/meta.go
Normal file
38
drivers/aliyundrive_open/meta.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package aliyundrive_open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootID
|
||||||
|
RefreshToken string `json:"refresh_token" required:"true"`
|
||||||
|
OrderBy string `json:"order_by" type:"select" options:"name,size,updated_at,created_at"`
|
||||||
|
OrderDirection string `json:"order_direction" type:"select" options:"ASC,DESC"`
|
||||||
|
OauthTokenURL string `json:"oauth_token_url" default:"https://api.nn.ci/alist/ali_open/token"`
|
||||||
|
ClientID string `json:"client_id" required:"false" help:"Keep it empty if you don't have one"`
|
||||||
|
ClientSecret string `json:"client_secret" required:"false" help:"Keep it empty if you don't have one"`
|
||||||
|
RemoveWay string `json:"remove_way" required:"true" type:"select" options:"trash,delete"`
|
||||||
|
InternalUpload bool `json:"internal_upload" help:"If you are using Aliyun ECS in Beijing, you can turn it on to boost the upload speed"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "AliyundriveOpen",
|
||||||
|
LocalSort: false,
|
||||||
|
OnlyLocal: false,
|
||||||
|
OnlyProxy: false,
|
||||||
|
NoCache: false,
|
||||||
|
NoUpload: false,
|
||||||
|
NeedMs: false,
|
||||||
|
DefaultRoot: "root",
|
||||||
|
NoOverwriteUpload: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &AliyundriveOpen{
|
||||||
|
base: "https://open.aliyundrive.com",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
67
drivers/aliyundrive_open/types.go
Normal file
67
drivers/aliyundrive_open/types.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package aliyundrive_open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrResp struct {
|
||||||
|
Code string `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Files struct {
|
||||||
|
Items []File `json:"items"`
|
||||||
|
NextMarker string `json:"next_marker"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
DriveId string `json:"drive_id"`
|
||||||
|
FileId string `json:"file_id"`
|
||||||
|
ParentFileId string `json:"parent_file_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
FileExtension string `json:"file_extension"`
|
||||||
|
ContentHash string `json:"content_hash"`
|
||||||
|
Category string `json:"category"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Thumbnail string `json:"thumbnail"`
|
||||||
|
Url string `json:"url"`
|
||||||
|
CreatedAt *time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func fileToObj(f File) *model.ObjThumb {
|
||||||
|
return &model.ObjThumb{
|
||||||
|
Object: model.Object{
|
||||||
|
ID: f.FileId,
|
||||||
|
Name: f.Name,
|
||||||
|
Size: f.Size,
|
||||||
|
Modified: f.UpdatedAt,
|
||||||
|
IsFolder: f.Type == "folder",
|
||||||
|
},
|
||||||
|
Thumbnail: model.Thumbnail{Thumbnail: f.Thumbnail},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateResp struct {
|
||||||
|
//Type string `json:"type"`
|
||||||
|
//ParentFileId string `json:"parent_file_id"`
|
||||||
|
//DriveId string `json:"drive_id"`
|
||||||
|
FileId string `json:"file_id"`
|
||||||
|
//RevisionId string `json:"revision_id"`
|
||||||
|
//EncryptMode string `json:"encrypt_mode"`
|
||||||
|
//DomainId string `json:"domain_id"`
|
||||||
|
//FileName string `json:"file_name"`
|
||||||
|
UploadId string `json:"upload_id"`
|
||||||
|
//Location string `json:"location"`
|
||||||
|
RapidUpload bool `json:"rapid_upload"`
|
||||||
|
PartInfoList []struct {
|
||||||
|
Etag interface{} `json:"etag"`
|
||||||
|
PartNumber int `json:"part_number"`
|
||||||
|
PartSize interface{} `json:"part_size"`
|
||||||
|
UploadUrl string `json:"upload_url"`
|
||||||
|
ContentType string `json:"content_type"`
|
||||||
|
} `json:"part_info_list"`
|
||||||
|
}
|
108
drivers/aliyundrive_open/util.go
Normal file
108
drivers/aliyundrive_open/util.go
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
package aliyundrive_open
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) refreshToken() error {
|
||||||
|
url := d.base + "/oauth/access_token"
|
||||||
|
if d.OauthTokenURL != "" && d.ClientID == "" {
|
||||||
|
url = d.OauthTokenURL
|
||||||
|
}
|
||||||
|
var resp base.TokenResp
|
||||||
|
var e ErrResp
|
||||||
|
_, err := base.RestyClient.R().
|
||||||
|
ForceContentType("application/json").
|
||||||
|
SetBody(base.Json{
|
||||||
|
"client_id": d.ClientID,
|
||||||
|
"client_secret": d.ClientSecret,
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
"refresh_token": d.RefreshToken,
|
||||||
|
}).
|
||||||
|
SetResult(&resp).
|
||||||
|
SetError(&e).
|
||||||
|
Post(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
return fmt.Errorf("failed to refresh token: %s", e.Message)
|
||||||
|
}
|
||||||
|
if resp.RefreshToken == "" {
|
||||||
|
return errors.New("failed to refresh token: refresh token is empty")
|
||||||
|
}
|
||||||
|
d.RefreshToken, d.AccessToken = resp.RefreshToken, resp.AccessToken
|
||||||
|
op.MustSaveDriverStorage(d)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) request(uri, method string, callback base.ReqCallback, retry ...bool) ([]byte, error) {
|
||||||
|
req := base.RestyClient.R()
|
||||||
|
// TODO check whether access_token is expired
|
||||||
|
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||||
|
if method == http.MethodPost {
|
||||||
|
req.SetHeader("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
}
|
||||||
|
var e ErrResp
|
||||||
|
req.SetError(&e)
|
||||||
|
res, err := req.Execute(method, d.base+uri)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
isRetry := len(retry) > 0 && retry[0]
|
||||||
|
if e.Code != "" {
|
||||||
|
if !isRetry && e.Code == "AccessTokenInvalid" {
|
||||||
|
err = d.refreshToken()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.request(uri, method, callback, true)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("%s:%s", e.Code, e.Message)
|
||||||
|
}
|
||||||
|
return res.Body(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) getFiles(fileId string) ([]File, error) {
|
||||||
|
marker := "first"
|
||||||
|
res := make([]File, 0)
|
||||||
|
for marker != "" {
|
||||||
|
if marker == "first" {
|
||||||
|
marker = ""
|
||||||
|
}
|
||||||
|
var resp Files
|
||||||
|
data := base.Json{
|
||||||
|
"drive_id": d.DriveId,
|
||||||
|
"limit": 200,
|
||||||
|
"marker": marker,
|
||||||
|
"order_by": d.OrderBy,
|
||||||
|
"order_direction": d.OrderDirection,
|
||||||
|
"parent_file_id": fileId,
|
||||||
|
//"category": "",
|
||||||
|
//"type": "",
|
||||||
|
//"video_thumbnail_time": 120000,
|
||||||
|
//"video_thumbnail_width": 480,
|
||||||
|
//"image_thumbnail_width": 480,
|
||||||
|
}
|
||||||
|
_, err := d.request("/adrive/v1.0/openFile/list", http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(data).SetResult(&resp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
marker = resp.NextMarker
|
||||||
|
res = append(res, resp.Items...)
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
@ -2,15 +2,16 @@ package aliyundrive_share
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/cron"
|
"github.com/alist-org/alist/v3/pkg/cron"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,40 +78,43 @@ func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.
|
|||||||
"share_id": d.ShareId,
|
"share_id": d.ShareId,
|
||||||
}
|
}
|
||||||
var resp ShareLinkResp
|
var resp ShareLinkResp
|
||||||
var e ErrorResp
|
_, err := d.request("https://api.aliyundrive.com/v2/file/get_share_link_download_url", http.MethodPost, func(req *resty.Request) {
|
||||||
_, err := base.RestyClient.R().
|
req.SetBody(data).SetResult(&resp)
|
||||||
SetError(&e).SetBody(data).SetResult(&resp).
|
})
|
||||||
SetHeader("content-type", "application/json").
|
|
||||||
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
|
|
||||||
SetHeader("x-share-token", d.ShareToken).
|
|
||||||
Post("https://api.aliyundrive.com/v2/file/get_share_link_download_url")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
var u string
|
|
||||||
if e.Code != "" {
|
|
||||||
if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" {
|
|
||||||
if e.Code == "AccessTokenInvalid" {
|
|
||||||
err = d.refreshToken()
|
|
||||||
} else {
|
|
||||||
err = d.getShareToken()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return d.Link(ctx, file, args)
|
|
||||||
} else {
|
|
||||||
return nil, errors.New(e.Code + ": " + e.Message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
u = resp.DownloadUrl
|
|
||||||
}
|
|
||||||
return &model.Link{
|
return &model.Link{
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
"Referer": []string{"https://www.aliyundrive.com/"},
|
"Referer": []string{"https://www.aliyundrive.com/"},
|
||||||
},
|
},
|
||||||
URL: u,
|
URL: resp.DownloadUrl,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
var resp base.Json
|
||||||
|
var url string
|
||||||
|
data := base.Json{
|
||||||
|
"share_id": d.ShareId,
|
||||||
|
"file_id": args.Obj.GetID(),
|
||||||
|
}
|
||||||
|
switch args.Method {
|
||||||
|
case "doc_preview":
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_office_preview_url"
|
||||||
|
case "video_preview":
|
||||||
|
url = "https://api.aliyundrive.com/v2/file/get_video_preview_play_info"
|
||||||
|
data["category"] = "live_transcoding"
|
||||||
|
default:
|
||||||
|
return nil, errs.NotSupport
|
||||||
|
}
|
||||||
|
_, err := d.request(url, http.MethodPost, func(req *resty.Request) {
|
||||||
|
req.SetBody(data).SetResult(&resp)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
var _ driver.Driver = (*AliyundriveShare)(nil)
|
var _ driver.Driver = (*AliyundriveShare)(nil)
|
||||||
|
@ -52,6 +52,40 @@ func (d *AliyundriveShare) getShareToken() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveShare) request(url, method string, callback base.ReqCallback) ([]byte, error) {
|
||||||
|
var e ErrorResp
|
||||||
|
req := base.RestyClient.R().
|
||||||
|
SetError(&e).
|
||||||
|
SetHeader("content-type", "application/json").
|
||||||
|
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
|
||||||
|
SetHeader("x-share-token", d.ShareToken)
|
||||||
|
if callback != nil {
|
||||||
|
callback(req)
|
||||||
|
} else {
|
||||||
|
req.SetBody("{}")
|
||||||
|
}
|
||||||
|
resp, err := req.Execute(method, url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if e.Code != "" {
|
||||||
|
if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" {
|
||||||
|
if e.Code == "AccessTokenInvalid" {
|
||||||
|
err = d.refreshToken()
|
||||||
|
} else {
|
||||||
|
err = d.getShareToken()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return d.request(url, method, callback)
|
||||||
|
} else {
|
||||||
|
return nil, errors.New(e.Code + ": " + e.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resp.Body(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (d *AliyundriveShare) getFiles(fileId string) ([]File, error) {
|
func (d *AliyundriveShare) getFiles(fileId string) ([]File, error) {
|
||||||
files := make([]File, 0)
|
files := make([]File, 0)
|
||||||
data := base.Json{
|
data := base.Json{
|
||||||
|
@ -6,12 +6,15 @@ import (
|
|||||||
_ "github.com/alist-org/alist/v3/drivers/139"
|
_ "github.com/alist-org/alist/v3/drivers/139"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/189"
|
_ "github.com/alist-org/alist/v3/drivers/189"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/189pc"
|
_ "github.com/alist-org/alist/v3/drivers/189pc"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/alias"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/alist_v2"
|
_ "github.com/alist-org/alist/v3/drivers/alist_v2"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/alist_v3"
|
_ "github.com/alist-org/alist/v3/drivers/alist_v3"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_open"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_share"
|
_ "github.com/alist-org/alist/v3/drivers/aliyundrive_share"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
|
_ "github.com/alist-org/alist/v3/drivers/baidu_netdisk"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/baidu_photo"
|
_ "github.com/alist-org/alist/v3/drivers/baidu_photo"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/baidu_share"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/cloudreve"
|
_ "github.com/alist-org/alist/v3/drivers/cloudreve"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
_ "github.com/alist-org/alist/v3/drivers/ftp"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/google_drive"
|
_ "github.com/alist-org/alist/v3/drivers/google_drive"
|
||||||
@ -31,6 +34,7 @@ import (
|
|||||||
_ "github.com/alist-org/alist/v3/drivers/teambition"
|
_ "github.com/alist-org/alist/v3/drivers/teambition"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/terabox"
|
_ "github.com/alist-org/alist/v3/drivers/terabox"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/thunder"
|
_ "github.com/alist-org/alist/v3/drivers/thunder"
|
||||||
|
_ "github.com/alist-org/alist/v3/drivers/trainbit"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/uss"
|
_ "github.com/alist-org/alist/v3/drivers/uss"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/virtual"
|
_ "github.com/alist-org/alist/v3/drivers/virtual"
|
||||||
_ "github.com/alist-org/alist/v3/drivers/webdav"
|
_ "github.com/alist-org/alist/v3/drivers/webdav"
|
||||||
|
@ -154,7 +154,7 @@ func (d *BaiduNetdisk) linkCrack(file model.Obj, args model.LinkArgs) (*model.Li
|
|||||||
"target": fmt.Sprintf("[\"%s\"]", file.GetPath()),
|
"target": fmt.Sprintf("[\"%s\"]", file.GetPath()),
|
||||||
"dlink": "1",
|
"dlink": "1",
|
||||||
"web": "5",
|
"web": "5",
|
||||||
"origin": "dlna",
|
//"origin": "dlna",
|
||||||
}
|
}
|
||||||
_, err := d.request("https://pan.baidu.com/api/filemetas", http.MethodGet, func(req *resty.Request) {
|
_, err := d.request("https://pan.baidu.com/api/filemetas", http.MethodGet, func(req *resty.Request) {
|
||||||
req.SetQueryParams(param)
|
req.SetQueryParams(param)
|
||||||
@ -165,7 +165,7 @@ func (d *BaiduNetdisk) linkCrack(file model.Obj, args model.LinkArgs) (*model.Li
|
|||||||
return &model.Link{
|
return &model.Link{
|
||||||
URL: resp.Info[0].Dlink,
|
URL: resp.Info[0].Dlink,
|
||||||
Header: http.Header{
|
Header: http.Header{
|
||||||
"User-Agent": []string{"pan.baidu.com"},
|
"User-Agent": []string{"netdisk"},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
153
drivers/baidu_share/driver.go
Normal file
153
drivers/baidu_share/driver.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package baidu_share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Xhofe/go-cache"
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BaiduShare struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
config map[string]string
|
||||||
|
dlinkCache cache.ICache[string]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Init(ctx context.Context) error {
|
||||||
|
// TODO login / refresh token
|
||||||
|
//op.MustSaveDriverStorage(d)
|
||||||
|
d.config = map[string]string{}
|
||||||
|
d.dlinkCache = cache.NewMemCache(cache.WithClearInterval[string](time.Duration(d.CacheExpiration) * time.Minute))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Drop(ctx context.Context) error {
|
||||||
|
d.dlinkCache.Clear()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
// TODO return the files list
|
||||||
|
body := url.Values{
|
||||||
|
"shorturl": {d.Surl},
|
||||||
|
"dir": {dir.GetPath()},
|
||||||
|
"root": {"0"},
|
||||||
|
"pwd": {d.Pwd},
|
||||||
|
"num": {"1000"},
|
||||||
|
"order": {"time"},
|
||||||
|
}
|
||||||
|
if body.Get("dir") == "" || body.Get("dir") == d.config["root"] {
|
||||||
|
body.Set("root", "1")
|
||||||
|
}
|
||||||
|
res := []model.Obj{}
|
||||||
|
var err error
|
||||||
|
var page int64 = 1
|
||||||
|
more := true
|
||||||
|
for more {
|
||||||
|
body.Set("page", strconv.FormatInt(page, 10))
|
||||||
|
req := base.RestyClient.R().
|
||||||
|
SetCookies([]*http.Cookie{{Name: "BDUSS", Value: d.BDUSS}}).
|
||||||
|
SetBody(body.Encode())
|
||||||
|
resp, e := req.Post("https://pan.baidu.com/share/wxlist?channel=weixin&version=2.2.2&clienttype=25&web=1")
|
||||||
|
err = e
|
||||||
|
jsonresp := jsonResp{}
|
||||||
|
if err == nil {
|
||||||
|
err = base.RestyClient.JSONUnmarshal(resp.Body(), &jsonresp)
|
||||||
|
}
|
||||||
|
if err == nil && jsonresp.Errno == 0 {
|
||||||
|
more = jsonresp.Data.More
|
||||||
|
page += 1
|
||||||
|
for _, v := range jsonresp.Data.List {
|
||||||
|
size, _ := v.Size.Int64()
|
||||||
|
mtime, _ := v.Time.Int64()
|
||||||
|
res = append(res, &model.Object{
|
||||||
|
ID: v.ID.String(),
|
||||||
|
Path: v.Path,
|
||||||
|
Name: v.Name,
|
||||||
|
Size: size,
|
||||||
|
Modified: time.Unix(mtime, 0),
|
||||||
|
IsFolder: v.Dir.String() == "1",
|
||||||
|
})
|
||||||
|
d.dlinkCache.Set(v.Path, v.Dlink, cache.WithEx[string](time.Duration(d.CacheExpiration/2)*time.Minute))
|
||||||
|
}
|
||||||
|
if len(res) > 0 && body.Get("root") == "1" {
|
||||||
|
d.config["root"] = path.Dir(res[0].GetPath())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err == nil {
|
||||||
|
err = fmt.Errorf("errno:%d", jsonresp.Errno)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
// TODO return link of file
|
||||||
|
var err error
|
||||||
|
req := base.RestyClient.R().SetCookies([]*http.Cookie{{Name: "BDUSS", Value: d.BDUSS}})
|
||||||
|
url, found := d.dlinkCache.Get(file.GetPath())
|
||||||
|
if !found {
|
||||||
|
_, err = d.List(ctx, &model.Object{Path: path.Dir(file.GetPath())}, model.ListArgs{})
|
||||||
|
url, found = d.dlinkCache.Get(file.GetPath())
|
||||||
|
if err == nil && !found {
|
||||||
|
err = errs.NotSupport
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &model.Link{URL: url, Header: req.Header}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
// TODO create folder
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
// TODO move obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
// TODO rename obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
// TODO copy obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
// TODO remove obj
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *BaiduShare) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
// TODO upload file
|
||||||
|
return errs.NotSupport
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (d *Template) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
|
||||||
|
// return nil, errs.NotSupport
|
||||||
|
//}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*BaiduShare)(nil)
|
34
drivers/baidu_share/meta.go
Normal file
34
drivers/baidu_share/meta.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package baidu_share
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
// Usually one of two
|
||||||
|
driver.RootPath
|
||||||
|
// driver.RootID
|
||||||
|
// define other
|
||||||
|
// Field string `json:"field" type:"select" required:"true" options:"a,b,c" default:"a"`
|
||||||
|
Surl string `json:"surl"`
|
||||||
|
Pwd string `json:"pwd"`
|
||||||
|
BDUSS string `json:"BDUSS"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "BaiduShare",
|
||||||
|
LocalSort: true,
|
||||||
|
OnlyLocal: false,
|
||||||
|
OnlyProxy: false,
|
||||||
|
NoCache: false,
|
||||||
|
NoUpload: true,
|
||||||
|
NeedMs: false,
|
||||||
|
DefaultRoot: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &BaiduShare{}
|
||||||
|
})
|
||||||
|
}
|
19
drivers/baidu_share/types.go
Normal file
19
drivers/baidu_share/types.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package baidu_share
|
||||||
|
|
||||||
|
import "encoding/json"
|
||||||
|
|
||||||
|
type jsonResp struct {
|
||||||
|
Errno int64 `json:"errno"`
|
||||||
|
Data struct {
|
||||||
|
More bool `json:"has_more"`
|
||||||
|
List []struct {
|
||||||
|
ID json.Number `json:"fs_id"`
|
||||||
|
Dir json.Number `json:"isdir"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Name string `json:"server_filename"`
|
||||||
|
Time json.Number `json:"server_mtime"`
|
||||||
|
Size json.Number `json:"size"`
|
||||||
|
Dlink string `json:"dlink"`
|
||||||
|
} `json:"list"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
3
drivers/baidu_share/util.go
Normal file
3
drivers/baidu_share/util.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package baidu_share
|
||||||
|
|
||||||
|
// do others that not defined in Driver interface
|
30
drivers/base/util.go
Normal file
30
drivers/base/util.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/http_range"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func HandleRange(link *model.Link, file io.ReadSeekCloser, header http.Header, size int64) {
|
||||||
|
if header.Get("Range") != "" {
|
||||||
|
r, err := http_range.ParseRange(header.Get("Range"), size)
|
||||||
|
if err == nil && len(r) > 0 {
|
||||||
|
_, err := file.Seek(r[0].Start, io.SeekStart)
|
||||||
|
if err == nil {
|
||||||
|
link.Data = utils.NewLimitReadCloser(file, func() error {
|
||||||
|
return file.Close()
|
||||||
|
}, r[0].Length)
|
||||||
|
link.Status = http.StatusPartialContent
|
||||||
|
link.Header = http.Header{
|
||||||
|
"Content-Range": []string{r[0].ContentRange(size)},
|
||||||
|
"Content-Length": []string{strconv.FormatInt(r[0].Length, 10)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
stdpath "path"
|
stdpath "path"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
@ -44,8 +45,7 @@ func (d *FTP) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res := make([]model.Obj, 0)
|
res := make([]model.Obj, 0)
|
||||||
for i, _ := range entries {
|
for _, entry := range entries {
|
||||||
entry := entries[i]
|
|
||||||
if entry.Name == "." || entry.Name == ".." {
|
if entry.Name == "." || entry.Name == ".." {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -64,13 +64,13 @@ func (d *FTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m
|
|||||||
if err := d.login(); err != nil {
|
if err := d.login(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp, err := d.conn.Retr(file.GetPath())
|
|
||||||
if err != nil {
|
r := NewFTPFileReader(d.conn, file.GetPath())
|
||||||
return nil, err
|
link := &model.Link{
|
||||||
|
Data: r,
|
||||||
}
|
}
|
||||||
return &model.Link{
|
base.HandleRange(link, r, args.Header, file.GetSize())
|
||||||
Data: resp,
|
return link, nil
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *FTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *FTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -1,6 +1,13 @@
|
|||||||
package ftp
|
package ftp
|
||||||
|
|
||||||
import "github.com/jlaffaye/ftp"
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jlaffaye/ftp"
|
||||||
|
)
|
||||||
|
|
||||||
// do others that not defined in Driver interface
|
// do others that not defined in Driver interface
|
||||||
|
|
||||||
@ -11,7 +18,7 @@ func (d *FTP) login() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
conn, err := ftp.Dial(d.Address)
|
conn, err := ftp.Dial(d.Address, ftp.DialWithShutTimeout(10*time.Second))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -22,3 +29,81 @@ func (d *FTP) login() error {
|
|||||||
d.conn = conn
|
d.conn = conn
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An FTP file reader that implements io.ReadSeekCloser for seeking.
|
||||||
|
type FTPFileReader struct {
|
||||||
|
conn *ftp.ServerConn
|
||||||
|
resp *ftp.Response
|
||||||
|
offset int64
|
||||||
|
mu sync.Mutex
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFTPFileReader(conn *ftp.ServerConn, path string) *FTPFileReader {
|
||||||
|
return &FTPFileReader{
|
||||||
|
conn: conn,
|
||||||
|
path: path,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FTPFileReader) Read(buf []byte) (n int, err error) {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
if r.resp == nil {
|
||||||
|
r.resp, err = r.conn.RetrFrom(r.path, uint64(r.offset))
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err = r.resp.Read(buf)
|
||||||
|
r.offset += int64(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FTPFileReader) Seek(offset int64, whence int) (int64, error) {
|
||||||
|
r.mu.Lock()
|
||||||
|
defer r.mu.Unlock()
|
||||||
|
|
||||||
|
oldOffset := r.offset
|
||||||
|
var newOffset int64
|
||||||
|
switch whence {
|
||||||
|
case io.SeekStart:
|
||||||
|
newOffset = offset
|
||||||
|
case io.SeekCurrent:
|
||||||
|
newOffset = oldOffset + offset
|
||||||
|
case io.SeekEnd:
|
||||||
|
size, err := r.conn.FileSize(r.path)
|
||||||
|
if err != nil {
|
||||||
|
return oldOffset, err
|
||||||
|
}
|
||||||
|
newOffset = offset + int64(size)
|
||||||
|
default:
|
||||||
|
return -1, os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if newOffset < 0 {
|
||||||
|
// offset out of range
|
||||||
|
return oldOffset, os.ErrInvalid
|
||||||
|
}
|
||||||
|
if newOffset == oldOffset {
|
||||||
|
// offset not changed, so return directly
|
||||||
|
return oldOffset, nil
|
||||||
|
}
|
||||||
|
r.offset = newOffset
|
||||||
|
|
||||||
|
if r.resp != nil {
|
||||||
|
// close the existing ftp data connection, otherwise the next read will be blocked
|
||||||
|
_ = r.resp.Close() // we do not care about whether it returns an error
|
||||||
|
r.resp = nil
|
||||||
|
}
|
||||||
|
return newOffset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FTPFileReader) Close() error {
|
||||||
|
if r.resp != nil {
|
||||||
|
return r.resp.Close()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
type Local struct {
|
type Local struct {
|
||||||
model.Storage
|
model.Storage
|
||||||
Addition
|
Addition
|
||||||
|
mkdirPerm int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Local) Config() driver.Config {
|
func (d *Local) Config() driver.Config {
|
||||||
@ -35,6 +36,15 @@ func (d *Local) Config() driver.Config {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Local) Init(ctx context.Context) error {
|
func (d *Local) Init(ctx context.Context) error {
|
||||||
|
if d.MkdirPerm == "" {
|
||||||
|
d.mkdirPerm = 0777
|
||||||
|
} else {
|
||||||
|
v, err := strconv.ParseUint(d.MkdirPerm, 8, 32)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.mkdirPerm = int32(v)
|
||||||
|
}
|
||||||
if !utils.Exists(d.GetRootPath()) {
|
if !utils.Exists(d.GetRootPath()) {
|
||||||
return fmt.Errorf("root folder %s not exists", d.GetRootPath())
|
return fmt.Errorf("root folder %s not exists", d.GetRootPath())
|
||||||
}
|
}
|
||||||
@ -68,10 +78,13 @@ func (d *Local) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
thumb := ""
|
thumb := ""
|
||||||
if d.Thumbnail && utils.GetFileType(f.Name()) == conf.IMAGE {
|
if d.Thumbnail {
|
||||||
thumb = common.GetApiUrl(nil) + stdpath.Join("/d", args.ReqPath, f.Name())
|
typeName := utils.GetFileType(f.Name())
|
||||||
thumb = utils.EncodePath(thumb, true)
|
if typeName == conf.IMAGE || typeName == conf.VIDEO {
|
||||||
thumb += "?type=thumb&sign=" + sign.Sign(stdpath.Join(args.ReqPath, f.Name()))
|
thumb = common.GetApiUrl(nil) + stdpath.Join("/d", args.ReqPath, f.Name())
|
||||||
|
thumb = utils.EncodePath(thumb, true)
|
||||||
|
thumb += "?type=thumb&sign=" + sign.Sign(stdpath.Join(args.ReqPath, f.Name()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
isFolder := f.IsDir() || isSymlinkDir(f, fullPath)
|
isFolder := f.IsDir() || isSymlinkDir(f, fullPath)
|
||||||
var size int64
|
var size int64
|
||||||
@ -123,11 +136,22 @@ func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
|||||||
fullPath := file.GetPath()
|
fullPath := file.GetPath()
|
||||||
var link model.Link
|
var link model.Link
|
||||||
if args.Type == "thumb" && utils.Ext(file.GetName()) != "svg" {
|
if args.Type == "thumb" && utils.Ext(file.GetName()) != "svg" {
|
||||||
imgData, err := ioutil.ReadFile(fullPath)
|
var srcBuf *bytes.Buffer
|
||||||
if err != nil {
|
if utils.GetFileType(file.GetName()) == conf.VIDEO {
|
||||||
return nil, err
|
videoBuf, err := GetSnapshot(fullPath, 10)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
srcBuf = videoBuf
|
||||||
|
} else {
|
||||||
|
imgData, err := os.ReadFile(fullPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
imgBuf := bytes.NewBuffer(imgData)
|
||||||
|
srcBuf = imgBuf
|
||||||
}
|
}
|
||||||
srcBuf := bytes.NewBuffer(imgData)
|
|
||||||
image, err := imaging.Decode(srcBuf)
|
image, err := imaging.Decode(srcBuf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -151,7 +175,7 @@ func (d *Local) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (
|
|||||||
|
|
||||||
func (d *Local) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *Local) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
fullPath := filepath.Join(parentDir.GetPath(), dirName)
|
fullPath := filepath.Join(parentDir.GetPath(), dirName)
|
||||||
err := os.MkdirAll(fullPath, 0777)
|
err := os.MkdirAll(fullPath, os.FileMode(d.mkdirPerm))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ import (
|
|||||||
|
|
||||||
type Addition struct {
|
type Addition struct {
|
||||||
driver.RootPath
|
driver.RootPath
|
||||||
Thumbnail bool `json:"thumbnail" required:"true" help:"enable thumbnail"`
|
Thumbnail bool `json:"thumbnail" required:"true" help:"enable thumbnail"`
|
||||||
ShowHidden bool `json:"show_hidden" default:"true" required:"false" help:"show hidden directories and files"`
|
ShowHidden bool `json:"show_hidden" default:"true" required:"false" help:"show hidden directories and files"`
|
||||||
|
MkdirPerm string `json:"mkdir_perm" default:"777"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
ffmpeg "github.com/u2takey/ffmpeg-go"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@ -23,3 +26,16 @@ func isSymlinkDir(f fs.FileInfo, path string) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSnapshot(videoPath string, frameNum int) (imgData *bytes.Buffer, err error) {
|
||||||
|
srcBuf := bytes.NewBuffer(nil)
|
||||||
|
err = ffmpeg.Input(videoPath).Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}).
|
||||||
|
Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
|
||||||
|
WithOutput(srcBuf, os.Stdout).
|
||||||
|
Run()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return srcBuf, nil
|
||||||
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
@ -75,9 +76,19 @@ func (d *Onedrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName str
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
parentPath := ""
|
||||||
|
if dstDir.GetID() == "" {
|
||||||
|
parentPath = dstDir.GetPath()
|
||||||
|
if utils.PathEqual(parentPath, "/") {
|
||||||
|
parentPath = path.Join("/drive/root", parentPath)
|
||||||
|
} else {
|
||||||
|
parentPath = path.Join("/drive/root:/", parentPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
data := base.Json{
|
data := base.Json{
|
||||||
"parentReference": base.Json{
|
"parentReference": base.Json{
|
||||||
"id": dstDir.GetID(),
|
"id": dstDir.GetID(),
|
||||||
|
"path": parentPath,
|
||||||
},
|
},
|
||||||
"name": srcObj.GetName(),
|
"name": srcObj.GetName(),
|
||||||
}
|
}
|
||||||
@ -89,13 +100,15 @@ func (d *Onedrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
func (d *Onedrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
//dstDir, err := op.GetUnwrap(ctx, d, stdpath.Dir(srcObj.GetPath()))
|
|
||||||
var parentID string
|
var parentID string
|
||||||
if o, ok := srcObj.(*Object); ok {
|
if o, ok := srcObj.(*Object); ok {
|
||||||
parentID = o.ParentID
|
parentID = o.ParentID
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("srcObj is not Object")
|
return fmt.Errorf("srcObj is not Object")
|
||||||
}
|
}
|
||||||
|
if parentID == "" {
|
||||||
|
parentID = "root"
|
||||||
|
}
|
||||||
data := base.Json{
|
data := base.Json{
|
||||||
"parentReference": base.Json{
|
"parentReference": base.Json{
|
||||||
"id": parentID,
|
"id": parentID,
|
||||||
|
@ -11,7 +11,7 @@ type Addition struct {
|
|||||||
IsSharepoint bool `json:"is_sharepoint"`
|
IsSharepoint bool `json:"is_sharepoint"`
|
||||||
ClientID string `json:"client_id" required:"true"`
|
ClientID string `json:"client_id" required:"true"`
|
||||||
ClientSecret string `json:"client_secret" required:"true"`
|
ClientSecret string `json:"client_secret" required:"true"`
|
||||||
RedirectUri string `json:"redirect_uri" required:"true" default:"https://tool.nn.ci/onedrive/callback"`
|
RedirectUri string `json:"redirect_uri" required:"true" default:"https://alist.nn.ci/tool/onedrive/callback"`
|
||||||
RefreshToken string `json:"refresh_token" required:"true"`
|
RefreshToken string `json:"refresh_token" required:"true"`
|
||||||
SiteId string `json:"site_id"`
|
SiteId string `json:"site_id"`
|
||||||
ChunkSize int64 `json:"chunk_size" type:"number" default:"5"`
|
ChunkSize int64 `json:"chunk_size" type:"number" default:"5"`
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PikPakShare struct {
|
type PikPakShare struct {
|
||||||
@ -71,10 +70,6 @@ func (d *PikPakShare) Link(ctx context.Context, file model.Obj, args model.LinkA
|
|||||||
link := model.Link{
|
link := model.Link{
|
||||||
URL: resp.FileInfo.WebContentLink,
|
URL: resp.FileInfo.WebContentLink,
|
||||||
}
|
}
|
||||||
if len(resp.FileInfo.Medias) > 0 && resp.FileInfo.Medias[0].Link.Url != "" {
|
|
||||||
log.Debugln("use media link")
|
|
||||||
link.URL = resp.FileInfo.Medias[0].Link.Url
|
|
||||||
}
|
|
||||||
return &link, nil
|
return &link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ var config = driver.Config{
|
|||||||
Name: "Quark",
|
Name: "Quark",
|
||||||
OnlyProxy: true,
|
OnlyProxy: true,
|
||||||
DefaultRoot: "0",
|
DefaultRoot: "0",
|
||||||
|
NoOverwriteUpload: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -59,7 +59,8 @@ func (d *S3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]mo
|
|||||||
|
|
||||||
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
func (d *S3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
path := getKey(file.GetPath(), false)
|
path := getKey(file.GetPath(), false)
|
||||||
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(stdpath.Base(path)))
|
filename := stdpath.Base(path)
|
||||||
|
disposition := fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, filename, url.PathEscape(filename))
|
||||||
input := &s3.GetObjectInput{
|
input := &s3.GetObjectInput{
|
||||||
Bucket: &d.Bucket,
|
Bucket: &d.Bucket,
|
||||||
Key: &path,
|
Key: &path,
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
@ -52,9 +53,11 @@ func (d *SFTP) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &model.Link{
|
link := &model.Link{
|
||||||
Data: remoteFile,
|
Data: remoteFile,
|
||||||
}, nil
|
}
|
||||||
|
base.HandleRange(link, remoteFile, args.Header, file.GetSize())
|
||||||
|
return link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SFTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *SFTP) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -5,8 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
@ -18,7 +18,7 @@ type SMB struct {
|
|||||||
model.Storage
|
model.Storage
|
||||||
Addition
|
Addition
|
||||||
fs *smb2.Share
|
fs *smb2.Share
|
||||||
lastConnTime time.Time
|
lastConnTime int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SMB) Config() driver.Config {
|
func (d *SMB) Config() driver.Config {
|
||||||
@ -79,10 +79,12 @@ func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*m
|
|||||||
d.cleanLastConnTime()
|
d.cleanLastConnTime()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.updateLastConnTime()
|
link := &model.Link{
|
||||||
return &model.Link{
|
|
||||||
Data: remoteFile,
|
Data: remoteFile,
|
||||||
}, nil
|
}
|
||||||
|
base.HandleRange(link, remoteFile, args.Header, file.GetSize())
|
||||||
|
d.updateLastConnTime()
|
||||||
|
return link, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
@ -6,17 +6,22 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hirochachacha/go-smb2"
|
"github.com/hirochachacha/go-smb2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (d *SMB) updateLastConnTime() {
|
func (d *SMB) updateLastConnTime() {
|
||||||
d.lastConnTime = time.Now()
|
atomic.StoreInt64(&d.lastConnTime, time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SMB) cleanLastConnTime() {
|
func (d *SMB) cleanLastConnTime() {
|
||||||
d.lastConnTime = time.Now().AddDate(0, 0, -1)
|
atomic.StoreInt64(&d.lastConnTime, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *SMB) getLastConnTime() time.Time {
|
||||||
|
return time.Unix(atomic.LoadInt64(&d.lastConnTime), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *SMB) initFS() error {
|
func (d *SMB) initFS() error {
|
||||||
@ -43,7 +48,7 @@ func (d *SMB) initFS() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *SMB) checkConn() error {
|
func (d *SMB) checkConn() error {
|
||||||
if time.Since(d.lastConnTime) < 5*time.Minute {
|
if time.Since(d.getLastConnTime()) < 5*time.Minute {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if d.fs != nil {
|
if d.fs != nil {
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
"github.com/alist-org/alist/v3/internal/driver"
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -124,11 +125,11 @@ func (d *Teambition) Remove(ctx context.Context, obj model.Obj) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Teambition) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *Teambition) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
res, err := d.request("/projects", http.MethodGet, nil, nil)
|
res, err := d.request("/api/v2/users/me", http.MethodGet, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
token := GetBetweenStr(string(res), "strikerAuth":"", "","phoneForLogin")
|
token := utils.Json.Get(res, "strikerAuth").ToString()
|
||||||
var newFile *FileUpload
|
var newFile *FileUpload
|
||||||
if stream.GetSize() <= 20971520 {
|
if stream.GetSize() <= 20971520 {
|
||||||
// post upload
|
// post upload
|
||||||
|
@ -210,7 +210,7 @@ func (d *Teambition) finishUpload(file *FileUpload, parentId string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBetweenStr(str, start, end string) string {
|
func getBetweenStr(str, start, end string) string {
|
||||||
n := strings.Index(str, start)
|
n := strings.Index(str, start)
|
||||||
if n == -1 {
|
if n == -1 {
|
||||||
return ""
|
return ""
|
||||||
|
@ -32,42 +32,42 @@ func (d *Template) Drop(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
func (d *Template) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
// TODO return the files list
|
// TODO return the files list, required
|
||||||
return nil, errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
func (d *Template) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
// TODO return link of file
|
// TODO return link of file, required
|
||||||
return nil, errs.NotImplement
|
return nil, errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *Template) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
// TODO create folder
|
// TODO create folder, optional
|
||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
func (d *Template) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
// TODO move obj
|
// TODO move obj, optional
|
||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
func (d *Template) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
// TODO rename obj
|
// TODO rename obj, optional
|
||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
func (d *Template) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
// TODO copy obj
|
// TODO copy obj, optional
|
||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Remove(ctx context.Context, obj model.Obj) error {
|
func (d *Template) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
// TODO remove obj
|
// TODO remove obj, optional
|
||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Template) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
func (d *Template) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
// TODO upload file
|
// TODO upload file, optional
|
||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,14 +14,17 @@ type Addition struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var config = driver.Config{
|
var config = driver.Config{
|
||||||
Name: "Template",
|
Name: "Template",
|
||||||
LocalSort: false,
|
LocalSort: false,
|
||||||
OnlyLocal: false,
|
OnlyLocal: false,
|
||||||
OnlyProxy: false,
|
OnlyProxy: false,
|
||||||
NoCache: false,
|
NoCache: false,
|
||||||
NoUpload: false,
|
NoUpload: false,
|
||||||
NeedMs: false,
|
NeedMs: false,
|
||||||
DefaultRoot: "root, / or other",
|
DefaultRoot: "root, / or other",
|
||||||
|
CheckStatus: false,
|
||||||
|
Alert: "",
|
||||||
|
NoOverwriteUpload: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
142
drivers/trainbit/driver.go
Normal file
142
drivers/trainbit/driver.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package trainbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Trainbit struct {
|
||||||
|
model.Storage
|
||||||
|
Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
var apiExpiredate, guid string
|
||||||
|
|
||||||
|
func (d *Trainbit) Config() driver.Config {
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) GetAddition() driver.Additional {
|
||||||
|
return &d.Addition
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Init(ctx context.Context) error {
|
||||||
|
http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||||
|
return http.ErrUseLastResponse
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
apiExpiredate, guid, err = getToken(d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Drop(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("parentid", strings.Split(dir.GetID(), "_")[0])
|
||||||
|
res, err := postForm("https://trainbit.com/lib/api/v1/listoffiles", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var jsonData any
|
||||||
|
json.Unmarshal(data, &jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
object, err := parseRawFileObject(jsonData.(map[string]any)["items"].([]any))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return object, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
res, err := get(fmt.Sprintf("https://trainbit.com/files/%s/", strings.Split(file.GetID(), "_")[0]), d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &model.Link{
|
||||||
|
URL: res.Header.Get("Location"),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("name", local2provider(dirName, true))
|
||||||
|
form.Set("parentid", strings.Split(parentDir.GetID(), "_")[0])
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/createfolder", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("sourceid", strings.Split(srcObj.GetID(), "_")[0])
|
||||||
|
form.Set("destinationid", strings.Split(dstDir.GetID(), "_")[0])
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/move", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("id", strings.Split(srcObj.GetID(), "_")[0])
|
||||||
|
form.Set("name", local2provider(newName, srcObj.IsDir()))
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/edit", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
|
||||||
|
return errs.NotImplement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Remove(ctx context.Context, obj model.Obj) error {
|
||||||
|
form := make(url.Values)
|
||||||
|
form.Set("id", strings.Split(obj.GetID(), "_")[0])
|
||||||
|
_, err := postForm("https://trainbit.com/lib/api/v1/delete", form, apiExpiredate, d.ApiKey, d.AUSHELLPORTAL)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Trainbit) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
|
||||||
|
endpoint, _ := url.Parse("https://tb28.trainbit.com/api/upload/send_raw/")
|
||||||
|
query := &url.Values{}
|
||||||
|
query.Add("q", strings.Split(dstDir.GetID(), "_")[1])
|
||||||
|
query.Add("guid", guid)
|
||||||
|
query.Add("name", url.QueryEscape(local2provider(stream.GetName(), false)))
|
||||||
|
endpoint.RawQuery = query.Encode()
|
||||||
|
var total int64
|
||||||
|
total = 0
|
||||||
|
progressReader := &ProgressReader{
|
||||||
|
stream,
|
||||||
|
func(byteNum int) {
|
||||||
|
total += int64(byteNum)
|
||||||
|
up(int(math.Round(float64(total) / float64(stream.GetSize()) * 100)))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, endpoint.String(), progressReader)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "text/json; charset=UTF-8")
|
||||||
|
_, err = http.DefaultClient.Do(req)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ driver.Driver = (*Trainbit)(nil)
|
29
drivers/trainbit/meta.go
Normal file
29
drivers/trainbit/meta.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package trainbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Addition struct {
|
||||||
|
driver.RootID
|
||||||
|
AUSHELLPORTAL string `json:"AUSHELLPORTAL" required:"true"`
|
||||||
|
ApiKey string `json:"apikey" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var config = driver.Config{
|
||||||
|
Name: "Trainbit",
|
||||||
|
LocalSort: false,
|
||||||
|
OnlyLocal: false,
|
||||||
|
OnlyProxy: false,
|
||||||
|
NoCache: false,
|
||||||
|
NoUpload: false,
|
||||||
|
NeedMs: false,
|
||||||
|
DefaultRoot: "0_000",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
op.RegisterDriver(func() driver.Driver {
|
||||||
|
return &Trainbit{}
|
||||||
|
})
|
||||||
|
}
|
1
drivers/trainbit/types.go
Normal file
1
drivers/trainbit/types.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package trainbit
|
150
drivers/trainbit/util.go
Normal file
150
drivers/trainbit/util.go
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
package trainbit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProgressReader struct {
|
||||||
|
io.Reader
|
||||||
|
reporter func(byteNum int)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (progressReader *ProgressReader) Read(data []byte) (int, error) {
|
||||||
|
byteNum, err := progressReader.Reader.Read(data)
|
||||||
|
progressReader.reporter(byteNum)
|
||||||
|
return byteNum, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func get(url string, apiKey string, AUSHELLPORTAL string) (*http.Response, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: ".AUSHELLPORTAL",
|
||||||
|
Value: AUSHELLPORTAL,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "retkeyapi",
|
||||||
|
Value: apiKey,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func postForm(endpoint string, data url.Values, apiExpiredate string, apiKey string, AUSHELLPORTAL string) (*http.Response, error) {
|
||||||
|
extData := make(url.Values)
|
||||||
|
for key, value := range data {
|
||||||
|
extData[key] = make([]string, len(value))
|
||||||
|
copy(extData[key], value)
|
||||||
|
}
|
||||||
|
extData.Set("apikey", apiKey)
|
||||||
|
extData.Set("expiredate", apiExpiredate)
|
||||||
|
req, err := http.NewRequest(http.MethodPost, endpoint, strings.NewReader(extData.Encode()))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: ".AUSHELLPORTAL",
|
||||||
|
Value: AUSHELLPORTAL,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "retkeyapi",
|
||||||
|
Value: apiKey,
|
||||||
|
MaxAge: 2 * 60,
|
||||||
|
})
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getToken(apiKey string, AUSHELLPORTAL string) (string, string, error) {
|
||||||
|
res, err := get("https://trainbit.com/files/", apiKey, AUSHELLPORTAL)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
data, err := io.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
text := string(data)
|
||||||
|
apiExpiredateReg := regexp.MustCompile(`core.api.expiredate = '([^']*)';`)
|
||||||
|
result := apiExpiredateReg.FindAllStringSubmatch(text, -1)
|
||||||
|
apiExpiredate := result[0][1]
|
||||||
|
guidReg := regexp.MustCompile(`app.vars.upload.guid = '([^']*)';`)
|
||||||
|
result = guidReg.FindAllStringSubmatch(text, -1)
|
||||||
|
guid := result[0][1]
|
||||||
|
return apiExpiredate, guid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func local2provider(filename string, isFolder bool) string {
|
||||||
|
filename = strings.Replace(filename, "%", url.QueryEscape("%"), -1)
|
||||||
|
filename = strings.Replace(filename, "/", url.QueryEscape("/"), -1)
|
||||||
|
filename = strings.Replace(filename, ":", url.QueryEscape(":"), -1)
|
||||||
|
filename = strings.Replace(filename, "*", url.QueryEscape("*"), -1)
|
||||||
|
filename = strings.Replace(filename, "?", url.QueryEscape("?"), -1)
|
||||||
|
filename = strings.Replace(filename, "\"", url.QueryEscape("\""), -1)
|
||||||
|
filename = strings.Replace(filename, "<", url.QueryEscape("<"), -1)
|
||||||
|
filename = strings.Replace(filename, ">", url.QueryEscape(">"), -1)
|
||||||
|
filename = strings.Replace(filename, "|", url.QueryEscape("|"), -1)
|
||||||
|
if isFolder {
|
||||||
|
return filename
|
||||||
|
}
|
||||||
|
return strings.Join([]string{filename, ".delete_suffix."}, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func provider2local(filename string) string {
|
||||||
|
index := strings.LastIndex(filename, ".delete_suffix.")
|
||||||
|
if index != -1 {
|
||||||
|
filename = filename[:index]
|
||||||
|
}
|
||||||
|
rawName := strings.Replace(filename, url.QueryEscape("/"), "/", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape(":"), ":", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape("*"), "*", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape("?"), "?", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape("\""), "\"", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape("<"), "<", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape(">"), ">", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape("|"), "|", -1)
|
||||||
|
rawName = strings.Replace(rawName, url.QueryEscape("%"), "%", -1)
|
||||||
|
return rawName
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRawFileObject(rawObject []any) ([]model.Obj, error) {
|
||||||
|
objectList := make([]model.Obj, 0)
|
||||||
|
for _, each := range rawObject {
|
||||||
|
object := each.(map[string]any)
|
||||||
|
if object["id"].(string) == "0" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
isFolder := int64(object["ty"].(float64)) == 1
|
||||||
|
var name string
|
||||||
|
if isFolder {
|
||||||
|
name = object["name"].(string)
|
||||||
|
} else {
|
||||||
|
name = strings.Join([]string{object["name"].(string), object["ext"].(string)}, ".")
|
||||||
|
}
|
||||||
|
modified, err := time.Parse("2006/01/02 15:04:05", object["modified"].(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
objectList = append(objectList, model.Obj(&model.Object{
|
||||||
|
ID: strings.Join([]string{object["id"].(string), strings.Split(object["uploadurl"].(string), "=")[1]}, "_"),
|
||||||
|
Name: provider2local(name),
|
||||||
|
Size: int64(object["byte"].(float64)),
|
||||||
|
Modified: modified.Add(-210 * time.Minute),
|
||||||
|
IsFolder: isFolder,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return objectList, nil
|
||||||
|
}
|
49
go.mod
49
go.mod
@ -3,18 +3,18 @@ module github.com/alist-org/alist/v3
|
|||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/SheltonZhu/115driver v1.0.13
|
github.com/SheltonZhu/115driver v1.0.14
|
||||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
|
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
|
||||||
github.com/aws/aws-sdk-go v1.44.194
|
github.com/aws/aws-sdk-go v1.44.194
|
||||||
github.com/blevesearch/bleve/v2 v2.3.6
|
github.com/blevesearch/bleve/v2 v2.3.6
|
||||||
github.com/caarlos0/env/v7 v7.0.0
|
github.com/caarlos0/env/v7 v7.1.0
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0
|
github.com/deckarep/golang-set/v2 v2.3.0
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564
|
||||||
github.com/gin-contrib/cors v1.4.0
|
github.com/gin-contrib/cors v1.4.0
|
||||||
github.com/gin-gonic/gin v1.8.2
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/go-resty/resty/v2 v2.7.0
|
github.com/go-resty/resty/v2 v2.7.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.3
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0
|
github.com/hirochachacha/go-smb2 v1.1.0
|
||||||
@ -27,14 +27,15 @@ require (
|
|||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/spf13/cobra v1.6.1
|
github.com/spf13/cobra v1.6.1
|
||||||
github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf
|
github.com/t3rm1n4l/go-mega v0.0.0-20230228171823-a01a2cda13ca
|
||||||
|
github.com/u2takey/ffmpeg-go v0.4.1
|
||||||
github.com/upyun/go-sdk/v3 v3.0.3
|
github.com/upyun/go-sdk/v3 v3.0.3
|
||||||
github.com/winfsp/cgofuse v1.5.0
|
github.com/winfsp/cgofuse v1.5.0
|
||||||
golang.org/x/crypto v0.6.0
|
golang.org/x/crypto v0.7.0
|
||||||
golang.org/x/image v0.4.0
|
golang.org/x/image v0.6.0
|
||||||
golang.org/x/net v0.6.0
|
golang.org/x/net v0.8.0
|
||||||
gorm.io/driver/mysql v1.4.6
|
gorm.io/driver/mysql v1.4.7
|
||||||
gorm.io/driver/postgres v1.4.7
|
gorm.io/driver/postgres v1.4.8
|
||||||
gorm.io/driver/sqlite v1.4.4
|
gorm.io/driver/sqlite v1.4.4
|
||||||
gorm.io/gorm v1.24.5
|
gorm.io/gorm v1.24.5
|
||||||
)
|
)
|
||||||
@ -63,14 +64,16 @@ require (
|
|||||||
github.com/blevesearch/zapx/v15 v15.3.8 // indirect
|
github.com/blevesearch/zapx/v15 v15.3.8 // indirect
|
||||||
github.com/bluele/gcache v0.0.2 // indirect
|
github.com/bluele/gcache v0.0.2 // indirect
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||||
|
github.com/bytedance/sonic v1.8.0 // indirect
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||||
github.com/gaoyb7/115drive-webdav v0.1.8 // indirect
|
github.com/gaoyb7/115drive-webdav v0.1.8 // indirect
|
||||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.0 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.0 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.11.1 // indirect
|
github.com/go-playground/validator/v10 v10.11.2 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||||
github.com/goccy/go-json v0.9.11 // indirect
|
github.com/goccy/go-json v0.10.0 // indirect
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||||
github.com/golang/protobuf v1.5.0 // indirect
|
github.com/golang/protobuf v1.5.0 // indirect
|
||||||
github.com/golang/snappy v0.0.1 // indirect
|
github.com/golang/snappy v0.0.1 // indirect
|
||||||
@ -79,13 +82,14 @@ require (
|
|||||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/pgx/v5 v5.2.0 // indirect
|
github.com/jackc/pgx/v5 v5.3.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||||
github.com/kr/fs v0.1.0 // indirect
|
github.com/kr/fs v0.1.0 // 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.16 // indirect
|
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
github.com/mattn/go-sqlite3 v1.14.15 // 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
|
||||||
@ -95,13 +99,16 @@ require (
|
|||||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
|
github.com/u2takey/go-utils v0.3.1 // indirect
|
||||||
|
github.com/ugorji/go/codec v1.2.9 // indirect
|
||||||
go.etcd.io/bbolt v1.3.5 // indirect
|
go.etcd.io/bbolt v1.3.5 // indirect
|
||||||
golang.org/x/sys v0.5.0 // indirect
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/sys v0.6.0 // indirect
|
||||||
|
golang.org/x/text v0.8.0 // indirect
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
138
go.sum
138
go.sum
@ -2,8 +2,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/RoaringBitmap/roaring v0.9.4 h1:ckvZSX5gwCRaJYBNe7syNawCU5oruY9gQmjXlp4riwo=
|
github.com/RoaringBitmap/roaring v0.9.4 h1:ckvZSX5gwCRaJYBNe7syNawCU5oruY9gQmjXlp4riwo=
|
||||||
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
|
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
|
||||||
github.com/SheltonZhu/115driver v1.0.13 h1:YEhQ3iMvd5TD6Xp1wKg+73KgdCMjc0pDoT1eCNXnA3M=
|
github.com/SheltonZhu/115driver v1.0.14 h1:uW3dl8J9KDMw+3gPxQdhTysoGhw0/uI1484GT9xhfU4=
|
||||||
github.com/SheltonZhu/115driver v1.0.13/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
github.com/SheltonZhu/115driver v1.0.14/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
||||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
||||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
||||||
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
||||||
@ -12,6 +12,7 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1e
|
|||||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||||
github.com/andreburgaud/crypt2go v1.1.0 h1:eitZxTPY1krUsxinsng3Qvt/Ud7q/aQmmYRh8p4hyPw=
|
github.com/andreburgaud/crypt2go v1.1.0 h1:eitZxTPY1krUsxinsng3Qvt/Ud7q/aQmmYRh8p4hyPw=
|
||||||
github.com/andreburgaud/crypt2go v1.1.0/go.mod h1:4qhZPzarj1dCIRmCkpdgCklwp+hBq9yEt0zPe9Ayuhc=
|
github.com/andreburgaud/crypt2go v1.1.0/go.mod h1:4qhZPzarj1dCIRmCkpdgCklwp+hBq9yEt0zPe9Ayuhc=
|
||||||
|
github.com/aws/aws-sdk-go v1.38.20/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.44.194 h1:1ZDK+QDcc5oRbZGgRZSz561eR8XVizXCeGpoZKo33NU=
|
github.com/aws/aws-sdk-go v1.44.194 h1:1ZDK+QDcc5oRbZGgRZSz561eR8XVizXCeGpoZKo33NU=
|
||||||
github.com/aws/aws-sdk-go v1.44.194/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
github.com/aws/aws-sdk-go v1.44.194/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||||
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
||||||
@ -52,19 +53,28 @@ github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
|||||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/caarlos0/env/v7 v7.0.0 h1:cyczlTd/zREwSr9ch/mwaDl7Hse7kJuUY8hvHfXu5WI=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/caarlos0/env/v7 v7.0.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
|
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
|
||||||
|
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||||
|
github.com/caarlos0/env/v7 v7.1.0 h1:9lzTF5amyQeWHZzuZeKlCb5FWSUxpG1js43mhbY8ozg=
|
||||||
|
github.com/caarlos0/env/v7 v7.1.0/go.mod h1:LPPWniDUq4JaO6Q41vtlyikhMknqymCLBw0eX4dcH1E=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||||
|
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
|
github.com/deckarep/golang-set/v2 v2.2.0 h1:2pMQd3Soi6qfw7E5MMKaEh5W5ES18bW3AbFFnGl6LgQ=
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
github.com/deckarep/golang-set/v2 v2.2.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
|
github.com/deckarep/golang-set/v2 v2.3.0 h1:qs18EKUfHm2X9fA50Mr/M5hccg2tNnVqsiBImnyDs0g=
|
||||||
|
github.com/deckarep/golang-set/v2 v2.3.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
|
||||||
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
|
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/gaoyb7/115drive-webdav v0.1.8 h1:EJt4PSmcbvBY4KUh2zSo5p6fN9LZFNkIzuKejipubVw=
|
github.com/gaoyb7/115drive-webdav v0.1.8 h1:EJt4PSmcbvBY4KUh2zSo5p6fN9LZFNkIzuKejipubVw=
|
||||||
github.com/gaoyb7/115drive-webdav v0.1.8/go.mod h1:BKbeY6j8SKs3+rzBFFALznGxbPmefEm3vA+dGhqgOGU=
|
github.com/gaoyb7/115drive-webdav v0.1.8/go.mod h1:BKbeY6j8SKs3+rzBFFALznGxbPmefEm3vA+dGhqgOGU=
|
||||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||||
@ -74,27 +84,31 @@ github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURU
|
|||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||||
github.com/gin-gonic/gin v1.8.2 h1:UzKToD9/PoFj/V4rvlKqTRKnQYyz8Sc1MJlv4JHPtvY=
|
github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
|
||||||
github.com/gin-gonic/gin v1.8.2/go.mod h1:qw5AYuDrzRTnhvusDsrov+fDIxp9Dleuu12h8nfB398=
|
github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
|
||||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||||
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
|
github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
|
||||||
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
|
||||||
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
|
||||||
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
|
||||||
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
|
||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
|
github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
|
||||||
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||||
|
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||||
@ -105,6 +119,7 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
|||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||||
@ -114,18 +129,18 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY
|
|||||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||||
|
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
||||||
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
|
github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA=
|
||||||
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
|
||||||
github.com/jackc/puddle/v2 v2.1.2/go.mod h1:2lpufsF5mRHO6SuZkm0fNYxM6SWHfvyFj62KwNzgels=
|
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
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.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
@ -138,8 +153,13 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
|
|||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||||
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||||
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
|
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||||
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||||
|
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
@ -155,13 +175,15 @@ github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ic
|
|||||||
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
|
github.com/maruel/natural v1.1.0 h1:2z1NgP/Vae+gYrtC0VuvrTJ6U35OuyUqDdfluLqMWuQ=
|
||||||
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
github.com/maruel/natural v1.1.0/go.mod h1:eFVhYCcUOfZFxXoDZam8Ktya72wa79fNC3lc/leA0DQ=
|
||||||
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-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||||
@ -170,12 +192,14 @@ github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4
|
|||||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 h1:dg/EaaJLPIg4xn2kaZil7Ax3wfoxcFXaBwyOTlcz5AI=
|
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 h1:dg/EaaJLPIg4xn2kaZil7Ax3wfoxcFXaBwyOTlcz5AI=
|
||||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77/go.mod h1:FD9a09Vw07CSMTdT0Y7ttStOa1WZsnPBslliMw2DkeM=
|
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77/go.mod h1:FD9a09Vw07CSMTdT0Y7ttStOa1WZsnPBslliMw2DkeM=
|
||||||
|
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
|
||||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
|
github.com/pkg/sftp v1.13.5 h1:a3RLUqkyjYRtBTZJZ1VRrKbN3zhuPLlUc3sphVz81go=
|
||||||
@ -193,6 +217,7 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0
|
|||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||||
|
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
@ -211,11 +236,18 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf h1:Y43S3e9P1NPs/QF4R5/SdlXj2d31540hP4Gk8VKNvDg=
|
github.com/t3rm1n4l/go-mega v0.0.0-20230228171823-a01a2cda13ca h1:I9rVnNXdIkij4UvMT7OmKhH9sOIvS8iXkxfPdnn9wQA=
|
||||||
github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf/go.mod h1:c+cGNU1qi9bO7ZF4IRMYk+KaZTNiQ/gQrSbyMmGFq1Q=
|
github.com/t3rm1n4l/go-mega v0.0.0-20230228171823-a01a2cda13ca/go.mod h1:suDIky6yrK07NnaBadCB4sS0CqFOvUK91lH7CR+JlDA=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
|
github.com/u2takey/ffmpeg-go v0.4.1 h1:l5ClIwL3N2LaH1zF3xivb3kP2HW95eyG5xhHE1JdZ9Y=
|
||||||
|
github.com/u2takey/ffmpeg-go v0.4.1/go.mod h1:ruZWkvC1FEiUNjmROowOAps3ZcWxEiOpFoHCvk97kGc=
|
||||||
|
github.com/u2takey/go-utils v0.3.1 h1:TaQTgmEZZeDHQFYfd+AdUT1cT4QJgJn/XVPELhHw4ys=
|
||||||
|
github.com/u2takey/go-utils v0.3.1/go.mod h1:6e+v5vEZ/6gu12w/DC2ixZdZtCrNokVxD0JUklcqdCs=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||||
|
github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
|
||||||
|
github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/upyun/go-sdk/v3 v3.0.3 h1:2wUkNk2fyJReMYHMvJyav050D83rYwSjN7mEPR0Pp8Q=
|
github.com/upyun/go-sdk/v3 v3.0.3 h1:2wUkNk2fyJReMYHMvJyav050D83rYwSjN7mEPR0Pp8Q=
|
||||||
github.com/upyun/go-sdk/v3 v3.0.3/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E=
|
github.com/upyun/go-sdk/v3 v3.0.3/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E=
|
||||||
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
||||||
@ -224,40 +256,46 @@ github.com/winfsp/cgofuse v1.5.0/go.mod h1:h3awhoUOcn2VYVKCwDaYxSLlZwnyK+A8KaDoL
|
|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
gocv.io/x/gocv v0.25.0/go.mod h1:Rar2PS6DV+T4FL+PM535EImD/h13hGVaHhnCu1xarBs=
|
||||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||||
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
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/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-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||||
golang.org/x/crypto v0.4.0/go.mod h1:3quD/ATkf6oY+rnes5c3ExXTbLc8mueNue5/DoinL80=
|
|
||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
|
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
|
||||||
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.4.0 h1:x1RWAiZIvERqkltrFjtQP1ycmiR5pmhjtCfVOtdURuQ=
|
golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
|
||||||
golang.org/x/image v0.4.0/go.mod h1:FVC7BI/5Ym8R25iw5OLsgshdUBbT1h5jZTpA+mvAdZ4=
|
golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/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-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
|
||||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -270,29 +308,32 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
|
||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
|
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
@ -309,6 +350,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
@ -316,10 +358,10 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||||||
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=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gorm.io/driver/mysql v1.4.6 h1:5zS3vIKcyb46byXZNcYxaT9EWNIhXzu0gPuvvVrwZ8s=
|
gorm.io/driver/mysql v1.4.7 h1:rY46lkCspzGHn7+IYsNpSfEv9tA+SU4SkkB+GFX125Y=
|
||||||
gorm.io/driver/mysql v1.4.6/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
|
gorm.io/driver/mysql v1.4.7/go.mod h1:SxzItlnT1cb6e1e4ZRpgJN2VYtcqJgqnHxWr4wsP8oc=
|
||||||
gorm.io/driver/postgres v1.4.7 h1:J06jXZCNq7Pdf7LIPn8tZn9LsWjd81BRSKveKNr0ZfA=
|
gorm.io/driver/postgres v1.4.8 h1:NDWizaclb7Q2aupT0jkwK8jx1HVCNzt+PQ8v/VnxviA=
|
||||||
gorm.io/driver/postgres v1.4.7/go.mod h1:UJChCNLFKeBqQRE+HrkFUbKbq9idPXmTOk2u4Wok8S4=
|
gorm.io/driver/postgres v1.4.8/go.mod h1:O9MruWGNLUBUWVYfWuBClpf3HeGjOoybY0SNmCs3wsw=
|
||||||
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
|
gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc=
|
||||||
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||||
@ -327,3 +369,5 @@ gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
|||||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||||
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE=
|
gorm.io/gorm v1.24.5 h1:g6OPREKqqlWq4kh/3MCQbZKImeB9e6Xgc4zD+JgNZGE=
|
||||||
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
gorm.io/gorm v1.24.5/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
|
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||||
|
@ -2,8 +2,10 @@ package bootstrap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/cmd/flags"
|
"github.com/alist-org/alist/v3/cmd/flags"
|
||||||
"github.com/alist-org/alist/v3/drivers/base"
|
"github.com/alist-org/alist/v3/drivers/base"
|
||||||
@ -79,6 +81,7 @@ func InitConfig() {
|
|||||||
if conf.Conf.TlsInsecureSkipVerify {
|
if conf.Conf.TlsInsecureSkipVerify {
|
||||||
base.RestyClient = base.RestyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
base.RestyClient = base.RestyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
|
||||||
}
|
}
|
||||||
|
initURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
func confFromEnv() {
|
func confFromEnv() {
|
||||||
@ -93,3 +96,14 @@ func confFromEnv() {
|
|||||||
log.Fatalf("load config from env error: %+v", err)
|
log.Fatalf("load config from env error: %+v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func initURL() {
|
||||||
|
if !strings.Contains(conf.Conf.SiteURL, "://") {
|
||||||
|
conf.Conf.SiteURL = utils.FixAndCleanPath(conf.Conf.SiteURL)
|
||||||
|
}
|
||||||
|
u, err := url.Parse(conf.Conf.SiteURL)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log.Fatalf("can't parse site_url: %+v", err)
|
||||||
|
}
|
||||||
|
conf.URL = u
|
||||||
|
}
|
||||||
|
@ -110,7 +110,7 @@ func InitialSettings() []model.SettingItem {
|
|||||||
"PDF.js":"https://alist-org.github.io/pdf.js/web/viewer.html?file=$e_url"
|
"PDF.js":"https://alist-org.github.io/pdf.js/web/viewer.html?file=$e_url"
|
||||||
},
|
},
|
||||||
"epub": {
|
"epub": {
|
||||||
"EPUB.js":"/static/epub.js/viewer.html?url=$e_url"
|
"EPUB.js":"https://alist-org.github.io/static/epub.js/viewer.html?url=$e_url"
|
||||||
}
|
}
|
||||||
}`, Type: conf.TypeText, Group: model.PREVIEW},
|
}`, Type: conf.TypeText, Group: model.PREVIEW},
|
||||||
// {Key: conf.OfficeViewers, Value: `{
|
// {Key: conf.OfficeViewers, Value: `{
|
||||||
@ -150,13 +150,15 @@ func InitialSettings() []model.SettingItem {
|
|||||||
{Key: conf.MaxIndexDepth, Value: "20", Type: conf.TypeNumber, Group: model.INDEX, Flag: model.PRIVATE, Help: `max depth of index`},
|
{Key: conf.MaxIndexDepth, Value: "20", Type: conf.TypeNumber, Group: model.INDEX, Flag: model.PRIVATE, Help: `max depth of index`},
|
||||||
{Key: conf.IndexProgress, Value: "{}", Type: conf.TypeText, Group: model.SINGLE, Flag: model.PRIVATE},
|
{Key: conf.IndexProgress, Value: "{}", Type: conf.TypeText, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||||
|
|
||||||
// GitHub settings
|
// SSO settings
|
||||||
{Key: conf.GithubClientId, Value: "", Type: conf.TypeString, Group: model.GITHUB, Flag: model.PRIVATE},
|
{Key: conf.SSOLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.SSO, Flag: model.PUBLIC},
|
||||||
{Key: conf.GithubClientSecrets, Value: "", Type: conf.TypeString, Group: model.GITHUB, Flag: model.PRIVATE},
|
{Key: conf.SSOLoginplatform, Type: conf.TypeSelect, Options: "Github,Microsoft,Google,Dingtalk", Group: model.SSO, Flag: model.PUBLIC},
|
||||||
{Key: conf.GithubLoginEnabled, Value: "false", Type: conf.TypeBool, Group: model.GITHUB, Flag: model.PUBLIC},
|
{Key: conf.SSOClientId, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.SSOClientSecret, Value: "", Type: conf.TypeString, Group: model.SSO, Flag: model.PRIVATE},
|
||||||
|
|
||||||
// qbittorrent settings
|
// qbittorrent settings
|
||||||
{Key: conf.QbittorrentUrl, Value: "http://admin:adminadmin@localhost:8080/", Type: conf.TypeString, Group: model.QBITTORRENT, Flag: model.PRIVATE},
|
{Key: conf.QbittorrentUrl, Value: "http://admin:adminadmin@localhost:8080/", Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.QbittorrentSeedtime, Value: "0", Type: conf.TypeNumber, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||||
}
|
}
|
||||||
if flags.Dev {
|
if flags.Dev {
|
||||||
initialSettingItems = append(initialSettingItems, []model.SettingItem{
|
initialSettingItems = append(initialSettingItems, []model.SettingItem{
|
||||||
|
@ -46,7 +46,7 @@ func Log() {
|
|||||||
MaxAge: logConfig.MaxAge, //days
|
MaxAge: logConfig.MaxAge, //days
|
||||||
Compress: logConfig.Compress, // disabled by default
|
Compress: logConfig.Compress, // disabled by default
|
||||||
}
|
}
|
||||||
if flags.Debug || flags.Dev {
|
if flags.Debug || flags.Dev || flags.LogStd {
|
||||||
w = io.MultiWriter(os.Stdout, w)
|
w = io.MultiWriter(os.Stdout, w)
|
||||||
}
|
}
|
||||||
logrus.SetOutput(w)
|
logrus.SetOutput(w)
|
||||||
|
@ -77,6 +77,6 @@ func DefaultConfig() *Config {
|
|||||||
MaxAge: 28,
|
MaxAge: 28,
|
||||||
},
|
},
|
||||||
MaxConnections: 0,
|
MaxConnections: 0,
|
||||||
TlsInsecureSkipVerify: false,
|
TlsInsecureSkipVerify: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,33 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
const (
|
const (
|
||||||
TypeString = "string"
|
TypeString = "string"
|
||||||
TypeSelect = "select"
|
TypeSelect = "select"
|
||||||
TypeBool = "bool"
|
TypeBool = "bool"
|
||||||
TypeText = "text"
|
TypeText = "text"
|
||||||
TypeNumber = "number"
|
TypeNumber = "number"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// site
|
// site
|
||||||
VERSION = "version"
|
VERSION = "version"
|
||||||
SiteTitle = "site_title"
|
SiteTitle = "site_title"
|
||||||
Announcement = "announcement"
|
Announcement = "announcement"
|
||||||
AllowIndexed = "allow_indexed"
|
AllowIndexed = "allow_indexed"
|
||||||
|
|
||||||
Logo = "logo"
|
Logo = "logo"
|
||||||
Favicon = "favicon"
|
Favicon = "favicon"
|
||||||
MainColor = "main_color"
|
MainColor = "main_color"
|
||||||
|
|
||||||
// preview
|
// preview
|
||||||
TextTypes = "text_types"
|
TextTypes = "text_types"
|
||||||
AudioTypes = "audio_types"
|
AudioTypes = "audio_types"
|
||||||
VideoTypes = "video_types"
|
VideoTypes = "video_types"
|
||||||
ImageTypes = "image_types"
|
ImageTypes = "image_types"
|
||||||
ProxyTypes = "proxy_types"
|
ProxyTypes = "proxy_types"
|
||||||
ProxyIgnoreHeaders = "proxy_ignore_headers"
|
ProxyIgnoreHeaders = "proxy_ignore_headers"
|
||||||
AudioAutoplay = "audio_autoplay"
|
AudioAutoplay = "audio_autoplay"
|
||||||
VideoAutoplay = "video_autoplay"
|
VideoAutoplay = "video_autoplay"
|
||||||
|
|
||||||
// global
|
// global
|
||||||
HideFiles = "hide_files"
|
HideFiles = "hide_files"
|
||||||
@ -39,36 +39,38 @@ const (
|
|||||||
OcrApi = "ocr_api"
|
OcrApi = "ocr_api"
|
||||||
FilenameCharMapping = "filename_char_mapping"
|
FilenameCharMapping = "filename_char_mapping"
|
||||||
ForwardDirectLinkParams = "forward_direct_link_params"
|
ForwardDirectLinkParams = "forward_direct_link_params"
|
||||||
|
|
||||||
// index
|
// index
|
||||||
SearchIndex = "search_index"
|
SearchIndex = "search_index"
|
||||||
AutoUpdateIndex = "auto_update_index"
|
AutoUpdateIndex = "auto_update_index"
|
||||||
IgnorePaths = "ignore_paths"
|
IgnorePaths = "ignore_paths"
|
||||||
MaxIndexDepth = "max_index_depth"
|
MaxIndexDepth = "max_index_depth"
|
||||||
|
|
||||||
// aria2
|
// aria2
|
||||||
Aria2Uri = "aria2_uri"
|
Aria2Uri = "aria2_uri"
|
||||||
Aria2Secret = "aria2_secret"
|
Aria2Secret = "aria2_secret"
|
||||||
|
|
||||||
// single
|
// single
|
||||||
Token = "token"
|
Token = "token"
|
||||||
IndexProgress = "index_progress"
|
IndexProgress = "index_progress"
|
||||||
|
|
||||||
//Github
|
//SSO
|
||||||
GithubClientId = "github_client_id"
|
SSOClientId = "sso_client_id"
|
||||||
GithubClientSecrets = "github_client_secrets"
|
SSOClientSecret = "sso_client_secret"
|
||||||
GithubLoginEnabled = "github_login_enabled"
|
SSOLoginEnabled = "sso_login_enabled"
|
||||||
|
SSOLoginplatform = "sso_login_platform"
|
||||||
|
|
||||||
// qbittorrent
|
// qbittorrent
|
||||||
QbittorrentUrl = "qbittorrent_url"
|
QbittorrentUrl = "qbittorrent_url"
|
||||||
|
QbittorrentSeedtime = "qbittorrent_seedtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
UNKNOWN = iota
|
UNKNOWN = iota
|
||||||
FOLDER
|
FOLDER
|
||||||
//OFFICE
|
//OFFICE
|
||||||
VIDEO
|
VIDEO
|
||||||
AUDIO
|
AUDIO
|
||||||
TEXT
|
TEXT
|
||||||
IMAGE
|
IMAGE
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package conf
|
package conf
|
||||||
|
|
||||||
import "regexp"
|
import (
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
BuiltAt string
|
BuiltAt string
|
||||||
@ -13,6 +16,7 @@ var (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
Conf *Config
|
Conf *Config
|
||||||
|
URL *url.URL
|
||||||
)
|
)
|
||||||
|
|
||||||
var SlicesMap = make(map[string][]string)
|
var SlicesMap = make(map[string][]string)
|
||||||
|
@ -51,6 +51,15 @@ func GetStorageById(id uint) (*model.Storage, error) {
|
|||||||
return &storage, nil
|
return &storage, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetStorageByMountPath Get Storage by mountPath, used to update storage usually
|
||||||
|
func GetStorageByMountPath(mountPath string) (*model.Storage, error) {
|
||||||
|
var storage model.Storage
|
||||||
|
if err := db.Where("mount_path = ?", mountPath).First(&storage).Error; err != nil {
|
||||||
|
return nil, errors.WithStack(err)
|
||||||
|
}
|
||||||
|
return &storage, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetEnabledStorages() ([]model.Storage, error) {
|
func GetEnabledStorages() ([]model.Storage, error) {
|
||||||
var storages []model.Storage
|
var storages []model.Storage
|
||||||
if err := db.Where(fmt.Sprintf("%s = ?", columnName("disabled")), false).Find(&storages).Error; err != nil {
|
if err := db.Where(fmt.Sprintf("%s = ?", columnName("disabled")), false).Find(&storages).Error; err != nil {
|
||||||
|
@ -21,10 +21,10 @@ func GetUserByName(username string) (*model.User, error) {
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByGithubID(githubID int) (*model.User, error) {
|
func GetUserBySSOID(ssoID string) (*model.User, error) {
|
||||||
user := model.User{GithubID: githubID}
|
user := model.User{SsoID: ssoID}
|
||||||
if err := db.Where(user).First(&user).Error; err != nil {
|
if err := db.Where(user).First(&user).Error; err != nil {
|
||||||
return nil, errors.Wrapf(err, "The Github ID is not associated with a user")
|
return nil, errors.Wrapf(err, "The single sign on platform is not bound to any users")
|
||||||
}
|
}
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,17 @@
|
|||||||
package driver
|
package driver
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
LocalSort bool `json:"local_sort"`
|
LocalSort bool `json:"local_sort"`
|
||||||
OnlyLocal bool `json:"only_local"`
|
OnlyLocal bool `json:"only_local"`
|
||||||
OnlyProxy bool `json:"only_proxy"`
|
OnlyProxy bool `json:"only_proxy"`
|
||||||
NoCache bool `json:"no_cache"`
|
NoCache bool `json:"no_cache"`
|
||||||
NoUpload bool `json:"no_upload"`
|
NoUpload bool `json:"no_upload"`
|
||||||
NeedMs bool `json:"need_ms"` // if need get message from user, such as validate code
|
NeedMs bool `json:"need_ms"` // if need get message from user, such as validate code
|
||||||
DefaultRoot string `json:"default_root"`
|
DefaultRoot string `json:"default_root"`
|
||||||
CheckStatus bool
|
CheckStatus bool `json:"-"`
|
||||||
|
Alert string `json:"alert"` //info,success,warning,danger
|
||||||
|
NoOverwriteUpload bool `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Config) MustProxy() bool {
|
func (c Config) MustProxy() bool {
|
||||||
|
@ -2,9 +2,12 @@ package fs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,5 +16,14 @@ func link(ctx context.Context, path string, args model.LinkArgs) (*model.Link, m
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.WithMessage(err, "failed get storage")
|
return nil, nil, errors.WithMessage(err, "failed get storage")
|
||||||
}
|
}
|
||||||
return op.Link(ctx, storage, actualPath, args)
|
l, obj, err := op.Link(ctx, storage, actualPath, args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.WithMessage(err, "failed link")
|
||||||
|
}
|
||||||
|
if l.URL != "" && !strings.HasPrefix(l.URL, "http://") && !strings.HasPrefix(l.URL, "https://") {
|
||||||
|
if c, ok := ctx.(*gin.Context); ok {
|
||||||
|
l.URL = common.GetApiUrl(c.Request) + l.URL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l, obj, nil
|
||||||
}
|
}
|
||||||
|
@ -8,8 +8,7 @@ const (
|
|||||||
GLOBAL
|
GLOBAL
|
||||||
ARIA2
|
ARIA2
|
||||||
INDEX
|
INDEX
|
||||||
GITHUB
|
SSO
|
||||||
QBITTORRENT
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -33,7 +33,7 @@ type User struct {
|
|||||||
// 10: can add qbittorrent tasks
|
// 10: can add qbittorrent tasks
|
||||||
Permission int32 `json:"permission"`
|
Permission int32 `json:"permission"`
|
||||||
OtpSecret string `json:"-"`
|
OtpSecret string `json:"-"`
|
||||||
GithubID int `json:"github_id"`
|
SsoID string `json:"sso_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u User) IsGuest() bool {
|
func (u User) IsGuest() bool {
|
||||||
|
@ -2,5 +2,6 @@ package op
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
WORK = "work"
|
WORK = "work"
|
||||||
|
DISABLED = "disabled"
|
||||||
RootName = "root"
|
RootName = "root"
|
||||||
)
|
)
|
||||||
|
@ -459,6 +459,7 @@ func Remove(ctx context.Context, storage driver.Driver, path string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
// if object not found, it's ok
|
// if object not found, it's ok
|
||||||
if errs.IsObjectNotFound(err) {
|
if errs.IsObjectNotFound(err) {
|
||||||
|
log.Debugf("%s have been removed", path)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return errors.WithMessage(err, "failed to get object")
|
return errors.WithMessage(err, "failed to get object")
|
||||||
@ -497,6 +498,8 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *mo
|
|||||||
// if file exist and size = 0, delete it
|
// if file exist and size = 0, delete it
|
||||||
dstDirPath = utils.FixAndCleanPath(dstDirPath)
|
dstDirPath = utils.FixAndCleanPath(dstDirPath)
|
||||||
dstPath := stdpath.Join(dstDirPath, file.GetName())
|
dstPath := stdpath.Join(dstDirPath, file.GetName())
|
||||||
|
tempName := file.GetName() + ".alist_to_delete"
|
||||||
|
tempPath := stdpath.Join(dstDirPath, tempName)
|
||||||
fi, err := GetUnwrap(ctx, storage, dstPath)
|
fi, err := GetUnwrap(ctx, storage, dstPath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if fi.GetSize() == 0 {
|
if fi.GetSize() == 0 {
|
||||||
@ -504,6 +507,12 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *mo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessagef(err, "failed remove file that exist and have size 0")
|
return errors.WithMessagef(err, "failed remove file that exist and have size 0")
|
||||||
}
|
}
|
||||||
|
} else if storage.Config().NoOverwriteUpload {
|
||||||
|
// try to rename old obj
|
||||||
|
err = Rename(ctx, storage, dstPath, tempName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
file.Old = fi
|
file.Old = fi
|
||||||
}
|
}
|
||||||
@ -542,10 +551,20 @@ func Put(ctx context.Context, storage driver.Driver, dstDirPath string, file *mo
|
|||||||
return errs.NotImplement
|
return errs.NotImplement
|
||||||
}
|
}
|
||||||
log.Debugf("put file [%s] done", file.GetName())
|
log.Debugf("put file [%s] done", file.GetName())
|
||||||
//if err == nil {
|
if storage.Config().NoOverwriteUpload && fi != nil && fi.GetSize() > 0 {
|
||||||
// //clear cache
|
if err != nil {
|
||||||
// key := stdpath.Join(storage.GetStorage().MountPath, dstDirPath)
|
// upload failed, recover old obj
|
||||||
// listCache.Del(key)
|
err := Rename(ctx, storage, tempPath, file.GetName())
|
||||||
//}
|
if err != nil {
|
||||||
|
log.Errorf("failed recover old obj: %+v", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// upload success, remove old obj
|
||||||
|
err := Remove(ctx, storage, tempPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,10 @@ func GetStorageAndActualPath(rawPath string) (storage driver.Driver, actualPath
|
|||||||
rawPath = utils.FixAndCleanPath(rawPath)
|
rawPath = utils.FixAndCleanPath(rawPath)
|
||||||
storage = GetBalancedStorage(rawPath)
|
storage = GetBalancedStorage(rawPath)
|
||||||
if storage == nil {
|
if storage == nil {
|
||||||
|
if rawPath == "/" {
|
||||||
|
err = errors.New("please add a storage first.")
|
||||||
|
return
|
||||||
|
}
|
||||||
err = errors.Errorf("can't find storage with rawPath: %s", rawPath)
|
err = errors.Errorf("can't find storage with rawPath: %s", rawPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -142,6 +142,7 @@ func DisableStorage(ctx context.Context, id uint) error {
|
|||||||
}
|
}
|
||||||
// delete the storage in the memory
|
// delete the storage in the memory
|
||||||
storage.Disabled = true
|
storage.Disabled = true
|
||||||
|
storage.SetStatus(DISABLED)
|
||||||
err = db.UpdateStorage(storage)
|
err = db.UpdateStorage(storage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed update storage in db")
|
return errors.WithMessage(err, "failed update storage in db")
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/internal/conf"
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
"github.com/alist-org/alist/v3/pkg/task"
|
"github.com/alist-org/alist/v3/pkg/task"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -50,6 +51,7 @@ func AddURL(ctx context.Context, url string, dstDirPath string) error {
|
|||||||
tsk: tsk,
|
tsk: tsk,
|
||||||
tempDir: tempDir,
|
tempDir: tempDir,
|
||||||
dstDirPath: dstDirPath,
|
dstDirPath: dstDirPath,
|
||||||
|
seedtime: setting.GetInt(conf.QbittorrentSeedtime, 0),
|
||||||
}
|
}
|
||||||
return m.Loop()
|
return m.Loop()
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,7 @@ type Client interface {
|
|||||||
AddFromLink(link string, savePath string, id string) error
|
AddFromLink(link string, savePath string, id string) error
|
||||||
GetInfo(id string) (TorrentInfo, error)
|
GetInfo(id string) (TorrentInfo, error)
|
||||||
GetFiles(id string) ([]FileInfo, error)
|
GetFiles(id string) ([]FileInfo, error)
|
||||||
Delete(id string) error
|
Delete(id string, deleteFiles bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type client struct {
|
type client struct {
|
||||||
@ -242,6 +242,19 @@ type TorrentInfo struct {
|
|||||||
Upspeed int `json:"upspeed"` // 上传速度(字节/秒)
|
Upspeed int `json:"upspeed"` // 上传速度(字节/秒)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InfoNotFoundError struct {
|
||||||
|
Id string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i InfoNotFoundError) Error() string {
|
||||||
|
return "there should be exactly one task with tag \"alist-" + i.Id + "\""
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInfoNotFoundError(id string) InfoNotFoundError {
|
||||||
|
return InfoNotFoundError{Id: id}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *client) GetInfo(id string) (TorrentInfo, error) {
|
func (c *client) GetInfo(id string) (TorrentInfo, error) {
|
||||||
var infos []TorrentInfo
|
var infos []TorrentInfo
|
||||||
|
|
||||||
@ -266,7 +279,7 @@ func (c *client) GetInfo(id string) (TorrentInfo, error) {
|
|||||||
return TorrentInfo{}, err
|
return TorrentInfo{}, err
|
||||||
}
|
}
|
||||||
if len(infos) != 1 {
|
if len(infos) != 1 {
|
||||||
return TorrentInfo{}, errors.New("there should be exactly one task with tag \"alist-" + id + "\"")
|
return TorrentInfo{}, NewInfoNotFoundError(id)
|
||||||
}
|
}
|
||||||
return infos[0], nil
|
return infos[0], nil
|
||||||
}
|
}
|
||||||
@ -313,7 +326,7 @@ func (c *client) GetFiles(id string) ([]FileInfo, error) {
|
|||||||
return infos, nil
|
return infos, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *client) Delete(id string) error {
|
func (c *client) Delete(id string, deleteFiles bool) error {
|
||||||
err := c.checkAuthorization()
|
err := c.checkAuthorization()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -325,13 +338,27 @@ func (c *client) Delete(id string) error {
|
|||||||
}
|
}
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("hashes", info.Hash)
|
v.Set("hashes", info.Hash)
|
||||||
v.Set("deleteFiles", "false")
|
if deleteFiles {
|
||||||
|
v.Set("deleteFiles", "true")
|
||||||
|
} else {
|
||||||
|
v.Set("deleteFiles", "false")
|
||||||
|
}
|
||||||
response, err := c.post("/api/v2/torrents/delete", v)
|
response, err := c.post("/api/v2/torrents/delete", v)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if response.StatusCode != 200 {
|
if response.StatusCode != 200 {
|
||||||
return errors.New("failed")
|
return errors.New("failed to delete qbittorrent task")
|
||||||
|
}
|
||||||
|
|
||||||
|
v = url.Values{}
|
||||||
|
v.Set("tags", "alist-"+id)
|
||||||
|
response, err = c.post("/api/v2/torrents/deleteTags", v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if response.StatusCode != 200 {
|
||||||
|
return errors.New("failed to delete qbittorrent tag")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -88,12 +88,6 @@ func TestAdd(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// test add
|
|
||||||
err = c.login()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
err = c.AddFromLink(
|
err = c.AddFromLink(
|
||||||
"https://releases.ubuntu.com/22.04/ubuntu-22.04.1-desktop-amd64.iso.torrent",
|
"https://releases.ubuntu.com/22.04/ubuntu-22.04.1-desktop-amd64.iso.torrent",
|
||||||
"D:\\qBittorrentDownload\\alist",
|
"D:\\qBittorrentDownload\\alist",
|
||||||
@ -145,7 +139,15 @@ func TestDelete(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
err = c.Delete("uuid-2")
|
err = c.AddFromLink(
|
||||||
|
"https://releases.ubuntu.com/22.04/ubuntu-22.04.1-desktop-amd64.iso.torrent",
|
||||||
|
"D:\\qBittorrentDownload\\alist",
|
||||||
|
"uuid-1",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
err = c.Delete("uuid-1", true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Error(err)
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,26 @@ package qbittorrent
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
"github.com/alist-org/alist/v3/pkg/task"
|
"github.com/alist-org/alist/v3/pkg/task"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Monitor struct {
|
type Monitor struct {
|
||||||
tsk *task.Task[string]
|
tsk *task.Task[string]
|
||||||
tempDir string
|
tempDir string
|
||||||
dstDirPath string
|
dstDirPath string
|
||||||
|
seedtime int
|
||||||
finish chan struct{}
|
finish chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,12 +31,36 @@ func (m *Monitor) Loop() error {
|
|||||||
completed bool
|
completed bool
|
||||||
)
|
)
|
||||||
m.finish = make(chan struct{})
|
m.finish = make(chan struct{})
|
||||||
|
|
||||||
|
// wait for qbittorrent to parse torrent and create task
|
||||||
|
m.tsk.SetStatus("waiting for qbittorrent to parse torrent and create task")
|
||||||
|
waitCount := 0
|
||||||
|
for {
|
||||||
|
_, err := qbclient.GetInfo(m.tsk.ID)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch err.(type) {
|
||||||
|
case InfoNotFoundError:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
waitCount += 1
|
||||||
|
if waitCount >= 60 {
|
||||||
|
return errors.New("torrent parse timeout")
|
||||||
|
}
|
||||||
|
timer := time.NewTimer(time.Second)
|
||||||
|
<-timer.C
|
||||||
|
}
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-m.tsk.Ctx.Done():
|
case <-m.tsk.Ctx.Done():
|
||||||
err = qbclient.Delete(m.tsk.ID)
|
// delete qbittorrent task and downloaded files when the task exits with error
|
||||||
return err
|
return qbclient.Delete(m.tsk.ID, true)
|
||||||
case <-time.After(time.Second * 2):
|
case <-time.After(time.Second * 2):
|
||||||
completed, err = m.update()
|
completed, err = m.update()
|
||||||
if completed {
|
if completed {
|
||||||
@ -61,29 +87,13 @@ func (m *Monitor) update() (bool, error) {
|
|||||||
progress := float64(info.Completed) / float64(info.Size) * 100
|
progress := float64(info.Completed) / float64(info.Size) * 100
|
||||||
m.tsk.SetProgress(int(progress))
|
m.tsk.SetProgress(int(progress))
|
||||||
switch info.State {
|
switch info.State {
|
||||||
case UPLOADING:
|
case UPLOADING, PAUSEDUP, QUEUEDUP, STALLEDUP, FORCEDUP, CHECKINGUP:
|
||||||
case PAUSEDUP:
|
|
||||||
case QUEUEDUP:
|
|
||||||
case STALLEDUP:
|
|
||||||
case FORCEDUP:
|
|
||||||
case CHECKINGUP:
|
|
||||||
err = m.complete()
|
err = m.complete()
|
||||||
return true, errors.WithMessage(err, "failed to transfer file")
|
return true, errors.WithMessage(err, "failed to transfer file")
|
||||||
case ALLOCATING:
|
case ALLOCATING, DOWNLOADING, METADL, PAUSEDDL, QUEUEDDL, STALLEDDL, CHECKINGDL, FORCEDDL, CHECKINGRESUMEDATA, MOVING:
|
||||||
case DOWNLOADING:
|
|
||||||
case METADL:
|
|
||||||
case PAUSEDDL:
|
|
||||||
case QUEUEDDL:
|
|
||||||
case STALLEDDL:
|
|
||||||
case CHECKINGDL:
|
|
||||||
case FORCEDDL:
|
|
||||||
case CHECKINGRESUMEDATA:
|
|
||||||
case MOVING:
|
|
||||||
case UNKNOWN: // or maybe should return an error for UNKNOWN?
|
|
||||||
m.tsk.SetStatus("qbittorrent downloading")
|
m.tsk.SetStatus("qbittorrent downloading")
|
||||||
return false, nil
|
return false, nil
|
||||||
case ERROR:
|
case ERROR, MISSINGFILES, UNKNOWN:
|
||||||
case MISSINGFILES:
|
|
||||||
return true, errors.Errorf("failed to download %s, error: %s", m.tsk.ID, info.State)
|
return true, errors.Errorf("failed to download %s, error: %s", m.tsk.ID, info.State)
|
||||||
}
|
}
|
||||||
return true, errors.New("unknown error occurred downloading qbittorrent") // should never happen
|
return true, errors.New("unknown error occurred downloading qbittorrent") // should never happen
|
||||||
@ -95,7 +105,7 @@ var TransferTaskManager = task.NewTaskManager(3, func(k *uint64) {
|
|||||||
|
|
||||||
func (m *Monitor) complete() error {
|
func (m *Monitor) complete() error {
|
||||||
// check dstDir again
|
// check dstDir again
|
||||||
storage, dstDirActualPath, err := op.GetStorageAndActualPath(m.dstDirPath)
|
storage, dstBaseDir, err := op.GetStorageAndActualPath(m.dstDirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.WithMessage(err, "failed get storage")
|
return errors.WithMessage(err, "failed get storage")
|
||||||
}
|
}
|
||||||
@ -105,41 +115,59 @@ func (m *Monitor) complete() error {
|
|||||||
return errors.Wrapf(err, "failed to get files of %s", m.tsk.ID)
|
return errors.Wrapf(err, "failed to get files of %s", m.tsk.ID)
|
||||||
}
|
}
|
||||||
log.Debugf("files len: %d", len(files))
|
log.Debugf("files len: %d", len(files))
|
||||||
|
// delete qbittorrent task but do not delete the files before transferring to avoid qbittorrent
|
||||||
|
// accessing downloaded files and throw `cannot access the file because it is being used by another process` error
|
||||||
|
// err = qbclient.Delete(m.tsk.ID, false)
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
// upload files
|
// upload files
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(len(files))
|
wg.Add(len(files))
|
||||||
go func() {
|
go func() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
err := os.RemoveAll(m.tempDir)
|
|
||||||
m.finish <- struct{}{}
|
m.finish <- struct{}{}
|
||||||
|
if m.seedtime < 0 {
|
||||||
|
log.Debugf("do not delete qb task %s", m.tsk.ID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("delete qb task %s after %d minutes", m.tsk.ID, m.seedtime)
|
||||||
|
<-time.After(time.Duration(m.seedtime) * time.Minute)
|
||||||
|
err := qbclient.Delete(m.tsk.ID, true)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorln(err.Error())
|
||||||
|
}
|
||||||
|
err = os.RemoveAll(m.tempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to remove qbittorrent temp dir: %+v", err.Error())
|
log.Errorf("failed to remove qbittorrent temp dir: %+v", err.Error())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
filePath := filepath.Join(m.tempDir, file.Name)
|
tempPath := filepath.Join(m.tempDir, file.Name)
|
||||||
|
dstPath := filepath.Join(dstBaseDir, file.Name)
|
||||||
|
dstDir := filepath.Dir(dstPath)
|
||||||
|
fileName := filepath.Base(dstPath)
|
||||||
TransferTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
TransferTaskManager.Submit(task.WithCancelCtx(&task.Task[uint64]{
|
||||||
Name: fmt.Sprintf("transfer %s to [%s](%s)", filePath, storage.GetStorage().MountPath, dstDirActualPath),
|
Name: fmt.Sprintf("transfer %s to [%s](%s)", tempPath, storage.GetStorage().MountPath, dstPath),
|
||||||
Func: func(tsk *task.Task[uint64]) error {
|
Func: func(tsk *task.Task[uint64]) error {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
size := file.Size
|
size := file.Size
|
||||||
mimetype := utils.GetMimeType(filePath)
|
mimetype := utils.GetMimeType(tempPath)
|
||||||
f, err := os.Open(filePath)
|
f, err := os.Open(tempPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "failed to open file %s", filePath)
|
return errors.Wrapf(err, "failed to open file %s", tempPath)
|
||||||
}
|
}
|
||||||
stream := &model.FileStream{
|
stream := &model.FileStream{
|
||||||
Obj: &model.Object{
|
Obj: &model.Object{
|
||||||
Name: path.Base(filePath),
|
Name: fileName,
|
||||||
Size: size,
|
Size: size,
|
||||||
Modified: time.Now(),
|
Modified: time.Now(),
|
||||||
IsFolder: false,
|
IsFolder: false,
|
||||||
},
|
},
|
||||||
ReadCloser: f,
|
ReadCloser: struct{ io.ReadSeekCloser }{f},
|
||||||
Mimetype: mimetype,
|
Mimetype: mimetype,
|
||||||
}
|
}
|
||||||
newDistDir := filepath.Join(dstDirActualPath, file.Name)
|
return op.Put(tsk.Ctx, storage, dstDir, stream, tsk.SetProgress)
|
||||||
return op.Put(tsk.Ctx, storage, newDistDir, stream, tsk.SetProgress)
|
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
107
pkg/http_range/range.go
Normal file
107
pkg/http_range/range.go
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Package http_range implements http range parsing.
|
||||||
|
package http_range
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Range specifies the byte range to be sent to the client.
|
||||||
|
type Range struct {
|
||||||
|
Start int64
|
||||||
|
Length int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContentRange returns Content-Range header value.
|
||||||
|
func (r Range) ContentRange(size int64) string {
|
||||||
|
return fmt.Sprintf("bytes %d-%d/%d", r.Start, r.Start+r.Length-1, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ErrNoOverlap is returned by ParseRange if first-byte-pos of
|
||||||
|
// all of the byte-range-spec values is greater than the content size.
|
||||||
|
ErrNoOverlap = errors.New("invalid range: failed to overlap")
|
||||||
|
|
||||||
|
// ErrInvalid is returned by ParseRange on invalid input.
|
||||||
|
ErrInvalid = errors.New("invalid range")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseRange parses a Range header string as per RFC 7233.
|
||||||
|
// ErrNoOverlap is returned if none of the ranges overlap.
|
||||||
|
// ErrInvalid is returned if s is invalid range.
|
||||||
|
func ParseRange(s string, size int64) ([]Range, error) { // nolint:gocognit
|
||||||
|
if s == "" {
|
||||||
|
return nil, nil // header not present
|
||||||
|
}
|
||||||
|
const b = "bytes="
|
||||||
|
if !strings.HasPrefix(s, b) {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
var ranges []Range
|
||||||
|
noOverlap := false
|
||||||
|
for _, ra := range strings.Split(s[len(b):], ",") {
|
||||||
|
ra = textproto.TrimString(ra)
|
||||||
|
if ra == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
i := strings.Index(ra, "-")
|
||||||
|
if i < 0 {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
start, end := textproto.TrimString(ra[:i]), textproto.TrimString(ra[i+1:])
|
||||||
|
var r Range
|
||||||
|
if start == "" {
|
||||||
|
// If no start is specified, end specifies the
|
||||||
|
// range start relative to the end of the file,
|
||||||
|
// and we are dealing with <suffix-length>
|
||||||
|
// which has to be a non-negative integer as per
|
||||||
|
// RFC 7233 Section 2.1 "Byte-Ranges".
|
||||||
|
if end == "" || end[0] == '-' {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
i, err := strconv.ParseInt(end, 10, 64)
|
||||||
|
if i < 0 || err != nil {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
if i > size {
|
||||||
|
i = size
|
||||||
|
}
|
||||||
|
r.Start = size - i
|
||||||
|
r.Length = size - r.Start
|
||||||
|
} else {
|
||||||
|
i, err := strconv.ParseInt(start, 10, 64)
|
||||||
|
if err != nil || i < 0 {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
if i >= size {
|
||||||
|
// If the range begins after the size of the content,
|
||||||
|
// then it does not overlap.
|
||||||
|
noOverlap = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Start = i
|
||||||
|
if end == "" {
|
||||||
|
// If no end is specified, range extends to end of the file.
|
||||||
|
r.Length = size - r.Start
|
||||||
|
} else {
|
||||||
|
i, err := strconv.ParseInt(end, 10, 64)
|
||||||
|
if err != nil || r.Start > i {
|
||||||
|
return nil, ErrInvalid
|
||||||
|
}
|
||||||
|
if i >= size {
|
||||||
|
i = size - 1
|
||||||
|
}
|
||||||
|
r.Length = i - r.Start + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ranges = append(ranges, r)
|
||||||
|
}
|
||||||
|
if noOverlap && len(ranges) == 0 {
|
||||||
|
// The specified ranges did not overlap with the content.
|
||||||
|
return nil, ErrNoOverlap
|
||||||
|
}
|
||||||
|
return ranges, nil
|
||||||
|
}
|
@ -122,6 +122,10 @@ func (tm *Manager[K]) ClearDone() {
|
|||||||
tm.RemoveByStates(SUCCEEDED, CANCELED, ERRORED)
|
tm.RemoveByStates(SUCCEEDED, CANCELED, ERRORED)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tm *Manager[K]) ClearSucceeded() {
|
||||||
|
tm.RemoveByStates(SUCCEEDED)
|
||||||
|
}
|
||||||
|
|
||||||
func (tm *Manager[K]) RawTasks() *generic_sync.MapOf[K, *Task[K]] {
|
func (tm *Manager[K]) RawTasks() *generic_sync.MapOf[K, *Task[K]] {
|
||||||
return &tm.tasks
|
return &tm.tasks
|
||||||
}
|
}
|
||||||
|
@ -69,3 +69,25 @@ func (l limitWriter) Write(p []byte) (n int, err error) {
|
|||||||
func LimitWriter(w io.Writer, size int64) io.Writer {
|
func LimitWriter(w io.Writer, size int64) io.Writer {
|
||||||
return &limitWriter{w: w, limit: size}
|
return &limitWriter{w: w, limit: size}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReadCloser struct {
|
||||||
|
io.Reader
|
||||||
|
io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
type CloseFunc func() error
|
||||||
|
|
||||||
|
func (c CloseFunc) Close() error {
|
||||||
|
return c()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReadCloser(reader io.Reader, close CloseFunc) io.ReadCloser {
|
||||||
|
return ReadCloser{
|
||||||
|
Reader: reader,
|
||||||
|
Closer: close,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLimitReadCloser(reader io.Reader, close CloseFunc, limit int64) io.ReadCloser {
|
||||||
|
return NewReadCloser(io.LimitReader(reader, limit), close)
|
||||||
|
}
|
||||||
|
21
pkg/utils/url.go
Normal file
21
pkg/utils/url.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InjectQuery(raw string, query url.Values) (string, error) {
|
||||||
|
param := query.Encode()
|
||||||
|
if param == "" {
|
||||||
|
return raw, nil
|
||||||
|
}
|
||||||
|
u, err := url.Parse(raw)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
joiner := "?"
|
||||||
|
if u.RawQuery != "" {
|
||||||
|
joiner = "&"
|
||||||
|
}
|
||||||
|
return raw + joiner + param, nil
|
||||||
|
}
|
@ -14,7 +14,7 @@ func GetApiUrl(r *http.Request) string {
|
|||||||
if strings.HasPrefix(api, "http") {
|
if strings.HasPrefix(api, "http") {
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
if r != nil && api == "" {
|
if r != nil {
|
||||||
protocol := "http"
|
protocol := "http"
|
||||||
if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" {
|
if r.TLS != nil || r.Header.Get("X-Forwarded-Proto") == "https" {
|
||||||
protocol = "https"
|
protocol = "https"
|
||||||
@ -25,6 +25,6 @@ func GetApiUrl(r *http.Request) string {
|
|||||||
}
|
}
|
||||||
api = fmt.Sprintf("%s://%s", protocol, stdpath.Join(host, api))
|
api = fmt.Sprintf("%s://%s", protocol, stdpath.Join(host, api))
|
||||||
}
|
}
|
||||||
strings.TrimSuffix(api, "/")
|
api = strings.TrimSuffix(api, "/")
|
||||||
return api
|
return api
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
|
"github.com/alist-org/alist/v3/internal/driver"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
)
|
)
|
||||||
@ -15,12 +18,20 @@ func CanWrite(meta *model.Meta, path string) bool {
|
|||||||
return meta.WSub || meta.Path == path
|
return meta.WSub || meta.Path == path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsApply(metaPath, reqPath string, applySub bool) bool {
|
||||||
|
if utils.PathEqual(metaPath, reqPath) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return utils.IsSubPath(metaPath, reqPath) && applySub
|
||||||
|
}
|
||||||
|
|
||||||
func CanAccess(user *model.User, meta *model.Meta, reqPath string, password string) bool {
|
func CanAccess(user *model.User, meta *model.Meta, reqPath string, password string) bool {
|
||||||
// if the reqPath is in hide (only can check the nearest meta) and user can't see hides, can't access
|
// if the reqPath is in hide (only can check the nearest meta) and user can't see hides, can't access
|
||||||
if meta != nil && !user.CanSeeHides() && meta.Hide != "" && !utils.PathEqual(meta.Path, reqPath) {
|
if meta != nil && !user.CanSeeHides() && meta.Hide != "" &&
|
||||||
|
IsApply(meta.Path, path.Dir(reqPath), meta.HSub) { // the meta should apply to the parent of current path
|
||||||
for _, hide := range strings.Split(meta.Hide, "\n") {
|
for _, hide := range strings.Split(meta.Hide, "\n") {
|
||||||
re := regexp.MustCompile(hide)
|
re := regexp.MustCompile(hide)
|
||||||
if re.MatchString(reqPath[len(meta.Path)+1:]) {
|
if re.MatchString(path.Base(reqPath)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,3 +51,18 @@ func CanAccess(user *model.User, meta *model.Meta, reqPath string, password stri
|
|||||||
// validate password
|
// validate password
|
||||||
return meta.Password == password
|
return meta.Password == password
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ShouldProxy TODO need optimize
|
||||||
|
// when should be proxy?
|
||||||
|
// 1. config.MustProxy()
|
||||||
|
// 2. storage.WebProxy
|
||||||
|
// 3. proxy_types
|
||||||
|
func ShouldProxy(storage driver.Driver, filename string) bool {
|
||||||
|
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if utils.SliceContains(conf.SlicesMap[conf.ProxyTypes], utils.Ext(filename)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
24
server/common/check_test.go
Normal file
24
server/common/check_test.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsApply(t *testing.T) {
|
||||||
|
datas := []struct {
|
||||||
|
metaPath string
|
||||||
|
reqPath string
|
||||||
|
applySub bool
|
||||||
|
result bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
metaPath: "/",
|
||||||
|
reqPath: "/test",
|
||||||
|
applySub: true,
|
||||||
|
result: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for i, data := range datas {
|
||||||
|
if IsApply(data.metaPath, data.reqPath, data.applySub) != data.result {
|
||||||
|
t.Errorf("TestIsApply %d failed", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -26,8 +26,9 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
|
|||||||
defer func() {
|
defer func() {
|
||||||
_ = link.Data.Close()
|
_ = link.Data.Close()
|
||||||
}()
|
}()
|
||||||
|
filename := file.GetName()
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, file.GetName(), url.QueryEscape(file.GetName())))
|
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, filename, url.PathEscape(filename)))
|
||||||
w.Header().Set("Content-Length", strconv.FormatInt(file.GetSize(), 10))
|
w.Header().Set("Content-Length", strconv.FormatInt(file.GetSize(), 10))
|
||||||
if link.Header != nil {
|
if link.Header != nil {
|
||||||
// TODO clean header with blacklist or whitelist
|
// TODO clean header with blacklist or whitelist
|
||||||
|
@ -101,7 +101,7 @@ func UpdateCurrent(c *gin.Context) {
|
|||||||
if req.Password != "" {
|
if req.Password != "" {
|
||||||
user.Password = req.Password
|
user.Password = req.Password
|
||||||
}
|
}
|
||||||
user.GithubID = req.GithubID
|
user.SsoID = req.SsoID
|
||||||
if err := op.UpdateUser(user); err != nil {
|
if err := op.UpdateUser(user); err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,7 +2,7 @@ package handles
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"io"
|
||||||
stdpath "path"
|
stdpath "path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Down(c *gin.Context) {
|
func Down(c *gin.Context) {
|
||||||
@ -25,11 +26,10 @@ func Down(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if shouldProxy(storage, filename) {
|
if common.ShouldProxy(storage, filename) {
|
||||||
Proxy(c)
|
Proxy(c)
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
link, _, err := fs.Link(c, rawPath, model.LinkArgs{
|
link, _, err := fs.Link(c, rawPath, model.LinkArgs{
|
||||||
IP: c.ClientIP(),
|
IP: c.ClientIP(),
|
||||||
Header: c.Request.Header,
|
Header: c.Request.Header,
|
||||||
@ -39,24 +39,24 @@ func Down(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if link.Data != nil {
|
||||||
|
defer func(Data io.ReadCloser) {
|
||||||
|
err := Data.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("close data error: %s", err)
|
||||||
|
}
|
||||||
|
}(link.Data)
|
||||||
|
}
|
||||||
c.Header("Referrer-Policy", "no-referrer")
|
c.Header("Referrer-Policy", "no-referrer")
|
||||||
c.Header("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
c.Header("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
||||||
if setting.GetBool(conf.ForwardDirectLinkParams) {
|
if setting.GetBool(conf.ForwardDirectLinkParams) {
|
||||||
params := c.Request.URL.Query()
|
query := c.Request.URL.Query()
|
||||||
params.Del("sign")
|
query.Del("sign")
|
||||||
u, err := url.Parse(link.URL)
|
link.URL, err = utils.InjectQuery(link.URL, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
values := u.Query()
|
|
||||||
for k := range params {
|
|
||||||
for i := range params[k] {
|
|
||||||
values.Set(k, params[k][i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u.RawQuery = values.Encode()
|
|
||||||
link.URL = u.String()
|
|
||||||
}
|
}
|
||||||
c.Redirect(302, link.URL)
|
c.Redirect(302, link.URL)
|
||||||
}
|
}
|
||||||
@ -92,21 +92,13 @@ func Proxy(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if link.URL != "" && setting.GetBool(conf.ForwardDirectLinkParams) {
|
if link.URL != "" && setting.GetBool(conf.ForwardDirectLinkParams) {
|
||||||
params := c.Request.URL.Query()
|
query := c.Request.URL.Query()
|
||||||
params.Del("sign")
|
query.Del("sign")
|
||||||
u, err := url.Parse(link.URL)
|
link.URL, err = utils.InjectQuery(link.URL, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
values := u.Query()
|
|
||||||
for k := range params {
|
|
||||||
for i := range params[k] {
|
|
||||||
values.Set(k, params[k][i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
u.RawQuery = values.Encode()
|
|
||||||
link.URL = u.String()
|
|
||||||
}
|
}
|
||||||
err = common.Proxy(c.Writer, c.Request, link, file)
|
err = common.Proxy(c.Writer, c.Request, link, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -119,21 +111,6 @@ func Proxy(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO need optimize
|
|
||||||
// when should be proxy?
|
|
||||||
// 1. config.MustProxy()
|
|
||||||
// 2. storage.WebProxy
|
|
||||||
// 3. proxy_types
|
|
||||||
func shouldProxy(storage driver.Driver, filename string) bool {
|
|
||||||
if storage.Config().MustProxy() || storage.GetStorage().WebProxy {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if utils.SliceContains(conf.SlicesMap[conf.ProxyTypes], utils.Ext(filename)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO need optimize
|
// TODO need optimize
|
||||||
// when can be proxy?
|
// when can be proxy?
|
||||||
// 1. text file
|
// 1. text file
|
||||||
|
@ -2,17 +2,21 @@ package handles
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
stdpath "path"
|
stdpath "path"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/errs"
|
"github.com/alist-org/alist/v3/internal/errs"
|
||||||
"github.com/alist-org/alist/v3/internal/fs"
|
"github.com/alist-org/alist/v3/internal/fs"
|
||||||
"github.com/alist-org/alist/v3/internal/model"
|
"github.com/alist-org/alist/v3/internal/model"
|
||||||
"github.com/alist-org/alist/v3/internal/op"
|
"github.com/alist-org/alist/v3/internal/op"
|
||||||
"github.com/alist-org/alist/v3/internal/sign"
|
"github.com/alist-org/alist/v3/internal/sign"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/generic"
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MkdirOrLinkReq struct {
|
type MkdirOrLinkReq struct {
|
||||||
@ -92,6 +96,93 @@ func FsMove(c *gin.Context) {
|
|||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RecursiveMoveReq struct {
|
||||||
|
SrcDir string `json:"src_dir"`
|
||||||
|
DstDir string `json:"dst_dir"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FsRecursiveMove(c *gin.Context) {
|
||||||
|
var req RecursiveMoveReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := c.MustGet("user").(*model.User)
|
||||||
|
if !user.CanMove() {
|
||||||
|
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
srcDir, err := user.JoinPath(req.SrcDir)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dstDir, err := user.JoinPath(req.DstDir)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, err := op.GetNearestMeta(srcDir)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||||
|
common.ErrorResp(c, err, 500, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Set("meta", meta)
|
||||||
|
|
||||||
|
rootFiles, err := fs.List(c, srcDir, false)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// record the file path
|
||||||
|
filePathMap := make(map[model.Obj]string)
|
||||||
|
movingFiles := generic.NewQueue[model.Obj]()
|
||||||
|
for _, file := range rootFiles {
|
||||||
|
movingFiles.Push(file)
|
||||||
|
filePathMap[file] = srcDir
|
||||||
|
}
|
||||||
|
|
||||||
|
for !movingFiles.IsEmpty() {
|
||||||
|
|
||||||
|
movingFile := movingFiles.Pop()
|
||||||
|
movingFilePath := fmt.Sprintf("%s/%s", filePathMap[movingFile], movingFile.GetName())
|
||||||
|
if movingFile.IsDir() {
|
||||||
|
// directory, recursive move
|
||||||
|
subFilePath := movingFilePath
|
||||||
|
subFiles, err := fs.List(c, subFilePath, true)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, subFile := range subFiles {
|
||||||
|
movingFiles.Push(subFile)
|
||||||
|
filePathMap[subFile] = subFilePath
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
if movingFilePath == dstDir {
|
||||||
|
// same directory, don't move
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// move
|
||||||
|
err := fs.Move(c, movingFilePath, dstDir, movingFiles.IsEmpty())
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
||||||
|
|
||||||
func FsCopy(c *gin.Context) {
|
func FsCopy(c *gin.Context) {
|
||||||
var req MoveCopyReq
|
var req MoveCopyReq
|
||||||
if err := c.ShouldBind(&req); err != nil {
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
@ -163,6 +254,67 @@ func FsRename(c *gin.Context) {
|
|||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RegexRenameReq struct {
|
||||||
|
SrcDir string `json:"src_dir"`
|
||||||
|
SrcNameRegex string `json:"src_name_regex"`
|
||||||
|
NewNameRegex string `json:"new_name_regex"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FsRegexRename(c *gin.Context) {
|
||||||
|
var req RegexRenameReq
|
||||||
|
if err := c.ShouldBind(&req); err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user := c.MustGet("user").(*model.User)
|
||||||
|
if !user.CanRename() {
|
||||||
|
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
reqPath, err := user.JoinPath(req.SrcDir)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 403)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
meta, err := op.GetNearestMeta(reqPath)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||||
|
common.ErrorResp(c, err, 500, true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Set("meta", meta)
|
||||||
|
|
||||||
|
srcRegexp, err := regexp.Compile(req.SrcNameRegex)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
files, err := fs.List(c, reqPath, false)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range files {
|
||||||
|
|
||||||
|
if srcRegexp.MatchString(file.GetName()) {
|
||||||
|
filePath := fmt.Sprintf("%s/%s", reqPath, file.GetName())
|
||||||
|
newFileName := srcRegexp.ReplaceAllString(file.GetName(), req.NewNameRegex)
|
||||||
|
if err := fs.Rename(c, filePath, newFileName); err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
||||||
|
|
||||||
type RemoveReq struct {
|
type RemoveReq struct {
|
||||||
Dir string `json:"dir"`
|
Dir string `json:"dir"`
|
||||||
Names []string `json:"names"`
|
Names []string `json:"names"`
|
||||||
@ -229,6 +381,14 @@ func Link(c *gin.Context) {
|
|||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if link.Data != nil {
|
||||||
|
defer func(Data io.ReadCloser) {
|
||||||
|
err := Data.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("close link data error: %v", err)
|
||||||
|
}
|
||||||
|
}(link.Data)
|
||||||
|
}
|
||||||
common.SuccessResp(c, link)
|
common.SuccessResp(c, link)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -1,101 +0,0 @@
|
|||||||
package handles
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/alist-org/alist/v3/internal/db"
|
|
||||||
"github.com/alist-org/alist/v3/pkg/utils"
|
|
||||||
"github.com/alist-org/alist/v3/server/common"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/go-resty/resty/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func GithubLoginRedirect(c *gin.Context) {
|
|
||||||
method := c.Query("method")
|
|
||||||
callbackURL := c.Query("callback_url")
|
|
||||||
withParams := c.Query("with_params")
|
|
||||||
enabled, err := db.GetSettingItemByKey("github_login_enabled")
|
|
||||||
clientId, err := db.GetSettingItemByKey("github_client_id")
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 400)
|
|
||||||
return
|
|
||||||
} else if enabled.Value == "true" {
|
|
||||||
urlValues := url.Values{}
|
|
||||||
urlValues.Add("client_id", clientId.Value)
|
|
||||||
if method == "get_github_id" {
|
|
||||||
urlValues.Add("allow_signup", "true")
|
|
||||||
} else if method == "github_callback_login" {
|
|
||||||
urlValues.Add("allow_signup", "false")
|
|
||||||
}
|
|
||||||
if method == "" {
|
|
||||||
common.ErrorStrResp(c, "no method provided", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if withParams != "" {
|
|
||||||
urlValues.Add("redirect_uri", common.GetApiUrl(c.Request)+"/api/auth/github_callback"+"?method="+method+"&callback_url="+callbackURL+"&with_params="+withParams)
|
|
||||||
} else {
|
|
||||||
urlValues.Add("redirect_uri", common.GetApiUrl(c.Request)+"/api/auth/github_callback"+"?method="+method+"&callback_url="+callbackURL)
|
|
||||||
}
|
|
||||||
c.Redirect(302, "https://github.com/login/oauth/authorize?"+urlValues.Encode())
|
|
||||||
} else {
|
|
||||||
common.ErrorStrResp(c, "github Login not enabled", 403)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var githubClient = resty.New().SetRetryCount(3)
|
|
||||||
|
|
||||||
func GithubLoginCallback(c *gin.Context) {
|
|
||||||
argument := c.Query("method")
|
|
||||||
callbackUrl := c.Query("callback_url")
|
|
||||||
if argument == "get_github_id" || argument == "github_login" {
|
|
||||||
enabled, err := db.GetSettingItemByKey("github_login_enabled")
|
|
||||||
clientId, err := db.GetSettingItemByKey("github_client_id")
|
|
||||||
clientSecret, err := db.GetSettingItemByKey("github_client_secrets")
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 400)
|
|
||||||
return
|
|
||||||
} else if enabled.Value == "true" {
|
|
||||||
callbackCode := c.Query("code")
|
|
||||||
if callbackCode == "" {
|
|
||||||
common.ErrorStrResp(c, "No code provided", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
resp, err := githubClient.R().SetHeader("content-type", "application/json").
|
|
||||||
SetBody(map[string]string{
|
|
||||||
"client_id": clientId.Value,
|
|
||||||
"client_secret": clientSecret.Value,
|
|
||||||
"code": callbackCode,
|
|
||||||
"redirect_uri": common.GetApiUrl(c.Request) + "/api/auth/github_callback",
|
|
||||||
}).Post("https://github.com/login/oauth/access_token")
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
accessToken := utils.Json.Get(resp.Body(), "access_token").ToString()
|
|
||||||
resp, err = githubClient.R().SetHeader("Authorization", "Bearer "+accessToken).
|
|
||||||
Get("https://api.github.com/user")
|
|
||||||
ghUserID := utils.Json.Get(resp.Body(), "id").ToInt()
|
|
||||||
if argument == "get_github_id" {
|
|
||||||
c.Redirect(302, callbackUrl+"?githubID="+strconv.Itoa(ghUserID))
|
|
||||||
}
|
|
||||||
if argument == "github_login" {
|
|
||||||
user, err := db.GetUserByGithubID(ghUserID)
|
|
||||||
if err != nil {
|
|
||||||
common.ErrorResp(c, err, 400)
|
|
||||||
}
|
|
||||||
token, err := common.GenerateToken(user.Username)
|
|
||||||
withParams := c.Query("with_params")
|
|
||||||
if withParams == "true" {
|
|
||||||
c.Redirect(302, callbackUrl+"&token="+token)
|
|
||||||
} else if withParams == "false" {
|
|
||||||
c.Redirect(302, callbackUrl+"?token="+token)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
common.ErrorResp(c, errors.New("invalid request"), 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type SetQbittorrentReq struct {
|
type SetQbittorrentReq struct {
|
||||||
Url string `json:"url" form:"url"`
|
Url string `json:"url" form:"url"`
|
||||||
|
Seedtime string `json:"seedtime" form:"seedtime"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetQbittorrent(c *gin.Context) {
|
func SetQbittorrent(c *gin.Context) {
|
||||||
@ -20,7 +21,8 @@ func SetQbittorrent(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
items := []model.SettingItem{
|
items := []model.SettingItem{
|
||||||
{Key: conf.QbittorrentUrl, Value: req.Url, Type: conf.TypeString, Group: model.QBITTORRENT, Flag: model.PRIVATE},
|
{Key: conf.QbittorrentUrl, Value: req.Url, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||||
|
{Key: conf.QbittorrentSeedtime, Value: req.Seedtime, Type: conf.TypeNumber, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||||
}
|
}
|
||||||
if err := op.SaveSettingItems(items); err != nil {
|
if err := op.SaveSettingItems(items); err != nil {
|
||||||
common.ErrorResp(c, err, 500)
|
common.ErrorResp(c, err, 500)
|
||||||
|
181
server/handles/ssologin.go
Normal file
181
server/handles/ssologin.go
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package handles
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/internal/conf"
|
||||||
|
"github.com/alist-org/alist/v3/internal/db"
|
||||||
|
"github.com/alist-org/alist/v3/internal/setting"
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
"github.com/alist-org/alist/v3/server/common"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/go-resty/resty/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func SSOLoginRedirect(c *gin.Context) {
|
||||||
|
method := c.Query("method")
|
||||||
|
enabled := setting.GetBool(conf.SSOLoginEnabled)
|
||||||
|
clientId := setting.GetStr(conf.SSOClientId)
|
||||||
|
platform := setting.GetStr(conf.SSOLoginplatform)
|
||||||
|
var r_url string
|
||||||
|
var redirect_uri string
|
||||||
|
if enabled {
|
||||||
|
urlValues := url.Values{}
|
||||||
|
if method == "" {
|
||||||
|
common.ErrorStrResp(c, "no method provided", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
redirect_uri = common.GetApiUrl(c.Request) + "/api/auth/sso_callback" + "?method=" + method
|
||||||
|
urlValues.Add("response_type", "code")
|
||||||
|
urlValues.Add("redirect_uri", redirect_uri)
|
||||||
|
urlValues.Add("client_id", clientId)
|
||||||
|
switch platform {
|
||||||
|
case "Github":
|
||||||
|
r_url = "https://github.com/login/oauth/authorize?"
|
||||||
|
urlValues.Add("scope", "read:user")
|
||||||
|
case "Microsoft":
|
||||||
|
r_url = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?"
|
||||||
|
urlValues.Add("scope", "user.read")
|
||||||
|
urlValues.Add("response_mode", "query")
|
||||||
|
case "Google":
|
||||||
|
r_url = "https://accounts.google.com/o/oauth2/v2/auth?"
|
||||||
|
urlValues.Add("scope", "https://www.googleapis.com/auth/userinfo.profile")
|
||||||
|
case "Dingtalk":
|
||||||
|
r_url = "https://login.dingtalk.com/oauth2/auth?"
|
||||||
|
urlValues.Add("scope", "openid")
|
||||||
|
urlValues.Add("prompt", "consent")
|
||||||
|
urlValues.Add("response_type", "code")
|
||||||
|
default:
|
||||||
|
common.ErrorStrResp(c, "invalid platform", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.Redirect(302, r_url+urlValues.Encode())
|
||||||
|
} else {
|
||||||
|
common.ErrorStrResp(c, "Single sign-on is not enabled", 403)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ssoClient = resty.New().SetRetryCount(3)
|
||||||
|
|
||||||
|
func SSOLoginCallback(c *gin.Context) {
|
||||||
|
argument := c.Query("method")
|
||||||
|
if argument == "get_sso_id" || argument == "sso_get_token" {
|
||||||
|
enabled := setting.GetBool(conf.SSOLoginEnabled)
|
||||||
|
clientId := setting.GetStr(conf.SSOClientId)
|
||||||
|
platform := setting.GetStr(conf.SSOLoginplatform)
|
||||||
|
clientSecret := setting.GetStr(conf.SSOClientSecret)
|
||||||
|
var url1, url2, additionalbody, scope, authstring, idstring string
|
||||||
|
switch platform {
|
||||||
|
case "Github":
|
||||||
|
url1 = "https://github.com/login/oauth/access_token"
|
||||||
|
url2 = "https://api.github.com/user"
|
||||||
|
additionalbody = ""
|
||||||
|
authstring = "code"
|
||||||
|
scope = "read:user"
|
||||||
|
idstring = "id"
|
||||||
|
case "Microsoft":
|
||||||
|
url1 = "https://login.microsoftonline.com/common/oauth2/v2.0/token"
|
||||||
|
url2 = "https://graph.microsoft.com/v1.0/me"
|
||||||
|
additionalbody = "&grant_type=authorization_code"
|
||||||
|
scope = "user.read"
|
||||||
|
authstring = "code"
|
||||||
|
idstring = "id"
|
||||||
|
case "Google":
|
||||||
|
url1 = "https://oauth2.googleapis.com/token"
|
||||||
|
url2 = "https://www.googleapis.com/oauth2/v1/userinfo"
|
||||||
|
additionalbody = "&grant_type=authorization_code"
|
||||||
|
scope = "https://www.googleapis.com/auth/userinfo.profile"
|
||||||
|
authstring = "code"
|
||||||
|
idstring = "id"
|
||||||
|
case "Dingtalk":
|
||||||
|
url1 = "https://api.dingtalk.com/v1.0/oauth2/userAccessToken"
|
||||||
|
url2 = "https://api.dingtalk.com/v1.0/contact/users/me"
|
||||||
|
authstring = "authCode"
|
||||||
|
idstring = "unionId"
|
||||||
|
default:
|
||||||
|
common.ErrorStrResp(c, "invalid platform", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if enabled {
|
||||||
|
callbackCode := c.Query(authstring)
|
||||||
|
if callbackCode == "" {
|
||||||
|
common.ErrorStrResp(c, "No code provided", 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var resp *resty.Response
|
||||||
|
var err error
|
||||||
|
if platform == "Dingtalk" {
|
||||||
|
resp, err = ssoClient.R().SetHeader("content-type", "application/json").SetHeader("Accept", "application/json").
|
||||||
|
SetBody(map[string]string{
|
||||||
|
"clientId": clientId,
|
||||||
|
"clientSecret": clientSecret,
|
||||||
|
"code": callbackCode,
|
||||||
|
"grantType": "authorization_code",
|
||||||
|
}).
|
||||||
|
Post(url1)
|
||||||
|
} else {
|
||||||
|
resp, err = ssoClient.R().SetHeader("content-type", "application/x-www-form-urlencoded").SetHeader("Accept", "application/json").
|
||||||
|
SetBody("client_id=" + clientId + "&client_secret=" + clientSecret + "&code=" + callbackCode + "&redirect_uri=" + common.GetApiUrl(c.Request) + "/api/auth/sso_callback?method=" + argument + "&scope=" + scope + additionalbody).
|
||||||
|
Post(url1)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if platform == "Dingtalk" {
|
||||||
|
accessToken := utils.Json.Get(resp.Body(), "accessToken").ToString()
|
||||||
|
resp, err = ssoClient.R().SetHeader("x-acs-dingtalk-access-token", accessToken).
|
||||||
|
Get(url2)
|
||||||
|
} else {
|
||||||
|
accessToken := utils.Json.Get(resp.Body(), "access_token").ToString()
|
||||||
|
resp, err = ssoClient.R().SetHeader("Authorization", "Bearer "+accessToken).
|
||||||
|
Get(url2)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
UserID := utils.Json.Get(resp.Body(), idstring).ToString()
|
||||||
|
if UserID == "0" {
|
||||||
|
common.ErrorResp(c, errors.New("error occured"), 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if argument == "get_sso_id" {
|
||||||
|
html := fmt.Sprintf(`<!DOCTYPE html>
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
window.opener.postMessage({"sso_id": "%s"}, "*")
|
||||||
|
window.close()
|
||||||
|
</script>
|
||||||
|
</body>`, UserID)
|
||||||
|
c.Data(200, "text/html; charset=utf-8", []byte(html))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if argument == "sso_get_token" {
|
||||||
|
user, err := db.GetUserBySSOID(UserID)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
}
|
||||||
|
token, err := common.GenerateToken(user.Username)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
}
|
||||||
|
html := fmt.Sprintf(`<!DOCTYPE html>
|
||||||
|
<head></head>
|
||||||
|
<body>
|
||||||
|
<script>
|
||||||
|
window.opener.postMessage({"token":"%s"}, "*")
|
||||||
|
window.close()
|
||||||
|
</script>
|
||||||
|
</body>`, token)
|
||||||
|
c.Data(200, "text/html; charset=utf-8", []byte(html))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
common.ErrorResp(c, errors.New("invalid request"), 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -92,10 +92,27 @@ func taskRoute[K comparable](g *gin.RouterGroup, manager *task.Manager[K], k2Str
|
|||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
g.POST("/retry", func(c *gin.Context) {
|
||||||
|
tid := c.Query("tid")
|
||||||
|
id, err := str2K(tid)
|
||||||
|
if err != nil {
|
||||||
|
common.ErrorResp(c, err, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := manager.Retry(id); err != nil {
|
||||||
|
common.ErrorResp(c, err, 500)
|
||||||
|
} else {
|
||||||
|
common.SuccessResp(c)
|
||||||
|
}
|
||||||
|
})
|
||||||
g.POST("/clear_done", func(c *gin.Context) {
|
g.POST("/clear_done", func(c *gin.Context) {
|
||||||
manager.ClearDone()
|
manager.ClearDone()
|
||||||
common.SuccessResp(c)
|
common.SuccessResp(c)
|
||||||
})
|
})
|
||||||
|
g.POST("/clear_succeeded", func(c *gin.Context) {
|
||||||
|
manager.ClearSucceeded()
|
||||||
|
common.SuccessResp(c)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupTaskRoute(g *gin.RouterGroup) {
|
func SetupTaskRoute(g *gin.RouterGroup) {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user