Compare commits

...

85 Commits

Author SHA1 Message Date
d988f98b81 💚 fix upx 2022-02-26 00:12:00 +08:00
10634c7b77 👷 add build for macos 2022-02-25 23:59:27 +08:00
135d505192 back to cgo sqlite3 2022-02-25 23:55:57 +08:00
3f2be8a6ca 🔧 change default assets path 2022-02-25 22:08:12 +08:00
79bef09ee7 🔀 Merge branch 'feature/upyun' into dev 2022-02-25 21:06:49 +08:00
3534f6afac 💚 fix cal md5 2022-02-24 23:07:35 +08:00
106c1d069c 💚 change build platform 2022-02-24 22:55:50 +08:00
8ed0afe80d 🎇 add unupx version 2022-02-24 22:42:15 +08:00
6a6e3944d5 🔀 Merge branch 'feature/purego' into dev 2022-02-24 16:26:20 +08:00
94d5b5e47e direct but proxy types 2022-02-24 16:25:17 +08:00
e61b0f8e34 🔥 remove cgo to pure go 2022-02-24 16:08:49 +08:00
f7fbe1de6c 👷 build for all branch 2022-02-23 20:17:50 +08:00
01de01630e 🔥 remove placeholder for uss 2022-02-23 20:16:57 +08:00
f9f92e2198 ✏️ fix typo 2022-02-23 20:13:52 +08:00
7d5f50b04a 👷 build for all branch 2022-02-23 20:12:21 +08:00
72b5d25e4c ✏️ fix label typo 2022-02-23 20:07:45 +08:00
cae7f36531 🎇 refresh one folder 2022-02-23 19:16:33 +08:00
aa79f49e25 🐛 fix #600 aliyundrive move file 2022-02-23 14:56:17 +08:00
b4ad301d53 🐛 fix #599 lanzou url without password 2022-02-23 11:18:51 +08:00
00ed54c4c9 upyun uss support 2022-02-23 11:07:19 +08:00
ffa52794db 🍺 change login http method 2022-02-22 15:57:39 +08:00
24058d0c36 💚 fix dev build 2022-02-21 21:47:40 +08:00
641ca67671 💚 fix web replace 2022-02-21 20:31:01 +08:00
52ee2e0a8b 🐛 close #581 teambition update time error 2022-02-21 17:20:05 +08:00
724fc7f37e 💚 fix build web 2022-02-20 16:46:47 +08:00
9d279b104b dynamic public path 2022-02-20 15:14:18 +08:00
eb61f70164 🐛 fix show balance account 2022-02-20 13:06:59 +08:00
b3a8201768 🐛 fix that only two accounts can be load balanced 2022-02-19 21:49:37 +08:00
185795954b 🔊 add reason of failed to auto migrate model 2022-02-19 17:25:50 +08:00
cc62cc99d2 🐛 fix plist ipa name 2022-02-18 19:01:30 +08:00
270349f37c 🐛 fix write status sequence 2022-02-18 18:58:02 +08:00
977888070a 🐛 close #558 fix local file can't download 2022-02-18 18:50:01 +08:00
192d0f2bf3 🐛 fix update can't start 2022-02-18 18:49:40 +08:00
f2ec7884ec 🐛 fix only proxy webdav_direct 2022-02-17 17:49:03 +08:00
815975a4d2 🐛 fix 139 delete dir 2022-02-17 17:37:45 +08:00
f96a0238fc 👷 change issues-month-statistics 2022-02-17 17:29:45 +08:00
f695bd0959 🔧 change bundle-version 2022-02-17 16:54:34 +08:00
efe8f46e17 ✏️ fix typo 2022-02-17 11:56:00 +08:00
515daa22a9 🐛 fix #551 add S3ForcePathStyle config 2022-02-17 11:30:34 +08:00
f11e22deaf 🚧 change base64 characters 2022-02-17 09:14:21 +08:00
5be976169f 🚧 fix bundle-identifier 2022-02-17 00:44:30 +08:00
a6e08f3bf4 📝 update readme 2022-02-17 00:08:37 +08:00
944e68a979 🚧 plist generate 2022-02-17 00:06:10 +08:00
b3a6e33ce1 🎇 quark support 2022-02-16 20:20:39 +08:00
cb53ddc8e8 🐛 fix nil pointer 2022-02-16 16:10:39 +08:00
693417be4f 🔧 add hide files setting item 2022-02-15 14:51:50 +08:00
5c3f91bb55 support empty password 2022-02-15 14:42:24 +08:00
8a219d0732 👷 add issue bot 2022-02-14 20:03:42 +08:00
146a544af3 ✏️ fix typo 2022-02-14 19:43:43 +08:00
48dccc6c0b 📝 update readme 2022-02-14 15:41:20 +08:00
ce1740cec4 ✏️ fix typo 2022-02-14 15:36:21 +08:00
d40dbeae3e 💬 add issue template 2022-02-14 15:33:48 +08:00
5094b673c4 👷 add some issue bot 2022-02-14 15:32:25 +08:00
228e6d10e7 🔧 close #519 customize temp dir 2022-02-14 15:06:57 +08:00
e90b979d15 close #535 request set timeout 2022-02-14 14:59:00 +08:00
fb05a6ca48 🐛 fix #533 only encode fileName 2022-02-14 14:31:09 +08:00
e055ed3afa 🎇 lanzou proxy add user-agent 2022-02-13 17:46:07 +08:00
4371c470b3 webdav direct proxy 2022-02-13 15:57:42 +08:00
7bb237d0ef 🐛 fix #527 189 upload file name contains % 2022-02-13 13:03:34 +08:00
5c42354b01 🐛 fix #527 189 upload name contains + 2022-02-12 20:27:38 +08:00
387e8af422 🐛 fix 189 upload while filename contains & 2022-02-12 13:08:46 +08:00
5dca777caf 🔒 fix baidu direct link 2022-02-12 12:04:10 +08:00
0814778a14 🐛 fix no account error while only one 2022-02-11 17:11:02 +08:00
6827af3997 🎇 close #522 hide account for guest webdav 2022-02-11 16:19:55 +08:00
435bdea8f7 🔧 #523 add some default setting 2022-02-11 16:17:07 +08:00
4f81735af6 🎇 close #512 favicon redirect 2022-02-08 18:07:13 +08:00
bef3d2f88d 🐛 Fix the temp folder is not created at the first startup 2022-02-08 16:22:53 +08:00
ba99c7dc03 🐛 fix multiple accounts with the same prefix cannot be load balanced 2022-02-08 16:02:47 +08:00
f5c5162a9b load balance 2022-02-08 15:51:58 +08:00
a22903533e 🐛 set random seed 2022-02-04 16:08:15 +08:00
86cda58b22 🚧 echo password 2022-02-04 14:58:48 +08:00
7804cf9d5c 🔒 random webdav admin password 2022-02-04 14:39:11 +08:00
2bb7036110 🔧 change logo 2022-02-03 21:28:13 +08:00
ba545555cf 📝 update readme 2022-02-03 21:00:08 +08:00
be55ca690c 🔧 change default logo 2022-02-03 20:43:33 +08:00
9013add749 🔒 random initial password 2022-02-03 12:27:50 +08:00
3201b6da76 🐛 fix #376 windows webdav upload 2022-02-02 18:48:34 +08:00
feb42f1f4b 🔥 delete proxy interface 2022-02-02 18:03:54 +08:00
6f14d0eb5c 🎇 baidu disk support 2022-02-02 17:32:11 +08:00
7530d8f5b2 🐛 fix lanzou for download change 2022-02-01 22:33:19 +08:00
e25fe05a53 yandex disk support 2022-02-01 17:15:11 +08:00
8e0ab8f780 🔥 remove onedrive refresh token cron 2022-02-01 15:20:17 +08:00
cb2a3c2b42 🎨 change proxy interface 2022-02-01 14:28:21 +08:00
1b6ec94f33 🐛 fix s3 custom host 2022-02-01 11:34:32 +08:00
cb23edc1fe 🐛 fix #462 check connect while get ftp client 2022-01-31 11:13:29 +08:00
80 changed files with 3100 additions and 419 deletions

View File

@ -5,7 +5,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: | value: |
Thanks for taking the time to fill out this bug report! Thanks for taking the time to fill out this bug report, please confirm that your issue is not a duplicate issue
- type: input - type: input
id: version id: version
attributes: attributes:

View File

@ -1,5 +1,5 @@
blank_issues_enabled: false blank_issues_enabled: false
contact_links: contact_links:
- name: Questions & Discussions & Feature request - name: Questions & Discussions
url: https://github.com/Xhofe/alist/discussions url: https://github.com/Xhofe/alist/discussions
about: Use GitHub discussions for message-board style questions and discussions or feature request. about: Use GitHub discussions for message-board style questions and discussions.

View File

@ -0,0 +1,24 @@
name: "Feature request"
description: Feature request
labels: ["enhancement: pending triage"]
body:
- type: textarea
id: feature-description
attributes:
label: Description of the feature / 需求描述
validations:
required: true
- type: textarea
id: suggested-solution
attributes:
label: Suggested solution / 实现思路
description: |
Solutions to achieve this requirement.
实现此需求的解决思路。
- type: textarea
id: additional-context
attributes:
label: Additional context / 附件
description: |
Any other context or screenshots about the feature request here, or information you find helpful.
相关的任何其他上下文或截图,或者你觉得有帮助的信息

View File

@ -2,9 +2,9 @@ name: build
on: on:
push: push:
branches: [ v2 ] branches: [ '**' ]
pull_request: pull_request:
branches: [ v2 ] branches: [ '**' ]
jobs: jobs:
build: build:
@ -28,10 +28,9 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
ref: v2
path: alist path: alist
- name: Set up xgo - name: Install upx
run: | run: |
docker pull techknowlogick/xgo:latest docker pull techknowlogick/xgo:latest
go install src.techknowlogick.com/xgo@latest go install src.techknowlogick.com/xgo@latest

View File

@ -0,0 +1,20 @@
name: Check need info
on:
schedule:
- cron: "0 0 */7 * *"
jobs:
check-need-info:
runs-on: ubuntu-latest
steps:
- name: close-issues
uses: actions-cool/issues-helper@v2
with:
actions: 'close-issues'
token: ${{ secrets.GITHUB_TOKEN }}
labels: 'question'
inactive-day: 7
body: |
Hello @${{ github.event.issue.user.login }}, this issue was closed due to no activities in 7 days.
你好 @${{ github.event.issue.user.login }}此issue因超过7天未回复被关闭。

25
.github/workflows/issue_duplicate.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Issue Duplicate
on:
issues:
types: [labeled]
jobs:
create-comment:
runs-on: ubuntu-latest
if: github.event.label.name == 'duplicate'
steps:
- name: Create comment
uses: actions-cool/issues-helper@v2
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}, your issue is a duplicate and will be closed.
你好 @${{ github.event.issue.user.login }}你的issue是重复的将被关闭。
- name: Close issue
uses: actions-cool/issues-helper@v2
with:
actions: 'close-issue'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,16 @@
name: Issue Month Statistics
on:
schedule:
- cron: "0 1 1 * *"
jobs:
month-statistics:
runs-on: ubuntu-latest
steps:
- name: month-statistics
uses: actions-cool/issues-month-statistics@v1
with:
count-lables: true
count-comments: true
emoji: 'eyes'

20
.github/workflows/issue_question.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Issue Question
on:
issues:
types: [labeled]
jobs:
create-comment:
runs-on: ubuntu-latest
if: github.event.label.name == 'question'
steps:
- name: Create comment
uses: actions-cool/issues-helper@v2.0.0
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}, please input issue by template and add detail. Issues labeled by `question` will be closed if no activities in 7 days.
你好 @${{ github.event.issue.user.login }}请按照issue模板填写, 并详细说明问题/复现步骤/实现思路或提供更多信息等, 7天内未回复issue自动关闭。

25
.github/workflows/issue_wontfix.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: Issue Wontfix
on:
issues:
types: [labeled]
jobs:
lock-issue:
runs-on: ubuntu-latest
if: github.event.label.name == 'wontfix'
steps:
- name: Create comment
uses: actions-cool/issues-helper@v2
with:
actions: 'create-comment'
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
body: |
Hello @${{ github.event.issue.user.login }}, this issue will not be worked on and will be closed.
你好 @${{ github.event.issue.user.login }},这不会被处理,将被关闭。
- name: Close issue
uses: actions-cool/issues-helper@v2
with:
actions: 'close-issue'
token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -27,7 +27,7 @@ jobs:
persist-credentials: false persist-credentials: false
fetch-depth: 0 fetch-depth: 0
- name: Set up xgo - name: Install upx
run: | run: |
docker pull techknowlogick/xgo:latest docker pull techknowlogick/xgo:latest
go install src.techknowlogick.com/xgo@latest go install src.techknowlogick.com/xgo@latest

View File

