Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
f1a9b68022 | |||
dda1da4576 | |||
5b7aa9c1cf | |||
a28aaceaad | |||
2bb200af87 | |||
97f1efbb72 | |||
bf8b6f4c2c | |||
bd33c200dc | |||
bc6baf1be0 | |||
dc8d5106f9 | |||
8c0dfe2f3d | |||
4e1be9bee6 | |||
4c5285e094 | |||
0838feeb82 | |||
ae791c8634 | |||
09f480318c | |||
4c5be5f07f | |||
9c1ffdbb82 | |||
18a63e34dd | |||
ff0bcfef8a | |||
4980b71ba3 | |||
b5bf5f4325 | |||
f9788ea7cf | |||
83644dab85 | |||
d94cf72da2 | |||
e98561ceb1 | |||
76f37373e0 | |||
61a06992c3 | |||
ddcba93eea | |||
bb969d8dc6 | |||
2383e851e2 | |||
330a767fd7 | |||
2b902de6fd | |||
85e1350af8 | |||
c09800790b | |||
25fd343069 | |||
518487e3df | |||
a02d9c8463 | |||
8beeba7c0c | |||
50fb49f0c3 | |||
4dcaa24758 | |||
3fbdf6f022 | |||
aa9ba289bb | |||
3b6d8987db | |||
6e3df9f847 |
9
.github/workflows/auto_lang.yml
vendored
9
.github/workflows/auto_lang.yml
vendored
@ -7,6 +7,8 @@ on:
|
||||
paths:
|
||||
- 'drivers/**'
|
||||
- 'internal/bootstrap/data/setting.go'
|
||||
- 'internal/conf/const.go'
|
||||
- 'cmd/lang.go'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@ -19,12 +21,12 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Setup go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout alist
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: alist
|
||||
|
||||
@ -42,6 +44,7 @@ jobs:
|
||||
cd alist
|
||||
go run ./main.go lang
|
||||
cd ..
|
||||
|
||||
- name: Copy lang file
|
||||
run: |
|
||||
cp -f ./alist/lang/*.json ./alist-web/src/lang/en/ 2>/dev/null || :
|
||||
@ -61,4 +64,4 @@ jobs:
|
||||
github_token: ${{ secrets.MY_TOKEN }}
|
||||
branch: main
|
||||
directory: alist-web
|
||||
repository: alist-org/alist-web
|
||||
repository: alist-org/alist-web
|
||||
|
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@ -2,9 +2,9 @@ name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ '**' ]
|
||||
branches: [ 'main' ]
|
||||
pull_request:
|
||||
branches: [ '**' ]
|
||||
branches: [ 'main' ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
@ -25,8 +25,8 @@ jobs:
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
docker pull techknowlogick/xgo:latest
|
||||
go install src.techknowlogick.com/xgo@latest
|
||||
docker pull crazymax/xgo:latest
|
||||
go install github.com/crazy-max/xgo@latest
|
||||
sudo apt install upx
|
||||
|
||||
- name: Build
|
||||
@ -34,7 +34,7 @@ jobs:
|
||||
bash build.sh dev
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: alist
|
||||
path: dist
|
12
.github/workflows/build_docker.yml
vendored
12
.github/workflows/build_docker.yml
vendored
@ -10,27 +10,27 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: xhofe/alist
|
||||
- name: Replace release with dev
|
||||
run: |
|
||||
sed -i 's/release/dev/g' Dockerfile
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: xhofe
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
2
.github/workflows/issue_duplicate.yml
vendored
2
.github/workflows/issue_duplicate.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
if: github.event.label.name == 'duplicate'
|
||||
steps:
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
2
.github/workflows/issue_invalid.yml
vendored
2
.github/workflows/issue_invalid.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
if: github.event.label.name == 'invalid'
|
||||
steps:
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
2
.github/workflows/issue_question.yml
vendored
2
.github/workflows/issue_question.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
if: github.event.label.name == 'question'
|
||||
steps:
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@v2.0.0
|
||||
uses: actions-cool/issues-helper@v3.3.3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
2
.github/workflows/issue_wontfix.yml
vendored
2
.github/workflows/issue_wontfix.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
if: github.event.label.name == 'wontfix'
|
||||
steps:
|
||||
- name: Create comment
|
||||
uses: actions-cool/issues-helper@v2
|
||||
uses: actions-cool/issues-helper@v3
|
||||
with:
|
||||
actions: 'create-comment'
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: npx changelogithub # or changelogithub@0.12 if ensure the stable result
|
||||
@ -27,19 +27,19 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
docker pull techknowlogick/xgo:latest
|
||||
go install src.techknowlogick.com/xgo@latest
|
||||
docker pull crazymax/xgo:latest
|
||||
go install github.com/crazy-max/xgo@latest
|
||||
sudo apt install upx
|
||||
|
||||
- name: Build
|
||||
|
12
.github/workflows/release_docker.yml
vendored
12
.github/workflows/release_docker.yml
vendored
@ -11,29 +11,29 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v3
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: xhofe/alist
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: xhofe
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
id: docker_build
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
|
@ -15,6 +15,7 @@ func Init() {
|
||||
bootstrap.InitConfig()
|
||||
bootstrap.Log()
|
||||
bootstrap.InitDB()
|
||||
bootstrap.InitIndex()
|
||||
data.InitData()
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ func writeFile(name string, data interface{}) {
|
||||
} else {
|
||||
log.Infof("%s.json changed, update file", name)
|
||||
//log.Infof("old: %+v\nnew:%+v", oldData, data)
|
||||
utils.WriteJsonToFile(fmt.Sprintf("lang/%s.json", name), data)
|
||||
utils.WriteJsonToFile(fmt.Sprintf("lang/%s.json", name), newData, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,9 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -70,7 +70,7 @@ func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
|
||||
|
||||
func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
if f, ok := file.(File); ok {
|
||||
var resp DownResp
|
||||
//var resp DownResp
|
||||
var headers map[string]string
|
||||
if !utils.IsLocalIPAddr(args.IP) {
|
||||
headers = map[string]string{
|
||||
@ -87,13 +87,14 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
||||
"size": f.Size,
|
||||
"type": f.Type,
|
||||
}
|
||||
_, err := d.request("https://www.123pan.com/api/file/download_info", http.MethodPost, func(req *resty.Request) {
|
||||
resp, err := d.request("https://www.123pan.com/api/file/download_info", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data).SetHeaders(headers)
|
||||
}, &resp)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
u, err := url.Parse(resp.Data.DownloadUrl)
|
||||
downloadUrl := utils.Json.Get(resp, "data", "DownloadUrl").ToString()
|
||||
u, err := url.Parse(downloadUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -112,7 +113,7 @@ func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs)
|
||||
}
|
||||
log.Debug(res.String())
|
||||
link := model.Link{
|
||||
URL: resp.Data.DownloadUrl,
|
||||
URL: downloadUrl,
|
||||
}
|
||||
log.Debugln("res code: ", res.StatusCode())
|
||||
if res.StatusCode() == 302 {
|
||||
@ -229,13 +230,13 @@ func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStr
|
||||
"type": 0,
|
||||
}
|
||||
var resp UploadResp
|
||||
_, err := d.request("https://www.123pan.com/api/file/upload_request", http.MethodPost, func(req *resty.Request) {
|
||||
_, err := d.request("https://www.123pan.com/a/api/file/upload_request", http.MethodPost, func(req *resty.Request) {
|
||||
req.SetBody(data)
|
||||
}, &resp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Data.Key == "" {
|
||||
if resp.Data.Reuse || resp.Data.Key == "" {
|
||||
return nil
|
||||
}
|
||||
cfg := &aws.Config{
|
||||
|
@ -5,15 +5,16 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
type BaseResp struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
//type BaseResp struct {
|
||||
// Code interface{} `json:"code"`
|
||||
// Message string `json:"message"`
|
||||
//}
|
||||
|
||||
type TokenResp struct {
|
||||
BaseResp
|
||||
//BaseResp
|
||||
Data struct {
|
||||
Token string `json:"token"`
|
||||
} `json:"data"`
|
||||
@ -39,7 +40,7 @@ func (f File) GetSize() int64 {
|
||||
}
|
||||
|
||||
func (f File) GetName() string {
|
||||
return f.FileName
|
||||
return utils.MappingName(f.FileName)
|
||||
}
|
||||
|
||||
func (f File) ModTime() time.Time {
|
||||
@ -62,22 +63,22 @@ var _ model.Obj = (*File)(nil)
|
||||
//var _ model.Thumb = (*File)(nil)
|
||||
|
||||
type Files struct {
|
||||
BaseResp
|
||||
//BaseResp
|
||||
Data struct {
|
||||
InfoList []File `json:"InfoList"`
|
||||
Next string `json:"Next"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type DownResp struct {
|
||||
BaseResp
|
||||
Data struct {
|
||||
DownloadUrl string `json:"DownloadUrl"`
|
||||
} `json:"data"`
|
||||
}
|
||||
//type DownResp struct {
|
||||
// //BaseResp
|
||||
// Data struct {
|
||||
// DownloadUrl string `json:"DownloadUrl"`
|
||||
// } `json:"data"`
|
||||
//}
|
||||
|
||||
type UploadResp struct {
|
||||
BaseResp
|
||||
//BaseResp
|
||||
Data struct {
|
||||
AccessKeyId string `json:"AccessKeyId"`
|
||||
Bucket string `json:"Bucket"`
|
||||
@ -85,5 +86,6 @@ type UploadResp struct {
|
||||
SecretAccessKey string `json:"SecretAccessKey"`
|
||||
SessionToken string `json:"SessionToken"`
|
||||
FileId int64 `json:"FileId"`
|
||||
Reuse bool `json:"Reuse"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
|
||||
func (d *Pan123) login() error {
|
||||
var body base.Json
|
||||
url := "https://www.123pan.com/api/user/sign_in"
|
||||
url := "https://www.123pan.com/a/api/user/sign_in"
|
||||
if utils.IsEmailFormat(d.Username) {
|
||||
body = base.Json{
|
||||
"mail": d.Username,
|
||||
@ -29,14 +29,14 @@ func (d *Pan123) login() error {
|
||||
}
|
||||
}
|
||||
var resp TokenResp
|
||||
_, err := base.RestyClient.R().
|
||||
res, err := base.RestyClient.R().
|
||||
SetResult(&resp).
|
||||
SetBody(body).Post(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if resp.Code != 200 {
|
||||
err = fmt.Errorf(resp.Message)
|
||||
if utils.Json.Get(res.Body(), "code").ToInt() != 200 {
|
||||
err = fmt.Errorf(utils.Json.Get(res.Body(), "message").ToString())
|
||||
} else {
|
||||
d.AccessToken = resp.Data.Token
|
||||
}
|
||||
@ -45,7 +45,12 @@ func (d *Pan123) login() error {
|
||||
|
||||
func (d *Pan123) request(url string, method string, callback base.ReqCallback, resp interface{}) ([]byte, error) {
|
||||
req := base.RestyClient.R()
|
||||
req.SetHeader("Authorization", "Bearer "+d.AccessToken)
|
||||
req.SetHeaders(map[string]string{
|
||||
"origin": "https://www.123pan.com",
|
||||
"authorization": "Bearer " + d.AccessToken,
|
||||
"platform": "web",
|
||||
"app-version": "1.2",
|
||||
})
|
||||
if callback != nil {
|
||||
callback(req)
|
||||
}
|
||||
@ -57,7 +62,7 @@ func (d *Pan123) request(url string, method string, callback base.ReqCallback, r
|
||||
return nil, err
|
||||
}
|
||||
body := res.Body()
|
||||
code := jsoniter.Get(body, "code").ToInt()
|
||||
code := utils.Json.Get(body, "code").ToInt()
|
||||
if code != 0 {
|
||||
if code == 401 {
|
||||
err := d.login()
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
// 居然有四种返回方式
|
||||
@ -134,7 +136,7 @@ type Cloud189File struct {
|
||||
}
|
||||
|
||||
func (c *Cloud189File) GetSize() int64 { return c.Size }
|
||||
func (c *Cloud189File) GetName() string { return c.Name }
|
||||
func (c *Cloud189File) GetName() string { return utils.MappingName(c.Name) }
|
||||
func (c *Cloud189File) ModTime() time.Time {
|
||||
if c.parseTime == nil {
|
||||
c.parseTime = MustParseTime(c.LastOpTime)
|
||||
@ -166,7 +168,7 @@ type Cloud189Folder struct {
|
||||
}
|
||||
|
||||
func (c *Cloud189Folder) GetSize() int64 { return 0 }
|
||||
func (c *Cloud189Folder) GetName() string { return c.Name }
|
||||
func (c *Cloud189Folder) GetName() string { return utils.MappingName(c.Name) }
|
||||
func (c *Cloud189Folder) ModTime() time.Time {
|
||||
if c.parseTime == nil {
|
||||
c.parseTime = MustParseTime(c.LastOpTime)
|
||||
|
@ -30,6 +30,9 @@ func (d *AListV2) Init(ctx context.Context, storage model.Storage) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(d.Addition.Address) > 0 && string(d.Addition.Address[len(d.Addition.Address)-1]) == "/" {
|
||||
d.Addition.Address = d.Addition.Address[0 : len(d.Addition.Address)-1]
|
||||
}
|
||||
// TODO login / refresh token
|
||||
//op.MustSaveDriverStorage(d)
|
||||
return err
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/alist-org/alist/v3/server/handles"
|
||||
)
|
||||
|
||||
type AListV3 struct {
|
||||
@ -31,6 +30,9 @@ func (d *AListV3) Init(ctx context.Context, storage model.Storage) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(d.Addition.Address) > 0 && string(d.Addition.Address[len(d.Addition.Address)-1]) == "/" {
|
||||
d.Addition.Address = d.Addition.Address[0 : len(d.Addition.Address)-1]
|
||||
}
|
||||
// TODO login / refresh token
|
||||
//op.MustSaveDriverStorage(d)
|
||||
return err
|
||||
@ -42,12 +44,12 @@ func (d *AListV3) Drop(ctx context.Context) error {
|
||||
|
||||
func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||
url := d.Address + "/api/fs/list"
|
||||
var resp common.Resp[handles.FsListResp]
|
||||
var resp common.Resp[FsListResp]
|
||||
_, err := base.RestyClient.R().
|
||||
SetResult(&resp).
|
||||
SetHeader("Authorization", d.AccessToken).
|
||||
SetBody(handles.ListReq{
|
||||
PageReq: common.PageReq{
|
||||
SetBody(ListReq{
|
||||
PageReq: model.PageReq{
|
||||
Page: 1,
|
||||
PerPage: 0,
|
||||
},
|
||||
@ -81,11 +83,11 @@ func (d *AListV3) List(ctx context.Context, dir model.Obj, args model.ListArgs)
|
||||
|
||||
func (d *AListV3) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
url := d.Address + "/api/fs/get"
|
||||
var resp common.Resp[handles.FsGetResp]
|
||||
var resp common.Resp[FsGetResp]
|
||||
_, err := base.RestyClient.R().
|
||||
SetResult(&resp).
|
||||
SetHeader("Authorization", d.AccessToken).
|
||||
SetBody(handles.FsGetReq{
|
||||
SetBody(FsGetReq{
|
||||
Path: file.GetPath(),
|
||||
Password: d.Password,
|
||||
}).Post(url)
|
||||
|
@ -1 +1,45 @@
|
||||
package alist_v3
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
type ListReq struct {
|
||||
model.PageReq
|
||||
Path string `json:"path" form:"path"`
|
||||
Password string `json:"password" form:"password"`
|
||||
Refresh bool `json:"refresh"`
|
||||
}
|
||||
|
||||
type ObjResp struct {
|
||||
Name string `json:"name"`
|
||||
Size int64 `json:"size"`
|
||||
IsDir bool `json:"is_dir"`
|
||||
Modified time.Time `json:"modified"`
|
||||
Sign string `json:"sign"`
|
||||
Thumb string `json:"thumb"`
|
||||
Type int `json:"type"`
|
||||
}
|
||||
|
||||
type FsListResp struct {
|
||||
Content []ObjResp `json:"content"`
|
||||
Total int64 `json:"total"`
|
||||
Readme string `json:"readme"`
|
||||
Write bool `json:"write"`
|
||||
Provider string `json:"provider"`
|
||||
}
|
||||
|
||||
type FsGetReq struct {
|
||||
Path string `json:"path" form:"path"`
|
||||
Password string `json:"password" form:"password"`
|
||||
}
|
||||
|
||||
type FsGetResp struct {
|
||||
ObjResp
|
||||
RawURL string `json:"raw_url"`
|
||||
Readme string `json:"readme"`
|
||||
Provider string `json:"provider"`
|
||||
Related []ObjResp `json:"related"`
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ func (d *AliDrive) request(url, method string, callback base.ReqCallback, resp i
|
||||
return d.request(url, method, callback, resp)
|
||||
}
|
||||
return nil, errors.New(e.Message), e
|
||||
} else if res.IsError() {
|
||||
return nil, errors.New("bad status code " + res.Status()), e
|
||||
}
|
||||
return res.Body(), nil, e
|
||||
}
|
||||
|
@ -80,62 +80,40 @@ func (d *AliyundriveShare) List(ctx context.Context, dir model.Obj, args model.L
|
||||
|
||||
func (d *AliyundriveShare) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
data := base.Json{
|
||||
"drive_id": d.DriveId,
|
||||
"file_id": file.GetID(),
|
||||
"expire_sec": 14400,
|
||||
"drive_id": d.DriveId,
|
||||
"file_id": file.GetID(),
|
||||
// // Only ten minutes lifetime
|
||||
"expire_sec": 600,
|
||||
"share_id": d.ShareId,
|
||||
}
|
||||
var resp ShareLinkResp
|
||||
var e ErrorResp
|
||||
res, err := base.RestyClient.R().
|
||||
SetError(&e).SetBody(data).
|
||||
_, err := base.RestyClient.R().
|
||||
SetError(&e).SetBody(data).SetResult(&resp).
|
||||
SetHeader("content-type", "application/json").
|
||||
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
|
||||
Post("https://api.aliyundrive.com/v2/file/get_download_url")
|
||||
SetHeader("x-share-token", d.ShareToken).
|
||||
Post("https://api.aliyundrive.com/v2/file/get_share_link_download_url")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var u string
|
||||
if e.Code != "" {
|
||||
if e.Code == "AccessTokenInvalid" {
|
||||
err = d.refreshToken()
|
||||
if e.Code == "AccessTokenInvalid" || e.Code == "ShareLinkTokenInvalid" {
|
||||
if e.Code == "AccessTokenInvalid" {
|
||||
err = d.refreshToken()
|
||||
} else {
|
||||
err = d.getShareToken()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.Link(ctx, file, args)
|
||||
} else if e.Code == "ForbiddenNoPermission.File" {
|
||||
data = utils.MergeMap(data, base.Json{
|
||||
// Only ten minutes valid
|
||||
"expire_sec": 600,
|
||||
"share_id": d.ShareId,
|
||||
})
|
||||
var resp ShareLinkResp
|
||||
var e2 ErrorResp
|
||||
_, err = base.RestyClient.R().
|
||||
SetError(&e2).SetBody(data).SetResult(&resp).
|
||||
SetHeader("content-type", "application/json").
|
||||
SetHeader("Authorization", "Bearer\t"+d.AccessToken).
|
||||
SetHeader("x-share-token", d.ShareToken).
|
||||
Post("https://api.aliyundrive.com/v2/file/get_share_link_download_url")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if e2.Code != "" {
|
||||
if e2.Code == "AccessTokenInvalid" || e2.Code == "ShareLinkTokenInvalid" {
|
||||
err = d.getShareToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.Link(ctx, file, args)
|
||||
} else {
|
||||
return nil, errors.New(e2.Code + ":" + e2.Message)
|
||||
}
|
||||
} else {
|
||||
u = resp.DownloadUrl
|
||||
}
|
||||
} else {
|
||||
return nil, errors.New(e.Code + ":" + e.Message)
|
||||
return nil, errors.New(e.Code + ": " + e.Message)
|
||||
}
|
||||
} else {
|
||||
u = utils.Json.Get(res.Body(), "url").ToString()
|
||||
u = resp.DownloadUrl
|
||||
}
|
||||
return &model.Link{
|
||||
Header: http.Header{
|
||||
|
@ -3,6 +3,8 @@ package baiduphoto
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
type TokenErrResp struct {
|
||||
@ -100,7 +102,7 @@ type (
|
||||
)
|
||||
|
||||
func (a *Album) GetSize() int64 { return 0 }
|
||||
func (a *Album) GetName() string { return fmt.Sprint(a.Title) }
|
||||
func (a *Album) GetName() string { return utils.MappingName(a.Title) }
|
||||
func (a *Album) ModTime() time.Time {
|
||||
if a.parseTime == nil {
|
||||
a.parseTime = toTime(a.Mtime)
|
||||
|
@ -63,6 +63,10 @@ func (d *GoogleDrive) List(ctx context.Context, dir model.Obj, args model.ListAr
|
||||
|
||||
func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
|
||||
_, err := d.request(url, http.MethodGet, nil, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link := model.Link{
|
||||
URL: url + "&alt=media",
|
||||
Header: http.Header{
|
||||
|
@ -12,7 +12,7 @@ type Addition struct {
|
||||
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"`
|
||||
ClientID string `json:"client_id" required:"true" default:"202264815644.apps.googleusercontent.com"`
|
||||
ClientSecret string `json:"client_secret" required:"true" default:"X4Z3ca8xfWDb1Voo-F9a7ZxJ"`
|
||||
ChunkSize int64 `json:"chunk_size" default:"5" help:"chunk size while uploading (unit: MB)"`
|
||||
ChunkSize int64 `json:"chunk_size" type:"number" default:"5" help:"chunk size while uploading (unit: MB)"`
|
||||
}
|
||||
|
||||
var config = driver.Config{
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/disintegration/imaging"
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
type Local struct {
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/driver"
|
||||
@ -34,6 +35,9 @@ func (d *SMB) Init(ctx context.Context, storage model.Storage) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if strings.Index(d.Addition.Address, ":") < 0{
|
||||
d.Addition.Address = d.Addition.Address + ":445"
|
||||
}
|
||||
return d.initFS()
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
type ErrResp struct {
|
||||
@ -147,7 +149,7 @@ type Files struct {
|
||||
}
|
||||
|
||||
func (c *Files) GetSize() int64 { size, _ := strconv.ParseInt(c.Size, 10, 64); return size }
|
||||
func (c *Files) GetName() string { return c.Name }
|
||||
func (c *Files) GetName() string { return utils.MappingName(c.Name) }
|
||||
func (c *Files) ModTime() time.Time { return c.ModifiedTime }
|
||||
func (c *Files) IsDir() bool { return c.Kind == FOLDER }
|
||||
func (c *Files) GetID() string { return c.ID }
|
||||
|
@ -73,24 +73,14 @@ func (d *WebDav) List(ctx context.Context, dir model.Obj, args model.ListArgs) (
|
||||
//}
|
||||
|
||||
func (d *WebDav) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||
callback := func(r *http.Request) {
|
||||
if args.Header.Get("Range") != "" {
|
||||
r.Header.Set("Range", args.Header.Get("Range"))
|
||||
}
|
||||
if args.Header.Get("If-Range") != "" {
|
||||
r.Header.Set("If-Range", args.Header.Get("If-Range"))
|
||||
}
|
||||
}
|
||||
reader, header, err := d.client.ReadStream(file.GetPath(), callback)
|
||||
url, header, err := d.client.Link(file.GetPath())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
link := &model.Link{Data: reader}
|
||||
if header.Get("Content-Range") != "" {
|
||||
link.Status = 206
|
||||
link.Header = header
|
||||
}
|
||||
return link, nil
|
||||
return &model.Link{
|
||||
URL: url,
|
||||
Header: header,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *WebDav) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||
|
@ -16,7 +16,7 @@ type Addition struct {
|
||||
var config = driver.Config{
|
||||
Name: "WebDav",
|
||||
LocalSort: true,
|
||||
OnlyLocal: true,
|
||||
OnlyProxy: true,
|
||||
DefaultRoot: "/",
|
||||
}
|
||||
|
||||
|
70
go.mod
70
go.mod
@ -3,41 +3,61 @@ module github.com/alist-org/alist/v3
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/SheltonZhu/115driver v1.0.12
|
||||
github.com/SheltonZhu/115driver v1.0.13
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a
|
||||
github.com/aws/aws-sdk-go v1.44.88
|
||||
github.com/caarlos0/env/v6 v6.9.3
|
||||
github.com/aws/aws-sdk-go v1.44.152
|
||||
github.com/blevesearch/bleve/v2 v2.3.5
|
||||
github.com/caarlos0/env/v6 v6.10.1
|
||||
github.com/deckarep/golang-set/v2 v2.1.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/gin-contrib/cors v1.3.1
|
||||
github.com/gin-contrib/cors v1.4.0
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/go-resty/resty/v2 v2.7.0
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/hirochachacha/go-smb2 v1.1.0
|
||||
github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4
|
||||
github.com/jlaffaye/ftp v0.1.0
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pkg/sftp v1.13.5
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/sirupsen/logrus v1.8.1
|
||||
github.com/spf13/cobra v1.5.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf
|
||||
github.com/upyun/go-sdk/v3 v3.0.3
|
||||
github.com/winfsp/cgofuse v1.5.0
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
|
||||
gorm.io/driver/mysql v1.3.4
|
||||
gorm.io/driver/postgres v1.3.7
|
||||
gorm.io/driver/sqlite v1.3.4
|
||||
gorm.io/gorm v1.23.6
|
||||
golang.org/x/crypto v0.3.0
|
||||
golang.org/x/image v0.1.0
|
||||
golang.org/x/net v0.2.0
|
||||
gorm.io/driver/mysql v1.4.4
|
||||
gorm.io/driver/postgres v1.4.5
|
||||
gorm.io/driver/sqlite v1.4.3
|
||||
gorm.io/gorm v1.24.2
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||
github.com/aead/ecdh v0.2.0 // indirect
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible // indirect
|
||||
github.com/andreburgaud/crypt2go v1.1.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.4 // indirect
|
||||
github.com/blevesearch/geo v0.1.15 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.3 // indirect
|
||||
github.com/blevesearch/segment v0.9.0 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
|
||||
github.com/blevesearch/vellum v1.0.9 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.6 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.6 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.6 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.6 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.6 // indirect
|
||||
github.com/bluele/gcache v0.0.2 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/gaoyb7/115drive-webdav v0.1.8 // indirect
|
||||
@ -48,35 +68,39 @@ require (
|
||||
github.com/go-playground/validator/v10 v10.11.0 // indirect
|
||||
github.com/go-sql-driver/mysql v1.6.0 // indirect
|
||||
github.com/goccy/go-json v0.9.7 // indirect
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||
github.com/golang/protobuf v1.5.0 // indirect
|
||||
github.com/golang/snappy v0.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
||||
github.com/jackc/pgconn v1.12.1 // indirect
|
||||
github.com/jackc/pgconn v1.13.0 // indirect
|
||||
github.com/jackc/pgio v1.0.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.0 // indirect
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jackc/pgtype v1.11.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.16.1 // indirect
|
||||
github.com/jackc/pgtype v1.12.0 // indirect
|
||||
github.com/jackc/pgx/v4 v4.17.2 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.13 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.15 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/ugorji/go/codec v1.2.7 // indirect
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
|
||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
go.etcd.io/bbolt v1.3.5 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
|
||||
google.golang.org/protobuf v1.28.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
|
178
go.sum
178
go.sum
@ -2,8 +2,10 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/SheltonZhu/115driver v1.0.12 h1:+GlIM5h8tzuec6MzK0wFwb7bY77nav7JhY8lTljzls4=
|
||||
github.com/SheltonZhu/115driver v1.0.12/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
||||
github.com/RoaringBitmap/roaring v0.9.4 h1:ckvZSX5gwCRaJYBNe7syNawCU5oruY9gQmjXlp4riwo=
|
||||
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
|
||||
github.com/SheltonZhu/115driver v1.0.13 h1:YEhQ3iMvd5TD6Xp1wKg+73KgdCMjc0pDoT1eCNXnA3M=
|
||||
github.com/SheltonZhu/115driver v1.0.13/go.mod h1:00ixivHH5HqDj4S7kAWbkuUrjtsJTxc7cGv5RMw3RVs=
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a h1:RenIAa2q4H8UcS/cqmwdT1WCWIAH5aumP8m8RpbqVsE=
|
||||
github.com/Xhofe/go-cache v0.0.0-20220723083548-714439c8af9a/go.mod h1:sSBbaOg90XwWKtpT56kVujF0bIeVITnPlssLclogS04=
|
||||
github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ=
|
||||
@ -12,14 +14,49 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible h1:QoRMR0TCctLDqBCMyOu1e
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.5+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/andreburgaud/crypt2go v1.1.0 h1:eitZxTPY1krUsxinsng3Qvt/Ud7q/aQmmYRh8p4hyPw=
|
||||
github.com/andreburgaud/crypt2go v1.1.0/go.mod h1:4qhZPzarj1dCIRmCkpdgCklwp+hBq9yEt0zPe9Ayuhc=
|
||||
github.com/aws/aws-sdk-go v1.44.88 h1:9jhiZsTx9koQQsM29RTgwI0g4mfyphCdc3bkUcKrdwA=
|
||||
github.com/aws/aws-sdk-go v1.44.88/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
github.com/aws/aws-sdk-go v1.44.152 h1:L9aaepO8wHB67gwuGD8VgIYH/cmQDxieCt7FeLa0+fI=
|
||||
github.com/aws/aws-sdk-go v1.44.152/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
|
||||
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
|
||||
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/blevesearch/bleve/v2 v2.3.5 h1:1wuR7eB8Fk9UaCaBUfnQt5V7zIpi4VDok9ExN7Rl+/8=
|
||||
github.com/blevesearch/bleve/v2 v2.3.5/go.mod h1:FneKGHMRrCLrp4X9+iy3wlBqgM2ALucg7bp8jUuAi/s=
|
||||
github.com/blevesearch/bleve_index_api v1.0.3/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
|
||||
github.com/blevesearch/bleve_index_api v1.0.4 h1:mtlzsyJjMIlDngqqB1mq8kPryUMIuEVVbRbJHOWEexU=
|
||||
github.com/blevesearch/bleve_index_api v1.0.4/go.mod h1:YXMDwaXFFXwncRS8UobWs7nvo0DmusriM1nztTlj1ms=
|
||||
github.com/blevesearch/geo v0.1.15 h1:0NybEduqE5fduFRYiUKF0uqybAIFKXYjkBdXKYn7oA4=
|
||||
github.com/blevesearch/geo v0.1.15/go.mod h1:cRIvqCdk3cgMhGeHNNe6yPzb+w56otxbfo1FBJfR2Pc=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
|
||||
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
|
||||
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
|
||||
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.3 h1:2UzpR2dR5DvSZk8tVJkcQ7D5xhoK/UBelYw8ttBHrRQ=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.3/go.mod h1:eZrfp1y+lUh+DzFjUcTBUSnKGuunyFIpBIvqYVzJfvc=
|
||||
github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac=
|
||||
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
github.com/blevesearch/snowballstem v0.9.0/go.mod h1:PivSj3JMc8WuaFkTSRDW2SlrulNWPl4ABg1tC/hlgLs=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 h1:1SYRwyoFLwG3sj0ed89RLtM15amfX2pXlYbFOnF8zNU=
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1/go.mod h1:MQDVGpHZrpe3Uy26zJBf/a8h0FZY6xJbthIMm8myH2Q=
|
||||
github.com/blevesearch/vellum v1.0.9 h1:PL+NWVk3dDGPCV0hoDu9XLLJgqU4E5s/dOeEJByQ2uQ=
|
||||
github.com/blevesearch/vellum v1.0.9/go.mod h1:ul1oT0FhSMDIExNjIxHqJoGpVrBpKCdgDQNxfqgJt7k=
|
||||
github.com/blevesearch/zapx/v11 v11.3.6 h1:50jET4HUJ6eCqGxdhUt+mjybMvEX2MWyqLGtCx3yUgc=
|
||||
github.com/blevesearch/zapx/v11 v11.3.6/go.mod h1:B0CzJRj/pS7hJIroflRtFsa9mRHpMSucSgre0FVINns=
|
||||
github.com/blevesearch/zapx/v12 v12.3.6 h1:G304NHBLgQeZ+IHK/XRCM0nhHqAts8MEvHI6LhoDNM4=
|
||||
github.com/blevesearch/zapx/v12 v12.3.6/go.mod h1:iYi7tIKpauwU5os5wTxJITixr5Km21Hl365otMwdaP0=
|
||||
github.com/blevesearch/zapx/v13 v13.3.6 h1:vavltQHNdjQezhLZs5nIakf+w/uOa1oqZxB58Jy/3Ig=
|
||||
github.com/blevesearch/zapx/v13 v13.3.6/go.mod h1:X+FsTwCU8qOHtK0d/ArvbOH7qiIgViSQ1GQvcR6LSkI=
|
||||
github.com/blevesearch/zapx/v14 v14.3.6 h1:b9lub7TvcwUyJxK/cQtnN79abngKxsI7zMZnICU0WhE=
|
||||
github.com/blevesearch/zapx/v14 v14.3.6/go.mod h1:9X8W3XoikagU0rwcTqwZho7p9cC7m7zhPZO94S4wUvM=
|
||||
github.com/blevesearch/zapx/v15 v15.3.6 h1:VSswg/ysDxHgitcNkpUNtaTYS4j3uItpXWLAASphl6k=
|
||||
github.com/blevesearch/zapx/v15 v15.3.6/go.mod h1:5DbhhDTGtuQSns1tS2aJxJLPc91boXCvjOMeCLD1saM=
|
||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/caarlos0/env/v6 v6.9.3 h1:Tyg69hoVXDnpO5Qvpsu8EoquarbPyQb+YwExWHP8wWU=
|
||||
github.com/caarlos0/env/v6 v6.9.3/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
github.com/caarlos0/env/v6 v6.10.1 h1:t1mPSxNpei6M5yAeu1qtRdPAK29Nbcf/n3G7x+b3/II=
|
||||
github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5Zu0ddvwc=
|
||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
@ -30,27 +67,26 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
|
||||
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/gaoyb7/115drive-webdav v0.1.8 h1:EJt4PSmcbvBY4KUh2zSo5p6fN9LZFNkIzuKejipubVw=
|
||||
github.com/gaoyb7/115drive-webdav v0.1.8/go.mod h1:BKbeY6j8SKs3+rzBFFALznGxbPmefEm3vA+dGhqgOGU=
|
||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
||||
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
|
||||
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
|
||||
github.com/gin-contrib/cors v1.4.0 h1:oJ6gwtUl3lqV0WEIwM/LxPF1QZ5qe2lGWdY2+bz7y0g=
|
||||
github.com/gin-contrib/cors v1.4.0/go.mod h1:bs9pNM0x/UsmHPBWT2xZz9ROh8xYjYkiURUfmBoMlcs=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8=
|
||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
|
||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
@ -65,13 +101,18 @@ github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@ -84,8 +125,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hirochachacha/go-smb2 v1.1.0 h1:b6hs9qKIql9eVXAiN0M2wSFY5xnhbHAQoCwRKbaRTZI=
|
||||
github.com/hirochachacha/go-smb2 v1.1.0/go.mod h1:8F1A4d5EZzrGu5R7PU163UcMRDJQl4FtcxjBfsY8TZE=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
||||
@ -96,8 +137,8 @@ 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.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.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
|
||||
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
|
||||
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
|
||||
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
|
||||
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/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
||||
@ -113,38 +154,38 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
|
||||
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.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
|
||||
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
|
||||
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
||||
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.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
|
||||
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
||||
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
|
||||
github.com/jackc/pgtype v1.12.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-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.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
|
||||
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
|
||||
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
|
||||
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
|
||||
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 v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
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/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4 h1:8bWaY08VCoFn17gezYWKLhCwAJr2Er8tUOZCvDVshos=
|
||||
github.com/jlaffaye/ftp v0.0.0-20220829015825-b85cf1edccd4/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
|
||||
github.com/jlaffaye/ftp v0.1.0 h1:DLGExl5nBoSFoNshAUHwXAezXwXBvFdx7/qwhucWNSE=
|
||||
github.com/jlaffaye/ftp v0.1.0/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
@ -161,7 +202,6 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
@ -173,20 +213,19 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.13 h1:1tj15ngiFfcZzii7yd82foL+ks+ouQcj8j/TPq3fk1I=
|
||||
github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
||||
github.com/orzogc/fake115uploader v0.3.3-0.20221009101310-08b764073b77 h1:dg/EaaJLPIg4xn2kaZil7Ax3wfoxcFXaBwyOTlcz5AI=
|
||||
@ -219,17 +258,19 @@ github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXY
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
|
||||
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@ -238,12 +279,11 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf h1:Y43S3e9P1NPs/QF4R5/SdlXj2d31540hP4Gk8VKNvDg=
|
||||
github.com/t3rm1n4l/go-mega v0.0.0-20220725095014-c4e0c2b5debf/go.mod h1:c+cGNU1qi9bO7ZF4IRMYk+KaZTNiQ/gQrSbyMmGFq1Q=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/upyun/go-sdk/v3 v3.0.3 h1:2wUkNk2fyJReMYHMvJyav050D83rYwSjN7mEPR0Pp8Q=
|
||||
@ -251,7 +291,10 @@ github.com/upyun/go-sdk/v3 v3.0.3/go.mod h1:P/SnuuwhrIgAVRd/ZpzDWqCsBAf/oHg7Uggb
|
||||
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
|
||||
github.com/winfsp/cgofuse v1.5.0 h1:MsBP7Mi/LiJf/7/F3O/7HjjR009ds6KCdqXzKpZSWxI=
|
||||
github.com/winfsp/cgofuse v1.5.0/go.mod h1:h3awhoUOcn2VYVKCwDaYxSLlZwnyK+A8KaDoLUp2lbU=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
@ -278,14 +321,16 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b h1:huxqepDufQpLLIRXiVkTvnxrzJlpwmIWAObmcCcUFr0=
|
||||
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
|
||||
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk=
|
||||
golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@ -293,10 +338,12 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -306,6 +353,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -313,21 +361,27 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM=
|
||||
golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@ -338,7 +392,9 @@ golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgw
|
||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -354,8 +410,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
@ -367,13 +421,15 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gorm.io/driver/mysql v1.3.4 h1:/KoBMgsUHC3bExsekDcmNYaBnfH2WNeFuXqqrqMc98Q=
|
||||
gorm.io/driver/mysql v1.3.4/go.mod h1:s4Tq0KmD0yhPGHbZEwg1VPlH0vT/GBHJZorPzhcxBUE=
|
||||
gorm.io/driver/postgres v1.3.7 h1:FKF6sIMDHDEvvMF/XJvbnCl0nu6KSKUaPXevJ4r+VYQ=
|
||||
gorm.io/driver/postgres v1.3.7/go.mod h1:f02ympjIcgtHEGFMZvdgTxODZ9snAHDb4hXfigBVuNI=
|
||||
gorm.io/driver/sqlite v1.3.4 h1:NnFOPVfzi4CPsJPH4wXr6rMkPb4ElHEqKMvrsx9c9Fk=
|
||||
gorm.io/driver/sqlite v1.3.4/go.mod h1:B+8GyC9K7VgzJAcrcXMRPdnMcck+8FgJynEehEPM16U=
|
||||
gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.23.6 h1:KFLdNgri4ExFFGTRGGFWON2P1ZN28+9SJRN8voOoYe0=
|
||||
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/driver/mysql v1.4.4 h1:MX0K9Qvy0Na4o7qSC/YI7XxqUw5KDw01umqgID+svdQ=
|
||||
gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM=
|
||||
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
|
||||
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
|
||||
gorm.io/driver/sqlite v1.4.3 h1:HBBcZSDnWi5BW3B3rwvVTc510KGkBkexlOg0QrmLUuU=
|
||||
gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
|
||||
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
|
||||
gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0=
|
||||
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
|
@ -39,7 +39,7 @@ func TestConnect(t *testing.T) {
|
||||
|
||||
func TestDown(t *testing.T) {
|
||||
TestConnect(t)
|
||||
err := op.CreateStorage(context.Background(), model.Storage{
|
||||
_, err := op.CreateStorage(context.Background(), model.Storage{
|
||||
ID: 0,
|
||||
MountPath: "/",
|
||||
Order: 0,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@ -171,7 +172,12 @@ func (m *Monitor) Complete() error {
|
||||
ReadCloser: f,
|
||||
Mimetype: mimetype,
|
||||
}
|
||||
return op.Put(tsk.Ctx, storage, dstDirActualPath, stream, tsk.SetProgress)
|
||||
relDir, err := filepath.Rel(m.tempDir, filepath.Dir(file.Path))
|
||||
if err != nil {
|
||||
log.Errorf("find relation directory error: %v", err)
|
||||
}
|
||||
newDistDir := filepath.Join(dstDirActualPath, relDir)
|
||||
return op.Put(tsk.Ctx, storage, newDistDir, stream, tsk.SetProgress)
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
@ -6,9 +6,11 @@ import (
|
||||
)
|
||||
|
||||
func InitAria2() {
|
||||
_, err := aria2.InitClient(2)
|
||||
if err != nil {
|
||||
//utils.Log.Errorf("failed to init aria2 client: %+v", err)
|
||||
utils.Log.Infof("Aria2 not ready.")
|
||||
}
|
||||
go func() {
|
||||
_, err := aria2.InitClient(2)
|
||||
if err != nil {
|
||||
//utils.Log.Errorf("failed to init aria2 client: %+v", err)
|
||||
utils.Log.Infof("Aria2 not ready.")
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -13,12 +13,14 @@ import (
|
||||
|
||||
func InitConfig() {
|
||||
if flags.ForceBinDir {
|
||||
ex, err := os.Executable()
|
||||
if err != nil {
|
||||
utils.Log.Fatal(err)
|
||||
if !filepath.IsAbs(flags.DataDir) {
|
||||
ex, err := os.Executable()
|
||||
if err != nil {
|
||||
utils.Log.Fatal(err)
|
||||
}
|
||||
exPath := filepath.Dir(ex)
|
||||
flags.DataDir = filepath.Join(exPath, flags.DataDir)
|
||||
}
|
||||
exPath := filepath.Dir(ex)
|
||||
flags.DataDir = filepath.Join(exPath, "data")
|
||||
}
|
||||
configPath := filepath.Join(flags.DataDir, "config.json")
|
||||
log.Infof("reading config file: %s", configPath)
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func initDevData() {
|
||||
err := op.CreateStorage(context.Background(), model.Storage{
|
||||
_, err := op.CreateStorage(context.Background(), model.Storage{
|
||||
MountPath: "/",
|
||||
Order: 0,
|
||||
Driver: "Local",
|
||||
|
@ -25,12 +25,13 @@ func initSettings() {
|
||||
settings[i].Flag = model.DEPRECATED
|
||||
}
|
||||
}
|
||||
if settings != nil && len(settings) > 0 {
|
||||
err = db.SaveSettingItems(settings)
|
||||
if err != nil {
|
||||
log.Fatalf("failed save settings: %+v", err)
|
||||
}
|
||||
}
|
||||
// what's going on here???
|
||||
//if settings != nil && len(settings) > 0 {
|
||||
// err = db.SaveSettingItems(settings)
|
||||
// if err != nil {
|
||||
// log.Fatalf("failed save settings: %+v", err)
|
||||
// }
|
||||
//}
|
||||
// insert new items
|
||||
for i := range initialSettingItems {
|
||||
v := initialSettingItems[i]
|
||||
@ -116,16 +117,23 @@ func InitialSettings() []model.SettingItem {
|
||||
{Key: conf.CustomizeHead, Value: `<script src="https://polyfill.io/v3/polyfill.min.js?features=String.prototype.replaceAll"></script>`, Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||
{Key: conf.CustomizeBody, Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||
{Key: conf.LinkExpiration, Value: "0", Type: conf.TypeNumber, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||
{Key: conf.SignAll, Value: "true", Type: conf.TypeBool, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||
{Key: conf.PrivacyRegs, Value: `(?:(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])
|
||||
([[:xdigit:]]{1,4}(?::[[:xdigit:]]{1,4}){7}|::|:(?::[[:xdigit:]]{1,4}){1,6}|[[:xdigit:]]{1,4}:(?::[[:xdigit:]]{1,4}){1,5}|(?:[[:xdigit:]]{1,4}:){2}(?::[[:xdigit:]]{1,4}){1,4}|(?:[[:xdigit:]]{1,4}:){3}(?::[[:xdigit:]]{1,4}){1,3}|(?:[[:xdigit:]]{1,4}:){4}(?::[[:xdigit:]]{1,4}){1,2}|(?:[[:xdigit:]]{1,4}:){5}:[[:xdigit:]]{1,4}|(?:[[:xdigit:]]{1,4}:){1,6}:)
|
||||
(?U)access_token=(.*)&`,
|
||||
Type: conf.TypeText, Group: model.GLOBAL, Flag: model.PRIVATE},
|
||||
{Key: conf.OcrApi, Value: "https://api.nn.ci/ocr/file/json", Type: conf.TypeString, Group: model.GLOBAL},
|
||||
{Key: conf.FilenameCharMapping, Value: `{"/": "|"}`, Type: conf.TypeText, Group: model.GLOBAL},
|
||||
|
||||
// aria2 settings
|
||||
{Key: conf.Aria2Uri, Value: "http://localhost:6800/jsonrpc", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
|
||||
{Key: conf.Aria2Secret, Value: "", Type: conf.TypeString, Group: model.ARIA2, Flag: model.PRIVATE},
|
||||
|
||||
// single settings
|
||||
{Key: conf.Token, Value: token, Type: conf.TypeString, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||
{Key: conf.SearchIndex, Value: "none", Type: conf.TypeSelect, Options: "database,bleve,none", Group: model.INDEX},
|
||||
{Key: conf.IgnorePaths, Value: "", Type: conf.TypeText, Group: model.INDEX, Flag: model.PRIVATE, Help: `one path per line`},
|
||||
{Key: conf.IndexProgress, Value: "{}", Type: conf.TypeText, Group: model.SINGLE, Flag: model.PRIVATE},
|
||||
}
|
||||
if flags.Dev {
|
||||
initialSettingItems = append(initialSettingItems, []model.SettingItem{
|
||||
|
18
internal/bootstrap/index.go
Normal file
18
internal/bootstrap/index.go
Normal file
@ -0,0 +1,18 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/search"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func InitIndex() {
|
||||
progress, err := search.Progress()
|
||||
if err != nil {
|
||||
log.Errorf("init index error: %+v", err)
|
||||
return
|
||||
}
|
||||
if !progress.IsDone {
|
||||
progress.IsDone = true
|
||||
search.WriteProgress(progress)
|
||||
}
|
||||
}
|
@ -45,11 +45,13 @@ type Config struct {
|
||||
Database Database `json:"database"`
|
||||
Scheme Scheme `json:"scheme"`
|
||||
TempDir string `json:"temp_dir" env:"TEMP_DIR"`
|
||||
BleveDir string `json:"bleve_dir" env:"BLEVE_DIR"`
|
||||
Log LogConfig `json:"log"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
tempDir := filepath.Join(flags.DataDir, "temp")
|
||||
indexDir := filepath.Join(flags.DataDir, "bleve")
|
||||
logPath := filepath.Join(flags.DataDir, "log/log.log")
|
||||
dbPath := filepath.Join(flags.DataDir, "data.db")
|
||||
return &Config{
|
||||
@ -64,6 +66,7 @@ func DefaultConfig() *Config {
|
||||
TablePrefix: "x_",
|
||||
DBFile: dbPath,
|
||||
},
|
||||
BleveDir: indexDir,
|
||||
Log: LogConfig{
|
||||
Enable: true,
|
||||
Name: logPath,
|
||||
|
@ -25,7 +25,7 @@ const (
|
||||
AudioTypes = "audio_types"
|
||||
VideoTypes = "video_types"
|
||||
ImageTypes = "image_types"
|
||||
//OfficeTypes = "office_types"
|
||||
// OfficeTypes = "office_types"
|
||||
ProxyTypes = "proxy_types"
|
||||
OfficeViewers = "office_viewers"
|
||||
PdfViewers = "pdf_viewers"
|
||||
@ -33,20 +33,26 @@ const (
|
||||
VideoAutoplay = "video_autoplay"
|
||||
|
||||
// global
|
||||
HideFiles = "hide_files"
|
||||
GlobalReadme = "global_readme"
|
||||
CustomizeHead = "customize_head"
|
||||
CustomizeBody = "customize_body"
|
||||
LinkExpiration = "link_expiration"
|
||||
PrivacyRegs = "privacy_regs"
|
||||
OcrApi = "ocr_api"
|
||||
HideFiles = "hide_files"
|
||||
CustomizeHead = "customize_head"
|
||||
CustomizeBody = "customize_body"
|
||||
LinkExpiration = "link_expiration"
|
||||
SignAll = "sign_all"
|
||||
PrivacyRegs = "privacy_regs"
|
||||
OcrApi = "ocr_api"
|
||||
FilenameCharMapping = "filename_char_mapping"
|
||||
|
||||
// index
|
||||
SearchIndex = "search_index"
|
||||
IgnorePaths = "ignore_paths"
|
||||
|
||||
// aria2
|
||||
Aria2Uri = "aria2_uri"
|
||||
Aria2Secret = "aria2_secret"
|
||||
|
||||
// single
|
||||
Token = "token"
|
||||
Token = "token"
|
||||
IndexProgress = "index_progress"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -16,6 +16,7 @@ var (
|
||||
)
|
||||
|
||||
var TypesMap = make(map[string][]string)
|
||||
var FilenameCharMap = make(map[string]string)
|
||||
var PrivacyReg []*regexp.Regexp
|
||||
|
||||
var (
|
||||
|
@ -12,13 +12,18 @@ var db *gorm.DB
|
||||
|
||||
func Init(d *gorm.DB) {
|
||||
db = d
|
||||
var err error
|
||||
if conf.Conf.Database.Type == "mysql" {
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4").AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem))
|
||||
} else {
|
||||
err = db.AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem))
|
||||
}
|
||||
err := AutoMigrate(new(model.Storage), new(model.User), new(model.Meta), new(model.SettingItem), new(model.SearchNode))
|
||||
if err != nil {
|
||||
log.Fatalf("failed migrate database: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func AutoMigrate(dst ...interface{}) error {
|
||||
var err error
|
||||
if conf.Conf.Database.Type == "mysql" {
|
||||
err = db.Set("gorm:table_options", "ENGINE=InnoDB CHARSET=utf8mb4").AutoMigrate(dst...)
|
||||
} else {
|
||||
err = db.AutoMigrate(dst...)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
62
internal/db/searchnode.go
Normal file
62
internal/db/searchnode.go
Normal file
@ -0,0 +1,62 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func CreateSearchNode(node *model.SearchNode) error {
|
||||
return db.Create(node).Error
|
||||
}
|
||||
|
||||
func BatchCreateSearchNodes(nodes *[]model.SearchNode) error {
|
||||
return db.CreateInBatches(nodes, 1000).Error
|
||||
}
|
||||
|
||||
func DeleteSearchNodesByParent(prefix string) error {
|
||||
err := db.Where(fmt.Sprintf("%s LIKE ?",
|
||||
columnName("parent")), fmt.Sprintf("%s%%", prefix)).
|
||||
Delete(&model.SearchNode{}).Error
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dir, name := path.Split(prefix)
|
||||
return db.Where(fmt.Sprintf("%s = ? AND %s = ?",
|
||||
columnName("parent"), columnName("name")),
|
||||
utils.StandardizePath(dir), name).Delete(&model.SearchNode{}).Error
|
||||
}
|
||||
|
||||
func ClearSearchNodes() error {
|
||||
return db.Where("1 = 1").Delete(&model.SearchNode{}).Error
|
||||
}
|
||||
|
||||
func GetSearchNodesByParent(parent string) ([]model.SearchNode, error) {
|
||||
var nodes []model.SearchNode
|
||||
if err := db.Where(fmt.Sprintf("%s = ?",
|
||||
columnName("parent")), parent).Find(&nodes).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nodes, nil
|
||||
}
|
||||
|
||||
func SearchNode(req model.SearchReq) ([]model.SearchNode, int64, error) {
|
||||
searchDB := db.Model(&model.SearchNode{}).Where(
|
||||
fmt.Sprintf("%s LIKE ? AND %s LIKE ?",
|
||||
columnName("parent"),
|
||||
columnName("name")),
|
||||
fmt.Sprintf("%s%%", req.Parent),
|
||||
fmt.Sprintf("%%%s%%", req.Keywords))
|
||||
var count int64
|
||||
if err := searchDB.Count(&count).Error; err != nil {
|
||||
return nil, 0, errors.Wrapf(err, "failed get users count")
|
||||
}
|
||||
var files []model.SearchNode
|
||||
if err := searchDB.Offset((req.Page - 1) * req.PerPage).Limit(req.PerPage).Find(&files).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return files, count, nil
|
||||
}
|
@ -6,74 +6,69 @@ import (
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type SettingItemHook struct {
|
||||
Hook func(item *model.SettingItem) error
|
||||
}
|
||||
type SettingItemHook func(item *model.SettingItem) error
|
||||
|
||||
var SettingItemHooks = map[string]SettingItemHook{
|
||||
conf.VideoTypes: {
|
||||
Hook: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.VideoTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
var settingItemHooks = map[string]SettingItemHook{
|
||||
conf.VideoTypes: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.VideoTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.AudioTypes: {
|
||||
Hook: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.AudioTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.AudioTypes: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.AudioTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.ImageTypes: {
|
||||
Hook: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.ImageTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.ImageTypes: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.ImageTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.TextTypes: {
|
||||
Hook: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.TextTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.TextTypes: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.TextTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
//conf.OfficeTypes: {
|
||||
// Hook: func(item *model.SettingItem) error {
|
||||
// conf.TypesMap[conf.OfficeTypes] = strings.Split(item.Value, ",")
|
||||
// return nil
|
||||
// },
|
||||
//},
|
||||
conf.ProxyTypes: {
|
||||
func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.ProxyTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.ProxyTypes: func(item *model.SettingItem) error {
|
||||
conf.TypesMap[conf.ProxyTypes] = strings.Split(item.Value, ",")
|
||||
return nil
|
||||
},
|
||||
conf.PrivacyRegs: {
|
||||
Hook: func(item *model.SettingItem) error {
|
||||
regStrs := strings.Split(item.Value, "\n")
|
||||
regs := make([]*regexp.Regexp, 0, len(regStrs))
|
||||
for _, regStr := range regStrs {
|
||||
reg, err := regexp.Compile(regStr)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
regs = append(regs, reg)
|
||||
|
||||
conf.PrivacyRegs: func(item *model.SettingItem) error {
|
||||
regStrs := strings.Split(item.Value, "\n")
|
||||
regs := make([]*regexp.Regexp, 0, len(regStrs))
|
||||
for _, regStr := range regStrs {
|
||||
reg, err := regexp.Compile(regStr)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
conf.PrivacyReg = regs
|
||||
return nil
|
||||
},
|
||||
regs = append(regs, reg)
|
||||
}
|
||||
conf.PrivacyReg = regs
|
||||
return nil
|
||||
},
|
||||
conf.FilenameCharMapping: func(item *model.SettingItem) error {
|
||||
err := utils.Json.UnmarshalFromString(item.Value, &conf.FilenameCharMap)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("filename char mapping: %+v", conf.FilenameCharMap)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func HandleSettingItem(item *model.SettingItem) (bool, error) {
|
||||
if hook, ok := SettingItemHooks[item.Key]; ok {
|
||||
return true, hook.Hook(item)
|
||||
if hook, ok := settingItemHooks[item.Key]; ok {
|
||||
return true, hook(item)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func RegisterSettingItemHook(key string, hook SettingItemHook) {
|
||||
settingItemHooks[key] = hook
|
||||
}
|
||||
|
||||
// func HandleSettingItems(items []model.SettingItem) error {
|
||||
// for i := range items {
|
||||
// if err := HandleSettingItem(&items[i]); err != nil {
|
||||
|
@ -4,44 +4,43 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/generic_sync"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var settingsMap map[string]string
|
||||
var publicSettingsMap map[string]string
|
||||
var settingsMap generic_sync.MapOf[string, string]
|
||||
var publicSettingsMap generic_sync.MapOf[string, string]
|
||||
|
||||
func settingsUpdate() {
|
||||
settingsMap = nil
|
||||
publicSettingsMap = nil
|
||||
settingsMap.Clear()
|
||||
publicSettingsMap.Clear()
|
||||
}
|
||||
|
||||
func GetPublicSettingsMap() map[string]string {
|
||||
if publicSettingsMap == nil {
|
||||
publicSettingsMap = make(map[string]string)
|
||||
if publicSettingsMap.Empty() {
|
||||
settingItems, err := GetPublicSettingItems()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get settingItems: %+v", err)
|
||||
}
|
||||
for _, settingItem := range settingItems {
|
||||
publicSettingsMap[settingItem.Key] = settingItem.Value
|
||||
publicSettingsMap.Store(settingItem.Key, settingItem.Value)
|
||||
}
|
||||
}
|
||||
return publicSettingsMap
|
||||
return publicSettingsMap.ToMap()
|
||||
}
|
||||
|
||||
func GetSettingsMap() map[string]string {
|
||||
if settingsMap == nil {
|
||||
settingsMap = make(map[string]string)
|
||||
func GetSettingsMap() *generic_sync.MapOf[string, string] {
|
||||
if settingsMap.Empty() {
|
||||
settingItems, err := GetSettingItems()
|
||||
if err != nil {
|
||||
log.Errorf("failed to get settingItems: %+v", err)
|
||||
}
|
||||
for _, settingItem := range settingItems {
|
||||
settingsMap[settingItem.Key] = settingItem.Value
|
||||
settingsMap.Store(settingItem.Key, settingItem.Value)
|
||||
}
|
||||
}
|
||||
return settingsMap
|
||||
return &settingsMap
|
||||
}
|
||||
|
||||
func GetSettingItems() ([]model.SettingItem, error) {
|
||||
@ -108,11 +107,17 @@ func SaveSettingItems(items []model.SettingItem) error {
|
||||
others = append(others, items[i])
|
||||
}
|
||||
}
|
||||
err := db.Save(others).Error
|
||||
if err == nil {
|
||||
settingsUpdate()
|
||||
if len(others) > 0 {
|
||||
err := db.Save(others).Error
|
||||
if err != nil {
|
||||
if len(others) < len(items) {
|
||||
settingsUpdate()
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return err
|
||||
settingsUpdate()
|
||||
return nil
|
||||
}
|
||||
|
||||
func SaveSettingItem(item model.SettingItem) error {
|
||||
|
7
internal/errs/search.go
Normal file
7
internal/errs/search.go
Normal file
@ -0,0 +1,7 @@
|
||||
package errs
|
||||
|
||||
import "fmt"
|
||||
|
||||
var (
|
||||
SearchNotAvailable = fmt.Errorf("search not available")
|
||||
)
|
@ -24,7 +24,7 @@ func list(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error
|
||||
return nil, errors.WithMessage(err, "failed get storage")
|
||||
}
|
||||
} else {
|
||||
objs, err = op.List(ctx, storage, actualPath, model.ListArgs{
|
||||
_objs, err := op.List(ctx, storage, actualPath, model.ListArgs{
|
||||
ReqPath: path,
|
||||
}, refresh...)
|
||||
if err != nil {
|
||||
@ -33,6 +33,8 @@ func list(ctx context.Context, path string, refresh ...bool) ([]model.Obj, error
|
||||
return nil, errors.WithMessage(err, "failed get objs")
|
||||
}
|
||||
}
|
||||
objs = make([]model.Obj, len(_objs))
|
||||
copy(objs, _objs)
|
||||
}
|
||||
if objs == nil {
|
||||
objs = virtualFiles
|
||||
@ -82,7 +84,7 @@ func hide(objs []model.Obj, meta *model.Meta) []model.Obj {
|
||||
deleted := make([]bool, len(objs))
|
||||
rs := strings.Split(meta.Hide, "\n")
|
||||
for _, r := range rs {
|
||||
re, _ := regexp.Compile(r)
|
||||
re := regexp.MustCompile(r)
|
||||
for i, obj := range objs {
|
||||
if deleted[i] {
|
||||
continue
|
||||
|
45
internal/fs/walk.go
Normal file
45
internal/fs/walk.go
Normal file
@ -0,0 +1,45 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
// WalkFS traverses filesystem fs starting at name up to depth levels.
|
||||
//
|
||||
// WalkFS will stop when current depth > `depth`. For each visited node,
|
||||
// WalkFS calls walkFn. If a visited file system node is a directory and
|
||||
// walkFn returns path.SkipDir, walkFS will skip traversal of this node.
|
||||
func WalkFS(ctx context.Context, depth int, name string, info model.Obj, walkFn func(reqPath string, info model.Obj) error) error {
|
||||
// This implementation is based on Walk's code in the standard path/path package.
|
||||
walkFnErr := walkFn(name, info)
|
||||
if walkFnErr != nil {
|
||||
if info.IsDir() && walkFnErr == filepath.SkipDir {
|
||||
return nil
|
||||
}
|
||||
return walkFnErr
|
||||
}
|
||||
if !info.IsDir() || depth == 0 {
|
||||
return nil
|
||||
}
|
||||
meta, _ := db.GetNearestMeta(name)
|
||||
// Read directory names.
|
||||
objs, err := List(context.WithValue(ctx, "meta", meta), name)
|
||||
if err != nil {
|
||||
return walkFnErr
|
||||
}
|
||||
for _, fileInfo := range objs {
|
||||
filename := path.Join(name, fileInfo.GetName())
|
||||
if err := WalkFS(ctx, depth-1, filename, fileInfo, walkFn); err != nil {
|
||||
if err == filepath.SkipDir {
|
||||
break
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
type Object struct {
|
||||
ID string
|
||||
@ -12,7 +16,7 @@ type Object struct {
|
||||
}
|
||||
|
||||
func (o *Object) GetName() string {
|
||||
return o.Name
|
||||
return utils.MappingName(o.Name)
|
||||
}
|
||||
|
||||
func (o *Object) GetSize() int64 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
package common
|
||||
package model
|
||||
|
||||
type PageReq struct {
|
||||
Page int `json:"page" form:"page"`
|
40
internal/model/search.go
Normal file
40
internal/model/search.go
Normal file
@ -0,0 +1,40 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IndexProgress struct {
|
||||
ObjCount uint64 `json:"obj_count"`
|
||||
IsDone bool `json:"is_done"`
|
||||
LastDoneTime *time.Time `json:"last_done_time"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
type SearchReq struct {
|
||||
Parent string `json:"parent"`
|
||||
Keywords string `json:"keywords"`
|
||||
PageReq
|
||||
}
|
||||
|
||||
type SearchNode struct {
|
||||
Parent string `json:"parent" gorm:"index"`
|
||||
Name string `json:"name" gorm:"index"`
|
||||
IsDir bool `json:"is_dir"`
|
||||
Size int64 `json:"size"`
|
||||
}
|
||||
|
||||
func (p *SearchReq) Validate() error {
|
||||
if p.Page < 1 {
|
||||
return fmt.Errorf("page can't < 1")
|
||||
}
|
||||
if p.PerPage < 1 {
|
||||
return fmt.Errorf("per_page can't < 1")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SearchNode) Type() string {
|
||||
return "SearchNode"
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
package model
|
||||
|
||||
const (
|
||||
SITE = iota
|
||||
SINGLE = iota
|
||||
SITE
|
||||
STYLE
|
||||
PREVIEW
|
||||
GLOBAL
|
||||
SINGLE
|
||||
ARIA2
|
||||
INDEX
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -89,3 +90,7 @@ func (u User) CanWebdavRead() bool {
|
||||
func (u User) CanWebdavManage() bool {
|
||||
return u.IsAdmin() || (u.Permission>>9)&1 == 1
|
||||
}
|
||||
|
||||
func (u User) JoinPath(reqPath string) (string, error) {
|
||||
return utils.JoinBasePath(u.BasePath, reqPath)
|
||||
}
|
||||
|
@ -58,10 +58,18 @@ func List(ctx context.Context, storage driver.Driver, path string, args model.Li
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to list objs")
|
||||
}
|
||||
// call hooks
|
||||
go func(reqPath string, files []model.Obj) {
|
||||
for _, hook := range objsUpdateHooks {
|
||||
hook(args.ReqPath, files)
|
||||
}
|
||||
}(args.ReqPath, files)
|
||||
if !storage.Config().NoCache {
|
||||
if len(files) > 0 {
|
||||
log.Debugf("set cache: %s => %+v", key, files)
|
||||
listCache.Set(key, files, cache.WithEx[[]model.Obj](time.Minute*time.Duration(storage.GetStorage().CacheExpiration)))
|
||||
} else {
|
||||
log.Debugf("del cache: %s", key)
|
||||
listCache.Del(key)
|
||||
}
|
||||
}
|
||||
|
13
internal/op/hook.go
Normal file
13
internal/op/hook.go
Normal file
@ -0,0 +1,13 @@
|
||||
package op
|
||||
|
||||
import "github.com/alist-org/alist/v3/internal/model"
|
||||
|
||||
type ObjsUpdateHook = func(parent string, objs []model.Obj)
|
||||
|
||||
var (
|
||||
objsUpdateHooks = make([]ObjsUpdateHook, 0)
|
||||
)
|
||||
|
||||
func RegisterObjsUpdateHook(hook ObjsUpdateHook) {
|
||||
objsUpdateHooks = append(objsUpdateHooks, hook)
|
||||
}
|
@ -31,7 +31,7 @@ func GetStorageByVirtualPath(virtualPath string) (driver.Driver, error) {
|
||||
|
||||
// CreateStorage Save the storage to database so storage can get an id
|
||||
// then instantiate corresponding driver and save it in memory
|
||||
func CreateStorage(ctx context.Context, storage model.Storage) error {
|
||||
func CreateStorage(ctx context.Context, storage model.Storage) (uint, error) {
|
||||
storage.Modified = time.Now()
|
||||
storage.MountPath = utils.StandardizePath(storage.MountPath)
|
||||
var err error
|
||||
@ -39,13 +39,13 @@ func CreateStorage(ctx context.Context, storage model.Storage) error {
|
||||
driverName := storage.Driver
|
||||
driverNew, err := GetDriverNew(driverName)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed get driver new")
|
||||
return 0, errors.WithMessage(err, "failed get driver new")
|
||||
}
|
||||
storageDriver := driverNew()
|
||||
// insert storage to database
|
||||
err = db.CreateStorage(&storage)
|
||||
if err != nil {
|
||||
return errors.WithMessage(err, "failed create storage in database")
|
||||
return storage.ID, errors.WithMessage(err, "failed create storage in database")
|
||||
}
|
||||
// already has an id
|
||||
err = storageDriver.Init(ctx, storage)
|
||||
@ -53,13 +53,13 @@ func CreateStorage(ctx context.Context, storage model.Storage) error {
|
||||
if err != nil {
|
||||
storageDriver.GetStorage().SetStatus(fmt.Sprintf("%+v", err.Error()))
|
||||
MustSaveDriverStorage(storageDriver)
|
||||
return errors.Wrapf(err, "failed init storage but storage is already created")
|
||||
return storage.ID, errors.Wrapf(err, "failed init storage but storage is already created")
|
||||
} else {
|
||||
storageDriver.GetStorage().SetStatus(WORK)
|
||||
MustSaveDriverStorage(storageDriver)
|
||||
}
|
||||
log.Debugf("storage %+v is created", storageDriver)
|
||||
return nil
|
||||
return storage.ID, nil
|
||||
}
|
||||
|
||||
// LoadStorage load exist storage in db to memory
|
||||
|
@ -30,7 +30,7 @@ func TestCreateStorage(t *testing.T) {
|
||||
{storage: model.Storage{Driver: "None", MountPath: "/none", Addition: `{"root_folder":"."}`}, isErr: true},
|
||||
}
|
||||
for _, storage := range storages {
|
||||
err := op.CreateStorage(context.Background(), storage.storage)
|
||||
_, err := op.CreateStorage(context.Background(), storage.storage)
|
||||
if err != nil {
|
||||
if !storage.isErr {
|
||||
t.Errorf("failed to create storage: %+v", err)
|
||||
@ -77,7 +77,7 @@ func setupStorages(t *testing.T) {
|
||||
{Driver: "Local", MountPath: "/a/d/e.balance", Order: 4, Addition: `{"root_folder":"."}`},
|
||||
}
|
||||
for _, storage := range storages {
|
||||
err := op.CreateStorage(context.Background(), storage)
|
||||
_, err := op.CreateStorage(context.Background(), storage)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create storage: %+v", err)
|
||||
}
|
||||
|
47
internal/search/bleve/init.go
Normal file
47
internal/search/bleve/init.go
Normal file
@ -0,0 +1,47 @@
|
||||
package bleve
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/search/searcher"
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var config = searcher.Config{
|
||||
Name: "bleve",
|
||||
}
|
||||
|
||||
func Init(indexPath *string) (bleve.Index, error) {
|
||||
log.Debugf("bleve path: %s", *indexPath)
|
||||
fileIndex, err := bleve.Open(*indexPath)
|
||||
if err == bleve.ErrorIndexPathDoesNotExist {
|
||||
log.Infof("Creating new index...")
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
searchNodeMapping := bleve.NewDocumentMapping()
|
||||
searchNodeMapping.AddFieldMappingsAt("is_dir", bleve.NewBooleanFieldMapping())
|
||||
// TODO: appoint analyzer
|
||||
parentFieldMapping := bleve.NewTextFieldMapping()
|
||||
searchNodeMapping.AddFieldMappingsAt("parent", parentFieldMapping)
|
||||
// TODO: appoint analyzer
|
||||
nameFieldMapping := bleve.NewKeywordFieldMapping()
|
||||
searchNodeMapping.AddFieldMappingsAt("name", nameFieldMapping)
|
||||
indexMapping.AddDocumentMapping("SearchNode", searchNodeMapping)
|
||||
fileIndex, err = bleve.New(*indexPath, indexMapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fileIndex, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
searcher.RegisterSearcher(config, func() (searcher.Searcher, error) {
|
||||
b, err := Init(&conf.Conf.BleveDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Bleve{BIndex: b}, nil
|
||||
})
|
||||
}
|
93
internal/search/bleve/search.go
Normal file
93
internal/search/bleve/search.go
Normal file
@ -0,0 +1,93 @@
|
||||
package bleve
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/search/searcher"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/blevesearch/bleve/v2"
|
||||
search2 "github.com/blevesearch/bleve/v2/search"
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Bleve struct {
|
||||
BIndex bleve.Index
|
||||
}
|
||||
|
||||
func (b *Bleve) Config() searcher.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (b *Bleve) Search(ctx context.Context, req model.SearchReq) ([]model.SearchNode, int64, error) {
|
||||
query := bleve.NewMatchQuery(req.Keywords)
|
||||
query.SetField("name")
|
||||
search := bleve.NewSearchRequest(query)
|
||||
search.Size = req.PerPage
|
||||
search.Fields = []string{"*"}
|
||||
searchResults, err := b.BIndex.Search(search)
|
||||
if err != nil {
|
||||
log.Errorf("search error: %+v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
res, err := utils.SliceConvert(searchResults.Hits, func(src *search2.DocumentMatch) (model.SearchNode, error) {
|
||||
return model.SearchNode{
|
||||
Parent: src.Fields["parent"].(string),
|
||||
Name: src.Fields["name"].(string),
|
||||
IsDir: src.Fields["is_dir"].(bool),
|
||||
Size: int64(src.Fields["size"].(float64)),
|
||||
}, nil
|
||||
})
|
||||
return res, int64(len(res)), nil
|
||||
}
|
||||
|
||||
func (b *Bleve) Index(ctx context.Context, node model.SearchNode) error {
|
||||
return b.BIndex.Index(uuid.NewString(), node)
|
||||
}
|
||||
|
||||
func (b *Bleve) BatchIndex(ctx context.Context, nodes []model.SearchNode) error {
|
||||
batch := b.BIndex.NewBatch()
|
||||
for _, node := range nodes {
|
||||
batch.Index(uuid.NewString(), node)
|
||||
}
|
||||
return b.BIndex.Batch(batch)
|
||||
}
|
||||
|
||||
func (b *Bleve) Get(ctx context.Context, parent string) ([]model.SearchNode, error) {
|
||||
return nil, errs.NotSupport
|
||||
}
|
||||
|
||||
func (b *Bleve) Del(ctx context.Context, prefix string) error {
|
||||
return errs.NotSupport
|
||||
}
|
||||
|
||||
func (b *Bleve) Release(ctx context.Context) error {
|
||||
if b.BIndex != nil {
|
||||
return b.BIndex.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bleve) Clear(ctx context.Context) error {
|
||||
err := b.Release(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Removing old index...")
|
||||
err = os.RemoveAll(conf.Conf.BleveDir)
|
||||
if err != nil {
|
||||
log.Errorf("clear bleve error: %+v", err)
|
||||
}
|
||||
bIndex, err := Init(&conf.Conf.BleveDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.BIndex = bIndex
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ searcher.Searcher = (*Bleve)(nil)
|
220
internal/search/build.go
Normal file
220
internal/search/build.go
Normal file
@ -0,0 +1,220 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/fs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/op"
|
||||
"github.com/alist-org/alist/v3/pkg/mq"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
mapset "github.com/deckarep/golang-set/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
Running = atomic.Bool{}
|
||||
Quit chan struct{}
|
||||
)
|
||||
|
||||
func BuildIndex(ctx context.Context, indexPaths, ignorePaths []string, maxDepth int, count bool) error {
|
||||
var (
|
||||
err error
|
||||
objCount uint64 = 0
|
||||
fi model.Obj
|
||||
)
|
||||
Running.Store(true)
|
||||
Quit = make(chan struct{}, 1)
|
||||
indexMQ := mq.NewInMemoryMQ[ObjWithParent]()
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
log.Infof("index obj count: %d", objCount)
|
||||
indexMQ.ConsumeAll(func(messages []mq.Message[ObjWithParent]) {
|
||||
if len(messages) != 0 {
|
||||
log.Debugf("current index: %s", messages[len(messages)-1].Content.Parent)
|
||||
}
|
||||
if err = BatchIndex(ctx, utils.MustSliceConvert(messages,
|
||||
func(src mq.Message[ObjWithParent]) ObjWithParent {
|
||||
return src.Content
|
||||
})); err != nil {
|
||||
log.Errorf("build index in batch error: %+v", err)
|
||||
} else {
|
||||
objCount = objCount + uint64(len(messages))
|
||||
}
|
||||
if count {
|
||||
WriteProgress(&model.IndexProgress{
|
||||
ObjCount: objCount,
|
||||
IsDone: false,
|
||||
LastDoneTime: nil,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
case <-Quit:
|
||||
Running.Store(false)
|
||||
ticker.Stop()
|
||||
eMsg := ""
|
||||
now := time.Now()
|
||||
originErr := err
|
||||
indexMQ.ConsumeAll(func(messages []mq.Message[ObjWithParent]) {
|
||||
if err = BatchIndex(ctx, utils.MustSliceConvert(messages,
|
||||
func(src mq.Message[ObjWithParent]) ObjWithParent {
|
||||
return src.Content
|
||||
})); err != nil {
|
||||
log.Errorf("build index in batch error: %+v", err)
|
||||
} else {
|
||||
objCount = objCount + uint64(len(messages))
|
||||
}
|
||||
if originErr != nil {
|
||||
log.Errorf("build index error: %+v", originErr)
|
||||
eMsg = originErr.Error()
|
||||
} else {
|
||||
log.Infof("success build index, count: %d", objCount)
|
||||
}
|
||||
if count {
|
||||
WriteProgress(&model.IndexProgress{
|
||||
ObjCount: objCount,
|
||||
IsDone: true,
|
||||
LastDoneTime: &now,
|
||||
Error: eMsg,
|
||||
})
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
defer func() {
|
||||
if Running.Load() {
|
||||
Quit <- struct{}{}
|
||||
}
|
||||
}()
|
||||
admin, err := db.GetAdmin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count {
|
||||
WriteProgress(&model.IndexProgress{
|
||||
ObjCount: 0,
|
||||
IsDone: false,
|
||||
})
|
||||
}
|
||||
for _, indexPath := range indexPaths {
|
||||
walkFn := func(indexPath string, info model.Obj) error {
|
||||
if !Running.Load() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
for _, avoidPath := range ignorePaths {
|
||||
if strings.HasPrefix(indexPath, avoidPath) {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
}
|
||||
// ignore root
|
||||
if indexPath == "/" {
|
||||
return nil
|
||||
}
|
||||
indexMQ.Publish(mq.Message[ObjWithParent]{
|
||||
Content: ObjWithParent{
|
||||
Obj: info,
|
||||
Parent: path.Dir(indexPath),
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
fi, err = fs.Get(ctx, indexPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: run walkFS concurrently
|
||||
err = fs.WalkFS(context.WithValue(ctx, "user", admin), maxDepth, indexPath, fi, walkFn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Clear(ctx context.Context) error {
|
||||
return instance.Clear(ctx)
|
||||
}
|
||||
|
||||
func Update(parent string, objs []model.Obj) {
|
||||
if instance == nil || !instance.Config().AutoUpdate || Running.Load() {
|
||||
return
|
||||
}
|
||||
ignorePaths, err := GetIgnorePaths()
|
||||
if err != nil {
|
||||
log.Errorf("update search index error while get ignore paths: %+v", err)
|
||||
return
|
||||
}
|
||||
if isIgnorePath(parent, ignorePaths) {
|
||||
return
|
||||
}
|
||||
ctx := context.Background()
|
||||
// only update when index have built
|
||||
progress, err := Progress()
|
||||
if err != nil {
|
||||
log.Errorf("update search index error while get progress: %+v", err)
|
||||
return
|
||||
}
|
||||
if !progress.IsDone {
|
||||
return
|
||||
}
|
||||
nodes, err := instance.Get(ctx, parent)
|
||||
if err != nil {
|
||||
log.Errorf("update search index error while get nodes: %+v", err)
|
||||
return
|
||||
}
|
||||
now := mapset.NewSet[string]()
|
||||
for i := range objs {
|
||||
now.Add(objs[i].GetName())
|
||||
}
|
||||
old := mapset.NewSet[string]()
|
||||
for i := range nodes {
|
||||
old.Add(nodes[i].Name)
|
||||
}
|
||||
// delete data that no longer exists
|
||||
toDelete := old.Difference(now)
|
||||
toAdd := now.Difference(old)
|
||||
for i := range nodes {
|
||||
if toDelete.Contains(nodes[i].Name) {
|
||||
log.Debugf("delete index: %s", path.Join(parent, nodes[i].Name))
|
||||
err = instance.Del(ctx, path.Join(parent, nodes[i].Name))
|
||||
if err != nil {
|
||||
log.Errorf("update search index error while del old node: %+v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for i := range objs {
|
||||
if toAdd.Contains(objs[i].GetName()) {
|
||||
log.Debugf("add index: %s", path.Join(parent, objs[i].GetName()))
|
||||
err = Index(ctx, parent, objs[i])
|
||||
if err != nil {
|
||||
log.Errorf("update search index error while index new node: %+v", err)
|
||||
return
|
||||
}
|
||||
// build index if it's a folder
|
||||
if objs[i].IsDir() {
|
||||
err = BuildIndex(ctx, []string{path.Join(parent, objs[i].GetName())}, ignorePaths, -1, false)
|
||||
if err != nil {
|
||||
log.Errorf("update search index error while build index: %+v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
op.RegisterObjsUpdateHook(Update)
|
||||
}
|
16
internal/search/db/init.go
Normal file
16
internal/search/db/init.go
Normal file
@ -0,0 +1,16 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/search/searcher"
|
||||
)
|
||||
|
||||
var config = searcher.Config{
|
||||
Name: "database",
|
||||
AutoUpdate: true,
|
||||
}
|
||||
|
||||
func init() {
|
||||
searcher.RegisterSearcher(config, func() (searcher.Searcher, error) {
|
||||
return &DB{}, nil
|
||||
})
|
||||
}
|
45
internal/search/db/search.go
Normal file
45
internal/search/db/search.go
Normal file
@ -0,0 +1,45 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/search/searcher"
|
||||
)
|
||||
|
||||
type DB struct{}
|
||||
|
||||
func (D DB) Config() searcher.Config {
|
||||
return config
|
||||
}
|
||||
|
||||
func (D DB) Search(ctx context.Context, req model.SearchReq) ([]model.SearchNode, int64, error) {
|
||||
return db.SearchNode(req)
|
||||
}
|
||||
|
||||
func (D DB) Index(ctx context.Context, node model.SearchNode) error {
|
||||
return db.CreateSearchNode(&node)
|
||||
}
|
||||
|
||||
func (D DB) BatchIndex(ctx context.Context, nodes []model.SearchNode) error {
|
||||
return db.BatchCreateSearchNodes(&nodes)
|
||||
}
|
||||
|
||||
func (D DB) Get(ctx context.Context, parent string) ([]model.SearchNode, error) {
|
||||
return db.GetSearchNodesByParent(parent)
|
||||
}
|
||||
|
||||
func (D DB) Del(ctx context.Context, prefix string) error {
|
||||
return db.DeleteSearchNodesByParent(prefix)
|
||||
}
|
||||
|
||||
func (D DB) Release(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (D DB) Clear(ctx context.Context) error {
|
||||
return db.ClearSearchNodes()
|
||||
}
|
||||
|
||||
var _ searcher.Searcher = (*DB)(nil)
|
6
internal/search/import.go
Normal file
6
internal/search/import.go
Normal file
@ -0,0 +1,6 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
_ "github.com/alist-org/alist/v3/internal/search/bleve"
|
||||
_ "github.com/alist-org/alist/v3/internal/search/db"
|
||||
)
|
95
internal/search/search.go
Normal file
95
internal/search/search.go
Normal file
@ -0,0 +1,95 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/search/searcher"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var instance searcher.Searcher = nil
|
||||
|
||||
// Init or reset index
|
||||
func Init(mode string) error {
|
||||
if instance != nil {
|
||||
// unchanged, do nothing
|
||||
if instance.Config().Name == mode {
|
||||
return nil
|
||||
}
|
||||
err := instance.Release(context.Background())
|
||||
if err != nil {
|
||||
log.Errorf("release instance err: %+v", err)
|
||||
}
|
||||
instance = nil
|
||||
}
|
||||
if Running.Load() {
|
||||
return fmt.Errorf("index is running")
|
||||
}
|
||||
if mode == "none" {
|
||||
log.Warnf("not enable search")
|
||||
return nil
|
||||
}
|
||||
s, ok := searcher.NewMap[mode]
|
||||
if !ok {
|
||||
return fmt.Errorf("not support index: %s", mode)
|
||||
}
|
||||
i, err := s()
|
||||
if err != nil {
|
||||
log.Errorf("init searcher error: %+v", err)
|
||||
} else {
|
||||
instance = i
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func Search(ctx context.Context, req model.SearchReq) ([]model.SearchNode, int64, error) {
|
||||
return instance.Search(ctx, req)
|
||||
}
|
||||
|
||||
func Index(ctx context.Context, parent string, obj model.Obj) error {
|
||||
if instance == nil {
|
||||
return errs.SearchNotAvailable
|
||||
}
|
||||
return instance.Index(ctx, model.SearchNode{
|
||||
Parent: parent,
|
||||
Name: obj.GetName(),
|
||||
IsDir: obj.IsDir(),
|
||||
Size: obj.GetSize(),
|
||||
})
|
||||
}
|
||||
|
||||
type ObjWithParent struct {
|
||||
Parent string
|
||||
model.Obj
|
||||
}
|
||||
|
||||
func BatchIndex(ctx context.Context, objs []ObjWithParent) error {
|
||||
if instance == nil {
|
||||
return errs.SearchNotAvailable
|
||||
}
|
||||
if len(objs) == 0 {
|
||||
return nil
|
||||
}
|
||||
var searchNodes []model.SearchNode
|
||||
for i := range objs {
|
||||
searchNodes = append(searchNodes, model.SearchNode{
|
||||
Parent: objs[i].Parent,
|
||||
Name: objs[i].GetName(),
|
||||
IsDir: objs[i].IsDir(),
|
||||
Size: objs[i].GetSize(),
|
||||
})
|
||||
}
|
||||
return instance.BatchIndex(ctx, searchNodes)
|
||||
}
|
||||
|
||||
func init() {
|
||||
db.RegisterSettingItemHook(conf.SearchIndex, func(item *model.SettingItem) error {
|
||||
log.Debugf("searcher init, mode: %s", item.Value)
|
||||
return Init(item.Value)
|
||||
})
|
||||
}
|
9
internal/search/searcher/manage.go
Normal file
9
internal/search/searcher/manage.go
Normal file
@ -0,0 +1,9 @@
|
||||
package searcher
|
||||
|
||||
type New func() (Searcher, error)
|
||||
|
||||
var NewMap = map[string]New{}
|
||||
|
||||
func RegisterSearcher(config Config, searcher New) {
|
||||
NewMap[config.Name] = searcher
|
||||
}
|
31
internal/search/searcher/searcher.go
Normal file
31
internal/search/searcher/searcher.go
Normal file
@ -0,0 +1,31 @@
|
||||
package searcher
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Name string
|
||||
AutoUpdate bool
|
||||
}
|
||||
|
||||
type Searcher interface {
|
||||
// Config of the searcher
|
||||
Config() Config
|
||||
// Search specific keywords in specific path
|
||||
Search(ctx context.Context, req model.SearchReq) ([]model.SearchNode, int64, error)
|
||||
// Index obj with parent
|
||||
Index(ctx context.Context, node model.SearchNode) error
|
||||
// Index obj with parent in batches
|
||||
BatchIndex(ctx context.Context, nodes []model.SearchNode) error
|
||||
// Get by parent
|
||||
Get(ctx context.Context, parent string) ([]model.SearchNode, error)
|
||||
// Del with prefix
|
||||
Del(ctx context.Context, prefix string) error
|
||||
// Release resource
|
||||
Release(ctx context.Context) error
|
||||
// Clear all index
|
||||
Clear(ctx context.Context) error
|
||||
}
|
65
internal/search/util.go
Normal file
65
internal/search/util.go
Normal file
@ -0,0 +1,65 @@
|
||||
package search
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func Progress() (*model.IndexProgress, error) {
|
||||
p := setting.GetStr(conf.IndexProgress)
|
||||
var progress model.IndexProgress
|
||||
err := utils.Json.UnmarshalFromString(p, &progress)
|
||||
return &progress, err
|
||||
}
|
||||
|
||||
func WriteProgress(progress *model.IndexProgress) {
|
||||
p, err := utils.Json.MarshalToString(progress)
|
||||
if err != nil {
|
||||
log.Errorf("marshal progress error: %+v", err)
|
||||
}
|
||||
err = db.SaveSettingItem(model.SettingItem{
|
||||
Key: conf.IndexProgress,
|
||||
Value: p,
|
||||
Type: conf.TypeText,
|
||||
Group: model.SINGLE,
|
||||
Flag: model.PRIVATE,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("save progress error: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetIgnorePaths() ([]string, error) {
|
||||
storages, err := db.GetEnabledStorages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ignorePaths := make([]string, 0)
|
||||
var skipDrivers = []string{"AList V2", "AList V3"}
|
||||
for _, storage := range storages {
|
||||
if utils.SliceContains(skipDrivers, storage.Driver) {
|
||||
// TODO: request for indexing permission
|
||||
ignorePaths = append(ignorePaths, storage.MountPath)
|
||||
}
|
||||
}
|
||||
customIgnorePaths := setting.GetStr(conf.IgnorePaths)
|
||||
if customIgnorePaths != "" {
|
||||
ignorePaths = append(ignorePaths, strings.Split(customIgnorePaths, "\n")...)
|
||||
}
|
||||
return ignorePaths, nil
|
||||
}
|
||||
|
||||
func isIgnorePath(path string, ignorePaths []string) bool {
|
||||
for _, ignorePath := range ignorePaths {
|
||||
if strings.HasPrefix(path, ignorePath) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func GetStr(key string, defaultValue ...string) string {
|
||||
val, ok := db.GetSettingsMap()[key]
|
||||
val, ok := db.GetSettingsMap().Load(key)
|
||||
if !ok {
|
||||
if len(defaultValue) > 0 {
|
||||
return defaultValue[0]
|
||||
|
75
pkg/generic/queue.go
Normal file
75
pkg/generic/queue.go
Normal file
@ -0,0 +1,75 @@
|
||||
package generic
|
||||
|
||||
type Queue[T any] struct {
|
||||
queue []T
|
||||
}
|
||||
|
||||
func NewQueue[T any]() *Queue[T] {
|
||||
return &Queue[T]{queue: make([]T, 0)}
|
||||
}
|
||||
|
||||
func (q *Queue[T]) Push(v T) {
|
||||
q.queue = append(q.queue, v)
|
||||
}
|
||||
|
||||
func (q *Queue[T]) Pop() T {
|
||||
v := q.queue[0]
|
||||
q.queue = q.queue[1:]
|
||||
return v
|
||||
}
|
||||
|
||||
func (q *Queue[T]) Len() int {
|
||||
return len(q.queue)
|
||||
}
|
||||
|
||||
func (q *Queue[T]) IsEmpty() bool {
|
||||
return len(q.queue) == 0
|
||||
}
|
||||
|
||||
func (q *Queue[T]) Clear() {
|
||||
q.queue = nil
|
||||
}
|
||||
|
||||
func (q *Queue[T]) Peek() T {
|
||||
return q.queue[0]
|
||||
}
|
||||
|
||||
func (q *Queue[T]) PeekN(n int) []T {
|
||||
return q.queue[:n]
|
||||
}
|
||||
|
||||
func (q *Queue[T]) PopN(n int) []T {
|
||||
v := q.queue[:n]
|
||||
q.queue = q.queue[n:]
|
||||
return v
|
||||
}
|
||||
|
||||
func (q *Queue[T]) PopAll() []T {
|
||||
v := q.queue
|
||||
q.queue = nil
|
||||
return v
|
||||
}
|
||||
|
||||
func (q *Queue[T]) PopWhile(f func(T) bool) []T {
|
||||
var i int
|
||||
for i = 0; i < len(q.queue); i++ {
|
||||
if !f(q.queue[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
v := q.queue[:i]
|
||||
q.queue = q.queue[i:]
|
||||
return v
|
||||
}
|
||||
|
||||
func (q *Queue[T]) PopUntil(f func(T) bool) []T {
|
||||
var i int
|
||||
for i = 0; i < len(q.queue); i++ {
|
||||
if f(q.queue[i]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
v := q.queue[:i]
|
||||
q.queue = q.queue[i:]
|
||||
return v
|
||||
}
|
@ -347,6 +347,23 @@ func (m *MapOf[K, V]) Values() []V {
|
||||
return values
|
||||
}
|
||||
|
||||
func (m *MapOf[K, V]) Count() int {
|
||||
return len(m.dirty)
|
||||
}
|
||||
|
||||
func (m *MapOf[K, V]) Empty() bool {
|
||||
return m.Count() == 0
|
||||
}
|
||||
|
||||
func (m *MapOf[K, V]) ToMap() map[K]V {
|
||||
ans := make(map[K]V)
|
||||
m.Range(func(key K, value V) bool {
|
||||
ans[key] = value
|
||||
return true
|
||||
})
|
||||
return ans
|
||||
}
|
||||
|
||||
func (m *MapOf[K, V]) Clear() {
|
||||
m.Range(func(key K, value V) bool {
|
||||
m.Delete(key)
|
||||
|
@ -342,6 +342,58 @@ func (c *Client) Read(path string) ([]byte, error) {
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *Client) Link(path string) (string, http.Header, error) {
|
||||
method := "HEAD"
|
||||
url := PathEscape(Join(c.root, path))
|
||||
r, err := http.NewRequest(method, url, nil)
|
||||
|
||||
if err != nil {
|
||||
return "", nil, newPathErrorErr("Link", path, err)
|
||||
}
|
||||
|
||||
for k, vals := range c.headers {
|
||||
for _, v := range vals {
|
||||
r.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
c.authMutex.Lock()
|
||||
auth := c.auth
|
||||
c.authMutex.Unlock()
|
||||
|
||||
auth.Authorize(r, method, path)
|
||||
|
||||
if c.interceptor != nil {
|
||||
c.interceptor(method, r)
|
||||
}
|
||||
|
||||
rs, err := c.c.Do(r)
|
||||
if err != nil {
|
||||
return "", nil, newPathErrorErr("Link", path, err)
|
||||
}
|
||||
|
||||
if rs.StatusCode == 401 {
|
||||
wwwAuthenticateHeader := strings.ToLower(rs.Header.Get("Www-Authenticate"))
|
||||
if strings.Contains(wwwAuthenticateHeader, "digest") {
|
||||
c.authMutex.Lock()
|
||||
c.auth = &DigestAuth{auth.User(), auth.Pass(), digestParts(rs)}
|
||||
c.auth.Authorize(r, method, path)
|
||||
c.authMutex.Unlock()
|
||||
} else if strings.Contains(wwwAuthenticateHeader, "basic") {
|
||||
c.authMutex.Lock()
|
||||
c.auth = &BasicAuth{auth.User(), auth.Pass()}
|
||||
c.auth.Authorize(r, method, path)
|
||||
c.authMutex.Unlock()
|
||||
} else {
|
||||
return "", nil, newPathError("Authorize", c.root, rs.StatusCode)
|
||||
}
|
||||
} else if rs.StatusCode > 400 {
|
||||
return "", nil, newPathError("Authorize", path, rs.StatusCode)
|
||||
}
|
||||
|
||||
return r.URL.String(), r.Header, nil
|
||||
}
|
||||
|
||||
// ReadStream reads the stream for a given path
|
||||
func (c *Client) ReadStream(path string, callback func(rq *http.Request)) (io.ReadCloser, http.Header, error) {
|
||||
rs, err := c.req("GET", path, nil, callback)
|
||||
|
56
pkg/mq/mq.go
Normal file
56
pkg/mq/mq.go
Normal file
@ -0,0 +1,56 @@
|
||||
package mq
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/alist-org/alist/v3/pkg/generic"
|
||||
)
|
||||
|
||||
type Message[T any] struct {
|
||||
Content T
|
||||
}
|
||||
|
||||
type BasicConsumer[T any] func(Message[T])
|
||||
type AllConsumer[T any] func([]Message[T])
|
||||
|
||||
type MQ[T any] interface {
|
||||
Publish(Message[T])
|
||||
Consume(BasicConsumer[T])
|
||||
ConsumeAll(AllConsumer[T])
|
||||
Clear()
|
||||
}
|
||||
|
||||
type inMemoryMQ[T any] struct {
|
||||
queue generic.Queue[Message[T]]
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewInMemoryMQ[T any]() MQ[T] {
|
||||
return &inMemoryMQ[T]{queue: *generic.NewQueue[Message[T]]()}
|
||||
}
|
||||
|
||||
func (mq *inMemoryMQ[T]) Publish(msg Message[T]) {
|
||||
mq.Lock()
|
||||
defer mq.Unlock()
|
||||
mq.queue.Push(msg)
|
||||
}
|
||||
|
||||
func (mq *inMemoryMQ[T]) Consume(consumer BasicConsumer[T]) {
|
||||
mq.Lock()
|
||||
defer mq.Unlock()
|
||||
for !mq.queue.IsEmpty() {
|
||||
consumer(mq.queue.Pop())
|
||||
}
|
||||
}
|
||||
|
||||
func (mq *inMemoryMQ[T]) ConsumeAll(consumer AllConsumer[T]) {
|
||||
mq.Lock()
|
||||
defer mq.Unlock()
|
||||
consumer(mq.queue.PopAll())
|
||||
}
|
||||
|
||||
func (mq *inMemoryMQ[T]) Clear() {
|
||||
mq.Lock()
|
||||
defer mq.Unlock()
|
||||
mq.queue.Clear()
|
||||
}
|
@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
type Manager[K comparable] struct {
|
||||
workerC chan struct{}
|
||||
curID K
|
||||
workerC chan struct{}
|
||||
updateID func(*K)
|
||||
tasks generic_sync.MapOf[K, *Task[K]]
|
||||
}
|
||||
@ -94,7 +94,7 @@ func (tm *Manager[K]) RemoveByStates(states ...string) {
|
||||
tasks := tm.GetAll()
|
||||
for _, task := range tasks {
|
||||
if utils.SliceContains(states, task.GetState()) {
|
||||
tm.Remove(task.ID)
|
||||
_ = tm.Remove(task.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -138,6 +138,13 @@ func GetFileType(filename string) int {
|
||||
return conf.UNKNOWN
|
||||
}
|
||||
|
||||
func GetObjType(filename string, isDir bool) int {
|
||||
if isDir {
|
||||
return conf.FOLDER
|
||||
}
|
||||
return GetFileType(filename)
|
||||
}
|
||||
|
||||
func GetMimeType(name string) string {
|
||||
ext := path.Ext(name)
|
||||
m := mime.TypeByExtension(ext)
|
||||
|
@ -1,6 +1,7 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
stdjson "encoding/json"
|
||||
"os"
|
||||
|
||||
json "github.com/json-iterator/go"
|
||||
@ -10,8 +11,11 @@ import (
|
||||
var Json = json.ConfigCompatibleWithStandardLibrary
|
||||
|
||||
// WriteJsonToFile write struct to json file
|
||||
func WriteJsonToFile(dst string, data interface{}) bool {
|
||||
func WriteJsonToFile(dst string, data interface{}, std ...bool) bool {
|
||||
str, err := json.MarshalIndent(data, "", " ")
|
||||
if len(std) > 0 && std[0] {
|
||||
str, err = stdjson.MarshalIndent(data, "", " ")
|
||||
}
|
||||
if err != nil {
|
||||
log.Errorf("failed convert Conf to []byte:%s", err.Error())
|
||||
return false
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
)
|
||||
|
||||
// StandardizePath convert path like '/' '/root' '/a/b'
|
||||
@ -60,3 +62,10 @@ func EncodePath(path string, all ...bool) string {
|
||||
}
|
||||
return strings.Join(seg, "/")
|
||||
}
|
||||
|
||||
func JoinBasePath(basePath, reqPath string) (string, error) {
|
||||
if strings.HasSuffix(reqPath, "..") || strings.Contains(reqPath, "../") {
|
||||
return "", errs.RelativePath
|
||||
}
|
||||
return stdpath.Join(basePath, reqPath), nil
|
||||
}
|
||||
|
@ -35,3 +35,12 @@ func SliceConvert[S any, D any](srcS []S, convert func(src S) (D, error)) ([]D,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func MustSliceConvert[S any, D any](srcS []S, convert func(src S) D) []D {
|
||||
var res []D
|
||||
for i := range srcS {
|
||||
dst := convert(srcS[i])
|
||||
res = append(res, dst)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
14
pkg/utils/str.go
Normal file
14
pkg/utils/str.go
Normal file
@ -0,0 +1,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
)
|
||||
|
||||
func MappingName(name string) string {
|
||||
for k, v := range conf.FilenameCharMap {
|
||||
name = strings.ReplaceAll(name, k, v)
|
||||
}
|
||||
return name
|
||||
}
|
6
renovate.json
Normal file
6
renovate.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
42
server/common/check.go
Normal file
42
server/common/check.go
Normal file
@ -0,0 +1,42 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
)
|
||||
|
||||
func CanWrite(meta *model.Meta, path string) bool {
|
||||
if meta == nil || !meta.Write {
|
||||
return false
|
||||
}
|
||||
return meta.WSub || meta.Path == path
|
||||
}
|
||||
|
||||
func CanAccess(user *model.User, meta *model.Meta, reqPath string, password string) bool {
|
||||
// if the reqPath is in hide (only can check the nearest meta) and user can't see hides, can't access
|
||||
if meta != nil && !user.CanSeeHides() && meta.Hide != "" {
|
||||
for _, hide := range strings.Split(meta.Hide, "\n") {
|
||||
re := regexp.MustCompile(hide)
|
||||
if re.MatchString(reqPath[len(meta.Path):]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
// if is not guest and can access without password
|
||||
if user.CanAccessWithoutPassword() {
|
||||
return true
|
||||
}
|
||||
// if meta is nil or password is empty, can access
|
||||
if meta == nil || meta.Password == "" {
|
||||
return true
|
||||
}
|
||||
// if meta doesn't apply to sub_folder, can access
|
||||
if !utils.PathEqual(meta.Path, reqPath) && !meta.PSub {
|
||||
return true
|
||||
}
|
||||
// validate password
|
||||
return meta.Password == password
|
||||
}
|
@ -21,6 +21,23 @@ func hidePrivacy(msg string) string {
|
||||
// ErrorResp is used to return error response
|
||||
// @param l: if true, log error
|
||||
func ErrorResp(c *gin.Context, err error, code int, l ...bool) {
|
||||
ErrorWithDataResp(c, err, code, nil, l...)
|
||||
//if len(l) > 0 && l[0] {
|
||||
// if flags.Debug || flags.Dev {
|
||||
// log.Errorf("%+v", err)
|
||||
// } else {
|
||||
// log.Errorf("%v", err)
|
||||
// }
|
||||
//}
|
||||
//c.JSON(200, Resp[interface{}]{
|
||||
// Code: code,
|
||||
// Message: hidePrivacy(err.Error()),
|
||||
// Data: nil,
|
||||
//})
|
||||
//c.Abort()
|
||||
}
|
||||
|
||||
func ErrorWithDataResp(c *gin.Context, err error, code int, data interface{}, l ...bool) {
|
||||
if len(l) > 0 && l[0] {
|
||||
if flags.Debug || flags.Dev {
|
||||
log.Errorf("%+v", err)
|
||||
@ -31,7 +48,7 @@ func ErrorResp(c *gin.Context, err error, code int, l ...bool) {
|
||||
c.JSON(200, Resp[interface{}]{
|
||||
Code: code,
|
||||
Message: hidePrivacy(err.Error()),
|
||||
Data: nil,
|
||||
Data: data,
|
||||
})
|
||||
c.Abort()
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, file.GetName(), url.QueryEscape(file.GetName())))
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(file.GetSize(), 10))
|
||||
if link.Header != nil {
|
||||
// TODO clean header with blacklist or whitelist
|
||||
link.Header.Del("set-cookie")
|
||||
for h, val := range link.Header {
|
||||
w.Header()[h] = val
|
||||
}
|
||||
@ -81,6 +83,8 @@ func Proxy(w http.ResponseWriter, r *http.Request, link *model.Link, file model.
|
||||
_ = res.Body.Close()
|
||||
}()
|
||||
log.Debugf("proxy status: %d", res.StatusCode)
|
||||
// TODO clean header with blacklist or whitelist
|
||||
res.Header.Del("set-cookie")
|
||||
for h, v := range res.Header {
|
||||
w.Header()[h] = v
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/sign"
|
||||
stdpath "path"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/internal/sign"
|
||||
)
|
||||
|
||||
func Sign(obj model.Obj, parent string, encrypt bool) string {
|
||||
if obj.IsDir() || !encrypt {
|
||||
if obj.IsDir() || (!encrypt && !setting.GetBool(conf.SignAll)) {
|
||||
return ""
|
||||
}
|
||||
return sign.Sign(stdpath.Join(parent, obj.GetName()))
|
||||
|
@ -1,8 +1,6 @@
|
||||
package handles
|
||||
|
||||
import (
|
||||
stdpath "path"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/aria2"
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
@ -58,9 +56,13 @@ func AddAria2(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
reqPath, err := user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
for _, url := range req.Urls {
|
||||
err := aria2.AddURI(c, url, req.Path)
|
||||
err := aria2.AddURI(c, url, reqPath)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
|
@ -26,35 +26,32 @@ func FsMkdir(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
reqPath, err := user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
if !user.CanWrite() {
|
||||
meta, err := db.GetNearestMeta(stdpath.Dir(req.Path))
|
||||
meta, err := db.GetNearestMeta(stdpath.Dir(reqPath))
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !canWrite(meta, req.Path) {
|
||||
if !common.CanWrite(meta, reqPath) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := fs.MakeDir(c, req.Path); err != nil {
|
||||
if err := fs.MakeDir(c, reqPath); err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
fs.ClearCache(stdpath.Dir(req.Path))
|
||||
fs.ClearCache(stdpath.Dir(reqPath))
|
||||
common.SuccessResp(c)
|
||||
}
|
||||
|
||||
func canWrite(meta *model.Meta, path string) bool {
|
||||
if meta == nil || !meta.Write {
|
||||
return false
|
||||
}
|
||||
return meta.WSub || meta.Path == path
|
||||
}
|
||||
|
||||
type MoveCopyReq struct {
|
||||
SrcDir string `json:"src_dir"`
|
||||
DstDir string `json:"dst_dir"`
|
||||
@ -76,17 +73,25 @@ func FsMove(c *gin.Context) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
req.SrcDir = stdpath.Join(user.BasePath, req.SrcDir)
|
||||
req.DstDir = stdpath.Join(user.BasePath, req.DstDir)
|
||||
srcDir, err := user.JoinPath(req.SrcDir)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
dstDir, err := user.JoinPath(req.DstDir)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
for _, name := range req.Names {
|
||||
err := fs.Move(c, stdpath.Join(req.SrcDir, name), req.DstDir)
|
||||
err := fs.Move(c, stdpath.Join(srcDir, name), dstDir)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
}
|
||||
fs.ClearCache(req.SrcDir)
|
||||
fs.ClearCache(req.DstDir)
|
||||
fs.ClearCache(srcDir)
|
||||
fs.ClearCache(dstDir)
|
||||
common.SuccessResp(c)
|
||||
}
|
||||
|
||||
@ -105,11 +110,19 @@ func FsCopy(c *gin.Context) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
req.SrcDir = stdpath.Join(user.BasePath, req.SrcDir)
|
||||
req.DstDir = stdpath.Join(user.BasePath, req.DstDir)
|
||||
srcDir, err := user.JoinPath(req.SrcDir)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
dstDir, err := user.JoinPath(req.DstDir)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
var addedTask []string
|
||||
for _, name := range req.Names {
|
||||
ok, err := fs.Copy(c, stdpath.Join(req.SrcDir, name), req.DstDir)
|
||||
ok, err := fs.Copy(c, stdpath.Join(srcDir, name), dstDir)
|
||||
if ok {
|
||||
addedTask = append(addedTask, name)
|
||||
}
|
||||
@ -119,7 +132,7 @@ func FsCopy(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
if len(req.Names) != len(addedTask) {
|
||||
fs.ClearCache(req.DstDir)
|
||||
fs.ClearCache(dstDir)
|
||||
}
|
||||
if len(addedTask) > 0 {
|
||||
common.SuccessResp(c, fmt.Sprintf("Added %d tasks", len(addedTask)))
|
||||
@ -144,12 +157,16 @@ func FsRename(c *gin.Context) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
if err := fs.Rename(c, req.Path, req.Name); err != nil {
|
||||
reqPath, err := user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
if err := fs.Rename(c, reqPath, req.Name); err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
fs.ClearCache(stdpath.Dir(req.Path))
|
||||
fs.ClearCache(stdpath.Dir(reqPath))
|
||||
common.SuccessResp(c)
|
||||
}
|
||||
|
||||
@ -173,9 +190,13 @@ func FsRemove(c *gin.Context) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
req.Dir = stdpath.Join(user.BasePath, req.Dir)
|
||||
reqDir, err := user.JoinPath(req.Dir)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
for _, name := range req.Names {
|
||||
err := fs.Remove(c, stdpath.Join(req.Dir, name))
|
||||
err := fs.Remove(c, stdpath.Join(reqDir, name))
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
@ -192,8 +213,10 @@ func Link(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
rawPath := stdpath.Join(user.BasePath, req.Path)
|
||||
//user := c.MustGet("user").(*model.User)
|
||||
//rawPath := stdpath.Join(user.BasePath, req.Path)
|
||||
// why need not join base_path? because it's always the full path
|
||||
rawPath := req.Path
|
||||
storage, err := fs.GetStorage(rawPath)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
|
@ -6,7 +6,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/fs"
|
||||
@ -19,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type ListReq struct {
|
||||
common.PageReq
|
||||
model.PageReq
|
||||
Path string `json:"path" form:"path"`
|
||||
Password string `json:"password" form:"password"`
|
||||
Refresh bool `json:"refresh"`
|
||||
@ -57,8 +56,12 @@ func FsList(c *gin.Context) {
|
||||
}
|
||||
req.Validate()
|
||||
user := c.MustGet("user").(*model.User)
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
meta, err := db.GetNearestMeta(req.Path)
|
||||
reqPath, err := user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
meta, err := db.GetNearestMeta(reqPath)
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
@ -66,30 +69,30 @@ func FsList(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
c.Set("meta", meta)
|
||||
if !canAccess(user, meta, req.Path, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect", 403)
|
||||
if !common.CanAccess(user, meta, reqPath, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403)
|
||||
return
|
||||
}
|
||||
if !user.CanWrite() && !canWrite(meta, req.Path) && req.Refresh {
|
||||
if !user.CanWrite() && !common.CanWrite(meta, reqPath) && req.Refresh {
|
||||
common.ErrorStrResp(c, "Refresh without permission", 403)
|
||||
return
|
||||
}
|
||||
objs, err := fs.List(c, req.Path, req.Refresh)
|
||||
objs, err := fs.List(c, reqPath, req.Refresh)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
total, objs := pagination(objs, &req.PageReq)
|
||||
provider := "unknown"
|
||||
storage, err := fs.GetStorage(req.Path)
|
||||
storage, err := fs.GetStorage(reqPath)
|
||||
if err == nil {
|
||||
provider = storage.GetStorage().Driver
|
||||
}
|
||||
common.SuccessResp(c, FsListResp{
|
||||
Content: toObjResp(objs, req.Path, isEncrypt(meta, req.Path)),
|
||||
Content: toObjsResp(objs, reqPath, isEncrypt(meta, reqPath)),
|
||||
Total: int64(total),
|
||||
Readme: getReadme(meta, req.Path),
|
||||
Write: user.CanWrite() || canWrite(meta, req.Path),
|
||||
Readme: getReadme(meta, reqPath),
|
||||
Write: user.CanWrite() || common.CanWrite(meta, reqPath),
|
||||
Provider: provider,
|
||||
})
|
||||
}
|
||||
@ -101,15 +104,21 @@ func FsDirs(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
reqPath := req.Path
|
||||
if req.ForceRoot {
|
||||
if !user.IsAdmin() {
|
||||
common.ErrorStrResp(c, "Permission denied", 403)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
tmp, err := user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
reqPath = tmp
|
||||
}
|
||||
meta, err := db.GetNearestMeta(req.Path)
|
||||
meta, err := db.GetNearestMeta(reqPath)
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
@ -117,11 +126,11 @@ func FsDirs(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
c.Set("meta", meta)
|
||||
if !canAccess(user, meta, req.Path, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect", 403)
|
||||
if !common.CanAccess(user, meta, reqPath, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403)
|
||||
return
|
||||
}
|
||||
objs, err := fs.List(c, req.Path)
|
||||
objs, err := fs.List(c, reqPath)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
@ -155,23 +164,6 @@ func getReadme(meta *model.Meta, path string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func canAccess(user *model.User, meta *model.Meta, path string, password string) bool {
|
||||
// if is not guest, can access
|
||||
if user.CanAccessWithoutPassword() {
|
||||
return true
|
||||
}
|
||||
// if meta is nil or password is empty, can access
|
||||
if meta == nil || meta.Password == "" {
|
||||
return true
|
||||
}
|
||||
// if meta doesn't apply to sub_folder, can access
|
||||
if !utils.PathEqual(meta.Path, path) && !meta.PSub {
|
||||
return true
|
||||
}
|
||||
// validate password
|
||||
return meta.Password == password
|
||||
}
|
||||
|
||||
func isEncrypt(meta *model.Meta, path string) bool {
|
||||
if meta == nil || meta.Password == "" {
|
||||
return false
|
||||
@ -182,7 +174,7 @@ func isEncrypt(meta *model.Meta, path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
|
||||
func pagination(objs []model.Obj, req *model.PageReq) (int, []model.Obj) {
|
||||
pageIndex, pageSize := req.Page, req.PerPage
|
||||
total := len(objs)
|
||||
start := (pageIndex - 1) * pageSize
|
||||
@ -196,17 +188,13 @@ func pagination(objs []model.Obj, req *common.PageReq) (int, []model.Obj) {
|
||||
return total, objs[start:end]
|
||||
}
|
||||
|
||||
func toObjResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
|
||||
func toObjsResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
|
||||
var resp []ObjResp
|
||||
for _, obj := range objs {
|
||||
thumb := ""
|
||||
if t, ok := obj.(model.Thumb); ok {
|
||||
thumb = t.Thumb()
|
||||
}
|
||||
tp := conf.FOLDER
|
||||
if !obj.IsDir() {
|
||||
tp = utils.GetFileType(obj.GetName())
|
||||
}
|
||||
resp = append(resp, ObjResp{
|
||||
Name: obj.GetName(),
|
||||
Size: obj.GetSize(),
|
||||
@ -214,7 +202,7 @@ func toObjResp(objs []model.Obj, parent string, encrypt bool) []ObjResp {
|
||||
Modified: obj.ModTime(),
|
||||
Sign: common.Sign(obj, parent, encrypt),
|
||||
Thumb: thumb,
|
||||
Type: tp,
|
||||
Type: utils.GetObjType(obj.GetName(), obj.IsDir()),
|
||||
})
|
||||
}
|
||||
return resp
|
||||
@ -240,8 +228,12 @@ func FsGet(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
meta, err := db.GetNearestMeta(req.Path)
|
||||
reqPath, err := user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
meta, err := db.GetNearestMeta(reqPath)
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500)
|
||||
@ -249,18 +241,18 @@ func FsGet(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
c.Set("meta", meta)
|
||||
if !canAccess(user, meta, req.Path, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect", 403)
|
||||
if !common.CanAccess(user, meta, reqPath, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403)
|
||||
return
|
||||
}
|
||||
obj, err := fs.Get(c, req.Path)
|
||||
obj, err := fs.Get(c, reqPath)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
var rawURL string
|
||||
|
||||
storage, err := fs.GetStorage(req.Path)
|
||||
storage, err := fs.GetStorage(reqPath)
|
||||
provider := "unknown"
|
||||
if err == nil {
|
||||
provider = storage.Config().Name
|
||||
@ -274,13 +266,13 @@ func FsGet(c *gin.Context) {
|
||||
if storage.GetStorage().DownProxyUrl != "" {
|
||||
rawURL = fmt.Sprintf("%s%s?sign=%s",
|
||||
strings.Split(storage.GetStorage().DownProxyUrl, "\n")[0],
|
||||
utils.EncodePath(req.Path, true),
|
||||
sign.Sign(req.Path))
|
||||
utils.EncodePath(reqPath, true),
|
||||
sign.Sign(reqPath))
|
||||
} else {
|
||||
rawURL = fmt.Sprintf("%s/p%s?sign=%s",
|
||||
common.GetApiUrl(c.Request),
|
||||
utils.EncodePath(req.Path, true),
|
||||
sign.Sign(req.Path))
|
||||
utils.EncodePath(reqPath, true),
|
||||
sign.Sign(reqPath))
|
||||
}
|
||||
} else {
|
||||
// file have raw url
|
||||
@ -288,7 +280,7 @@ func FsGet(c *gin.Context) {
|
||||
rawURL = u.URL()
|
||||
} else {
|
||||
// if storage is not proxy, use raw url by fs.Link
|
||||
link, _, err := fs.Link(c, req.Path, model.LinkArgs{IP: c.ClientIP(), Header: c.Request.Header})
|
||||
link, _, err := fs.Link(c, reqPath, model.LinkArgs{IP: c.ClientIP(), Header: c.Request.Header})
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
@ -298,7 +290,7 @@ func FsGet(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
var related []model.Obj
|
||||
parentPath := stdpath.Dir(req.Path)
|
||||
parentPath := stdpath.Dir(reqPath)
|
||||
sameLevelFiles, err := fs.List(c, parentPath)
|
||||
if err == nil {
|
||||
related = filterRelated(sameLevelFiles, obj)
|
||||
@ -310,13 +302,13 @@ func FsGet(c *gin.Context) {
|
||||
Size: obj.GetSize(),
|
||||
IsDir: obj.IsDir(),
|
||||
Modified: obj.ModTime(),
|
||||
Sign: common.Sign(obj, parentPath, isEncrypt(meta, req.Path)),
|
||||
Sign: common.Sign(obj, parentPath, isEncrypt(meta, reqPath)),
|
||||
Type: utils.GetFileType(obj.GetName()),
|
||||
},
|
||||
RawURL: rawURL,
|
||||
Readme: getReadme(meta, req.Path),
|
||||
Readme: getReadme(meta, reqPath),
|
||||
Provider: provider,
|
||||
Related: toObjResp(related, parentPath, isEncrypt(parentMeta, parentPath)),
|
||||
Related: toObjsResp(related, parentPath, isEncrypt(parentMeta, parentPath)),
|
||||
})
|
||||
}
|
||||
|
||||
@ -346,7 +338,12 @@ func FsOther(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
req.Path = stdpath.Join(user.BasePath, req.Path)
|
||||
var err error
|
||||
req.Path, err = user.JoinPath(req.Path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
meta, err := db.GetNearestMeta(req.Path)
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
@ -355,8 +352,8 @@ func FsOther(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
c.Set("meta", meta)
|
||||
if !canAccess(user, meta, req.Path, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect", 403)
|
||||
if !common.CanAccess(user, meta, req.Path, req.Password) {
|
||||
common.ErrorStrResp(c, "password is incorrect or you have no permission", 403)
|
||||
return
|
||||
}
|
||||
res, err := fs.Other(c, req.FsOtherArgs)
|
||||
|
@ -6,13 +6,10 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/fs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func FsStream(c *gin.Context) {
|
||||
@ -24,21 +21,11 @@ func FsStream(c *gin.Context) {
|
||||
}
|
||||
asTask := c.GetHeader("As-Task") == "true"
|
||||
user := c.MustGet("user").(*model.User)
|
||||
path = stdpath.Join(user.BasePath, path)
|
||||
if !user.CanWrite() {
|
||||
meta, err := db.GetNearestMeta(stdpath.Dir(path))
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !canWrite(meta, path) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
path, err = user.JoinPath(path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
|
||||
dir, name := stdpath.Split(path)
|
||||
sizeStr := c.GetHeader("Content-Length")
|
||||
size, err := strconv.ParseInt(sizeStr, 10, 64)
|
||||
@ -77,19 +64,10 @@ func FsForm(c *gin.Context) {
|
||||
}
|
||||
asTask := c.GetHeader("As-Task") == "true"
|
||||
user := c.MustGet("user").(*model.User)
|
||||
path = stdpath.Join(user.BasePath, path)
|
||||
if !user.CanWrite() {
|
||||
meta, err := db.GetNearestMeta(stdpath.Dir(path))
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
return
|
||||
}
|
||||
}
|
||||
if !canWrite(meta, path) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
return
|
||||
}
|
||||
path, err = user.JoinPath(path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
storage, err := fs.GetStorage(path)
|
||||
if err != nil {
|
||||
|
65
server/handles/index.go
Normal file
65
server/handles/index.go
Normal file
@ -0,0 +1,65 @@
|
||||
package handles
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/search"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type BuildIndexReq struct {
|
||||
Paths []string `json:"paths"`
|
||||
MaxDepth int `json:"max_depth"`
|
||||
IgnorePaths []string `json:"ignore_paths"`
|
||||
}
|
||||
|
||||
func BuildIndex(c *gin.Context) {
|
||||
var req BuildIndexReq
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
if search.Running.Load() {
|
||||
common.ErrorStrResp(c, "index is running", 400)
|
||||
return
|
||||
}
|
||||
ignorePaths, err := search.GetIgnorePaths()
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
ignorePaths = append(ignorePaths, req.IgnorePaths...)
|
||||
go func() {
|
||||
ctx := context.Background()
|
||||
err := search.Clear(ctx)
|
||||
if err != nil {
|
||||
log.Errorf("clear index error: %+v", err)
|
||||
return
|
||||
}
|
||||
err = search.BuildIndex(context.Background(), req.Paths, ignorePaths, req.MaxDepth, true)
|
||||
if err != nil {
|
||||
log.Errorf("build index error: %+v", err)
|
||||
}
|
||||
}()
|
||||
common.SuccessResp(c)
|
||||
}
|
||||
|
||||
func StopIndex(c *gin.Context) {
|
||||
if !search.Running.Load() {
|
||||
common.ErrorStrResp(c, "index is not running", 400)
|
||||
return
|
||||
}
|
||||
search.Quit <- struct{}{}
|
||||
common.SuccessResp(c)
|
||||
}
|
||||
|
||||
func GetProgress(c *gin.Context) {
|
||||
progress, err := search.Progress()
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
common.SuccessResp(c, progress)
|
||||
}
|
@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
func ListMetas(c *gin.Context) {
|
||||
var req common.PageReq
|
||||
var req model.PageReq
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
|
64
server/handles/search.go
Normal file
64
server/handles/search.go
Normal file
@ -0,0 +1,64 @@
|
||||
package handles
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/search"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SearchResp struct {
|
||||
model.SearchNode
|
||||
Type int `json:"type"`
|
||||
}
|
||||
|
||||
func Search(c *gin.Context) {
|
||||
var req model.SearchReq
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
if err := req.Validate(); err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
nodes, total, err := search.Search(c, req)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 500)
|
||||
return
|
||||
}
|
||||
filteredNodes := []model.SearchNode{}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
for _, node := range nodes {
|
||||
if !strings.HasPrefix(node.Parent, user.BasePath) {
|
||||
continue
|
||||
}
|
||||
meta, err := db.GetNearestMeta(node.Parent)
|
||||
if err != nil && !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
continue
|
||||
}
|
||||
if !common.CanAccess(user, meta, path.Join(node.Parent, node.Name), "") {
|
||||
continue
|
||||
}
|
||||
// node.Parent = "/" + strings.Replace(node.Parent, user.BasePath, "", 1)
|
||||
filteredNodes = append(filteredNodes, node)
|
||||
}
|
||||
common.SuccessResp(c, common.PageResp{
|
||||
Content: utils.MustSliceConvert(filteredNodes, nodeToSearchResp),
|
||||
Total: total,
|
||||
})
|
||||
}
|
||||
|
||||
func nodeToSearchResp(node model.SearchNode) SearchResp {
|
||||
return SearchResp{
|
||||
SearchNode: node,
|
||||
Type: utils.GetObjType(node.Name, node.IsDir),
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func ListStorages(c *gin.Context) {
|
||||
var req common.PageReq
|
||||
var req model.PageReq
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
@ -36,10 +36,14 @@ func CreateStorage(c *gin.Context) {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
}
|
||||
if err := op.CreateStorage(c, req); err != nil {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
if id, err := op.CreateStorage(c, req); err != nil {
|
||||
common.ErrorWithDataResp(c, err, 500, gin.H{
|
||||
"id": id,
|
||||
}, true)
|
||||
} else {
|
||||
common.SuccessResp(c)
|
||||
common.SuccessResp(c, gin.H{
|
||||
"id": id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func ListUsers(c *gin.Context) {
|
||||
var req common.PageReq
|
||||
var req model.PageReq
|
||||
if err := c.ShouldBind(&req); err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
return
|
||||
|
@ -3,9 +3,11 @@ package middlewares
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/internal/sign"
|
||||
"github.com/alist-org/alist/v3/pkg/utils"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
@ -44,6 +46,9 @@ func parsePath(path string) string {
|
||||
}
|
||||
|
||||
func needSign(meta *model.Meta, path string) bool {
|
||||
if setting.GetBool(conf.SignAll) {
|
||||
return true
|
||||
}
|
||||
if meta == nil || meta.Password == "" {
|
||||
return false
|
||||
}
|
||||
|
44
server/middlewares/fsup.go
Normal file
44
server/middlewares/fsup.go
Normal file
@ -0,0 +1,44 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
stdpath "path"
|
||||
|
||||
"github.com/alist-org/alist/v3/internal/db"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/model"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func FsUp(c *gin.Context) {
|
||||
path := c.GetHeader("File-Path")
|
||||
password := c.GetHeader("Password")
|
||||
path, err := url.PathUnescape(path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 400)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
user := c.MustGet("user").(*model.User)
|
||||
path, err = user.JoinPath(path)
|
||||
if err != nil {
|
||||
common.ErrorResp(c, err, 403)
|
||||
return
|
||||
}
|
||||
meta, err := db.GetNearestMeta(stdpath.Dir(path))
|
||||
if err != nil {
|
||||
if !errors.Is(errors.Cause(err), errs.MetaNotFound) {
|
||||
common.ErrorResp(c, err, 500, true)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
if !(common.CanAccess(user, meta, path, password) && (user.CanWrite() || common.CanWrite(meta, path))) {
|
||||
common.ErrorResp(c, errs.PermissionDenied, 403)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
19
server/middlewares/search.go
Normal file
19
server/middlewares/search.go
Normal file
@ -0,0 +1,19 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"github.com/alist-org/alist/v3/internal/conf"
|
||||
"github.com/alist-org/alist/v3/internal/errs"
|
||||
"github.com/alist-org/alist/v3/internal/setting"
|
||||
"github.com/alist-org/alist/v3/server/common"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SearchIndex(c *gin.Context) {
|
||||
mode := setting.GetStr(conf.SearchIndex)
|
||||
if mode == "none" {
|
||||
common.ErrorResp(c, errs.SearchNotAvailable, 500)
|
||||
c.Abort()
|
||||
} else {
|
||||
c.Next()
|
||||
}
|
||||
}
|
@ -107,10 +107,16 @@ func admin(g *gin.RouterGroup) {
|
||||
ms := g.Group("/message")
|
||||
ms.POST("/get", message.HttpInstance.GetHandle)
|
||||
ms.POST("/send", message.HttpInstance.SendHandle)
|
||||
|
||||
index := g.Group("/index")
|
||||
index.POST("/build", middlewares.SearchIndex, handles.BuildIndex)
|
||||
index.POST("/stop", middlewares.SearchIndex, handles.StopIndex)
|
||||
index.GET("/progress", middlewares.SearchIndex, handles.GetProgress)
|
||||
}
|
||||
|
||||
func _fs(g *gin.RouterGroup) {
|
||||
g.Any("/list", handles.FsList)
|
||||
g.Any("/search", middlewares.SearchIndex, handles.Search)
|
||||
g.Any("/get", handles.FsGet)
|
||||
g.Any("/other", handles.FsOther)
|
||||
g.Any("/dirs", handles.FsDirs)
|
||||
@ -119,8 +125,8 @@ func _fs(g *gin.RouterGroup) {
|
||||
g.POST("/move", handles.FsMove)
|
||||
g.POST("/copy", handles.FsCopy)
|
||||
g.POST("/remove", handles.FsRemove)
|
||||
g.PUT("/put", handles.FsStream)
|
||||
g.PUT("/form", handles.FsForm)
|
||||
g.PUT("/put", middlewares.FsUp, handles.FsStream)
|
||||
g.PUT("/form", middlewares.FsUp, handles.FsForm)
|
||||
g.POST("/link", middlewares.AuthAdmin, handles.Link)
|
||||
g.POST("/add_aria2", handles.AddAria2)
|
||||
}
|
||||
@ -128,6 +134,7 @@ func _fs(g *gin.RouterGroup) {
|
||||
func Cors(r *gin.Engine) {
|
||||
config := cors.DefaultConfig()
|
||||
config.AllowAllOrigins = true
|
||||
config.AllowHeaders = append(config.AllowHeaders, "Authorization", "range", "File-Path", "As-Task")
|
||||
//config.AllowHeaders = append(config.AllowHeaders, "Authorization", "range", "File-Path", "As-Task", "Password")
|
||||
config.AllowHeaders = []string{"*"}
|
||||
r.Use(cors.New(config))
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package static
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
@ -18,7 +19,7 @@ import (
|
||||
func InitIndex() {
|
||||
index, err := public.Public.ReadFile("dist/index.html")
|
||||
if err != nil {
|
||||
if err == fs.ErrNotExist {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
utils.Log.Fatalf("index.html not exist, you may forget to put dist of frontend to public/dist")
|
||||
}
|
||||
utils.Log.Fatalf("failed to read index.html: %v", err)
|
||||
|
46
server/webdav/buffered_response_writer.go
Normal file
46
server/webdav/buffered_response_writer.go
Normal file
@ -0,0 +1,46 @@
|
||||
package webdav
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type bufferedResponseWriter struct {
|
||||
statusCode int
|
||||
data []byte
|
||||
header http.Header
|
||||
}
|
||||
|
||||
func (w *bufferedResponseWriter) Header() http.Header {
|
||||
if w.header == nil {
|
||||
w.header = make(http.Header)
|
||||
}
|
||||
return w.header
|
||||
}
|
||||
|
||||
func (w *bufferedResponseWriter) Write(bytes []byte) (int, error) {
|
||||
w.data = append(w.data, bytes...)
|
||||
return len(bytes), nil
|
||||
}
|
||||
|
||||
func (w *bufferedResponseWriter) WriteHeader(statusCode int) {
|
||||
if w.statusCode == 0 {
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
}
|
||||
|
||||
func (w *bufferedResponseWriter) WriteToResponse(rw http.ResponseWriter) (int, error) {
|
||||
h := rw.Header()
|
||||
for k, vs := range w.header {
|
||||
for _, v := range vs {
|
||||
h.Add(k, v)
|
||||
}
|
||||
}
|
||||
rw.WriteHeader(w.statusCode)
|
||||
return rw.Write(w.data)
|
||||
}
|
||||
|
||||
func newBufferedResponseWriter() *bufferedResponseWriter {
|
||||
return &bufferedResponseWriter{
|
||||
statusCode: 0,
|
||||
}
|
||||
}
|
@ -45,30 +45,33 @@ func (h *Handler) stripPrefix(p string) (string, int, error) {
|
||||
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
status, err := http.StatusBadRequest, errUnsupportedMethod
|
||||
brw := newBufferedResponseWriter()
|
||||
useBufferedWriter := true
|
||||
if h.LockSystem == nil {
|
||||
status, err = http.StatusInternalServerError, errNoLockSystem
|
||||
} else {
|
||||
switch r.Method {
|
||||
case "OPTIONS":
|
||||
status, err = h.handleOptions(w, r)
|
||||
status, err = h.handleOptions(brw, r)
|
||||
case "GET", "HEAD", "POST":
|
||||
useBufferedWriter = false
|
||||
status, err = h.handleGetHeadPost(w, r)
|
||||
case "DELETE":
|
||||
status, err = h.handleDelete(w, r)
|
||||
status, err = h.handleDelete(brw, r)
|
||||
case "PUT":
|
||||
status, err = h.handlePut(w, r)
|
||||
status, err = h.handlePut(brw, r)
|
||||
case "MKCOL":
|
||||
status, err = h.handleMkcol(w, r)
|
||||
status, err = h.handleMkcol(brw, r)
|
||||
case "COPY", "MOVE":
|
||||
status, err = h.handleCopyMove(w, r)
|
||||
status, err = h.handleCopyMove(brw, r)
|
||||
case "LOCK":
|
||||
status, err = h.handleLock(w, r)
|
||||
status, err = h.handleLock(brw, r)
|
||||
case "UNLOCK":
|
||||
status, err = h.handleUnlock(w, r)
|
||||
status, err = h.handleUnlock(brw, r)
|
||||
case "PROPFIND":
|
||||
status, err = h.handlePropfind(w, r)
|
||||
status, err = h.handlePropfind(brw, r)
|
||||
case "PROPPATCH":
|
||||
status, err = h.handleProppatch(w, r)
|
||||
status, err = h.handleProppatch(brw, r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +80,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if status != http.StatusNoContent {
|
||||
w.Write([]byte(StatusText(status)))
|
||||
}
|
||||
} else if useBufferedWriter {
|
||||
brw.WriteToResponse(w)
|
||||
}
|
||||
if h.Logger != nil && err != nil {
|
||||
h.Logger(r, err)
|
||||
@ -178,7 +183,10 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request) (status
|
||||
}
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
allow := "OPTIONS, LOCK, PUT, MKCOL"
|
||||
if fi, err := fs.Get(ctx, reqPath); err == nil {
|
||||
if fi.IsDir() {
|
||||
@ -203,7 +211,10 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
|
||||
// TODO: check locks for read-only access??
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
fi, err := fs.Get(ctx, reqPath)
|
||||
if err != nil {
|
||||
return http.StatusNotFound, err
|
||||
@ -218,7 +229,8 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
|
||||
w.Header().Set("ETag", etag)
|
||||
// Let ServeContent determine the Content-Type header.
|
||||
storage, _ := fs.GetStorage(reqPath)
|
||||
if storage.GetStorage().WebdavNative() {
|
||||
downProxyUrl := storage.GetStorage().DownProxyUrl
|
||||
if storage.GetStorage().WebdavNative() || (storage.GetStorage().WebdavProxy() && downProxyUrl == "") {
|
||||
link, _, err := fs.Link(ctx, reqPath, model.LinkArgs{Header: r.Header})
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
@ -227,19 +239,19 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request) (sta
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
} else if storage.Config().MustProxy() || storage.GetStorage().WebdavProxy() {
|
||||
u := fmt.Sprintf("%s/p%s?sign=%s",
|
||||
common.GetApiUrl(r),
|
||||
} else if storage.GetStorage().WebdavProxy() && downProxyUrl != "" {
|
||||
u := fmt.Sprintf("%s%s?sign=%s",
|
||||
strings.Split(downProxyUrl, "\n")[0],
|
||||
utils.EncodePath(reqPath, true),
|
||||
sign.Sign(reqPath))
|
||||
w.Header().Set("Cache-Control", "max-age=0, no-cache, no-store, must-revalidate")
|
||||
http.Redirect(w, r, u, 302)
|
||||
http.Redirect(w, r, u, http.StatusFound)
|
||||
} else {
|
||||
link, _, err := fs.Link(ctx, reqPath, model.LinkArgs{IP: utils.ClientIP(r)})
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
http.Redirect(w, r, link.URL, 302)
|
||||
http.Redirect(w, r, link.URL, http.StatusFound)
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
@ -257,7 +269,10 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request) (status i
|
||||
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
// TODO: return MultiStatus where appropriate.
|
||||
|
||||
// "godoc os RemoveAll" says that "If the path does not exist, RemoveAll
|
||||
@ -290,7 +305,10 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request) (status int,
|
||||
// comments in http.checkEtag.
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
obj := model.Object{
|
||||
Name: path.Base(reqPath),
|
||||
Size: r.ContentLength,
|
||||
@ -336,7 +354,10 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request) (status in
|
||||
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
|
||||
if r.ContentLength > 0 {
|
||||
return http.StatusUnsupportedMediaType, nil
|
||||
@ -383,8 +404,14 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request) (status
|
||||
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
src = path.Join(user.BasePath, src)
|
||||
dst = path.Join(user.BasePath, dst)
|
||||
src, err = user.JoinPath(src)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
dst, err = user.JoinPath(dst)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
|
||||
if r.Method == "COPY" {
|
||||
// Section 7.5.1 says that a COPY only needs to lock the destination,
|
||||
@ -475,7 +502,10 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request) (retStatus
|
||||
}
|
||||
}
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
if err != nil {
|
||||
return status, err
|
||||
}
|
||||
@ -556,7 +586,10 @@ func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request) (status
|
||||
}
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
fi, err := fs.Get(ctx, reqPath)
|
||||
if err != nil {
|
||||
if errs.IsObjectNotFound(err) {
|
||||
@ -632,8 +665,10 @@ func (h *Handler) handleProppatch(w http.ResponseWriter, r *http.Request) (statu
|
||||
|
||||
ctx := r.Context()
|
||||
user := ctx.Value("user").(*model.User)
|
||||
reqPath = path.Join(user.BasePath, reqPath)
|
||||
|
||||
reqPath, err = user.JoinPath(reqPath)
|
||||
if err != nil {
|
||||
return 403, err
|
||||
}
|
||||
if _, err := fs.Get(ctx, reqPath); err != nil {
|
||||
if errs.IsObjectNotFound(err) {
|
||||
return http.StatusNotFound, err
|
||||
|
Reference in New Issue
Block a user