@ -1,5 +1,5 @@
<div align="center"> <div align="center">
<h3><a href="https://alist.nn.ci">Alist</a></h3> <a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/assets@main/logo.svg"/></a>
<p><em>🗂Another file list program that supports multiple storage, powered by Gin and React.</em></p> <p><em>🗂Another file list program that supports multiple storage, powered by Gin and React.</em></p>
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a> <a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a> <a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
@ -12,6 +12,7 @@
</div> </div>
--- ---
English | [中文](./README_cn.md) English | [中文](./README_cn.md)
@ -35,6 +36,9 @@ English | [中文](./README_cn.md)
- [x] Teambition([China](https://www.teambition.com/ ),[International](https://us.teambition.com/ )) - [x] Teambition([China](https://www.teambition.com/ ),[International](https://us.teambition.com/ ))
- [x] [Mediatrack](https://www.mediatrack.cn/) - [x] [Mediatrack](https://www.mediatrack.cn/)
- [x] [139yun](https://yun.139.com/) (Personal, Family) - [x] [139yun](https://yun.139.com/) (Personal, Family)
- [x] [Yandex.Disk](https://disk.yandex.com/)
- [x] [Baidu Disk](http://pan.baidu.com/)
- [x] [Quark](https://pan.quark.cn)
- [x] Easy to deploy and out-of-the-box - [x] Easy to deploy and out-of-the-box
- [x] File preview (PDF, markdown, code, plain text, ...) - [x] File preview (PDF, markdown, code, plain text, ...)
- [x] Image preview in gallery mode - [x] Image preview in gallery mode
@ -54,7 +58,7 @@ English | [中文](./README_cn.md)
## Discussion ## Discussion
Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports only.** Please go to our [discussion forum](https://github.com/Xhofe/alist/discussions) for general questions, **issues are for bug reports and feature request only.**
## Demo ## Demo

View File

@ -1,5 +1,5 @@
<div align="center"> <div align="center">
<h3><a href="https://alist.nn.ci">Alist</a></h3> <a href="https://alist.nn.ci"><img height="100px" alt="logo" src="https://cdn.jsdelivr.net/gh/alist-org/assets@main/logo.svg"/></a>
<p><em>🗂️一个支持多存储的文件列表程序,使用 Gin 和 React 。</em></p> <p><em>🗂️一个支持多存储的文件列表程序,使用 Gin 和 React 。</em></p>
<a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a> <a href="https://github.com/Xhofe/alist/releases"><img src="https://img.shields.io/github/release/Xhofe/alist?style=flat-square" alt="latest version"></a>
<a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a> <a href="https://github.com/Xhofe/alist/discussions"><img src="https://img.shields.io/github/discussions/Xhofe/alist?color=%23ED8936&style=flat-square" alt="discussions"></a>
@ -11,6 +11,7 @@
</a> </a>
</div> </div>
--- ---
[English](./README.md) | 中文 [English](./README.md) | 中文
@ -34,6 +35,9 @@
- [x] Teambition[中国](https://www.teambition.com/ )[国际](https://us.teambition.com/ ) - [x] Teambition[中国](https://www.teambition.com/ )[国际](https://us.teambition.com/ )
- [x] [分秒帧](https://www.mediatrack.cn/) - [x] [分秒帧](https://www.mediatrack.cn/)
- [x] [和彩云](https://yun.139.com/) (个人云, 家庭云) - [x] [和彩云](https://yun.139.com/) (个人云, 家庭云)
- [x] [Yandex.Disk](https://disk.yandex.com/)
- [x] [百度网盘](http://pan.baidu.com/)
- [x] [夸克网盘](https://pan.quark.cn)
- [x] 部署方便,开箱即用 - [x] 部署方便,开箱即用
- [x] 文件预览PDF、markdown、代码、纯文本…… - [x] 文件预览PDF、markdown、代码、纯文本……
- [x] 画廊模式下的图像预览 - [x] 画廊模式下的图像预览
@ -53,7 +57,7 @@
## 讨论 ## 讨论
一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) **issue仅针对错误报告。** 一般问题请到[讨论论坛](https://github.com/Xhofe/alist/discussions) **issue仅针对错误报告和功能请求。**
## 演示 ## 演示

View File

@ -22,7 +22,7 @@ func Init() bool {
log.Errorf(err.Error()) log.Errorf(err.Error())
return false return false
} }
log.Infof("current password: %s", pass.Value) fmt.Printf("your password: %s\n", pass.Value)
return false return false
} }
server.InitIndex() server.InitIndex()
@ -34,7 +34,8 @@ func Init() bool {
func main() { func main() {
if conf.Version { if conf.Version {
fmt.Printf("Built At: %s\nGo Version: %s\nAuthor: %s\nCommit ID: %s\nVersion: %s\n", conf.BuiltAt, conf.GoVersion, conf.GitAuthor, conf.GitCommit, conf.GitTag) fmt.Printf("Built At: %s\nGo Version: %s\nAuthor: %s\nCommit ID: %s\nVersion: %s\nWebVersion: %s\n",
conf.BuiltAt, conf.GoVersion, conf.GitAuthor, conf.GitCommit, conf.GitTag, conf.WebTag)
return return
} }
if !Init() { if !Init() {

View File

@ -21,28 +21,28 @@ func InitConf() {
if !utils.WriteToJson(conf.ConfigFile, conf.Conf) { if !utils.WriteToJson(conf.ConfigFile, conf.Conf) {
log.Fatalf("failed to create default config file") log.Fatalf("failed to create default config file")
} }
return } else {
config, err := ioutil.ReadFile(conf.ConfigFile)
if err != nil {
log.Fatalf("reading config file error:%s", err.Error())
}
conf.Conf = conf.DefaultConfig()
err = utils.Json.Unmarshal(config, conf.Conf)
if err != nil {
log.Fatalf("load config error: %s", err.Error())
}
log.Debugf("config:%+v", conf.Conf)
// update config.json struct
confBody, err := utils.Json.MarshalIndent(conf.Conf, "", " ")
if err != nil {
log.Fatalf("marshal config error:%s", err.Error())
}
err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777)
if err != nil {
log.Fatalf("update config struct error: %s", err.Error())
}
} }
config, err := ioutil.ReadFile(conf.ConfigFile) err := os.MkdirAll(conf.Conf.TempDir, 0700)
if err != nil {
log.Fatalf("reading config file error:%s", err.Error())
}
conf.Conf = new(conf.Config)
err = utils.Json.Unmarshal(config, conf.Conf)
if err != nil {
log.Fatalf("load config error: %s", err.Error())
}
log.Debugf("config:%+v", conf.Conf)
// update config.json struct
confBody, err := utils.Json.MarshalIndent(conf.Conf, "", " ")
if err != nil {
log.Fatalf("marshal config error:%s", err.Error())
}
err = ioutil.WriteFile(conf.ConfigFile, confBody, 0777)
if err != nil {
log.Fatalf("update config struct error: %s", err.Error())
}
err = os.MkdirAll("data/temp", 0700)
if err != nil { if err != nil {
log.Fatalf("create temp dir error: %s", err.Error()) log.Fatalf("create temp dir error: %s", err.Error())
} }

View File

@ -12,6 +12,9 @@ func InitLog() {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
log.SetReportCaller(true) log.SetReportCaller(true)
} }
if conf.Password || conf.Version {
log.SetLevel(log.WarnLevel)
}
log.SetFormatter(&log.TextFormatter{ log.SetFormatter(&log.TextFormatter{
//DisableColors: true, //DisableColors: true,
ForceColors: true, ForceColors: true,
@ -29,4 +32,4 @@ func init() {
flag.BoolVar(&conf.Password, "password", false, "print current password") flag.BoolVar(&conf.Password, "password", false, "print current password")
flag.Parse() flag.Parse()
InitLog() InitLog()
} }

View File

@ -79,6 +79,6 @@ func InitModel() {
err = conf.DB.AutoMigrate(&model.SettingItem{}, &model.Account{}, &model.Meta{}) err = conf.DB.AutoMigrate(&model.SettingItem{}, &model.Account{}, &model.Meta{})
} }
if err != nil { if err != nil {
log.Fatalf("failed to auto migrate") log.Fatalf("failed to auto migrate: %s", err.Error())
} }
} }

View File

@ -3,6 +3,7 @@ package bootstrap
import ( import (
"github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"gorm.io/gorm" "gorm.io/gorm"
"strings" "strings"
@ -27,7 +28,7 @@ func InitSettings() {
}, },
{ {
Key: "password", Key: "password",
Value: "alist", Value: utils.RandomStr(8),
Description: "password", Description: "password",
Type: "string", Type: "string",
Access: model.PRIVATE, Access: model.PRIVATE,
@ -35,7 +36,7 @@ func InitSettings() {
}, },
{ {
Key: "logo", Key: "logo",
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png", Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/can_circle.svg",
Description: "logo", Description: "logo",
Type: "string", Type: "string",
Access: model.PUBLIC, Access: model.PUBLIC,
@ -43,7 +44,7 @@ func InitSettings() {
}, },
{ {
Key: "favicon", Key: "favicon",
Value: "https://store.heytapimage.com/cdo-portal/feedback/202112/05/1542f45f86b8609495b69c5380753135.png", Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg",
Description: "favicon", Description: "favicon",
Type: "string", Type: "string",
Access: model.PUBLIC, Access: model.PUBLIC,
@ -65,15 +66,23 @@ func InitSettings() {
Group: model.FRONT, Group: model.FRONT,
}, },
{ {
Key: "hide readme file", Key: "d_proxy types",
Value: "true", Value: strings.Join(conf.DProxyTypes, ","),
Type: "bool", Type: "string",
Description: "hide readme file? ", Description: "/d but proxy",
Access: model.PRIVATE,
Group: model.BACK,
},
{
Key: "hide files",
Value: "/\\/README.md/i",
Type: "text",
Description: "hide files, support RegExp, one per line",
Group: model.FRONT, Group: model.FRONT,
}, },
{ {
Key: "music cover", Key: "music cover",
Value: "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png", Value: "https://cdn.jsdelivr.net/gh/alist-org/logo@main/circle_center.svg",
Description: "music cover image", Description: "music cover image",
Type: "string", Type: "string",
Access: model.PUBLIC, Access: model.PUBLIC,
@ -157,7 +166,7 @@ func InitSettings() {
}, },
{ {
Key: "WebDAV username", Key: "WebDAV username",
Value: "alist_admin", Value: "admin",
Description: "WebDAV username", Description: "WebDAV username",
Type: "string", Type: "string",
Access: model.PRIVATE, Access: model.PRIVATE,
@ -165,7 +174,7 @@ func InitSettings() {
}, },
{ {
Key: "WebDAV password", Key: "WebDAV password",
Value: "alist_admin", Value: utils.RandomStr(8),
Description: "WebDAV password", Description: "WebDAV password",
Type: "string", Type: "string",
Access: model.PRIVATE, Access: model.PRIVATE,
@ -189,7 +198,7 @@ func InitSettings() {
}, },
{ {
Key: "Visitor WebDAV username", Key: "Visitor WebDAV username",
Value: "alist_visitor", Value: "guest",
Description: "Visitor WebDAV username", Description: "Visitor WebDAV username",
Type: "string", Type: "string",
Access: model.PRIVATE, Access: model.PRIVATE,
@ -197,7 +206,7 @@ func InitSettings() {
}, },
{ {
Key: "Visitor WebDAV password", Key: "Visitor WebDAV password",
Value: "alist_visitor", Value: "guest",
Description: "Visitor WebDAV password", Description: "Visitor WebDAV password",
Type: "string", Type: "string",
Access: model.PRIVATE, Access: model.PRIVATE,
@ -235,6 +244,9 @@ func InitSettings() {
if err != nil { if err != nil {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
err = model.SaveSetting(v) err = model.SaveSetting(v)
if v.Key == "password" {
log.Infof("Initial password: %s", v.Value)
}
if err != nil { if err != nil {
log.Fatalf("failed write setting: %s", err.Error()) log.Fatalf("failed write setting: %s", err.Error())
} }

View File

@ -25,6 +25,7 @@ BUILD_DOCKER() {
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
gitCommit=$(git log --pretty=format:"%h" -1) gitCommit=$(git log --pretty=format:"%h" -1)
gitTag=$(git describe --long --tags --dirty --always) gitTag=$(git describe --long --tags --dirty --always)
webTag=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
ldflags="\ ldflags="\
-w -s \ -w -s \
-X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \ -X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \
@ -32,6 +33,7 @@ BUILD_DOCKER() {
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \ -X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \ -X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \ -X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
-X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \
" "
go build -o ./bin/alist -ldflags="$ldflags" -tags=jsoniter alist.go go build -o ./bin/alist -ldflags="$ldflags" -tags=jsoniter alist.go
} }
@ -44,6 +46,7 @@ BUILD() {
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
gitCommit=$(git log --pretty=format:"%h" -1) gitCommit=$(git log --pretty=format:"%h" -1)
gitTag=$(git describe --long --tags --dirty --always) gitTag=$(git describe --long --tags --dirty --always)
webTag=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
echo "build version: $gitTag" echo "build version: $gitTag"
ldflags="\ ldflags="\
-w -s \ -w -s \
@ -52,18 +55,20 @@ BUILD() {
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \ -X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \ -X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \ -X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
-X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \
" "
if [ "$1" == "release" ]; then if [ "$1" == "release" ]; then
xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter . xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
else else
xgo -targets=linux/amd64,windows/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter . xgo -targets=linux/amd64,windows/amd64,darwin/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
fi fi
mkdir -p "build" mkdir -p "build"
mv alist-* build mv alist-* build
if [ "$1" != "release" ]; then if [ "$1" != "release" ]; then
cd build cd build
upx -9 ./* upx -9 ./alist-linux*
upx -9 ./alist-windows*
find . -type f -print0 | xargs -0 md5sum >md5.txt find . -type f -print0 | xargs -0 md5sum >md5.txt
cat md5.txt cat md5.txt
cd .. cd ..
@ -86,6 +91,7 @@ BUILD_MUSL() {
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD) gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
gitCommit=$(git log --pretty=format:"%h" -1) gitCommit=$(git log --pretty=format:"%h" -1)
gitTag=$(git describe --long --tags --dirty --always) gitTag=$(git describe --long --tags --dirty --always)
webTag=$(wget -qO- -t1 -T2 "https://api.github.com/repos/alist-org/alist-web/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g')
ldflags="\ ldflags="\
-w -s \ -w -s \
-X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \ -X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \
@ -93,6 +99,7 @@ BUILD_MUSL() {
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \ -X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \ -X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \ -X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
-X 'github.com/Xhofe/alist/conf.WebTag=$webTag' \
" "
OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-arm linux-musl-mips linux-musl-mips64 linux-musl-mips64le linux-musl-mipsle linux-musl-ppc64le linux-musl-s390x) OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-arm linux-musl-mips linux-musl-mips64 linux-musl-mips64le linux-musl-mipsle linux-musl-ppc64le linux-musl-s390x)
CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc arm-linux-musleabihf-gcc mips-linux-musl-gcc mips64-linux-musl-gcc mips64el-linux-musl-gcc mipsel-linux-musl-gcc powerpc64le-linux-musl-gcc s390x-linux-musl-gcc) CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc arm-linux-musleabihf-gcc mips-linux-musl-gcc mips64-linux-musl-gcc mips64el-linux-musl-gcc mipsel-linux-musl-gcc powerpc64le-linux-musl-gcc s390x-linux-musl-gcc)
@ -111,7 +118,8 @@ BUILD_MUSL() {
RELEASE() { RELEASE() {
cd alist/build cd alist/build
upx -9 ./* upx -9 ./alist-linux*
upx -9 ./alist-windows*
find . -type f -print0 | xargs -0 md5sum >md5.txt find . -type f -print0 | xargs -0 md5sum >md5.txt
cat md5.txt cat md5.txt
mkdir compress mkdir compress
@ -142,4 +150,4 @@ elif [ "$1" = "release" ]; then
RELEASE RELEASE
else else
echo -e "${RED_COLOR} Parameter error ${RES}" echo -e "${RED_COLOR} Parameter error ${RES}"
fi fi

View File

@ -30,13 +30,15 @@ type Config struct {
Database Database `json:"database"` Database Database `json:"database"`
Scheme Scheme `json:"scheme"` Scheme Scheme `json:"scheme"`
Cache CacheConfig `json:"cache"` Cache CacheConfig `json:"cache"`
TempDir string `json:"temp_dir"`
} }
func DefaultConfig() *Config { func DefaultConfig() *Config {
return &Config{ return &Config{
Address: "0.0.0.0", Address: "0.0.0.0",
Port: 5244, Port: 5244,
Assets: "jsdelivr", Assets: "https://npm.elemecdn.com/alist-web@$version/dist",
TempDir: "data/temp",
Database: Database{ Database: Database{
Type: "sqlite3", Type: "sqlite3",
Port: 0, Port: 0,

View File

@ -14,6 +14,7 @@ var (
GitAuthor string GitAuthor string
GitCommit string GitCommit string
GitTag string = "dev" GitTag string = "dev"
WebTag string
) )
var ( var (
@ -33,6 +34,7 @@ var (
TextTypes = []string{"txt", "htm", "html", "xml", "java", "properties", "sql", TextTypes = []string{"txt", "htm", "html", "xml", "java", "properties", "sql",
"js", "md", "json", "conf", "ini", "vue", "php", "py", "bat", "gitignore", "yml", "js", "md", "json", "conf", "ini", "vue", "php", "py", "bat", "gitignore", "yml",
"go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"} "go", "sh", "c", "cpp", "h", "hpp", "tsx", "vtt", "srt", "ass"}
DProxyTypes = []string{"m3u8"}
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"} OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv"} VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv"}
AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav"} AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav"}
@ -78,7 +80,7 @@ var (
"check parent folder", "check down link", "WebDAV username", "WebDAV password", "check parent folder", "check down link", "WebDAV username", "WebDAV password",
"Visitor WebDAV username", "Visitor WebDAV password", "Visitor WebDAV username", "Visitor WebDAV password",
"default page size", "load type", "default page size", "load type",
"ocr api", "ocr api", "favicon",
} }
) )

View File

@ -12,7 +12,6 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io" "io"
"io/ioutil" "io/ioutil"
@ -58,6 +57,7 @@ func (driver Pan123) Items() []base.Item {
Type: base.TypeSelect, Type: base.TypeSelect,
Values: "name,fileId,updateAt,createAt", Values: "name,fileId,updateAt,createAt",
Required: true, Required: true,
Default: "name",
}, },
{ {
Name: "order_direction", Name: "order_direction",
@ -65,6 +65,7 @@ func (driver Pan123) Items() []base.Item {
Type: base.TypeSelect, Type: base.TypeSelect,
Values: "asc,desc", Values: "asc,desc",
Required: true, Required: true,
Default: "asc",
}, },
} }
} }
@ -197,9 +198,9 @@ func (driver Pan123) Path(path string, account *model.Account) (*model.File, []m
return nil, files, nil return nil, files, nil
} }
func (driver Pan123) Proxy(c *gin.Context, account *model.Account) { //func (driver Pan123) Proxy(r *http.Request, account *model.Account) {
c.Request.Header.Del("origin") // r.Header.Del("origin")
} //}
func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) { func (driver Pan123) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport
@ -299,7 +300,7 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
return base.ErrNotFolder return base.ErrNotFolder
} }
parentFileId, _ := strconv.Atoi(parentFile.Id) parentFileId, _ := strconv.Atoi(parentFile.Id)
tempFile, err := ioutil.TempFile("data/temp", "file-*") tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
if err != nil { if err != nil {
return err return err
} }

View File

@ -17,7 +17,7 @@ import (
func (driver Cloud139) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) { func (driver Cloud139) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
url := "https://yun.139.com" + pathname url := "https://yun.139.com" + pathname
req := base.RestyClient.R() req := base.RestyClient.R()
randStr := randomStr(16) randStr := utils.RandomStr(16)
ts := time.Now().Format("2006-01-02 15:04:05") ts := time.Now().Format("2006-01-02 15:04:05")
log.Debugf("%+v", data) log.Debugf("%+v", data)
body, err := utils.Json.Marshal(data) body, err := utils.Json.Marshal(data)

View File

@ -7,7 +7,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io" "io"
"math" "math"
@ -163,9 +162,9 @@ func (driver Cloud139) Path(path string, account *model.Account) (*model.File, [
return nil, files, nil return nil, files, nil
} }
func (driver Cloud139) Proxy(c *gin.Context, account *model.Account) { //func (driver Cloud139) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver Cloud139) Preview(path string, account *model.Account) (interface{}, error) { func (driver Cloud139) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport
@ -335,7 +334,7 @@ func (driver Cloud139) Delete(path string, account *model.Account) error {
"taskInfo": base.Json{ "taskInfo": base.Json{
"newCatalogID": "", "newCatalogID": "",
"contentInfoList": contentInfoList, "contentInfoList": contentInfoList,
"catalogInfoList": contentInfoList, "catalogInfoList": catalogInfoList,
}, },
"commonAccountInfo": base.Json{ "commonAccountInfo": base.Json{
"account": account.Username, "account": account.Username,

View File

@ -5,7 +5,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"math/rand"
"net/url" "net/url"
"sort" "sort"
"strconv" "strconv"
@ -13,16 +12,6 @@ import (
"time" "time"
) )
func randomStr(n int) string {
builder := strings.Builder{}
t := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for i := 0; i < n; i++ {
r := rand.Intn(len(t))
builder.WriteString(t[r : r+1])
}
return builder.String()
}
func encodeURIComponent(str string) string { func encodeURIComponent(str string) string {
r := url.QueryEscape(str) r := url.QueryEscape(str)
r = strings.Replace(r, "+", "%20", -1) r = strings.Replace(r, "+", "%20", -1)

View File

@ -16,7 +16,6 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io" "io"
"math" "math"
mathRand "math/rand"
"net/http" "net/http"
"path/filepath" "path/filepath"
"regexp" "regexp"
@ -108,6 +107,7 @@ func (driver Cloud189) Login(account *model.Account) error {
//cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) //cookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
client = resty.New() client = resty.New()
//client.SetCookieJar(cookieJar) //client.SetCookieJar(cookieJar)
client.SetTimeout(base.DefaultTimeout)
client.SetRetryCount(3) client.SetRetryCount(3)
client.SetHeader("Referer", "https://cloud.189.cn/") client.SetHeader("Referer", "https://cloud.189.cn/")
} }
@ -462,7 +462,7 @@ func (driver Cloud189) UploadRequest(uri string, form map[string]string, account
c := strconv.FormatInt(time.Now().UnixMilli(), 10) c := strconv.FormatInt(time.Now().UnixMilli(), 10)
r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx") r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx") l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
l = l[0 : 16+int(16*mathRand.Float32())] l = l[0 : 16+int(16*utils.Rand.Float32())]
e := qs(form) e := qs(form)
data := AesEncrypt([]byte(e), []byte(l[0:16])) data := AesEncrypt([]byte(e), []byte(l[0:16]))
@ -525,7 +525,7 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
} }
res, err := driver.UploadRequest("/person/initMultiUpload", map[string]string{ res, err := driver.UploadRequest("/person/initMultiUpload", map[string]string{
"parentFolderId": parentFile.Id, "parentFolderId": parentFile.Id,
"fileName": file.Name, "fileName": encode(file.Name),
"fileSize": strconv.FormatInt(int64(file.Size), 10), "fileSize": strconv.FormatInt(int64(file.Size), 10),
"sliceSize": strconv.FormatInt(int64(DEFAULT), 10), "sliceSize": strconv.FormatInt(int64(DEFAULT), 10),
"lazyCheck": "1", "lazyCheck": "1",

View File

@ -6,7 +6,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
) )
@ -172,7 +171,12 @@ func (driver Cloud189) Link(args base.Args, account *model.Account) (*base.Link,
if err != nil { if err != nil {
return nil, err return nil, err
} }
link := base.Link{} link := base.Link{
Headers: []base.Header{
{Name: "User-Agent", Value: base.UserAgent},
{Name: "Authorization", Value: ""},
},
}
if res.StatusCode() == 302 { if res.StatusCode() == 302 {
link.Url = res.Header().Get("location") link.Url = res.Header().Get("location")
} else { } else {
@ -198,9 +202,9 @@ func (driver Cloud189) Path(path string, account *model.Account) (*model.File, [
return nil, files, nil return nil, files, nil
} }
func (driver Cloud189) Proxy(ctx *gin.Context, account *model.Account) { //func (driver Cloud189) Proxy(r *http.Request, account *model.Account) {
ctx.Request.Header.Del("Origin") // r.Header.Del("Origin")
} //}
func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) { func (driver Cloud189) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -14,18 +14,17 @@ import (
"encoding/pem" "encoding/pem"
"fmt" "fmt"
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
mathRand "math/rand"
"net/url" "net/url"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time"
) )
func random() string { func random() string {
return fmt.Sprintf("0.%17v", mathRand.New(mathRand.NewSource(time.Now().UnixNano())).Int63n(100000000000000000)) return fmt.Sprintf("0.%17v", utils.Rand.Int63n(100000000000000000))
} }
func RsaEncode(origData []byte, j_rsakey string, hex bool) string { func RsaEncode(origData []byte, j_rsakey string, hex bool) string {
@ -116,12 +115,24 @@ func EncodeParam(v url.Values) string {
} }
buf.WriteString(k) buf.WriteString(k)
buf.WriteByte('=') buf.WriteByte('=')
//if k == "fileName" {
// buf.WriteString(encode(v))
//} else {
buf.WriteString(v) buf.WriteString(v)
//}
} }
} }
return buf.String() return buf.String()
} }
func encode(str string) string {
//str = strings.ReplaceAll(str, "%", "%25")
//str = strings.ReplaceAll(str, "&", "%26")
//str = strings.ReplaceAll(str, "+", "%2B")
//return str
return url.QueryEscape(str)
}
func AesEncrypt(data, key []byte) []byte { func AesEncrypt(data, key []byte) []byte {
block, _ := aes.NewCipher(key) block, _ := aes.NewCipher(key)
if block == nil { if block == nil {
@ -169,7 +180,7 @@ func Random(v string) string {
reg := regexp.MustCompilePOSIX("[xy]") reg := regexp.MustCompilePOSIX("[xy]")
data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte { data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte {
var i int64 var i int64
t := int64(16 * mathRand.Float32()) t := int64(16 * utils.Rand.Float32())
if msg[0] == 120 { if msg[0] == 120 {
i = t i = t
} else { } else {

View File

@ -8,9 +8,9 @@ import (
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
"strings"
"time" "time"
) )
@ -190,7 +190,7 @@ func (driver AliDrive) rename(fileId, name string, account *model.Account) error
return fmt.Errorf("%+v", resp) return fmt.Errorf("%+v", resp)
} }
func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error { func (driver AliDrive) batch(srcId, dstId string, url string, account *model.Account) error {
var e AliRespError var e AliRespError
res, err := aliClient.R().SetError(&e). res, err := aliClient.R().SetError(&e).
SetHeader("authorization", "Bearer\t"+account.AccessToken). SetHeader("authorization", "Bearer\t"+account.AccessToken).
@ -208,6 +208,7 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
"to_drive_id": account.DriveId, "to_drive_id": account.DriveId,
"to_parent_file_id": dstId, "to_parent_file_id": dstId,
}, },
"url": url,
}, },
}, },
"resource": "file", "resource": "file",
@ -222,12 +223,13 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
return err return err
} else { } else {
_ = model.SaveAccount(account) _ = model.SaveAccount(account)
return driver.batch(srcId, dstId, account) return driver.batch(srcId, dstId, url, account)
} }
} }
return fmt.Errorf("%s", e.Message) return fmt.Errorf("%s", e.Message)
} }
if strings.Contains(res.String(), `"status":200`) { status := jsoniter.Get(res.Body(), "status").ToInt()
if status < 400 && status >= 100 {
return nil return nil
} }
return errors.New(res.String()) return errors.New(res.String())
@ -236,6 +238,7 @@ func (driver AliDrive) batch(srcId, dstId string, account *model.Account) error
func init() { func init() {
base.RegisterDriver(&AliDrive{}) base.RegisterDriver(&AliDrive{})
aliClient. aliClient.
SetTimeout(base.DefaultTimeout).
SetRetryCount(3). SetRetryCount(3).
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"). SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36").
SetHeader("content-type", "application/json"). SetHeader("content-type", "application/json").

View File

@ -8,7 +8,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3" "github.com/robfig/cron/v3"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io" "io"
@ -191,6 +190,12 @@ func (driver AliDrive) Link(args base.Args, account *model.Account) (*base.Link,
return nil, fmt.Errorf("%s", e.Message) return nil, fmt.Errorf("%s", e.Message)
} }
return &base.Link{ return &base.Link{
Headers: []base.Header{
{
Name: "Referer",
Value: "https://www.aliyundrive.com/",
},
},
Url: resp["url"].(string), Url: resp["url"].(string),
}, nil }, nil
} }
@ -212,10 +217,10 @@ func (driver AliDrive) Path(path string, account *model.Account) (*model.File, [
return nil, files, nil return nil, files, nil
} }
func (driver AliDrive) Proxy(c *gin.Context, account *model.Account) { //func (driver AliDrive) Proxy(r *http.Request, account *model.Account) {
c.Request.Header.Del("Origin") // r.Header.Del("Origin")
c.Request.Header.Set("Referer", "https://www.aliyundrive.com/") // r.Header.Set("Referer", "https://www.aliyundrive.com/")
} //}
func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) { func (driver AliDrive) Preview(path string, account *model.Account) (interface{}, error) {
file, err := driver.GetFile(path, account) file, err := driver.GetFile(path, account)
@ -304,7 +309,7 @@ func (driver AliDrive) Move(src string, dst string, account *model.Account) erro
if err != nil { if err != nil {
return err return err
} }
err = driver.batch(srcFile.Id, dstDirFile.Id, account) err = driver.batch(srcFile.Id, dstDirFile.Id, "/file/move", account)
return err return err
} }
@ -319,7 +324,17 @@ func (driver AliDrive) Rename(src string, dst string, account *model.Account) er
} }
func (driver AliDrive) Copy(src string, dst string, account *model.Account) error { func (driver AliDrive) Copy(src string, dst string, account *model.Account) error {
return base.ErrNotSupport dstDir, _ := filepath.Split(dst)
srcFile, err := driver.File(src, account)
if err != nil {
return err
}
dstDirFile, err := driver.File(dstDir, account)
if err != nil {
return err
}
err = driver.batch(srcFile.Id, dstDirFile.Id, "/file/copy", account)
return err
} }
func (driver AliDrive) Delete(path string, account *model.Account) error { func (driver AliDrive) Delete(path string, account *model.Account) error {

View File

@ -7,7 +7,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
@ -148,7 +147,7 @@ func (driver Alist) Path(path string, account *model.Account) (*model.File, []mo
return nil, resp.Data.Files, nil return nil, resp.Data.Files, nil
} }
func (driver Alist) Proxy(c *gin.Context, account *model.Account) {} //func (driver Alist) Proxy(r *http.Request, account *model.Account) {}
func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) { func (driver Alist) Preview(path string, account *model.Account) (interface{}, error) {
var resp PathResp var resp PathResp

View File

@ -6,6 +6,7 @@ import (
_ "github.com/Xhofe/alist/drivers/189" _ "github.com/Xhofe/alist/drivers/189"
_ "github.com/Xhofe/alist/drivers/alidrive" _ "github.com/Xhofe/alist/drivers/alidrive"
_ "github.com/Xhofe/alist/drivers/alist" _ "github.com/Xhofe/alist/drivers/alist"
_ "github.com/Xhofe/alist/drivers/baidu"
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
_ "github.com/Xhofe/alist/drivers/ftp" _ "github.com/Xhofe/alist/drivers/ftp"
_ "github.com/Xhofe/alist/drivers/google" _ "github.com/Xhofe/alist/drivers/google"
@ -14,10 +15,12 @@ import (
_ "github.com/Xhofe/alist/drivers/native" _ "github.com/Xhofe/alist/drivers/native"
_ "github.com/Xhofe/alist/drivers/onedrive" _ "github.com/Xhofe/alist/drivers/onedrive"
_ "github.com/Xhofe/alist/drivers/pikpak" _ "github.com/Xhofe/alist/drivers/pikpak"
_ "github.com/Xhofe/alist/drivers/quark"
_ "github.com/Xhofe/alist/drivers/s3" _ "github.com/Xhofe/alist/drivers/s3"
_ "github.com/Xhofe/alist/drivers/shandian" _ "github.com/Xhofe/alist/drivers/shandian"
_ "github.com/Xhofe/alist/drivers/teambition" _ "github.com/Xhofe/alist/drivers/teambition"
_ "github.com/Xhofe/alist/drivers/webdav" _ "github.com/Xhofe/alist/drivers/webdav"
_ "github.com/Xhofe/alist/drivers/yandex"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"strings" "strings"
) )

186
drivers/baidu/baidu.go Normal file
View File

@ -0,0 +1,186 @@
package baidu
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
jsoniter "github.com/json-iterator/go"
"path"
"strconv"
)
func (driver Baidu) RefreshToken(account *model.Account) error {
err := driver.refreshToken(account)
if err != nil && err == base.ErrEmptyToken {
err = driver.refreshToken(account)
}
if err != nil {
account.Status = err.Error()
}
_ = model.SaveAccount(account)
return err
}
func (driver Baidu) refreshToken(account *model.Account) error {
u := "https://openapi.baidu.com/oauth/2.0/token"
var resp base.TokenResp
var e TokenErrResp
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetQueryParams(map[string]string{
"grant_type": "refresh_token",
"refresh_token": account.RefreshToken,
"client_id": account.ClientId,
"client_secret": account.ClientSecret,
}).Get(u)
if err != nil {
return err
}
if e.Error != "" {
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
}
if resp.RefreshToken == "" {
return base.ErrEmptyToken
}
account.Status = "work"
account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken
return nil
}
func (driver Baidu) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
u := "https://pan.baidu.com/rest/2.0" + pathname
req := base.RestyClient.R()
req.SetQueryParam("access_token", account.AccessToken)
if headers != nil {
req.SetHeaders(headers)
}
if query != nil {
req.SetQueryParams(query)
}
if form != nil {
req.SetFormData(form)
}
if data != nil {
req.SetBody(data)
}
if resp != nil {
req.SetResult(resp)
}
var res *resty.Response
var err error
switch method {
case base.Get:
res, err = req.Get(u)
case base.Post:
res, err = req.Post(u)
case base.Patch:
res, err = req.Patch(u)
case base.Delete:
res, err = req.Delete(u)
case base.Put:
res, err = req.Put(u)
default:
return nil, base.ErrNotSupport
}
if err != nil {
return nil, err
}
//log.Debug(res.String())
errno := jsoniter.Get(res.Body(), "errno").ToInt()
if errno != 0 {
if errno == -6 {
err = driver.RefreshToken(account)
if err != nil {
return nil, err
}
return driver.Request(pathname, method, headers, query, form, data, resp, account)
}
return nil, fmt.Errorf("errno: %d, refer to https://pan.baidu.com/union/doc/", errno)
}
return res.Body(), nil
}
func (driver Baidu) Get(pathname string, params map[string]string, resp interface{}, account *model.Account) ([]byte, error) {
return driver.Request(pathname, base.Get, nil, params, nil, nil, resp, account)
}
func (driver Baidu) Post(pathname string, params map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
return driver.Request(pathname, base.Post, nil, params, nil, data, resp, account)
}
func (driver Baidu) manage(opera string, filelist interface{}, account *model.Account) ([]byte, error) {
params := map[string]string{
"method": "filemanager",
"opera": opera,
}
marshal, err := utils.Json.Marshal(filelist)
if err != nil {
return nil, err
}
data := fmt.Sprintf("async=0&filelist=%s&ondup=newcopy", string(marshal))
return driver.Post("/xpan/file", params, data, nil, account)
}
func (driver Baidu) GetFiles(dir string, account *model.Account) ([]model.File, error) {
dir = utils.Join(account.RootFolder, dir)
start := 0
limit := 200
params := map[string]string{
"method": "list",
"dir": dir,
"web": "web",
}
if account.OrderBy != "" {
params["order"] = account.OrderBy
if account.OrderDirection == "desc" {
params["desc"] = "1"
}
}
res := make([]model.File, 0)
for {
params["start"] = strconv.Itoa(start)
params["limit"] = strconv.Itoa(limit)
start += limit
var resp ListResp
_, err := driver.Get("/xpan/file", params, &resp, account)
if err != nil {
return nil, err
}
if len(resp.List) == 0 {
break
}
for _, f := range resp.List {
file := model.File{
Id: strconv.FormatInt(f.FsId, 10),
Name: f.ServerFilename,
Size: f.Size,
Driver: driver.Config().Name,
UpdatedAt: getTime(f.ServerMtime),
Thumbnail: f.Thumbs.Url3,
}
if f.Isdir == 1 {
file.Type = conf.FOLDER
} else {
file.Type = utils.GetFileType(path.Ext(f.ServerFilename))
}
res = append(res, file)
}
}
return res, nil
}
func (driver Baidu) create(path string, size uint64, isdir int, uploadid, block_list string, account *model.Account) ([]byte, error) {
params := map[string]string{
"method": "create",
}
data := fmt.Sprintf("path=%s&size=%d&isdir=%d", path, size, isdir)
if uploadid != "" {
data += fmt.Sprintf("&uploadid=%s&block_list=%s", uploadid, block_list)
}
return driver.Post("/xpan/file", params, data, nil, account)
}
func init() {
base.RegisterDriver(&Baidu{})
}

355
drivers/baidu/driver.go Normal file
View File

@ -0,0 +1,355 @@
package baidu
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
)
type Baidu struct{}
func (driver Baidu) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Baidu.Disk",
}
}
func (driver Baidu) Items() []base.Item {
return []base.Item{
{
Name: "refresh_token",
Label: "refresh token",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder path",
Type: base.TypeString,
Default: "/",
Required: true,
},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeSelect,
Default: "name",
Values: "name,time,size",
Required: false,
},
{
Name: "order_direction",
Label: "order_direction",
Type: base.TypeSelect,
Values: "asc,desc",
Default: "asc",
Required: false,
},
{
Name: "client_id",
Label: "client id",
Default: "iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v",
Type: base.TypeString,
Required: true,
},
{
Name: "client_secret",
Label: "client secret",
Default: "jXiFMOPVPCWlO2M5CwWQzffpNPaGTRBG",
Type: base.TypeString,
Required: true,
},
}
}
func (driver Baidu) Save(account *model.Account, old *model.Account) error {
return driver.RefreshToken(account)
}
func (driver Baidu) File(path string, account *model.Account) (*model.File, error) {
path = utils.ParsePath(path)
if path == "/" {
return &model.File{
Id: account.RootFolder,
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver Baidu) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
cache, err := base.GetCache(path, account)
if err == nil {
files, _ := cache.([]model.File)
return files, nil
}
files, err := driver.GetFiles(path, account)
if err != nil {
return nil, err
}
if len(files) > 0 {
_ = base.SetCache(path, files, account)
}
return files, nil
}
func (driver Baidu) Link(args base.Args, account *model.Account) (*base.Link, error) {
file, err := driver.File(args.Path, account)
if err != nil {
return nil, err
}
if file.IsDir() {
return nil, base.ErrNotFile
}
var resp DownloadResp
params := map[string]string{
"method": "filemetas",
"fsids": fmt.Sprintf("[%s]", file.Id),
"dlink": "1",
}
_, err = driver.Get("/xpan/multimedia", params, &resp, account)
if err != nil {
return nil, err
}
u := fmt.Sprintf("%s&access_token=%s", resp.List[0].Dlink, account.AccessToken)
res, err := base.NoRedirectClient.R().SetHeader("User-Agent", "pan.baidu.com").Head(u)
if err != nil {
return nil, err
}
//if res.StatusCode() == 302 {
u = res.Header().Get("location")
//}
return &base.Link{
Url: u,
Headers: []base.Header{
{Name: "User-Agent", Value: "pan.baidu.com"},
}}, nil
}
func (driver Baidu) Path(path string, account *model.Account) (*model.File, []model.File, error) {
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
//func (driver Baidu) Proxy(r *http.Request, account *model.Account) {
// r.Header.Set("User-Agent", "pan.baidu.com")
//}
func (driver Baidu) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver Baidu) MakeDir(path string, account *model.Account) error {
_, err := driver.create(utils.Join(account.RootFolder, path), 0, 1, "", "", account)
return err
}
func (driver Baidu) Move(src string, dst string, account *model.Account) error {
path := utils.Join(account.RootFolder, src)
dest, newname := utils.Split(utils.Join(account.RootFolder, dst))
data := []base.Json{
{
"path": path,
"dest": dest,
"newname": newname,
},
}
_, err := driver.manage("move", data, account)
return err
}
func (driver Baidu) Rename(src string, dst string, account *model.Account) error {
path := utils.Join(account.RootFolder, src)
newname := utils.Base(dst)
data := []base.Json{
{
"path": path,
"newname": newname,
},
}
_, err := driver.manage("rename", data, account)
return err
}
func (driver Baidu) Copy(src string, dst string, account *model.Account) error {
path := utils.Join(account.RootFolder, src)
dest, newname := utils.Split(utils.Join(account.RootFolder, dst))
data := []base.Json{
{
"path": path,
"dest": dest,
"newname": newname,
},
}
_, err := driver.manage("copy", data, account)
return err
}
func (driver Baidu) Delete(path string, account *model.Account) error {
path = utils.Join(account.RootFolder, path)
data := []string{path}
_, err := driver.manage("delete", data, account)
return err
}
func (driver Baidu) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
_, err = io.Copy(tempFile, file)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
var Default uint64 = 4 * 1024 * 1024
defaultByteData := make([]byte, Default)
count := int(math.Ceil(float64(file.GetSize()) / float64(Default)))
var SliceSize uint64 = 256 * 1024
// cal md5
h1 := md5.New()
h2 := md5.New()
block_list := make([]string, 0)
content_md5 := ""
slice_md5 := ""
left := file.GetSize()
for i := 0; i < count; i++ {
byteSize := Default
var byteData []byte
if left < Default {
byteSize = left
byteData = make([]byte, byteSize)
} else {
byteData = defaultByteData
}
left -= byteSize
_, err = io.ReadFull(tempFile, byteData)
if err != nil {
return err
}
h1.Write(byteData)
h2.Write(byteData)
block_list = append(block_list, fmt.Sprintf("\"%s\"", hex.EncodeToString(h2.Sum(nil))))
h2.Reset()
}
content_md5 = hex.EncodeToString(h1.Sum(nil))
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
if file.GetSize() <= SliceSize {
slice_md5 = content_md5
} else {
sliceData := make([]byte, SliceSize)
_, err = io.ReadFull(tempFile, sliceData)
if err != nil {
return err
}
h2.Write(sliceData)
slice_md5 = hex.EncodeToString(h2.Sum(nil))
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
}
path := encodeURIComponent(utils.Join(account.RootFolder, file.ParentPath, file.Name))
block_list_str := fmt.Sprintf("[%s]", strings.Join(block_list, ","))
data := fmt.Sprintf("path=%s&size=%d&isdir=0&autoinit=1&block_list=%s&content-md5=%s&slice-md5=%s",
path, file.GetSize(),
block_list_str,
content_md5, slice_md5)
params := map[string]string{
"method": "precreate",
}
var precreateResp PrecreateResp
_, err = driver.Post("/xpan/file", params, data, &precreateResp, account)
if err != nil {
return err
}
log.Debugf("%+v", precreateResp)
if precreateResp.ReturnType == 2 {
return nil
}
params = map[string]string{
"method": "upload",
"access_token": account.AccessToken,
"type": "tmpfile",
"path": path,
"uploadid": precreateResp.Uploadid,
}
left = file.GetSize()
for _, partseq := range precreateResp.BlockList {
byteSize := Default
var byteData []byte
if left < Default {
byteSize = left
byteData = make([]byte, byteSize)
} else {
byteData = defaultByteData
}
left -= byteSize
_, err = io.ReadFull(tempFile, byteData)
if err != nil {
return err
}
u := "https://d.pcs.baidu.com/rest/2.0/pcs/superfile2"
params["partseq"] = strconv.Itoa(partseq)
res, err := base.RestyClient.R().SetQueryParams(params).SetFileReader("file", file.Name, bytes.NewReader(byteData)).Post(u)
if err != nil {
return err
}
log.Debugln(res.String())
}
_, err = driver.create(path, file.GetSize(), 0, precreateResp.Uploadid, block_list_str, account)
return err
}
var _ base.Driver = (*Baidu)(nil)

84
drivers/baidu/types.go Normal file
View File

@ -0,0 +1,84 @@
package baidu
type TokenErrResp struct {
ErrorDescription string `json:"error_description"`
Error string `json:"error"`
}
type File struct {
//TkbindId int `json:"tkbind_id"`
//OwnerType int `json:"owner_type"`
//Category int `json:"category"`
//RealCategory string `json:"real_category"`
FsId int64 `json:"fs_id"`
ServerMtime int64 `json:"server_mtime"`
//OperId int `json:"oper_id"`
//ServerCtime int `json:"server_ctime"`
Thumbs struct {
//Icon string `json:"icon"`
Url3 string `json:"url3"`
//Url2 string `json:"url2"`
//Url1 string `json:"url1"`
} `json:"thumbs"`
//Wpfile int `json:"wpfile"`
//LocalMtime int `json:"local_mtime"`
Size int64 `json:"size"`
//ExtentTinyint7 int `json:"extent_tinyint7"`
Path string `json:"path"`
//Share int `json:"share"`
//ServerAtime int `json:"server_atime"`
//Pl int `json:"pl"`
//LocalCtime int `json:"local_ctime"`
ServerFilename string `json:"server_filename"`
//Md5 string `json:"md5"`
//OwnerId int `json:"owner_id"`
//Unlist int `json:"unlist"`
Isdir int `json:"isdir"`
}
type ListResp struct {
Errno int `json:"errno"`
GuidInfo string `json:"guid_info"`
List []File `json:"list"`
RequestId int64 `json:"request_id"`
Guid int `json:"guid"`
}
type DownloadResp struct {
Errmsg string `json:"errmsg"`
Errno int `json:"errno"`
List []struct {
//Category int `json:"category"`
//DateTaken int `json:"date_taken,omitempty"`
Dlink string `json:"dlink"`
//Filename string `json:"filename"`
//FsId int64 `json:"fs_id"`
//Height int `json:"height,omitempty"`
//Isdir int `json:"isdir"`
//Md5 string `json:"md5"`
//OperId int `json:"oper_id"`
//Path string `json:"path"`
//ServerCtime int `json:"server_ctime"`
//ServerMtime int `json:"server_mtime"`
//Size int `json:"size"`
//Thumbs struct {
// Icon string `json:"icon,omitempty"`
// Url1 string `json:"url1,omitempty"`
// Url2 string `json:"url2,omitempty"`
// Url3 string `json:"url3,omitempty"`
//} `json:"thumbs"`
//Width int `json:"width,omitempty"`
} `json:"list"`
//Names struct {
//} `json:"names"`
RequestId string `json:"request_id"`
}
type PrecreateResp struct {
Path string `json:"path"`
Uploadid string `json:"uploadid"`
ReturnType int `json:"return_type"`
BlockList []int `json:"block_list"`
Errno int `json:"errno"`
RequestId int64 `json:"request_id"`
}

18
drivers/baidu/util.go Normal file
View File

@ -0,0 +1,18 @@
package baidu
import (
"net/url"
"strings"
"time"
)
func getTime(t int64) *time.Time {
tm := time.Unix(t, 0)
return &tm
}
func encodeURIComponent(str string) string {
r := url.QueryEscape(str)
r = strings.ReplaceAll(r, "+", "%20")
return r
}

View File

@ -2,10 +2,10 @@ package base
import ( import (
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/http" "net/http"
"time"
) )
type DriverConfig struct { type DriverConfig struct {
@ -38,8 +38,8 @@ type Driver interface {
Link(args Args, account *model.Account) (*Link, error) Link(args Args, account *model.Account) (*Link, error)
// Path 取路径(文件或文件夹) // Path 取路径(文件或文件夹)
Path(path string, account *model.Account) (*model.File, []model.File, error) Path(path string, account *model.Account) (*model.File, []model.File, error)
// Proxy 代理处理 // Deprecated Proxy 代理处理
Proxy(c *gin.Context, account *model.Account) //Proxy(r *http.Request, account *model.Account)
// Preview 预览 // Preview 预览
Preview(path string, account *model.Account) (interface{}, error) Preview(path string, account *model.Account) (interface{}, error)
// MakeDir 创建文件夹 // MakeDir 创建文件夹
@ -62,6 +62,7 @@ type Item struct {
Name string `json:"name"` Name string `json:"name"`
Label string `json:"label"` Label string `json:"label"`
Type string `json:"type"` Type string `json:"type"`
Default string `json:"default"`
Values string `json:"values"` Values string `json:"values"`
Required bool `json:"required"` Required bool `json:"required"`
Description string `json:"description"` Description string `json:"description"`
@ -86,17 +87,19 @@ func GetDriversMap() map[string]Driver {
func GetDrivers() map[string][]Item { func GetDrivers() map[string][]Item {
res := make(map[string][]Item) res := make(map[string][]Item)
for k, v := range driversMap { for k, v := range driversMap {
webdavDirect := Item{
Name: "webdav_direct",
Label: "webdav direct",
Type: TypeBool,
Required: true,
Description: "Transfer the WebDAV of this account through the native",
}
if v.Config().OnlyProxy { if v.Config().OnlyProxy {
res[k] = v.Items() res[k] = append([]Item{
webdavDirect,
}, v.Items()...)
} else { } else {
res[k] = append([]Item{ res[k] = append([]Item{
//{
// Name: "allow_proxy",
// Label: "allow_proxy",
// Type: TypeBool,
// Required: true,
// Description: "allow proxy",
//},
{ {
Name: "proxy", Name: "proxy",
Label: "proxy", Label: "proxy",
@ -111,6 +114,7 @@ func GetDrivers() map[string][]Item {
Required: true, Required: true,
Description: "Transfer the WebDAV of this account through the server", Description: "Transfer the WebDAV of this account through the server",
}, },
webdavDirect,
}, v.Items()...) }, v.Items()...)
} }
res[k] = append([]Item{ res[k] = append([]Item{
@ -160,6 +164,8 @@ func GetDrivers() map[string][]Item {
var NoRedirectClient *resty.Client var NoRedirectClient *resty.Client
var RestyClient = resty.New() var RestyClient = resty.New()
var HttpClient = &http.Client{} var HttpClient = &http.Client{}
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
var DefaultTimeout = time.Second * 20
func init() { func init() {
NoRedirectClient = resty.New().SetRedirectPolicy( NoRedirectClient = resty.New().SetRedirectPolicy(
@ -167,8 +173,8 @@ func init() {
return http.ErrUseLastResponse return http.ErrUseLastResponse
}), }),
) )
userAgent := "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36" NoRedirectClient.SetHeader("user-agent", UserAgent)
NoRedirectClient.SetHeader("user-agent", userAgent) RestyClient.SetHeader("user-agent", UserAgent)
RestyClient.SetHeader("user-agent", userAgent)
RestyClient.SetRetryCount(3) RestyClient.SetRetryCount(3)
RestyClient.SetTimeout(DefaultTimeout)
} }

View File

@ -13,6 +13,7 @@ var (
ErrNotFolder = errors.New("not a folder") ErrNotFolder = errors.New("not a folder")
ErrEmptyFile = errors.New("empty file") ErrEmptyFile = errors.New("empty file")
ErrRelativePath = errors.New("access using relative path is not allowed") ErrRelativePath = errors.New("access using relative path is not allowed")
ErrEmptyToken = errors.New("empty token")
) )
const ( const (
@ -43,7 +44,8 @@ type Header struct {
} }
type Link struct { type Link struct {
Url string `json:"url"` Url string `json:"url"`
Headers []Header `json:"headers"` Headers []Header `json:"headers"`
Data io.ReadCloser Data io.ReadCloser
FilePath string `json:"path"` // for native
} }

View File

@ -5,7 +5,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"github.com/jlaffaye/ftp" "github.com/jlaffaye/ftp"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
@ -186,9 +185,9 @@ func (driver FTP) Path(path string, account *model.Account) (*model.File, []mode
return nil, files, nil return nil, files, nil
} }
func (driver FTP) Proxy(c *gin.Context, account *model.Account) { //func (driver FTP) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver FTP) Preview(path string, account *model.Account) (interface{}, error) { func (driver FTP) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -11,7 +11,12 @@ var connMap map[string]*ftp.ServerConn
func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) { func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
conn, ok := connMap[account.Name] conn, ok := connMap[account.Name]
if ok { if ok {
return conn, nil _, err := conn.CurrentDir()
if err == nil {
return conn, nil
} else {
delete(connMap, account.Name)
}
} }
conn, err := ftp.Connect(account.SiteUrl) conn, err := ftp.Connect(account.SiteUrl)
if err != nil { if err != nil {

View File

@ -6,7 +6,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
@ -178,9 +177,9 @@ func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File
return nil, files, nil return nil, files, nil
} }
func (driver GoogleDrive) Proxy(c *gin.Context, account *model.Account) { //func (driver GoogleDrive) Proxy(r *http.Request, account *model.Account) {
c.Request.Header.Add("Authorization", "Bearer "+account.AccessToken) // r.Header.Add("Authorization", "Bearer "+account.AccessToken)
} //}
func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) { func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -5,7 +5,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
) )
@ -138,6 +137,9 @@ func (driver Lanzou) Link(args base.Args, account *model.Account) (*base.Link, e
} }
link := base.Link{ link := base.Link{
Url: url, Url: url,
Headers: []base.Header{
{Name: "User-Agent", Value: base.UserAgent},
},
} }
return &link, nil return &link, nil
} }
@ -159,9 +161,9 @@ func (driver Lanzou) Path(path string, account *model.Account) (*model.File, []m
return nil, files, nil return nil, files, nil
} }
func (driver Lanzou) Proxy(c *gin.Context, account *model.Account) { //func (driver Lanzou) Proxy(r *http.Request, account *model.Account) {
c.Request.Header.Del("Origin") // r.Header.Del("Origin")
} //}
func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) { func (driver Lanzou) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -6,7 +6,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/url" "net/url"
"path/filepath" "path/filepath"
@ -15,8 +14,6 @@ import (
"time" "time"
) )
var lanzouClient = resty.New()
type LanZouFile struct { type LanZouFile struct {
Name string `json:"name"` Name string `json:"name"`
NameAll string `json:"name_all"` NameAll string `json:"name_all"`
@ -58,7 +55,7 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
files := make([]LanZouFile, 0) files := make([]LanZouFile, 0)
var resp LanZouFilesResp var resp LanZouFilesResp
// folders // folders
res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken). res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
SetFormData(map[string]string{ SetFormData(map[string]string{
"task": "47", "task": "47",
"folder_id": folderId, "folder_id": folderId,
@ -77,7 +74,7 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
// files // files
pg := 1 pg := 1
for { for {
_, err = lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken). _, err = base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
SetFormData(map[string]string{ SetFormData(map[string]string{
"task": "5", "task": "5",
"folder_id": folderId, "folder_id": folderId,
@ -108,7 +105,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
if err != nil { if err != nil {
return nil, err return nil, err
} }
res, err := lanzouClient.R().Get(shareUrl) res, err := base.RestyClient.R().Get(shareUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -121,7 +118,10 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
uid := regexp.MustCompile(`'uid':'(.+?)',`).FindStringSubmatch(res.String())[1] uid := regexp.MustCompile(`'uid':'(.+?)',`).FindStringSubmatch(res.String())[1]
rep := regexp.MustCompile(`'rep':'(.+?)',`).FindStringSubmatch(res.String())[1] rep := regexp.MustCompile(`'rep':'(.+?)',`).FindStringSubmatch(res.String())[1]
up := regexp.MustCompile(`'up':(.+?),`).FindStringSubmatch(res.String())[1] up := regexp.MustCompile(`'up':(.+?),`).FindStringSubmatch(res.String())[1]
ls := regexp.MustCompile(`'ls':(.+?),`).FindStringSubmatch(res.String())[1] ls := ""
if account.Password != "" {
ls = regexp.MustCompile(`'ls':(.+?),`).FindStringSubmatch(res.String())[1]
}
tName := regexp.MustCompile(`'t':(.+?),`).FindStringSubmatch(res.String())[1] tName := regexp.MustCompile(`'t':(.+?),`).FindStringSubmatch(res.String())[1]
kName := regexp.MustCompile(`'k':(.+?),`).FindStringSubmatch(res.String())[1] kName := regexp.MustCompile(`'k':(.+?),`).FindStringSubmatch(res.String())[1]
t := regexp.MustCompile(`var ` + tName + ` = '(.+?)';`).FindStringSubmatch(res.String())[1] t := regexp.MustCompile(`var ` + tName + ` = '(.+?)';`).FindStringSubmatch(res.String())[1]
@ -129,7 +129,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
pg := 1 pg := 1
for { for {
var resp LanZouFilesResp var resp LanZouFilesResp
res, err = lanzouClient.R().SetResult(&resp).SetFormData(map[string]string{ res, err = base.RestyClient.R().SetResult(&resp).SetFormData(map[string]string{
"lx": lx, "lx": lx,
"fid": fid, "fid": fid,
"uid": uid, "uid": uid,
@ -166,7 +166,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
// GetDownPageId 获取下载页面的ID // GetDownPageId 获取下载页面的ID
func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, error) { func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, error) {
var resp LanZouFilesResp var resp LanZouFilesResp
res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken). res, err := base.RestyClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
SetFormData(map[string]string{ SetFormData(map[string]string{
"task": "22", "task": "22",
"file_id": fileId, "file_id": fileId,
@ -201,7 +201,7 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
if err != nil { if err != nil {
return "", err return "", err
} }
res, err := lanzouClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId)) res, err := base.RestyClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId))
if err != nil { if err != nil {
return "", err return "", err
} }
@ -209,19 +209,24 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
if len(iframe) == 0 { if len(iframe) == 0 {
return "", fmt.Errorf("get down empty page") return "", fmt.Errorf("get down empty page")
} }
iframeUrl := "https://wwa.lanzouo.com" + iframe[1] iframeUrl := fmt.Sprintf("https://%s%s", u.Host, iframe[1])
res, err = lanzouClient.R().Get(iframeUrl) res, err = base.RestyClient.R().Get(iframeUrl)
if err != nil { if err != nil {
return "", err return "", err
} }
log.Debugln(res.String())
ajaxdata := regexp.MustCompile(`var ajaxdata = '(.+?)'`).FindStringSubmatch(res.String()) ajaxdata := regexp.MustCompile(`var ajaxdata = '(.+?)'`).FindStringSubmatch(res.String())
if len(ajaxdata) == 0 { if len(ajaxdata) == 0 {
return "", fmt.Errorf("get iframe empty page") return "", fmt.Errorf("get iframe empty page")
} }
signs := ajaxdata[1] signs := ajaxdata[1]
sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1] //sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1]
websign := regexp.MustCompile(`'websign':'(.+?)'`).FindStringSubmatch(res.String())[1] sign := regexp.MustCompile(`'sign':'(.+?)',`).FindStringSubmatch(res.String())[1]
websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1] //websign := regexp.MustCompile(`'websign':'(.+?)'`).FindStringSubmatch(res.String())[1]
//websign := regexp.MustCompile(`var websign = '(.+?)'`).FindStringSubmatch(res.String())[1]
websign := ""
//websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1]
websignkey := regexp.MustCompile(`var websignkey = '(.+?)';`).FindStringSubmatch(res.String())[1]
var resp LanzouLinkResp var resp LanzouLinkResp
form := map[string]string{ form := map[string]string{
"action": "downprocess", "action": "downprocess",
@ -232,7 +237,7 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
"websignkey": websignkey, "websignkey": websignkey,
} }
log.Debugf("form: %+v", form) log.Debugf("form: %+v", form)
res, err = lanzouClient.R().SetResult(&resp). res, err = base.RestyClient.R().SetResult(&resp).
SetHeader("origin", "https://"+u.Host). SetHeader("origin", "https://"+u.Host).
SetHeader("referer", iframeUrl). SetHeader("referer", iframeUrl).
SetFormData(form).Post(fmt.Sprintf("https://%s/ajaxm.php", u.Host)) SetFormData(form).Post(fmt.Sprintf("https://%s/ajaxm.php", u.Host))
@ -248,7 +253,4 @@ func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, er
func init() { func init() {
base.RegisterDriver(&Lanzou{}) base.RegisterDriver(&Lanzou{})
lanzouClient.
SetRetryCount(3).
SetHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36")
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/gin-gonic/gin"
"github.com/google/uuid" "github.com/google/uuid"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -46,11 +45,12 @@ func (driver MediaTrack) Items() []base.Item {
Required: true, Required: true,
}, },
{ {
Name: "order_by", Name: "order_by",
Label: "order_by", Label: "order_by",
Type: base.TypeSelect, Type: base.TypeSelect,
Values: "updated_at,title,size", Values: "updated_at,title,size",
Required: true, Required: true,
Description: "title",
}, },
{ {
Name: "order_direction", Name: "order_direction",
@ -58,6 +58,7 @@ func (driver MediaTrack) Items() []base.Item {
Type: base.TypeSelect, Type: base.TypeSelect,
Values: "true,false", Values: "true,false",
Required: true, Required: true,
Default: "false",
}, },
} }
} }
@ -148,9 +149,9 @@ func (driver MediaTrack) Path(path string, account *model.Account) (*model.File,
return nil, files, nil return nil, files, nil
} }
func (driver MediaTrack) Proxy(c *gin.Context, account *model.Account) { //func (driver MediaTrack) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver MediaTrack) Preview(path string, account *model.Account) (interface{}, error) { func (driver MediaTrack) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotImplement return nil, base.ErrNotImplement
@ -266,7 +267,7 @@ func (driver MediaTrack) Upload(file *model.FileStream, account *model.Account)
if err != nil { if err != nil {
return err return err
} }
tempFile, err := ioutil.TempFile("data/temp", "file-*") tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,7 +6,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io" "io"
"io/ioutil" "io/ioutil"
@ -133,7 +132,7 @@ func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, e
return nil, base.ErrNotFile return nil, base.ErrNotFile
} }
link := base.Link{ link := base.Link{
Url: fullPath, FilePath: fullPath,
} }
return &link, nil return &link, nil
} }
@ -156,9 +155,9 @@ func (driver Native) Path(path string, account *model.Account) (*model.File, []m
return nil, files, nil return nil, files, nil
} }
func (driver Native) Proxy(c *gin.Context, account *model.Account) { //func (driver Native) Proxy(r *http.Request, account *model.Account) {
// unnecessary // // unnecessary
} //}
func (driver Native) Preview(path string, account *model.Account) (interface{}, error) { func (driver Native) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -6,8 +6,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"github.com/robfig/cron/v3"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
) )
@ -92,9 +90,9 @@ func (driver Onedrive) Items() []base.Item {
} }
func (driver Onedrive) Save(account *model.Account, old *model.Account) error { func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
if old != nil { //if old != nil {
conf.Cron.Remove(cron.EntryID(old.CronId)) // conf.Cron.Remove(cron.EntryID(old.CronId))
} //}
if account == nil { if account == nil {
return nil return nil
} }
@ -104,28 +102,25 @@ func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
} }
account.RootFolder = utils.ParsePath(account.RootFolder) account.RootFolder = utils.ParsePath(account.RootFolder)
err := driver.RefreshToken(account) err := driver.RefreshToken(account)
_ = model.SaveAccount(account)
if err != nil { if err != nil {
return err return err
} }
cronId, err := conf.Cron.AddFunc("@every 1h", func() { //cronId, err := conf.Cron.AddFunc("@every 1h", func() {
name := account.Name // name := account.Name
log.Debugf("onedrive account name: %s", name) // log.Debugf("onedrive account name: %s", name)
newAccount, ok := model.GetAccount(name) // newAccount, ok := model.GetAccount(name)
log.Debugf("onedrive account: %+v", newAccount) // log.Debugf("onedrive account: %+v", newAccount)
if !ok { // if !ok {
return // return
} // }
err = driver.RefreshToken(&newAccount) // err = driver.RefreshToken(&newAccount)
_ = model.SaveAccount(&newAccount) // _ = model.SaveAccount(&newAccount)
}) //})
if err != nil { //if err != nil {
return err // return err
} //}
account.CronId = int(cronId) //account.CronId = int(cronId)
err = model.SaveAccount(account)
if err != nil {
return err
}
return nil return nil
} }
@ -205,9 +200,9 @@ func (driver Onedrive) Path(path string, account *model.Account) (*model.File, [
return nil, files, nil return nil, files, nil
} }
func (driver Onedrive) Proxy(c *gin.Context, account *model.Account) { //func (driver Onedrive) Proxy(r *http.Request, account *model.Account) {
c.Request.Header.Del("Origin") // r.Header.Del("Origin")
} //}
func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) { func (driver Onedrive) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -19,8 +19,6 @@ import (
"time" "time"
) )
var oneClient = resty.New()
type Host struct { type Host struct {
Oauth string Oauth string
Api string Api string
@ -47,7 +45,7 @@ var onedriveHostMap = map[string]Host{
func (driver Onedrive) GetMetaUrl(account *model.Account, auth bool, path string) string { func (driver Onedrive) GetMetaUrl(account *model.Account, auth bool, path string) string {
path = filepath.Join(account.RootFolder, path) path = filepath.Join(account.RootFolder, path)
log.Debugf(path) //log.Debugf(path)
host, _ := onedriveHostMap[account.Zone] host, _ := onedriveHostMap[account.Zone]
if auth { if auth {
return host.Oauth return host.Oauth
@ -81,7 +79,7 @@ type OneTokenErr struct {
func (driver Onedrive) RefreshToken(account *model.Account) error { func (driver Onedrive) RefreshToken(account *model.Account) error {
err := driver.refreshToken(account) err := driver.refreshToken(account)
if err != nil && err.Error() == "empty refresh_token" { if err != nil && err == base.ErrEmptyToken {
return driver.refreshToken(account) return driver.refreshToken(account)
} }
return err return err
@ -91,7 +89,7 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token" url := driver.GetMetaUrl(account, true, "") + "/common/oauth2/v2.0/token"
var resp base.TokenResp var resp base.TokenResp
var e OneTokenErr var e OneTokenErr
_, err := oneClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{ _, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
"grant_type": "refresh_token", "grant_type": "refresh_token",
"client_id": account.ClientId, "client_id": account.ClientId,
"client_secret": account.ClientSecret, "client_secret": account.ClientSecret,
@ -109,8 +107,8 @@ func (driver Onedrive) refreshToken(account *model.Account) error {
account.Status = "work" account.Status = "work"
} }
if resp.RefreshToken == "" { if resp.RefreshToken == "" {
account.Status = "empty refresh_token" account.Status = base.ErrEmptyToken.Error()
return errors.New("empty refresh_token") return base.ErrEmptyToken
} }
account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken account.RefreshToken, account.AccessToken = resp.RefreshToken, resp.AccessToken
return nil return nil
@ -178,16 +176,17 @@ func (driver Onedrive) GetFiles(account *model.Account, path string) ([]OneFile,
} }
for nextLink != "" { for nextLink != "" {
var files OneFiles var files OneFiles
var e OneRespErr _, err := driver.Request(nextLink, base.Get, nil, nil, nil, nil, &files, account)
_, err := oneClient.R().SetResult(&files).SetError(&e). //var e OneRespErr
SetHeader("Authorization", "Bearer "+account.AccessToken). //_, err := oneClient.R().SetResult(&files).SetError(&e).
Get(nextLink) // SetHeader("Authorization", "Bearer "+account.AccessToken).
// Get(nextLink)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if e.Error.Code != "" { //if e.Error.Code != "" {
return nil, fmt.Errorf("%s", e.Error.Message) // return nil, fmt.Errorf("%s", e.Error.Message)
} //}
res = append(res, files.Value...) res = append(res, files.Value...)
nextLink = files.NextLink nextLink = files.NextLink
} }
@ -196,16 +195,18 @@ func (driver Onedrive) GetFiles(account *model.Account, path string) ([]OneFile,
func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, error) { func (driver Onedrive) GetFile(account *model.Account, path string) (*OneFile, error) {
var file OneFile var file OneFile
var e OneRespErr //var e OneRespErr
_, err := oneClient.R().SetResult(&file).SetError(&e). u := driver.GetMetaUrl(account, false, path)
SetHeader("Authorization", "Bearer "+account.AccessToken). _, err := driver.Request(u, base.Get, nil, nil, nil, nil, &file, account)
Get(driver.GetMetaUrl(account, false, path)) //_, err := oneClient.R().SetResult(&file).SetError(&e).
// SetHeader("Authorization", "Bearer "+account.AccessToken).
// Get(driver.GetMetaUrl(account, false, path))
if err != nil { if err != nil {
return nil, err return nil, err
} }
if e.Error.Code != "" { //if e.Error.Code != "" {
return nil, fmt.Errorf("%s", e.Error.Message) // return nil, fmt.Errorf("%s", e.Error.Message)
} //}
return &file, nil return &file, nil
} }
@ -252,7 +253,7 @@ func (driver Onedrive) Request(url string, method int, headers, query, form map[
if err != nil { if err != nil {
return nil, err return nil, err
} }
log.Debug(res.String()) //log.Debug(res.String())
if e.Error.Code != "" { if e.Error.Code != "" {
if e.Error.Code == "InvalidAuthenticationToken" { if e.Error.Code == "InvalidAuthenticationToken" {
err = driver.RefreshToken(account) err = driver.RefreshToken(account)
@ -314,5 +315,4 @@ func (driver Onedrive) UploadBig(file *model.FileStream, account *model.Account)
func init() { func init() {
base.RegisterDriver(&Onedrive{}) base.RegisterDriver(&Onedrive{})
oneClient.SetRetryCount(3)
} }

View File

@ -10,7 +10,6 @@ import (
"github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
@ -142,9 +141,9 @@ func (driver PikPak) Path(path string, account *model.Account) (*model.File, []m
return nil, files, nil return nil, files, nil
} }
func (driver PikPak) Proxy(c *gin.Context, account *model.Account) { //func (driver PikPak) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver PikPak) Preview(path string, account *model.Account) (interface{}, error) { func (driver PikPak) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

320
drivers/quark/driver.go Normal file
View File

@ -0,0 +1,320 @@
package quark
import (
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"os"
"path/filepath"
)
type Quark struct{}
func (driver Quark) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Quark",
OnlyProxy: true,
}
}
func (driver Quark) Items() []base.Item {
return []base.Item{
{
Name: "access_token",
Label: "Cookie",
Type: base.TypeString,
Required: true,
Description: "Unknown expiration time",
},
{
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
Required: true,
Default: "0",
},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeSelect,
Values: "file_type,file_name,updated_at",
Required: true,
Default: "file_name",
},
{
Name: "order_direction",
Label: "order_direction",
Type: base.TypeSelect,
Values: "asc,desc",
Required: true,
Default: "asc",
},
}
}
func (driver Quark) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
_, err := driver.Get("/config", nil, nil, account)
return err
}
func (driver Quark) File(path string, account *model.Account) (*model.File, error) {
path = utils.ParsePath(path)
if path == "/" {
return &model.File{
Id: account.RootFolder,
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver Quark) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
var files []model.File
cache, err := base.GetCache(path, account)
if err == nil {
files, _ = cache.([]model.File)
} else {
file, err := driver.File(path, account)
if err != nil {
return nil, err
}
files, err = driver.GetFiles(file.Id, account)
if err != nil {
return nil, err
}
if len(files) > 0 {
_ = base.SetCache(path, files, account)
}
}
return files, nil
}
func (driver Quark) Link(args base.Args, account *model.Account) (*base.Link, error) {
path := args.Path
file, err := driver.File(path, account)
if err != nil {
return nil, err
}
data := base.Json{
"fids": []string{file.Id},
}
var resp DownResp
_, err = driver.Post("/file/download", data, &resp, account)
if err != nil {
return nil, err
}
return &base.Link{
Url: resp.Data[0].DownloadUrl,
Headers: []base.Header{
{Name: "Cookie", Value: account.AccessToken},
},
}, nil
}
func (driver Quark) Path(path string, account *model.Account) (*model.File, []model.File, error) {
path = utils.ParsePath(path)
log.Debugf("quark path: %s", path)
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
func (driver Quark) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver Quark) MakeDir(path string, account *model.Account) error {
parentFile, err := driver.File(utils.Dir(path), account)
if err != nil {
return err
}
data := base.Json{
"dir_init_lock": false,
"dir_path": "",
"file_name": utils.Base(path),
"pdir_fid": parentFile.Id,
}
_, err = driver.Post("/file", data, nil, account)
return err
}
func (driver Quark) Move(src string, dst string, account *model.Account) error {
srcFile, err := driver.File(src, account)
if err != nil {
return err
}
dstParentFile, err := driver.File(utils.Dir(dst), account)
if err != nil {
return err
}
data := base.Json{
"action_type": 1,
"exclude_fids": []string{},
"filelist": []string{srcFile.Id},
"to_pdir_fid": dstParentFile.Id,
}
_, err = driver.Post("/file/move", data, nil, account)
return err
}
func (driver Quark) Rename(src string, dst string, account *model.Account) error {
srcFile, err := driver.File(src, account)
if err != nil {
return err
}
data := base.Json{
"fid": srcFile.Id,
"file_name": utils.Base(dst),
}
_, err = driver.Post("/file/rename", data, nil, account)
return err
}
func (driver Quark) Copy(src string, dst string, account *model.Account) error {
return base.ErrNotSupport
}
func (driver Quark) Delete(path string, account *model.Account) error {
srcFile, err := driver.File(path, account)
if err != nil {
return err
}
data := base.Json{
"action_type": 1,
"exclude_fids": []string{},
"filelist": []string{srcFile.Id},
}
_, err = driver.Post("/file/delete", data, nil, account)
return err
}
func (driver Quark) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
tempFile, err := ioutil.TempFile(conf.Conf.TempDir, "file-*")
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
_, err = io.Copy(tempFile, file)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
m := md5.New()
_, err = io.Copy(m, tempFile)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
md5Str := hex.EncodeToString(m.Sum(nil))
s := sha1.New()
_, err = io.Copy(s, tempFile)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
sha1Str := hex.EncodeToString(s.Sum(nil))
// pre
pre, err := driver.UpPre(file, parentFile.Id, account)
if err != nil {
return err
}
log.Debugln("hash: ", md5Str, sha1Str)
// hash
finish, err := driver.UpHash(md5Str, sha1Str, pre.Data.TaskId, account)
if err != nil {
return err
}
if finish {
return nil
}
// part up
partSize := pre.Metadata.PartSize
var bytes []byte
md5s := make([]string, 0)
defaultBytes := make([]byte, partSize)
left := int64(file.GetSize())
partNumber := 1
for left > 0 {
if left > int64(partSize) {
bytes = defaultBytes
} else {
bytes = make([]byte, left)
}
_, err := io.ReadFull(tempFile, bytes)
if err != nil {
return err
}
left -= int64(partSize)
log.Debugf("left: %d", left)
m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account)
//m, err := driver.UpPart(pre, file.GetMIMEType(), partNumber, bytes, account, md5Str, sha1Str)
if err != nil {
return err
}
if m == "finish" {
return nil
}
md5s = append(md5s, m)
partNumber++
}
err = driver.UpCommit(pre, md5s, account)
if err != nil {
return err
}
return driver.UpFinish(pre, account)
}
var _ base.Driver = (*Quark)(nil)

266
drivers/quark/quark.go Normal file
View File

@ -0,0 +1,266 @@
package quark
import (
"crypto/md5"
"encoding/base64"
"errors"
"fmt"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"net/http"
"strconv"
"strings"
"time"
)
func (driver Quark) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
u := "https://drive.quark.cn/1/clouddrive" + pathname
req := base.RestyClient.R()
req.SetHeaders(map[string]string{
"Cookie": account.AccessToken,
"Accept": "application/json, text/plain, */*",
"Referer": "https://pan.quark.cn/",
})
req.SetQueryParam("pr", "ucpro")
req.SetQueryParam("fr", "pc")
if headers != nil {
req.SetHeaders(headers)
}
if query != nil {
req.SetQueryParams(query)
}
if form != nil {
req.SetFormData(form)
}
if data != nil {
req.SetBody(data)
}
if resp != nil {
req.SetResult(resp)
}
var e Resp
var err error
var res *resty.Response
req.SetError(&e)
switch method {
case base.Get:
res, err = req.Get(u)
case base.Post:
res, err = req.Post(u)
case base.Delete:
res, err = req.Delete(u)
case base.Patch:
res, err = req.Patch(u)
case base.Put:
res, err = req.Put(u)
default:
return nil, base.ErrNotSupport
}
if err != nil {
return nil, err
}
log.Debugf("%s response: %s", pathname, res.String())
if e.Status >= 400 || e.Code != 0 {
return nil, errors.New(e.Message)
}
return res.Body(), nil
}
func (driver Quark) Get(pathname string, query map[string]string, resp interface{}, account *model.Account) ([]byte, error) {
return driver.Request(pathname, base.Get, nil, query, nil, nil, resp, account)
}
func (driver Quark) Post(pathname string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
return driver.Request(pathname, base.Post, nil, nil, nil, data, resp, account)
}
func (driver Quark) GetFiles(parent string, account *model.Account) ([]model.File, error) {
files := make([]model.File, 0)
page := 1
size := 100
query := map[string]string{
"pdir_fid": parent,
"_size": strconv.Itoa(size),
"_fetch_total": "1",
"_sort": "file_type:asc," + account.OrderBy + ":" + account.OrderDirection,
}
for {
query["_page"] = strconv.Itoa(page)
var resp SortResp
_, err := driver.Get("/file/sort", query, &resp, account)
if err != nil {
return nil, err
}
for _, f := range resp.Data.List {
files = append(files, *driver.formatFile(&f))
}
if page*size >= resp.Metadata.Count {
break
}
page++
}
return files, nil
}
func (driver Quark) UpPre(file *model.FileStream, parentId string, account *model.Account) (UpPreResp, error) {
now := time.Now()
data := base.Json{
"ccp_hash_update": true,
"dir_name": "",
"file_name": file.Name,
"format_type": file.MIMEType,
"l_created_at": now.UnixMilli(),
"l_updated_at": now.UnixMilli(),
"pdir_fid": parentId,
"size": file.Size,
}
log.Debugf("uppre data: %+v", data)
var resp UpPreResp
_, err := driver.Post("/file/upload/pre", data, &resp, account)
return resp, err
}
func (driver Quark) UpHash(md5, sha1, taskId string, account *model.Account) (bool, error) {
data := base.Json{
"md5": md5,
"sha1": sha1,
"task_id": taskId,
}
log.Debugf("hash: %+v", data)
var resp HashResp
_, err := driver.Post("/file/update/hash", data, &resp, account)
return resp.Data.Finish, err
}
func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account) (string, error) {
//func (driver Quark) UpPart(pre UpPreResp, mineType string, partNumber int, bytes []byte, account *model.Account, md5Str, sha1Str string) (string, error) {
timeStr := time.Now().UTC().Format(http.TimeFormat)
data := base.Json{
"auth_info": pre.Data.AuthInfo,
"auth_meta": fmt.Sprintf(`PUT
%s
%s
x-oss-date:%s
x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
/%s/%s?partNumber=%d&uploadId=%s`,
mineType, timeStr, timeStr, pre.Data.Bucket, pre.Data.ObjKey, partNumber, pre.Data.UploadId),
"task_id": pre.Data.TaskId,
}
var resp UpAuthResp
_, err := driver.Post("/file/upload/auth", data, &resp, account)
if err != nil {
return "", err
}
//if partNumber == 1 {
// finish, err := driver.UpHash(md5Str, sha1Str, pre.Data.TaskId, account)
// if err != nil {
// return "", err
// }
// if finish {
// return "finish", nil
// }
//}
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
res, err := base.RestyClient.R().
SetHeaders(map[string]string{
"Authorization": resp.Data.AuthKey,
"Content-Type": mineType,
"Referer": "https://pan.quark.cn/",
"x-oss-date": timeStr,
"x-oss-user-agent": "aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit",
}).
SetQueryParams(map[string]string{
"partNumber": strconv.Itoa(partNumber),
"uploadId": pre.Data.UploadId,
}).SetBody(bytes).Put(u)
if res.StatusCode() != 200 {
return "", fmt.Errorf("up status: %d, error: %s", res.StatusCode(), res.String())
}
return res.Header().Get("ETag"), nil
}
func (driver Quark) UpCommit(pre UpPreResp, md5s []string, account *model.Account) error {
timeStr := time.Now().UTC().Format(http.TimeFormat)
log.Debugf("md5s: %+v", md5s)
bodyBuilder := strings.Builder{}
bodyBuilder.WriteString(`<?xml version="1.0" encoding="UTF-8"?>
<CompleteMultipartUpload>
`)
for i, m := range md5s {
bodyBuilder.WriteString(fmt.Sprintf(`<Part>
<PartNumber>%d</PartNumber>
<ETag>%s</ETag>
</Part>
`, i+1, m))
}
bodyBuilder.WriteString("</CompleteMultipartUpload>")
body := bodyBuilder.String()
m := md5.New()
m.Write([]byte(body))
contentMd5 := base64.StdEncoding.EncodeToString(m.Sum(nil))
callbackBytes, err := utils.Json.Marshal(pre.Data.Callback)
if err != nil {
return err
}
callbackBase64 := base64.StdEncoding.EncodeToString(callbackBytes)
data := base.Json{
"auth_info": pre.Data.AuthInfo,
"auth_meta": fmt.Sprintf(`POST
%s
application/xml
%s
x-oss-callback:%s
x-oss-date:%s
x-oss-user-agent:aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit
/%s/%s?uploadId=%s`,
contentMd5, timeStr, callbackBase64, timeStr,
pre.Data.Bucket, pre.Data.ObjKey, pre.Data.UploadId),
"task_id": pre.Data.TaskId,
}
log.Debugf("xml: %s", body)
log.Debugf("auth data: %+v", data)
var resp UpAuthResp
_, err = driver.Post("/file/upload/auth", data, &resp, account)
if err != nil {
return err
}
u := fmt.Sprintf("https://%s.%s/%s", pre.Data.Bucket, pre.Data.UploadUrl[7:], pre.Data.ObjKey)
res, err := base.RestyClient.R().
SetHeaders(map[string]string{
"Authorization": resp.Data.AuthKey,
"Content-MD5": contentMd5,
"Content-Type": "application/xml",
"Referer": "https://pan.quark.cn/",
"x-oss-callback": callbackBase64,
"x-oss-date": timeStr,
"x-oss-user-agent": "aliyun-sdk-js/6.6.1 Chrome 98.0.4758.80 on Windows 10 64-bit",
}).
SetQueryParams(map[string]string{
"uploadId": pre.Data.UploadId,
}).SetBody(body).Post(u)
if res.StatusCode() != 200 {
return fmt.Errorf("up status: %d, error: %s", res.StatusCode(), res.String())
}
return nil
}
func (driver Quark) UpFinish(pre UpPreResp, account *model.Account) error {
data := base.Json{
"obj_key": pre.Data.ObjKey,
"task_id": pre.Data.TaskId,
}
_, err := driver.Post("/file/upload/finish", data, nil, account)
if err != nil {
return err
}
time.Sleep(time.Second)
return nil
}
func init() {
base.RegisterDriver(&Quark{})
}

134
drivers/quark/types.go Normal file
View File

@ -0,0 +1,134 @@
package quark
type Resp struct {
Status int `json:"status"`
Code int `json:"code"`
Message string `json:"message"`
//ReqId string `json:"req_id"`
//Timestamp int `json:"timestamp"`
}
type File struct {
Fid string `json:"fid"`
FileName string `json:"file_name"`
//PdirFid string `json:"pdir_fid"`
//Category int `json:"category"`
//FileType int `json:"file_type"`
Size int64 `json:"size"`
//FormatType string `json:"format_type"`
//Status int `json:"status"`
//Tags string `json:"tags,omitempty"`
//LCreatedAt int64 `json:"l_created_at"`
LUpdatedAt int64 `json:"l_updated_at"`
//NameSpace int `json:"name_space"`
//IncludeItems int `json:"include_items,omitempty"`
//RiskType int `json:"risk_type"`
//BackupSign int `json:"backup_sign"`
//Duration int `json:"duration"`
//FileSource string `json:"file_source"`
File bool `json:"file"`
//CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
//PrivateExtra struct {} `json:"_private_extra"`
//ObjCategory string `json:"obj_category,omitempty"`
//Thumbnail string `json:"thumbnail,omitempty"`
}
type SortResp struct {
Resp
Data struct {
List []File `json:"list"`
} `json:"data"`
Metadata struct {
Size int `json:"_size"`
Page int `json:"_page"`
Count int `json:"_count"`
Total int `json:"_total"`
Way string `json:"way"`
} `json:"metadata"`
}
type DownResp struct {
Resp
Data []struct {
Fid string `json:"fid"`
FileName string `json:"file_name"`
PdirFid string `json:"pdir_fid"`
Category int `json:"category"`
FileType int `json:"file_type"`
Size int `json:"size"`
FormatType string `json:"format_type"`
Status int `json:"status"`
Tags string `json:"tags"`
LCreatedAt int64 `json:"l_created_at"`
LUpdatedAt int64 `json:"l_updated_at"`
NameSpace int `json:"name_space"`
Thumbnail string `json:"thumbnail"`
DownloadUrl string `json:"download_url"`
Md5 string `json:"md5"`
RiskType int `json:"risk_type"`
RangeSize int `json:"range_size"`
BackupSign int `json:"backup_sign"`
ObjCategory string `json:"obj_category"`
Duration int `json:"duration"`
FileSource string `json:"file_source"`
File bool `json:"file"`
CreatedAt int64 `json:"created_at"`
UpdatedAt int64 `json:"updated_at"`
PrivateExtra struct {
} `json:"_private_extra"`
} `json:"data"`
Metadata struct {
Acc2 string `json:"acc2"`
Acc1 string `json:"acc1"`
} `json:"metadata"`
}
type UpPreResp struct {
Resp
Data struct {
TaskId string `json:"task_id"`
Finish bool `json:"finish"`
UploadId string `json:"upload_id"`
ObjKey string `json:"obj_key"`
UploadUrl string `json:"upload_url"`
Fid string `json:"fid"`
Bucket string `json:"bucket"`
Callback struct {
CallbackUrl string `json:"callbackUrl"`
CallbackBody string `json:"callbackBody"`
} `json:"callback"`
FormatType string `json:"format_type"`
Size int `json:"size"`
AuthInfo string `json:"auth_info"`
} `json:"data"`
Metadata struct {
PartThread int `json:"part_thread"`
Acc2 string `json:"acc2"`
Acc1 string `json:"acc1"`
PartSize int `json:"part_size"` // 分片大小
} `json:"metadata"`
}
type HashResp struct {
Resp
Data struct {
Finish bool `json:"finish"`
Fid string `json:"fid"`
Thumbnail string `json:"thumbnail"`
FormatType string `json:"format_type"`
} `json:"data"`
Metadata struct {
} `json:"metadata"`
}
type UpAuthResp struct {
Resp
Data struct {
AuthKey string `json:"auth_key"`
Speed int `json:"speed"`
Headers []interface{} `json:"headers"`
} `json:"data"`
Metadata struct {
} `json:"metadata"`
}

31
drivers/quark/util.go Normal file
View File

@ -0,0 +1,31 @@
package quark
import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"path"
"time"
)
func getTime(t int64) *time.Time {
tm := time.UnixMilli(t)
//log.Debugln(tm)
return &tm
}
func (driver Quark) formatFile(f *File) *model.File {
file := model.File{
Id: f.Fid,
Name: f.FileName,
Size: f.Size,
Driver: driver.Config().Name,
UpdatedAt: getTime(f.UpdatedAt),
}
if f.File {
file.Type = utils.GetFileType(path.Ext(f.FileName))
} else {
file.Type = conf.FOLDER
}
return &file
}

View File

@ -8,7 +8,6 @@ import (
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"net/url" "net/url"
"path/filepath" "path/filepath"
@ -77,9 +76,21 @@ func (driver S3) Items() []base.Item {
{ {
Name: "zone", Name: "zone",
Label: "placeholder filename", Label: "placeholder filename",
Type: base.TypeNumber, Type: base.TypeString,
Description: "default empty string", Description: "default empty string",
}, },
{
Name: "bool_1",
Label: "S3ForcePathStyle",
Type: base.TypeBool,
},
{
Name: "internal_type",
Label: "ListObject Version",
Type: base.TypeSelect,
Values: "v1,v2",
Default: "v1",
},
} }
} }
@ -133,7 +144,11 @@ func (driver S3) Files(path string, account *model.Account) ([]model.File, error
if err == nil { if err == nil {
files, _ = cache.([]model.File) files, _ = cache.([]model.File)
} else { } else {
files, err = driver.List(path, account) if account.InternalType == "v2" {
files, err = driver.ListV2(path, account)
} else {
files, err = driver.List(path, account)
}
if err == nil && len(files) > 0 { if err == nil && len(files) > 0 {
_ = base.SetCache(path, files, account) _ = base.SetCache(path, files, account)
} }
@ -142,7 +157,7 @@ func (driver S3) Files(path string, account *model.Account) ([]model.File, error
} }
func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error) { func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error) {
client, err := driver.GetClient(account) client, err := driver.GetClient(account, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -189,9 +204,9 @@ func (driver S3) Path(path string, account *model.Account) (*model.File, []model
return nil, files, nil return nil, files, nil
} }
func (driver S3) Proxy(c *gin.Context, account *model.Account) { //func (driver S3) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver S3) Preview(path string, account *model.Account) (interface{}, error) { func (driver S3) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport
@ -215,7 +230,7 @@ func (driver S3) Rename(src string, dst string, account *model.Account) error {
} }
func (driver S3) Copy(src string, dst string, account *model.Account) error { func (driver S3) Copy(src string, dst string, account *model.Account) error {
client, err := driver.GetClient(account) client, err := driver.GetClient(account, false)
if err != nil { if err != nil {
return err return err
} }
@ -235,7 +250,7 @@ func (driver S3) Copy(src string, dst string, account *model.Account) error {
} }
func (driver S3) Delete(path string, account *model.Account) error { func (driver S3) Delete(path string, account *model.Account) error {
client, err := driver.GetClient(account) client, err := driver.GetClient(account, false)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,6 +1,7 @@
package s3 package s3
import ( import (
"errors"
"fmt" "fmt"
"github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
@ -22,20 +23,21 @@ var sessionsMap map[string]*session.Session
func (driver S3) NewSession(account *model.Account) (*session.Session, error) { func (driver S3) NewSession(account *model.Account) (*session.Session, error) {
cfg := &aws.Config{ cfg := &aws.Config{
Credentials: credentials.NewStaticCredentials(account.AccessKey, account.AccessSecret, ""), Credentials: credentials.NewStaticCredentials(account.AccessKey, account.AccessSecret, ""),
Region: &account.Region, Region: &account.Region,
Endpoint: &account.Endpoint, Endpoint: &account.Endpoint,
S3ForcePathStyle: aws.Bool(account.Bool1),
} }
return session.NewSession(cfg) return session.NewSession(cfg)
} }
func (driver S3) GetClient(account *model.Account) (*s3.S3, error) { func (driver S3) GetClient(account *model.Account, link bool) (*s3.S3, error) {
s, ok := sessionsMap[account.Name] s, ok := sessionsMap[account.Name]
if !ok { if !ok {
return nil, fmt.Errorf("can't find [%s] session", account.Name) return nil, fmt.Errorf("can't find [%s] session", account.Name)
} }
client := s3.New(s) client := s3.New(s)
if account.CustomHost != "" { if link && account.CustomHost != "" {
cURL, err := url.Parse(account.CustomHost) cURL, err := url.Parse(account.CustomHost)
if err != nil { if err != nil {
return nil, err return nil, err
@ -54,7 +56,7 @@ func (driver S3) GetClient(account *model.Account) (*s3.S3, error) {
func (driver S3) List(prefix string, account *model.Account) ([]model.File, error) { func (driver S3) List(prefix string, account *model.Account) ([]model.File, error) {
prefix = driver.GetKey(prefix, account, true) prefix = driver.GetKey(prefix, account, true)
log.Debugf("list: %s", prefix) log.Debugf("list: %s", prefix)
client, err := driver.GetClient(account) client, err := driver.GetClient(account, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -99,6 +101,9 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
} }
files = append(files, file) files = append(files, file)
} }
if listObjectsResult.IsTruncated == nil {
return nil, errors.New("IsTruncated nil")
}
if *listObjectsResult.IsTruncated { if *listObjectsResult.IsTruncated {
marker = *listObjectsResult.NextMarker marker = *listObjectsResult.NextMarker
} else { } else {
@ -108,6 +113,73 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
return files, nil return files, nil
} }
func (driver S3) ListV2(prefix string, account *model.Account) ([]model.File, error) {
prefix = driver.GetKey(prefix, account, true)
//if prefix == "" {
// prefix = "/"
//}
log.Debugf("list: %s", prefix)
client, err := driver.GetClient(account, false)
if err != nil {
return nil, err
}
files := make([]model.File, 0)
var continuationToken, startAfter *string
for {
input := &s3.ListObjectsV2Input{
Bucket: &account.Bucket,
ContinuationToken: continuationToken,
Prefix: &prefix,
Delimiter: aws.String("/"),
StartAfter: startAfter,
}
listObjectsResult, err := client.ListObjectsV2(input)
if err != nil {
return nil, err
}
log.Debugf("resp: %+v", listObjectsResult)
for _, object := range listObjectsResult.CommonPrefixes {
name := utils.Base(strings.Trim(*object.Prefix, "/"))
file := model.File{
//Id: *object.Key,
Name: name,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
TimeStr: "-",
Type: conf.FOLDER,
}
files = append(files, file)
}
for _, object := range listObjectsResult.Contents {
name := utils.Base(*object.Key)
if name == account.Zone {
continue
}
file := model.File{
//Id: *object.Key,
Name: name,
Size: *object.Size,
Driver: driver.Config().Name,
UpdatedAt: object.LastModified,
Type: utils.GetFileType(path.Ext(*object.Key)),
}
files = append(files, file)
}
if !aws.BoolValue(listObjectsResult.IsTruncated) {
break
}
if listObjectsResult.NextContinuationToken != nil {
continuationToken = listObjectsResult.NextContinuationToken
continue
}
if len(listObjectsResult.Contents) == 0 {
break
}
startAfter = listObjectsResult.Contents[len(listObjectsResult.Contents)-1].Key
}
return files, nil
}
func (driver S3) GetKey(path string, account *model.Account, dir bool) string { func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
path = utils.Join(account.RootFolder, path) path = utils.Join(account.RootFolder, path)
path = strings.TrimPrefix(path, "/") path = strings.TrimPrefix(path, "/")

View File

@ -6,7 +6,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
"strconv" "strconv"
@ -154,9 +153,9 @@ func (driver Shandian) Path(path string, account *model.Account) (*model.File, [
return nil, files, nil return nil, files, nil
} }
func (driver Shandian) Proxy(c *gin.Context, account *model.Account) { //func (driver Shandian) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) { func (driver Shandian) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -5,7 +5,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path/filepath" "path/filepath"
) )
@ -52,6 +51,7 @@ func (driver Teambition) Items() []base.Item {
Type: base.TypeSelect, Type: base.TypeSelect,
Values: "fileName,fileSize,updated,created", Values: "fileName,fileSize,updated,created",
Required: true, Required: true,
Default: "fileName",
}, },
{ {
Name: "order_direction", Name: "order_direction",
@ -59,6 +59,7 @@ func (driver Teambition) Items() []base.Item {
Type: base.TypeSelect, Type: base.TypeSelect,
Values: "Asc,Desc", Values: "Asc,Desc",
Required: true, Required: true,
Default: "Asc",
}, },
} }
} }
@ -149,9 +150,9 @@ func (driver Teambition) Path(path string, account *model.Account) (*model.File,
return nil, files, nil return nil, files, nil
} }
func (driver Teambition) Proxy(c *gin.Context, account *model.Account) { //func (driver Teambition) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver Teambition) Preview(path string, account *model.Account) (interface{}, error) { func (driver Teambition) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

View File

@ -98,7 +98,7 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo
Size: 0, Size: 0,
Type: conf.FOLDER, Type: conf.FOLDER,
Driver: driver.Config().Name, Driver: driver.Config().Name,
UpdatedAt: &collection.Updated, UpdatedAt: collection.Updated,
}) })
} }
} }
@ -126,7 +126,7 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo
Size: work.FileSize, Size: work.FileSize,
Type: utils.GetFileType(path.Ext(work.FileName)), Type: utils.GetFileType(path.Ext(work.FileName)),
Driver: driver.Config().Name, Driver: driver.Config().Name,
UpdatedAt: &work.Updated, UpdatedAt: work.Updated,
Thumbnail: work.Thumbnail, Thumbnail: work.Thumbnail,
Url: work.DownloadURL, Url: work.DownloadURL,
}) })
@ -157,8 +157,10 @@ func (driver Teambition) upload(file *model.FileStream, token string, account *m
func (driver Teambition) chunkUpload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) { func (driver Teambition) chunkUpload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) {
prefix := "tcs" prefix := "tcs"
referer := "https://www.teambition.com/"
if account.InternalType == "International" { if account.InternalType == "International" {
prefix = "us-tcs" prefix = "us-tcs"
referer = "https://us.teambition.com/"
} }
var newChunk ChunkUpload var newChunk ChunkUpload
_, err := base.RestyClient.R().SetResult(&newChunk).SetHeader("Authorization", token). _, err := base.RestyClient.R().SetResult(&newChunk).SetHeader("Authorization", token).
@ -187,7 +189,7 @@ func (driver Teambition) chunkUpload(file *model.FileStream, token string, accou
res, err := base.RestyClient.R().SetHeaders(map[string]string{ res, err := base.RestyClient.R().SetHeaders(map[string]string{
"Authorization": token, "Authorization": token,
"Content-Type": "application/octet-stream", "Content-Type": "application/octet-stream",
"Referer": "https://www.teambition.com/", "Referer": referer,
}).SetBody(chunkData).Post(u) }).SetBody(chunkData).Post(u)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -3,22 +3,22 @@ package teambition
import "time" import "time"
type Collection struct { type Collection struct {
ID string `json:"_id"` ID string `json:"_id"`
Title string `json:"title"` Title string `json:"title"`
Updated time.Time `json:"updated"` Updated *time.Time `json:"updated"`
} }
type Work struct { type Work struct {
ID string `json:"_id"` ID string `json:"_id"`
FileName string `json:"fileName"` FileName string `json:"fileName"`
FileSize int64 `json:"fileSize"` FileSize int64 `json:"fileSize"`
FileKey string `json:"fileKey"` FileKey string `json:"fileKey"`
FileCategory string `json:"fileCategory"` FileCategory string `json:"fileCategory"`
DownloadURL string `json:"downloadUrl"` DownloadURL string `json:"downloadUrl"`
ThumbnailURL string `json:"thumbnailUrl"` ThumbnailURL string `json:"thumbnailUrl"`
Thumbnail string `json:"thumbnail"` Thumbnail string `json:"thumbnail"`
Updated time.Time `json:"updated"` Updated *time.Time `json:"updated"`
PreviewURL string `json:"previewUrl"` PreviewURL string `json:"previewUrl"`
} }
type FileUpload struct { type FileUpload struct {

237
drivers/uss/driver.go Normal file
View File

@ -0,0 +1,237 @@
package uss
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"github.com/upyun/go-sdk/v3/upyun"
"net/url"
"path/filepath"
"strings"
"time"
)
type USS struct {
}
func (driver USS) Config() base.DriverConfig {
return base.DriverConfig{
Name: "USS",
LocalSort: true,
}
}
func (driver USS) Items() []base.Item {
return []base.Item{
{
Name: "bucket",
Label: "Bucket",
Type: base.TypeString,
Required: true,
},
{
Name: "endpoint",
Label: "Endpoint",
Type: base.TypeString,
Required: true,
},
{
Name: "access_key",
Label: "Operator Name",
Type: base.TypeString,
Required: true,
},
{
Name: "access_secret",
Label: "Operator Password",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder path",
Type: base.TypeString,
Required: false,
},
{
Name: "custom_host",
Label: "Custom Host",
Type: base.TypeString,
},
{
Name: "limit",
Label: "Sign url expire time(hours)",
Type: base.TypeNumber,
Default: "4",
Description: "default 4 hours",
},
//{
// Name: "zone",
// Label: "placeholder filename",
// Type: base.TypeString,
// Description: "default empty string",
//},
}
}
func (driver USS) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
if account.Limit == 0 {
account.Limit = 4
}
client, err := driver.NewUpYun(account)
if err != nil {
account.Status = err.Error()
} else {
clientsMap[account.Name] = client
account.Status = "work"
}
_ = model.SaveAccount(account)
return err
}
func (driver USS) File(path string, account *model.Account) (*model.File, error) {
path = utils.ParsePath(path)
if path == "/" {
return &model.File{
Id: account.RootFolder,
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver USS) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
var files []model.File
cache, err := base.GetCache(path, account)
if err == nil {
files, _ = cache.([]model.File)
} else {
files, err = driver.List(path, account)
if err == nil && len(files) > 0 {
_ = base.SetCache(path, files, account)
}
}
return files, err
}
func (driver USS) Link(args base.Args, account *model.Account) (*base.Link, error) {
key := driver.GetKey(args.Path, account, false)
host := account.CustomHost
if host == "" {
host = account.Endpoint
}
if strings.Contains(host, "://") {
host = "https://" + host
}
u := fmt.Sprintf("%s/%s", host, key)
downExp := time.Hour * time.Duration(account.Limit)
expireAt := time.Now().Add(downExp).Unix()
upd := url.QueryEscape(utils.Base(args.Path))
signStr := strings.Join([]string{account.AccessSecret, fmt.Sprint(expireAt), fmt.Sprintf("/%s", key)}, "&")
upt := utils.GetMD5Encode(signStr)[12:20] + fmt.Sprint(expireAt)
link := fmt.Sprintf("%s?_upd=%s&_upt=%s", u, upd, upt)
return &base.Link{Url: link}, nil
}
func (driver USS) Path(path string, account *model.Account) (*model.File, []model.File, error) {
path = utils.ParsePath(path)
log.Debugf("s3 path: %s", path)
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
func (driver USS) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver USS) MakeDir(path string, account *model.Account) error {
client, err := driver.GetClient(account)
if err != nil {
return err
}
return client.Mkdir(driver.GetKey(path, account, true))
}
func (driver USS) Move(src string, dst string, account *model.Account) error {
client, err := driver.GetClient(account)
if err != nil {
return err
}
return client.Move(&upyun.MoveObjectConfig{
SrcPath: driver.GetKey(src, account, false),
DestPath: driver.GetKey(dst, account, false),
})
}
func (driver USS) Rename(src string, dst string, account *model.Account) error {
return driver.Move(src, dst, account)
}
func (driver USS) Copy(src string, dst string, account *model.Account) error {
client, err := driver.GetClient(account)
if err != nil {
return err
}
return client.Copy(&upyun.CopyObjectConfig{
SrcPath: driver.GetKey(src, account, false),
DestPath: driver.GetKey(dst, account, false),
})
}
func (driver USS) Delete(path string, account *model.Account) error {
client, err := driver.GetClient(account)
if err != nil {
return err
}
return client.Delete(&upyun.DeleteObjectConfig{
Path: driver.GetKey(path, account, false),
Async: false,
})
}
func (driver USS) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
client, err := driver.GetClient(account)
if err != nil {
return err
}
return client.Put(&upyun.PutObjectConfig{
Path: driver.GetKey(utils.Join(file.ParentPath, file.GetFileName()), account, false),
Reader: file,
})
}
var _ base.Driver = (*USS)(nil)

82
drivers/uss/uss.go Normal file
View File

@ -0,0 +1,82 @@
package uss
import (
"errors"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/upyun/go-sdk/v3/upyun"
"path"
"strings"
)
var clientsMap map[string]*upyun.UpYun
func (driver USS) NewUpYun(account *model.Account) (*upyun.UpYun, error) {
return upyun.NewUpYun(&upyun.UpYunConfig{
Bucket: account.Bucket,
Operator: account.AccessKey,
Password: account.AccessToken,
}), nil
}
func (driver USS) GetClient(account *model.Account) (*upyun.UpYun, error) {
client, ok := clientsMap[account.Name]
if ok {
return client, nil
}
return nil, errors.New("can't get client")
}
func (driver USS) List(prefix string, account *model.Account) ([]model.File, error) {
prefix = driver.GetKey(prefix, account, true)
client, err := driver.GetClient(account)
if err != nil {
return nil, err
}
objsChan := make(chan *upyun.FileInfo, 10)
defer close(objsChan)
go func() {
err = client.List(&upyun.GetObjectsConfig{
Path: prefix,
ObjectsChan: objsChan,
MaxListObjects: 0,
MaxListLevel: 1,
})
}()
if err != nil {
return nil, err
}
res := make([]model.File, 0)
for obj := range objsChan {
t := obj.Time
f := model.File{
Name: obj.Name,
Size: obj.Size,
UpdatedAt: &t,
Driver: driver.Config().Name,
}
if obj.IsDir {
f.Type = conf.FOLDER
} else {
f.Type = utils.GetFileType(path.Ext(obj.Name))
}
res = append(res, f)
}
return res, err
}
func (driver USS) GetKey(path string, account *model.Account, dir bool) string {
path = utils.Join(account.RootFolder, path)
path = strings.TrimPrefix(path, "/")
if dir {
path += "/"
}
return path
}
func init() {
clientsMap = make(map[string]*upyun.UpYun)
base.RegisterDriver(&USS{})
}

View File

@ -5,7 +5,6 @@ import (
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model" "github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"path/filepath" "path/filepath"
) )
@ -137,9 +136,9 @@ func (driver WebDav) Path(path string, account *model.Account) (*model.File, []m
return nil, files, nil return nil, files, nil
} }
func (driver WebDav) Proxy(c *gin.Context, account *model.Account) { //func (driver WebDav) Proxy(r *http.Request, account *model.Account) {
//
} //}
func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) { func (driver WebDav) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport return nil, base.ErrNotSupport

224
drivers/yandex/driver.go Normal file
View File

@ -0,0 +1,224 @@
package yandex
import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"net/http"
"path/filepath"
"strconv"
)
type Yandex struct{}
func (driver Yandex) Config() base.DriverConfig {
return base.DriverConfig{
Name: "Yandex.Disk",
}
}
func (driver Yandex) Items() []base.Item {
return []base.Item{
{
Name: "refresh_token",
Label: "refresh token",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder path",
Type: base.TypeString,
Default: "/",
Required: true,
},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeSelect,
Default: "name",
Values: "name,path,created,modified,size",
Required: false,
},
{
Name: "order_direction",
Label: "order_direction",
Type: base.TypeSelect,
Values: "asc,desc",
Default: "asc",
Required: false,
},
{
Name: "client_id",
Label: "client id",
Default: "a78d5a69054042fa936f6c77f9a0ae8b",
Type: base.TypeString,
Required: true,
},
{
Name: "client_secret",
Label: "client secret",
Default: "9c119bbb04b346d2a52aa64401936b2b",
Type: base.TypeString,
Required: true,
},
}
}
func (driver Yandex) Save(account *model.Account, old *model.Account) error {
return driver.RefreshToken(account)
}
func (driver Yandex) File(path string, account *model.Account) (*model.File, error) {
path = utils.ParsePath(path)
if path == "/" {
return &model.File{
Id: account.RootFolder,
Name: account.Name,
Size: 0,
Type: conf.FOLDER,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
}, nil
}
dir, name := filepath.Split(path)
files, err := driver.Files(dir, account)
if err != nil {
return nil, err
}
for _, file := range files {
if file.Name == name {
return &file, nil
}
}
return nil, base.ErrPathNotFound
}
func (driver Yandex) Files(path string, account *model.Account) ([]model.File, error) {
path = utils.ParsePath(path)
cache, err := base.GetCache(path, account)
if err == nil {
files, _ := cache.([]model.File)
return files, nil
}
files, err := driver.GetFiles(path, account)
if err != nil {
return nil, err
}
if len(files) > 0 {
_ = base.SetCache(path, files, account)
}
return files, nil
}
func (driver Yandex) Link(args base.Args, account *model.Account) (*base.Link, error) {
path := utils.Join(account.RootFolder, args.Path)
log.Debugln("down path:", path)
var resp DownResp
_, err := driver.Request("/download", base.Get, nil, map[string]string{
"path": path,
}, nil, nil, &resp, account)
if err != nil {
return nil, err
}
link := base.Link{
Url: resp.Href,
}
return &link, nil
}
func (driver Yandex) Path(path string, account *model.Account) (*model.File, []model.File, error) {
file, err := driver.File(path, account)
if err != nil {
return nil, nil, err
}
if !file.IsDir() {
return file, nil, nil
}
files, err := driver.Files(path, account)
if err != nil {
return nil, nil, err
}
return nil, files, nil
}
//func (driver Yandex) Proxy(r *http.Request, account *model.Account) {
//
//}
func (driver Yandex) Preview(path string, account *model.Account) (interface{}, error) {
return nil, base.ErrNotSupport
}
func (driver Yandex) MakeDir(path string, account *model.Account) error {
path = utils.Join(account.RootFolder, path)
_, err := driver.Request("", base.Put, nil, map[string]string{
"path": path,
}, nil, nil, nil, account)
return err
}
func (driver Yandex) Move(src string, dst string, account *model.Account) error {
from := utils.Join(account.RootFolder, src)
path := utils.Join(account.RootFolder, dst)
_, err := driver.Request("/move", base.Post, nil, map[string]string{
"from": from,
"path": path,
"overwrite": "true",
}, nil, nil, nil, account)
return err
}
func (driver Yandex) Rename(src string, dst string, account *model.Account) error {
return driver.Move(src, dst, account)
}
func (driver Yandex) Copy(src string, dst string, account *model.Account) error {
from := utils.Join(account.RootFolder, src)
path := utils.Join(account.RootFolder, dst)
_, err := driver.Request("/copy", base.Post, nil, map[string]string{
"from": from,
"path": path,
"overwrite": "true",
}, nil, nil, nil, account)
return err
}
func (driver Yandex) Delete(path string, account *model.Account) error {
path = utils.Join(account.RootFolder, path)
_, err := driver.Request("", base.Delete, nil, map[string]string{
"path": path,
}, nil, nil, nil, account)
return err
}
func (driver Yandex) Upload(file *model.FileStream, account *model.Account) error {
if file == nil {
return base.ErrEmptyFile
}
path := utils.Join(account.RootFolder, file.ParentPath, file.Name)
var resp UploadResp
_, err := driver.Request("/upload", base.Get, nil, map[string]string{
"path": path,
"overwrite": "true",
}, nil, nil, &resp, account)
if err != nil {
return err
}
req, err := http.NewRequest(resp.Method, resp.Href, file)
if err != nil {
return err
}
req.Header.Set("Content-Length", strconv.FormatUint(file.Size, 10))
req.Header.Set("Content-Type", "application/octet-stream")
_, err = base.HttpClient.Do(req)
//res, err := base.RestyClient.R().
// SetHeader("Content-Length", strconv.FormatUint(file.Size, 10)).
// SetBody(file).Put(resp.Href)
//log.Debugln(res.Status(), res.String())
return err
}
var _ base.Driver = (*Yandex)(nil)

74
drivers/yandex/types.go Normal file
View File

@ -0,0 +1,74 @@
package yandex
import "time"
type TokenErrResp struct {
ErrorDescription string `json:"error_description"`
Error string `json:"error"`
}
type ErrResp struct {
Message string `json:"message"`
Description string `json:"description"`
Error string `json:"error"`
}
type File struct {
//AntivirusStatus string `json:"antivirus_status"`
Size int64 `json:"size"`
//CommentIds struct {
// PrivateResource string `json:"private_resource"`
// PublicResource string `json:"public_resource"`
//} `json:"comment_ids"`
Name string `json:"name"`
//Exif struct {
// DateTime time.Time `json:"date_time"`
//} `json:"exif"`
//Created time.Time `json:"created"`
//ResourceId string `json:"resource_id"`
Modified *time.Time `json:"modified"`
//MimeType string `json:"mime_type"`
File string `json:"file"`
//MediaType string `json:"media_type"`
Preview string `json:"preview"`
Path string `json:"path"`
//Sha256 string `json:"sha256"`
Type string `json:"type"`
//Md5 string `json:"md5"`
//Revision int64 `json:"revision"`
}
type FilesResp struct {
Embedded struct {
Sort string `json:"sort"`
Items []File `json:"items"`
Limit int `json:"limit"`
Offset int `json:"offset"`
Path string `json:"path"`
Total int `json:"total"`
} `json:"_embedded"`
Name string `json:"name"`
Exif struct {
} `json:"exif"`
ResourceId string `json:"resource_id"`
Created time.Time `json:"created"`
Modified time.Time `json:"modified"`
Path string `json:"path"`
CommentIds struct {
} `json:"comment_ids"`
Type string `json:"type"`
Revision int64 `json:"revision"`
}
type DownResp struct {
Href string `json:"href"`
Method string `json:"method"`
Templated bool `json:"templated"`
}
type UploadResp struct {
OperationId string `json:"operation_id"`
Href string `json:"href"`
Method string `json:"method"`
Templated bool `json:"templated"`
}

1
drivers/yandex/util.go Normal file
View File

@ -0,0 +1 @@
package yandex

154
drivers/yandex/yandex.go Normal file
View File

@ -0,0 +1,154 @@
package yandex
import (
"errors"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
"path"
"strconv"
)
func (driver Yandex) RefreshToken(account *model.Account) error {
err := driver.refreshToken(account)
if err != nil && err == base.ErrEmptyToken {
err = driver.refreshToken(account)
}
if err != nil {
account.Status = err.Error()
}
_ = model.SaveAccount(account)
return err
}
func (driver Yandex) refreshToken(account *model.Account) error {
u := "https://oauth.yandex.com/token"
var resp base.TokenResp
var e TokenErrResp
_, err := base.RestyClient.R().SetResult(&resp).SetError(&e).SetFormData(map[string]string{
"grant_type": "refresh_token",
"refresh_token": account.RefreshToken,
"client_id": account.ClientId,
"client_secret": account.ClientSecret,
}).Post(u)
if err != nil {
return err
}
if e.Error != "" {
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
}
if resp.RefreshToken == "" {
return base.ErrEmptyToken
}
account.Status = "work"
account.AccessToken, account.RefreshToken = resp.AccessToken, resp.RefreshToken
return nil
}
func (driver Yandex) Request(pathname string, method int, headers, query, form map[string]string, data interface{}, resp interface{}, account *model.Account) ([]byte, error) {
u := "https://cloud-api.yandex.net/v1/disk/resources" + pathname
req := base.RestyClient.R()
req.SetHeader("Authorization", "OAuth "+account.AccessToken)
if headers != nil {
req.SetHeaders(headers)
}
if query != nil {
req.SetQueryParams(query)
}
if form != nil {
req.SetFormData(form)
}
if data != nil {
req.SetBody(data)
}
if resp != nil {
req.SetResult(resp)
}
var res *resty.Response
var err error
var e ErrResp
req.SetError(&e)
switch method {
case base.Get:
res, err = req.Get(u)
case base.Post:
res, err = req.Post(u)
case base.Patch:
res, err = req.Patch(u)
case base.Delete:
res, err = req.Delete(u)
case base.Put:
res, err = req.Put(u)
default:
return nil, base.ErrNotSupport
}
if err != nil {
return nil, err
}
//log.Debug(res.String())
if e.Error != "" {
if e.Error == "UnauthorizedError" {
err = driver.RefreshToken(account)
if err != nil {
return nil, err
}
return driver.Request(pathname, method, headers, query, form, data, resp, account)
}
return nil, errors.New(e.Description)
}
return res.Body(), nil
}
func (driver Yandex) GetFiles(rawPath string, account *model.Account) ([]model.File, error) {
path_ := utils.Join(account.RootFolder, rawPath)
limit := 100
page := 1
res := make([]model.File, 0)
for {
offset := (page - 1) * limit
query := map[string]string{
"path": path_,
"limit": strconv.Itoa(limit),
"offset": strconv.Itoa(offset),
}
if account.OrderBy != "" {
if account.OrderDirection == "desc" {
query["sort"] = "-" + account.OrderBy
} else {
query["sort"] = account.OrderBy
}
}
var resp FilesResp
_, err := driver.Request("", base.Get, nil, query, nil, nil, &resp, account)
if err != nil {
return nil, err
}
for _, file := range resp.Embedded.Items {
f := model.File{
Name: file.Name,
Size: file.Size,
Driver: driver.Config().Name,
UpdatedAt: file.Modified,
Thumbnail: file.Preview,
Url: file.File,
}
if file.Type == "dir" {
f.Type = conf.FOLDER
} else {
f.Type = utils.GetFileType(path.Ext(file.Name))
}
res = append(res, f)
}
if resp.Embedded.Total <= offset+limit {
break
}
}
return res, nil
}
func init() {
base.RegisterDriver(&Yandex{})
}

21
go.mod
View File

@ -8,17 +8,19 @@ require (
github.com/gin-contrib/cors v1.3.1 github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.4 github.com/gin-gonic/gin v1.7.4
github.com/go-resty/resty/v2 v2.6.0 github.com/go-resty/resty/v2 v2.6.0
github.com/google/uuid v1.3.0
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/robfig/cron/v3 v3.0.0 github.com/robfig/cron/v3 v3.0.0
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f
github.com/upyun/go-sdk/v3 v3.0.2
golang.org/x/text v0.3.7 golang.org/x/text v0.3.7
gorm.io/driver/mysql v1.2.3 gorm.io/driver/mysql v1.3.2
gorm.io/driver/postgres v1.2.3 gorm.io/driver/postgres v1.3.1
gorm.io/driver/sqlite v1.2.6 gorm.io/driver/sqlite v1.3.1
gorm.io/gorm v1.22.5 gorm.io/gorm v1.23.1
) )
require ( require (
@ -35,21 +37,20 @@ require (
github.com/go-redis/redis/v8 v8.9.0 // indirect github.com/go-redis/redis/v8 v8.9.0 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.1 // indirect github.com/jackc/pgconn v1.11.0 // indirect
github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect github.com/jackc/pgproto3/v2 v2.2.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.9.1 // indirect github.com/jackc/pgtype v1.10.0 // indirect
github.com/jackc/pgx/v4 v4.14.1 // indirect github.com/jackc/pgx/v4 v4.15.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect github.com/jinzhu/now v1.1.4 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.10 // indirect github.com/mattn/go-sqlite3 v1.14.11 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
@ -63,7 +64,7 @@ require (
go.opentelemetry.io/otel v0.20.0 // indirect go.opentelemetry.io/otel v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect go.opentelemetry.io/otel/trace v0.20.0 // indirect
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
google.golang.org/protobuf v1.27.1 // indirect google.golang.org/protobuf v1.27.1 // indirect

49
go.sum
View File

@ -229,10 +229,10 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8= github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@ -248,7 +248,6 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns= github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
@ -258,27 +257,27 @@ github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01C
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0= github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0=
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8= github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU= github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU=
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M= github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas= github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
@ -342,11 +341,9 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk= github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -513,6 +510,8 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ= github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/upyun/go-sdk/v3 v3.0.2 h1:Ke+iOipK5CT0xzMwsgJsi7faJV7ID4lAs+wrH1RH0dA=
github.com/upyun/go-sdk/v3 v3.0.2/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7UggbAxyZa0E=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
@ -555,10 +554,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -593,6 +593,7 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -747,26 +748,20 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M=
gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM=
gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y= gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y=
gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo= gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
gorm.io/driver/postgres v1.1.2 h1:Amy3hCvLqM+/ICzjCnQr8wKFLVJTeOTdlMT7kCP+J1Q= gorm.io/driver/mysql v1.3.2 h1:QJryWiqQ91EvZ0jZL48NOpdlPdMjdip1hQ8bTgo4H7I=
gorm.io/driver/postgres v1.1.2/go.mod h1:/AGV0zvqF3mt9ZtzLzQmXWQ/5vr+1V1TyHZGZVjzmwI= gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To= gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs= gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
gorm.io/driver/sqlite v1.1.6 h1:p3U8WXkVFTOLPED4JjrZExfndjOtya3db8w9/vEMNyI= gorm.io/driver/postgres v1.3.1 h1:Pyv+gg1Gq1IgsLYytj/S2k7ebII3CzEdpqQkPOdH24g=
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8= gorm.io/driver/postgres v1.3.1/go.mod h1:WwvWOuR9unCLpGWCL6Y3JOeBWvbKi6JLhayiVclSZZU=
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4= gorm.io/driver/sqlite v1.3.1 h1:bwfE+zTEWklBYoEodIOIBwuWHpnx52Z9zJFW5F33WLk=
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY= gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.21.16 h1:YBIQLtP5PLfZQz59qfrq7xbrK7KWQ+JsXXCH/THlMqs=
gorm.io/gorm v1.21.16/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk= gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU= gorm.io/gorm v1.23.1 h1:aj5IlhDzEPsoIyOPtTRVI+SyaN1u6k613sbt4pwbxG0=
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -2,6 +2,9 @@ package model
import ( import (
"github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/conf"
log "github.com/sirupsen/logrus"
"strings"
"sync"
"time" "time"
) )
@ -30,8 +33,9 @@ type Account struct {
SiteUrl string `json:"site_url"` SiteUrl string `json:"site_url"`
SiteId string `json:"site_id"` SiteId string `json:"site_id"`
InternalType string `json:"internal_type"` InternalType string `json:"internal_type"`
WebdavProxy bool `json:"webdav_proxy"` // 开启之后只会webdav走中转 WebdavProxy bool `json:"webdav_proxy"` // 开启之后只会webdav走中转
Proxy bool `json:"proxy"` // 是否中转,开启之后web和webdav都会走中转 Proxy bool `json:"proxy"` // 是否中转,开启之后web和webdav都会走中转
WebdavDirect bool `json:"webdav_direct"` // webdav 下载不跳转
//AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载 //AllowProxy bool `json:"allow_proxy"` // 是否允许中转下载
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302 DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址 APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
@ -43,9 +47,12 @@ type Account struct {
AccessSecret string `json:"access_secret"` AccessSecret string `json:"access_secret"`
CustomHost string `json:"custom_host"` CustomHost string `json:"custom_host"`
ExtractFolder string `json:"extract_folder"` ExtractFolder string `json:"extract_folder"`
Bool1 bool `json:"bool_1"`
} }
var accountsMap = map[string]Account{} var accountsMap = make(map[string]Account)
var balance = ".balance"
// SaveAccount save account to database // SaveAccount save account to database
func SaveAccount(account *Account) error { func SaveAccount(account *Account) error {
@ -100,6 +107,46 @@ func GetAccount(name string) (Account, bool) {
return account, ok return account, ok
} }
func GetAccountsByName(name string) []Account {
accounts := make([]Account, 0)
if AccountsCount() == 1 {
account, _ := GetAccount("")
accounts = append(accounts, account)
return accounts
}
for _, v := range accountsMap {
if v.Name == name || strings.HasPrefix(v.Name, name+balance) {
accounts = append(accounts, v)
}
}
return accounts
}
var balanceMap sync.Map
func GetBalancedAccount(name string) (Account, bool) {
accounts := GetAccountsByName(name)
accountNum := len(accounts)
switch accountNum {
case 0:
return Account{}, false
case 1:
return accounts[0], true
default:
cur, ok := balanceMap.Load(name)
if ok {
i := cur.(int)
i = (i + 1) % accountNum
balanceMap.Store(name, i)
log.Debugln("use: ", i)
return accounts[i], true
} else {
balanceMap.Store(name, 0)
return accounts[0], true
}
}
}
func GetAccountById(id uint) (*Account, error) { func GetAccountById(id uint) (*Account, error) {
var account Account var account Account
account.ID = id account.ID = id
@ -116,6 +163,9 @@ func GetAccountFiles() ([]File, error) {
return nil, err return nil, err
} }
for _, v := range accounts { for _, v := range accounts {
if strings.Contains(v.Name, balance) {
continue
}
files = append(files, File{ files = append(files, File{
Name: v.Name, Name: v.Name,
Size: 0, Size: 0,

View File

@ -93,11 +93,15 @@ func LoadSettings() {
if err == nil { if err == nil {
conf.TextTypes = strings.Split(textTypes.Value, ",") conf.TextTypes = strings.Split(textTypes.Value, ",")
} }
dProxyTypes, err := GetSettingByKey("d_proxy types")
if err == nil {
conf.DProxyTypes = strings.Split(dProxyTypes.Value, ",")
}
// html // html
favicon, err := GetSettingByKey("favicon") favicon, err := GetSettingByKey("favicon")
if err == nil { if err == nil {
//conf.Favicon = favicon.Value //conf.Favicon = favicon.Value
conf.ManageHtml = strings.Replace(conf.RawIndexHtml, "https://store.heytapimage.com/cdo-portal/feedback/202110/30/d43c41c5d257c9bc36366e310374fb19.png", favicon.Value, 1) conf.ManageHtml = strings.Replace(conf.RawIndexHtml, "https://cdn.jsdelivr.net/gh/alist-org/logo@main/logo.svg", favicon.Value, 1)
} }
title, err := GetSettingByKey("title") title, err := GetSettingByKey("title")
if err == nil { if err == nil {
@ -114,7 +118,11 @@ func LoadSettings() {
// token // token
adminPassword, err := GetSettingByKey("password") adminPassword, err := GetSettingByKey("password")
if err == nil { if err == nil {
conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value)) if adminPassword.Value != "" {
conf.Token = utils.GetMD5Encode(fmt.Sprintf("https://github.com/Xhofe/alist-%s", adminPassword.Value))
} else {
conf.Token = ""
}
} }
// load settings // load settings
for _, key := range conf.LoadSettings { for _, key := range conf.LoadSettings {

View File

@ -40,7 +40,7 @@ func ParsePath(rawPath string) (*model.Account, string, base.Driver, error) {
path = "/" + strings.Join(paths[2:], "/") path = "/" + strings.Join(paths[2:], "/")
name = paths[1] name = paths[1]
} }
account, ok := model.GetAccount(name) account, ok := model.GetBalancedAccount(name)
if !ok { if !ok {
return nil, "", nil, fmt.Errorf("no [%s] account", name) return nil, "", nil, fmt.Errorf("no [%s] account", name)
} }

89
server/common/proxy.go Normal file
View File

@ -0,0 +1,89 @@
package common
import (
"errors"
"fmt"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"strconv"
)
var HttpClient = &http.Client{}
func Proxy(w http.ResponseWriter, r *http.Request, link *base.Link, file *model.File) error {
// 本机读取数据
var err error
if link.Data != nil {
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
defer func() {
_ = link.Data.Close()
}()
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
w.Header().Set("Content-Length", strconv.FormatInt(file.Size, 10))
w.WriteHeader(http.StatusOK)
_, err = io.Copy(w, link.Data)
if err != nil {
return err
}
return nil
}
// 本机文件直接返回文件
if link.FilePath != "" {
f, err := os.Open(link.FilePath)
if err != nil {
return err
}
defer func() {
_ = f.Close()
}()
fileStat, err := os.Stat(link.FilePath)
if err != nil {
return err
}
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
http.ServeContent(w, r, file.Name, fileStat.ModTime(), f)
return nil
} else {
req, err := http.NewRequest(r.Method, link.Url, nil)
if err != nil {
return err
}
for h, val := range r.Header {
req.Header[h] = val
}
for _, header := range link.Headers {
req.Header.Set(header.Name, header.Value)
}
res, err := HttpClient.Do(req)
if err != nil {
return err
}
defer func() {
_ = res.Body.Close()
}()
log.Debugf("proxy status: %d", res.StatusCode)
for h, v := range res.Header {
w.Header()[h] = v
}
w.WriteHeader(res.StatusCode)
if res.StatusCode >= 400 {
all, _ := ioutil.ReadAll(res.Body)
msg := string(all)
log.Debugln(msg)
return errors.New(msg)
}
_, err = io.Copy(w, res.Body)
if err != nil {
return err
}
return nil
}
}

View File

@ -1,27 +1,29 @@
package controllers package controllers
import ( import (
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base" "github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/server/common" "github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils" "github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"path"
) )
func Down(c *gin.Context) { func Down(c *gin.Context) {
rawPath := c.Param("path") rawPath := c.Param("path")
rawPath = utils.ParsePath(rawPath) rawPath = utils.ParsePath(rawPath)
log.Debugf("down: %s", rawPath) log.Debugf("down: %s", rawPath)
account, path, driver, err := common.ParsePath(rawPath) account, path_, driver, err := common.ParsePath(rawPath)
if err != nil { if err != nil {
common.ErrorResp(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
if driver.Config().OnlyProxy || account.Proxy { if driver.Config().OnlyProxy || account.Proxy || utils.IsContain(conf.DProxyTypes, path.Ext(rawPath)) {
Proxy(c) Proxy(c)
return return
} }
link, err := driver.Link(base.Args{Path: path, IP: c.ClientIP()}, account) link, err := driver.Link(base.Args{Path: path_, IP: c.ClientIP()}, account)
if err != nil { if err != nil {
common.ErrorResp(c, err, 500) common.ErrorResp(c, err, 500)
return return

View File

@ -0,0 +1,30 @@
package file
import (
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/server/common"
"github.com/gin-gonic/gin"
)
type RefreshReq struct {
Path string `json:"path"`
}
func RefreshFolder(c *gin.Context) {
var req RefreshReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
account, path_, _, err := common.ParsePath(req.Path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
err = base.DeleteCache(path_, account)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
common.SuccessResp(c)
}

View File

@ -0,0 +1,65 @@
package controllers
import (
"encoding/base64"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"strings"
)
func Favicon(c *gin.Context) {
c.Redirect(302, conf.GetStr("favicon"))
}
func Plist(c *gin.Context) {
data := c.Param("data")
data = strings.ReplaceAll(data, "_", "/")
data = strings.ReplaceAll(data, "-", "=")
bytes, err := base64.StdEncoding.DecodeString(data)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
u := string(bytes)
name := utils.Base(u)
ipaIndex := strings.Index(name, ".ipa")
if ipaIndex != -1 {
name = name[:ipaIndex]
}
plist := fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>items</key>
<array>
<dict>
<key>assets</key>
<array>
<dict>
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>%s</string>
</dict>
</array>
<key>metadata</key>
<dict>
<key>bundle-identifier</key>
<string>ci.nn.%s</string>
<key>bundle-version</key>
<string>4.4</string>
<key>kind</key>
<string>software</string>
<key>title</key>
<string>%s</string>
</dict>
</dict>
</array>
</dict>
</plist>`, u, name, name)
c.Header("Content-Type", "application/xml;charset=utf-8")
c.Status(200)
_, _ = c.Writer.WriteString(plist)
}

View File

@ -10,12 +10,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2" "github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"io"
"net/http"
"net/url"
"os"
"path/filepath" "path/filepath"
"strconv"
) )
func Proxy(c *gin.Context) { func Proxy(c *gin.Context) {
@ -32,7 +27,9 @@ func Proxy(c *gin.Context) {
// 2. driver只能中转 // 2. driver只能中转
// 3. 是文本类型文件 // 3. 是文本类型文件
// 4. 开启webdav中转需要验证sign // 4. 开启webdav中转需要验证sign
if !account.Proxy && !driver.Config().OnlyProxy && utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT { if !account.Proxy && !driver.Config().OnlyProxy &&
utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT &&
!utils.IsContain(conf.DProxyTypes, filepath.Ext(rawPath)) {
// 只开启了webdav中转验证sign // 只开启了webdav中转验证sign
ok := false ok := false
if account.WebdavProxy { if account.WebdavProxy {
@ -62,92 +59,17 @@ func Proxy(c *gin.Context) {
common.ErrorResp(c, err, 500) common.ErrorResp(c, err, 500)
return return
} }
// 本机读取数据 err = common.Proxy(c.Writer, c.Request, link, file)
if link.Data != nil { if err != nil {
//c.Data(http.StatusOK, "application/octet-stream", link.Data) common.ErrorResp(c, err, 500)
defer func() {
_ = link.Data.Close()
}()
c.Status(http.StatusOK)
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
c.Header("Content-Length", strconv.FormatInt(file.Size, 10))
_, err = io.Copy(c.Writer, link.Data)
if err != nil {
_, _ = c.Writer.WriteString(err.Error())
}
return
}
// 本机文件直接返回文件
if account.Type == "Native" {
// 对于名称为index.html的文件需要特殊处理
if utils.Base(rawPath) == "index.html" {
file, err := os.Open(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
defer func() {
_ = file.Close()
}()
fileStat, err := os.Stat(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
return
}
c.File(link.Url)
return
} else {
//if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
// Text(c, link)
// return
//}
driver.Proxy(c, account)
r := c.Request
w := c.Writer
//target, err := url.Parse(link.Url)
//if err != nil {
// common.ErrorResp(c, err, 500)
// return
//}
req, err := http.NewRequest("GET", link.Url, nil)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
for h, val := range r.Header {
req.Header[h] = val
}
res, err := HttpClient.Do(req)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
defer func() {
_ = res.Body.Close()
}()
log.Debugf("proxy status: %d", res.StatusCode)
w.WriteHeader(res.StatusCode)
for h, v := range res.Header {
w.Header()[h] = v
}
_, err = io.Copy(w, res.Body)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
} }
} }
var client *resty.Client var client *resty.Client
var HttpClient = &http.Client{}
func init() { func init() {
client = resty.New() client = resty.New()
client.SetRetryCount(3) client.SetRetryCount(3).SetTimeout(base.DefaultTimeout)
} }
func Text(c *gin.Context, link *base.Link) { func Text(c *gin.Context, link *base.Link) {

View File

@ -15,6 +15,8 @@ func InitApiRouter(r *gin.Engine) {
Cors(r) Cors(r)
r.GET("/d/*path", middlewares.DownCheck, controllers.Down) r.GET("/d/*path", middlewares.DownCheck, controllers.Down)
r.GET("/p/*path", middlewares.DownCheck, controllers.Proxy) r.GET("/p/*path", middlewares.DownCheck, controllers.Proxy)
r.GET("/favicon.ico", controllers.Favicon)
r.GET("/i/:data/ipa.plist", controllers.Plist)
api := r.Group("/api") api := r.Group("/api")
public := api.Group("/public") public := api.Group("/public")
@ -32,7 +34,7 @@ func InitApiRouter(r *gin.Engine) {
admin := api.Group("/admin") admin := api.Group("/admin")
{ {
admin.Use(middlewares.Auth) admin.Use(middlewares.Auth)
admin.GET("/login", common.Login) admin.Any("/login", common.Login)
admin.GET("/settings", controllers.GetSettings) admin.GET("/settings", controllers.GetSettings)
admin.POST("/settings", controllers.SaveSettings) admin.POST("/settings", controllers.SaveSettings)
admin.DELETE("/setting", controllers.DeleteSetting) admin.DELETE("/setting", controllers.DeleteSetting)
@ -56,6 +58,7 @@ func InitApiRouter(r *gin.Engine) {
admin.POST("/move", file.Move) admin.POST("/move", file.Move)
admin.POST("/copy", file.Copy) admin.POST("/copy", file.Copy)
admin.POST("/folder", file.Folder) admin.POST("/folder", file.Folder)
admin.POST("/refresh", file.RefreshFolder)
} }
WebDav(r) WebDav(r)
Static(r) Static(r)

View File

@ -1,7 +1,6 @@
package server package server
import ( import (
"fmt"
"github.com/Xhofe/alist/conf" "github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/public" "github.com/Xhofe/alist/public"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -15,24 +14,21 @@ import (
func InitIndex() { func InitIndex() {
var index fs.File var index fs.File
var err error var err error
//if conf.Conf.Local { if !strings.Contains(conf.Conf.Assets, "/") {
// index, err = public.Public.Open("local.html")
//} else {
// index, err = public.Public.Open("index.html")
//}
if conf.Conf.Assets == "" {
conf.Conf.Assets = conf.DefaultConfig().Assets conf.Conf.Assets = conf.DefaultConfig().Assets
} }
index, err = public.Public.Open(fmt.Sprintf("%s.html", conf.Conf.Assets)) index, err = public.Public.Open("index.html")
if err != nil { if err != nil {
log.Error(err.Error()) log.Fatal(err.Error())
index, err = public.Public.Open("index.html")
if err != nil {
log.Fatal(err.Error())
}
} }
data, _ := ioutil.ReadAll(index) data, _ := ioutil.ReadAll(index)
cdnUrl := strings.ReplaceAll(conf.Conf.Assets, "$version", conf.WebTag)
cdnUrl = strings.TrimRight(cdnUrl, "/")
conf.RawIndexHtml = string(data) conf.RawIndexHtml = string(data)
if strings.Contains(conf.RawIndexHtml, "CDN_URL") {
conf.RawIndexHtml = strings.ReplaceAll(conf.RawIndexHtml, "/CDN_URL", cdnUrl)
conf.RawIndexHtml = strings.ReplaceAll(conf.RawIndexHtml, "assets/", cdnUrl+"/assets/")
}
} }
func Static(r *gin.Engine) { func Static(r *gin.Engine) {
@ -48,8 +44,8 @@ func Static(r *gin.Engine) {
r.StaticFS("/assets/", http.FS(assets)) r.StaticFS("/assets/", http.FS(assets))
r.StaticFS("/public/", http.FS(pub)) r.StaticFS("/public/", http.FS(pub))
r.NoRoute(func(c *gin.Context) { r.NoRoute(func(c *gin.Context) {
c.Status(200)
c.Header("Content-Type", "text/html") c.Header("Content-Type", "text/html")
c.Status(200)
if strings.HasPrefix(c.Request.URL.Path, "/@manage") { if strings.HasPrefix(c.Request.URL.Path, "/@manage") {
_, _ = c.Writer.WriteString(conf.ManageHtml) _, _ = c.Writer.WriteString(conf.ManageHtml)
} else { } else {

View File

@ -24,8 +24,13 @@ import (
type FileSystem struct{} type FileSystem struct{}
var upFileMap = make(map[string]*model.File)
func (fs *FileSystem) File(rawPath string) (*model.File, error) { func (fs *FileSystem) File(rawPath string) (*model.File, error) {
rawPath = utils.ParsePath(rawPath) rawPath = utils.ParsePath(rawPath)
if f, ok := upFileMap[rawPath]; ok {
return f, nil
}
if model.AccountsCount() > 1 && rawPath == "/" { if model.AccountsCount() > 1 && rawPath == "/" {
now := time.Now() now := time.Now()
return &model.File{ return &model.File{
@ -45,18 +50,17 @@ func (fs *FileSystem) File(rawPath string) (*model.File, error) {
func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) { func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) {
rawPath = utils.ParsePath(rawPath) rawPath = utils.ParsePath(rawPath)
var files []model.File
var err error
if model.AccountsCount() > 1 && rawPath == "/" { if model.AccountsCount() > 1 && rawPath == "/" {
files, err := model.GetAccountFiles() files, err = model.GetAccountFiles()
} else {
account, path_, driver, err := common.ParsePath(rawPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return files, nil files, err = operate.Files(driver, account, path_)
} }
account, path_, driver, err := common.ParsePath(rawPath)
if err != nil {
return nil, err
}
files, err := operate.Files(driver, account, path_)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -91,7 +95,7 @@ func ClientIP(r *http.Request) string {
return "" return ""
} }
func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) { func (fs *FileSystem) Link(w http.ResponseWriter, r *http.Request, rawPath string) (string, error) {
rawPath = utils.ParsePath(rawPath) rawPath = utils.ParsePath(rawPath)
log.Debugf("get link path: %s", rawPath) log.Debugf("get link path: %s", rawPath)
if model.AccountsCount() > 1 && rawPath == "/" { if model.AccountsCount() > 1 && rawPath == "/" {
@ -106,6 +110,19 @@ func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
if r.TLS != nil { if r.TLS != nil {
protocol = "https" protocol = "https"
} }
// 直接返回
if account.WebdavDirect {
file, err := fs.File(rawPath)
if err != nil {
return "", err
}
link_, err := driver.Link(base.Args{Path: path_}, account)
if err != nil {
return "", err
}
err = common.Proxy(w, r, link_, file)
return "", err
}
if driver.Config().OnlyProxy || account.WebdavProxy { if driver.Config().OnlyProxy || account.WebdavProxy {
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath) link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
if conf.GetBool("check down link") { if conf.GetBool("check down link") {
@ -154,6 +171,17 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
// return err // return err
//} //}
filePath, fileName := filepath.Split(path_) filePath, fileName := filepath.Split(path_)
now := time.Now()
if fileSize == 0 {
upFileMap[rawPath] = &model.File{
Name: fileName,
Size: 0,
UpdatedAt: &now,
}
return nil
} else {
delete(upFileMap, rawPath)
}
fileData := model.FileStream{ fileData := model.FileStream{
MIMEType: r.Header.Get("Content-Type"), MIMEType: r.Header.Get("Content-Type"),
File: r.Body, File: r.Body,

View File

@ -237,11 +237,14 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
} }
w.Header().Set("ETag", etag) w.Header().Set("ETag", etag)
log.Debugf("url: %+v", r.URL) log.Debugf("url: %+v", r.URL)
link, err := fs.Link(r, reqPath) link, err := fs.Link(w, r, reqPath)
if err != nil { if err != nil {
log.Debugf("webdav link error: %s", err.Error())
return http.StatusInternalServerError, err return http.StatusInternalServerError, err
} }
http.Redirect(w, r, link, 302) if link != "" {
http.Redirect(w, r, link, 302)
}
return 0, nil return 0, nil
} }

View File

@ -123,3 +123,15 @@ func Join(elem ...string) string {
} }
return res return res
} }
func Split(p string) (string, string) {
return path.Split(p)
}
// FormatName TODO
func FormatName(name string) string {
name = strings.ReplaceAll(name, "/", " ")
name = strings.ReplaceAll(name, "#", " ")
name = strings.ReplaceAll(name, "?", " ")
return name
}

25
utils/random.go Normal file
View File

@ -0,0 +1,25 @@
package utils
import (
"math/rand"
"strings"
"time"
)
var Rand *rand.Rand
func init() {
s := rand.NewSource(time.Now().UnixNano())
Rand = rand.New(s)
}
func RandomStr(n int) string {
builder := strings.Builder{}
t := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
for i := 0; i < n; i++ {
r := Rand.Intn(len(t))
builder.WriteString(t[r : r+1])
}
return builder.String()
}