Compare commits

...

37 Commits

Author SHA1 Message Date
4bcc6359e3 💚 fix docker build 2022-01-20 20:52:14 +08:00
4144afcc92 🐛 fix #397 139yun file size overflow int32 2022-01-20 20:35:01 +08:00
2ad27046fb 🎨 split build and docker 2022-01-20 20:30:58 +08:00
9516ac6718 🎇 finish move api 2022-01-20 19:58:25 +08:00
de638c7c36 🐛 fix 123pan move 2022-01-20 19:58:10 +08:00
c6b34a033b 🔧 add swf to image types 2022-01-20 14:40:59 +08:00
31de3399d2 💚 fix musl prebuilt 2022-01-20 14:23:31 +08:00
0dc2ca019f 💚 fix musl prebuilt 2022-01-20 13:47:21 +08:00
04724f7f0f 👷 add prebuilt for musl-libc 2022-01-20 12:53:49 +08:00
75a983a965 🐛 fix webdav can't get file with password 2022-01-19 18:46:32 +08:00
e12d8bb8ca ⬆️ upgrade gorm 2022-01-19 09:15:00 +08:00
68f1ccfed4 add sslmode for postgres 2022-01-19 09:14:31 +08:00
54272db59c 🚧 add folder api 2022-01-18 19:08:44 +08:00
6d34e88360 🎇 hide files while webdav visitor 2022-01-18 18:48:08 +08:00
0a901a2eb0 🐛 fix can't find zhimg for dev 2022-01-18 18:19:58 +08:00
e1671a0511 🚧 add move api 2022-01-18 16:13:07 +08:00
dcb4ec695f rename and mkdir api 2022-01-18 14:31:52 +08:00
4a21b6fe1d 🔥 close res.body for proxy 2022-01-18 11:29:32 +08:00
96a237902b 🐛 close #379 fix google drive can't get text file 2022-01-17 09:54:19 +08:00
cfb51e9f80 Extract folder 2022-01-16 16:38:41 +08:00
e952f1c243 🔥 Optimize 189 cloud get client 2022-01-16 16:05:32 +08:00
07d6ca27db 🐛 Forbid MediaTrack to create a new folder with the same name 2022-01-16 13:21:29 +08:00
8245da485a 🐛 fix s3 for #362 2022-01-15 20:24:57 +08:00
5c759217cf 🐛 fix some lanzou can't down #360 2022-01-15 20:10:42 +08:00
0648fdebc2 ♻️ solve circular dependency 2022-01-15 19:59:24 +08:00
ed670e528f 🐛 fix 139Yun error phone close #365 2022-01-15 19:44:22 +08:00
2473309a51 🎇 execute save while delete account 2022-01-15 19:36:37 +08:00
21ca2f11b7 🔧 change default config 2022-01-14 21:20:45 +08:00
ccaa28a323 🔧 change default cdn 2022-01-14 20:56:20 +08:00
fea8b376f8 🔧 change config 2022-01-14 20:10:35 +08:00
55d244b726 🐛 fix mediatrack can't mkdir #351 2022-01-14 18:19:58 +08:00
1640a52789 cancel hide file for admin #343 2022-01-14 18:00:47 +08:00
424ec10692 🐛 fix ftp download error 2022-01-13 22:56:07 +08:00
b472c2ee18 🔒 not allowed delete root folder 2022-01-13 21:23:27 +08:00
65a01251e9 🐛 fix proxy not set status 2022-01-13 20:09:11 +08:00
96be6bbbd1 🐛 fix #334 proxy video can't use range 2022-01-13 19:49:02 +08:00
6f7465aab7 🐛 fix 139Yun no user error 2022-01-13 18:01:54 +08:00
61 changed files with 933 additions and 403 deletions

View File

@ -48,41 +48,4 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: artifact
path: alist/build
docker:
name: Docker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: xhofe/alist
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Build web
run: |
bash build.sh web
mv dist/* public
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: xhofe
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x
path: alist/build

46
.github/workflows/build_docker.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: build_docker
on:
push:
branches: [ v2 ]
pull_request:
branches: [ v2 ]
jobs:
build_docker:
name: Docker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: xhofe/alist
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Build web
run: |
bash build.sh web
mv dist/* public
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: xhofe
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x

View File

@ -43,41 +43,4 @@ jobs:
- name: Release
uses: softprops/action-gh-release@v1
with:
files: alist/build/compress/*
docker:
name: Docker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: xhofe/alist
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Build web
run: |
bash build.sh cdn
mv dist/* public
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: xhofe
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x
files: alist/build/compress/*

45
.github/workflows/release_docker.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: release_docker
on:
push:
tags:
- '*'
jobs:
docker_release:
name: Docker
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: xhofe/alist
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Build web
run: |
bash build.sh cdn
mv dist/* public
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: xhofe
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/386,linux/arm/v6,linux/s390x

2
.gitignore vendored
View File

@ -23,7 +23,7 @@ dist/
bin/*
/alist
*.json
public/index.html
public/*.html
public/assets/
public/public/
data/

View File

@ -3,7 +3,7 @@ LABEL stage=go-builder
WORKDIR /app/
COPY ./ ./
RUN apk add --no-cache bash git go gcc musl-dev; \
sh build.sh docker
bash build.sh docker
FROM alpine:edge
LABEL MAINTAINER="i@nn.ci"

View File

@ -33,7 +33,7 @@ func InitConf() {
}
log.Debugf("config:%+v", conf.Conf)
// update config.json struct
confBody, err := utils.Json.Marshal(conf.Conf)
confBody, err := utils.Json.MarshalIndent(conf.Conf, "", " ")
if err != nil {
log.Fatalf("marshal config error:%s", err.Error())
}

View File

@ -60,14 +60,13 @@ func InitModel() {
}
case "postgres":
{
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
databaseConfig.Host, databaseConfig.User, databaseConfig.Password, databaseConfig.Name, databaseConfig.Port)
dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=%s TimeZone=Asia/Shanghai",
databaseConfig.Host, databaseConfig.User, databaseConfig.Password, databaseConfig.Name, databaseConfig.Port, databaseConfig.SslMode)
db, err := gorm.Open(postgres.Open(dsn), gormConfig)
if err != nil {
log.Errorf("failed to connect database:%s", err.Error())
}
conf.DB = db
}
default:
log.Fatalf("not supported database type: %s", databaseConfig.Type)

View File

@ -57,19 +57,63 @@ BUILD() {
if [ "$1" == "release" ]; then
xgo -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
else
xgo -targets=linux/amd64,windows/amd64 -out alist -ldflags="$ldflags" -tags=jsoniter .
xgo -targets=linux/amd64,windows/amd64 -out "$appName" -ldflags="$ldflags" -tags=jsoniter .
fi
mkdir "build"
mkdir -p "build"
mv alist-* build
cd build
upx -9 ./*
find . -type f -print0 | xargs -0 md5sum >md5.txt
cat md5.txt
cd ../..
if [ "$1" != "release" ]; then
cd build
upx -9 ./*
find . -type f -print0 | xargs -0 md5sum >md5.txt
cat md5.txt
cd ..
fi
cd ..
}
BUILD_MUSL() {
BASE="https://musl.cc/"
FILES=(x86_64-linux-musl-cross aarch64-linux-musl-cross arm-linux-musleabihf-cross mips-linux-musl-cross mips64-linux-musl-cross mips64el-linux-musl-cross mipsel-linux-musl-cross powerpc64le-linux-musl-cross s390x-linux-musl-cross)
for i in "${FILES[@]}"; do
url="${BASE}${i}.tgz"
curl -L -o "${i}.tgz" "${url}"
sudo tar xf "${i}.tgz" --strip-components 1 -C /usr/local
done
cd alist
appName="alist"
builtAt="$(date +'%F %T %z')"
goVersion=$(go version | sed 's/go version //')
gitAuthor=$(git show -s --format='format:%aN <%ae>' HEAD)
gitCommit=$(git log --pretty=format:"%h" -1)
gitTag=$(git describe --long --tags --dirty --always)
ldflags="\
-w -s \
-X 'github.com/Xhofe/alist/conf.BuiltAt=$builtAt' \
-X 'github.com/Xhofe/alist/conf.GoVersion=$goVersion' \
-X 'github.com/Xhofe/alist/conf.GitAuthor=$gitAuthor' \
-X 'github.com/Xhofe/alist/conf.GitCommit=$gitCommit' \
-X 'github.com/Xhofe/alist/conf.GitTag=$gitTag' \
"
OS_ARCHES=(linux-musl-amd64 linux-musl-arm64 linux-musl-arm linux-musl-mips linux-musl-mips64 linux-musl-mips64le linux-musl-mipsle linux-musl-ppc64le linux-musl-s390x)
CGO_ARGS=(x86_64-linux-musl-gcc aarch64-linux-musl-gcc arm-linux-musleabihf-gcc mips-linux-musl-gcc mips64-linux-musl-gcc mips64el-linux-musl-gcc mipsel-linux-musl-gcc powerpc64le-linux-musl-gcc s390x-linux-musl-gcc)
for i in "${!OS_ARCHES[@]}"; do
os_arch=${OS_ARCHES[$i]}
cgo_cc=${CGO_ARGS[$i]}
echo building for ${os_arch}
export GOOS=${os_arch%%-*}
export GOARCH=${os_arch##*-}
export CC=${cgo_cc}
export CGO_ENABLED=1
go build -o ./build/$appName-$os_arch -ldflags="$ldflags" -tags=jsoniter alist.go
done
cd ..
}
RELEASE() {
cd alist/build
upx -9 ./*
find . -type f -print0 | xargs -0 md5sum >md5.txt
cat md5.txt
mkdir compress
mv md5.txt compress
for i in $(find . -type f -name "$appName-linux-*"); do
@ -94,7 +138,8 @@ elif [ "$1" = "build" ]; then
BUILD build
elif [ "$1" = "release" ]; then
BUILD release
BUILD_MUSL
RELEASE
else
echo -e "${RED_COLOR} 错误的命令${RES}"
echo -e "${RED_COLOR} Parameter error ${RES}"
fi

View File

@ -9,6 +9,7 @@ type Database struct {
Name string `json:"name"`
TablePrefix string `json:"table_prefix"`
DBFile string `json:"db_file"`
SslMode string `json:"ssl_mode"`
}
type Scheme struct {
@ -25,7 +26,7 @@ type CacheConfig struct {
type Config struct {
Address string `json:"address"`
Port int `json:"port"`
Local bool `json:"local"`
Assets string `json:"assets"`
Database Database `json:"database"`
Scheme Scheme `json:"scheme"`
Cache CacheConfig `json:"cache"`
@ -35,11 +36,13 @@ func DefaultConfig() *Config {
return &Config{
Address: "0.0.0.0",
Port: 5244,
Assets: "zhimg",
Database: Database{
Type: "sqlite3",
Port: 0,
TablePrefix: "x_",
DBFile: "data/data.db",
SslMode: "disable",
},
Cache: CacheConfig{
Expiration: 60,

View File

@ -36,7 +36,7 @@ var (
OfficeTypes = []string{"doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf"}
VideoTypes = []string{"mp4", "mkv", "avi", "mov", "rmvb", "webm", "flv"}
AudioTypes = []string{"mp3", "flac", "ogg", "m4a", "wav"}
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico"}
ImageTypes = []string{"jpg", "tiff", "jpeg", "png", "gif", "bmp", "svg", "ico", "swf"}
)
var settingsMap = make(map[string]string, 0)

View File

@ -65,6 +65,9 @@ func (driver Pan123) Items() []base.Item {
}
func (driver Pan123) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
if account.RootFolder == "" {
account.RootFolder = "0"
}
@ -234,7 +237,7 @@ func (driver Pan123) Move(src string, dst string, account *model.Account) error
}
parentFileId, _ := strconv.Atoi(dstDirFile.Id)
data := base.Json{
"fileId": fileId,
"fileIdList": []base.Json{{"FileId": fileId}},
"parentFileId": parentFileId,
}
_, err = driver.Request("https://www.123pan.com/api/file/mod_pid",

View File

@ -136,7 +136,7 @@ func (driver Cloud139) GetFiles(catalogID string, account *model.Account) ([]mod
f := model.File{
Id: content.ContentID,
Name: content.ContentName,
Size: int64(content.ContentSize),
Size: content.ContentSize,
Type: utils.GetFileType(path.Ext(content.ContentName)),
Driver: driver.Config().Name,
UpdatedAt: getTime(content.UpdateTime),
@ -158,7 +158,7 @@ func (driver Cloud139) GetLink(contentId string, account *model.Account) (string
"appName": "",
"contentID": contentId,
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
}

View File

@ -64,6 +64,9 @@ func (driver Cloud139) Items() []base.Item {
}
func (driver Cloud139) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
_, err := driver.Request("/orchestration/personalCloud/user/v1.0/qryUserExternInfo", base.Post, nil, nil, nil, base.Json{
"qryUserExternInfoReq": base.Json{
"commonAccountInfo": base.Json{
@ -131,7 +134,12 @@ func (driver Cloud139) Link(args base.Args, account *model.Account) (*base.Link,
if err != nil {
return nil, err
}
u, err := driver.GetLink(file.Id, account)
var u string
//if isFamily(account) {
// u, err = driver.familyLink(file.Id, account)
//} else {
u, err = driver.GetLink(file.Id, account)
//}
if err != nil {
return nil, err
}
@ -224,7 +232,7 @@ func (driver Cloud139) Move(src string, dst string, account *model.Account) erro
"newCatalogID": dstParentFile.Id,
},
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
},
@ -249,7 +257,7 @@ func (driver Cloud139) Rename(src string, dst string, account *model.Account) er
"catalogID": srcFile.Id,
"catalogName": utils.Base(dst),
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
}
@ -259,7 +267,7 @@ func (driver Cloud139) Rename(src string, dst string, account *model.Account) er
"contentID": srcFile.Id,
"contentName": utils.Base(dst),
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
}
@ -298,7 +306,7 @@ func (driver Cloud139) Copy(src string, dst string, account *model.Account) erro
"newCatalogID": dstParentFile.Id,
},
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
},
@ -330,7 +338,7 @@ func (driver Cloud139) Delete(path string, account *model.Account) error {
"catalogInfoList": contentInfoList,
},
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
},
@ -341,7 +349,7 @@ func (driver Cloud139) Delete(path string, account *model.Account) error {
"catalogList": catalogInfoList,
"contentList": contentInfoList,
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
"sourceCatalogType": 1002,
@ -377,7 +385,7 @@ func (driver Cloud139) Upload(file *model.FileStream, account *model.Account) er
"parentCatalogID": parentFile.Id,
"newCatalogName": "",
"commonAccountInfo": base.Json{
"account": "18627147660",
"account": account.Username,
"accountType": 1,
},
}

View File

@ -43,7 +43,7 @@ func (driver Cloud139) familyGetFiles(catalogID string, account *model.Account)
f := model.File{
Id: content.ContentID,
Name: content.ContentName,
Size: int64(content.ContentSize),
Size: content.ContentSize,
Type: utils.GetFileType(path.Ext(content.ContentName)),
Driver: driver.Config().Name,
UpdatedAt: getTime(content.LastUpdateTime),

View File

@ -40,7 +40,7 @@ type Content struct {
ContentID string `json:"contentID"`
ContentName string `json:"contentName"`
//ContentSuffix string `json:"contentSuffix"`
ContentSize int `json:"contentSize"`
ContentSize int64 `json:"contentSize"`
//ContentDesc string `json:"contentDesc"`
//ContentType int `json:"contentType"`
//ContentOrigin int `json:"contentOrigin"`
@ -132,43 +132,43 @@ type UploadResp struct {
}
type CloudContent struct {
ContentID string `json:"contentID"`
Modifier string `json:"modifier"`
Nickname string `json:"nickname"`
CloudNickName string `json:"cloudNickName"`
ContentName string `json:"contentName"`
ContentType int `json:"contentType"`
ContentSuffix string `json:"contentSuffix"`
ContentSize int `json:"contentSize"`
ContentDesc string `json:"contentDesc"`
CreateTime string `json:"createTime"`
Shottime interface{} `json:"shottime"`
LastUpdateTime string `json:"lastUpdateTime"`
ThumbnailURL string `json:"thumbnailURL"`
MidthumbnailURL string `json:"midthumbnailURL"`
BigthumbnailURL string `json:"bigthumbnailURL"`
PresentURL string `json:"presentURL"`
PresentLURL string `json:"presentLURL"`
PresentHURL string `json:"presentHURL"`
ParentCatalogID string `json:"parentCatalogID"`
Uploader string `json:"uploader"`
UploaderNickName string `json:"uploaderNickName"`
TreeInfo interface{} `json:"treeInfo"`
UpdateTime interface{} `json:"updateTime"`
ExtInfo struct {
Uploader string `json:"uploader"`
} `json:"extInfo"`
EtagOprType interface{} `json:"etagOprType"`
ContentID string `json:"contentID"`
//Modifier string `json:"modifier"`
//Nickname string `json:"nickname"`
//CloudNickName string `json:"cloudNickName"`
ContentName string `json:"contentName"`
//ContentType int `json:"contentType"`
//ContentSuffix string `json:"contentSuffix"`
ContentSize int64 `json:"contentSize"`
//ContentDesc string `json:"contentDesc"`
//CreateTime string `json:"createTime"`
//Shottime interface{} `json:"shottime"`
LastUpdateTime string `json:"lastUpdateTime"`
ThumbnailURL string `json:"thumbnailURL"`
//MidthumbnailURL string `json:"midthumbnailURL"`
//BigthumbnailURL string `json:"bigthumbnailURL"`
//PresentURL string `json:"presentURL"`
//PresentLURL string `json:"presentLURL"`
//PresentHURL string `json:"presentHURL"`
//ParentCatalogID string `json:"parentCatalogID"`
//Uploader string `json:"uploader"`
//UploaderNickName string `json:"uploaderNickName"`
//TreeInfo interface{} `json:"treeInfo"`
//UpdateTime interface{} `json:"updateTime"`
//ExtInfo struct {
// Uploader string `json:"uploader"`
//} `json:"extInfo"`
//EtagOprType interface{} `json:"etagOprType"`
}
type CloudCatalog struct {
CatalogID string `json:"catalogID"`
CatalogName string `json:"catalogName"`
CloudID string `json:"cloudID"`
CreateTime string `json:"createTime"`
LastUpdateTime string `json:"lastUpdateTime"`
Creator string `json:"creator"`
CreatorNickname string `json:"creatorNickname"`
CatalogID string `json:"catalogID"`
CatalogName string `json:"catalogName"`
//CloudID string `json:"cloudID"`
//CreateTime string `json:"createTime"`
LastUpdateTime string `json:"lastUpdateTime"`
//Creator string `json:"creator"`
//CreatorNickname string `json:"creatorNickname"`
}
type QueryContentListResp struct {

View File

@ -36,6 +36,22 @@ import (
var client189Map map[string]*resty.Client
func (driver Cloud189) getClient(account *model.Account) (*resty.Client, error) {
client, ok := client189Map[account.Name]
if ok {
return client, nil
}
err := driver.Login(account)
if err != nil {
return nil, err
}
client, ok = client189Map[account.Name]
if !ok {
return nil, fmt.Errorf("can't find [%s] client", account.Name)
}
return client, nil
}
func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
f := &model.File{
Id: strconv.FormatInt(file.Id, 10),
@ -264,9 +280,9 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
}
func (driver Cloud189) Request(url string, method int, query, form map[string]string, headers map[string]string, account *model.Account) ([]byte, error) {
client, ok := client189Map[account.Name]
if !ok {
return nil, fmt.Errorf("can't find [%s] client", account.Name)
client, err := driver.getClient(account)
if err != nil {
return nil, err
}
//var resp base.Json
if driver.isFamily(account) {
@ -293,7 +309,6 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
if headers != nil {
req = req.SetHeaders(headers)
}
var err error
var res *resty.Response
switch method {
case base.Get:

View File

@ -73,9 +73,12 @@ func (driver Cloud189) Items() []base.Item {
}
func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
if old != nil && old.Name != account.Name {
if old != nil {
delete(client189Map, old.Name)
}
if account == nil {
return nil
}
if err := driver.Login(account); err != nil {
account.Status = err.Error()
_ = model.SaveAccount(account)
@ -346,9 +349,9 @@ func (driver Cloud189) Upload(file *model.FileStream, account *model.Account) er
if file == nil {
return base.ErrEmptyFile
}
client, ok := client189Map[account.Name]
if !ok {
return fmt.Errorf("can't find [%s] client", account.Name)
client, err := driver.getClient(account)
if err != nil {
return err
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {

View File

@ -67,6 +67,9 @@ func (driver AliDrive) Save(account *model.Account, old *model.Account) error {
if old != nil {
conf.Cron.Remove(cron.EntryID(old.CronId))
}
if account == nil {
return nil
}
if account.RootFolder == "" {
account.RootFolder = "root"
}

View File

@ -48,6 +48,9 @@ func (driver Alist) Items() []base.Item {
}
func (driver Alist) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
account.SiteUrl = strings.TrimRight(account.SiteUrl, "/")
if account.RootFolder == "" {
account.RootFolder = "/"

View File

@ -119,6 +119,12 @@ func GetDrivers() map[string][]Item {
Label: "down_proxy_url",
Type: TypeString,
},
{
Name: "extract_folder",
Label: "extract_folder",
Values: "front,back",
Type: TypeSelect,
},
}, res[k]...)
if v.Config().ApiProxy {
res[k] = append([]Item{

View File

@ -53,15 +53,25 @@ func (driver FTP) Items() []base.Item {
}
func (driver FTP) Save(account *model.Account, old *model.Account) error {
if old != nil {
conn, ok := connMap[old.Name]
if ok {
err := conn.Quit()
log.Error("ftp:", err)
delete(connMap, old.Name)
}
}
if account == nil {
return nil
}
if account.RootFolder == "" {
account.RootFolder = "/"
}
conn, err := driver.Login(account)
_, err := driver.Login(account)
if err != nil {
account.Status = err.Error()
} else {
account.Status = "work"
_ = conn.Quit()
}
_ = model.SaveAccount(account)
return err
@ -106,7 +116,7 @@ func (driver FTP) Files(path string, account *model.Account) ([]model.File, erro
if err != nil {
return nil, err
}
defer func() { _ = conn.Quit() }()
//defer func() { _ = conn.Quit() }()
entries, err := conn.List(realPath)
if err != nil {
return nil, err
@ -144,7 +154,7 @@ func (driver FTP) Link(args base.Args, account *model.Account) (*base.Link, erro
if err != nil {
return nil, err
}
defer func() { _ = conn.Quit() }()
//defer func() { _ = conn.Quit() }()
resp, err := conn.Retr(realPath)
if err != nil {
return nil, err
@ -191,7 +201,7 @@ func (driver FTP) MakeDir(path string, account *model.Account) error {
if err != nil {
return err
}
defer func() { _ = conn.Quit() }()
//defer func() { _ = conn.Quit() }()
err = conn.MakeDir(realPath)
return err
}
@ -203,7 +213,7 @@ func (driver FTP) Move(src string, dst string, account *model.Account) error {
if err != nil {
return err
}
defer func() { _ = conn.Quit() }()
//defer func() { _ = conn.Quit() }()
err = conn.Rename(realSrc, realDst)
return err
}
@ -223,7 +233,7 @@ func (driver FTP) Delete(path string, account *model.Account) error {
if err != nil {
return err
}
defer func() { _ = conn.Quit() }()
//defer func() { _ = conn.Quit() }()
err = conn.Delete(realPath)
return err
}
@ -237,7 +247,7 @@ func (driver FTP) Upload(file *model.FileStream, account *model.Account) error {
if err != nil {
return err
}
defer func() { _ = conn.Quit() }()
//defer func() { _ = conn.Quit() }()
err = conn.Stor(realPath, file)
return err
}

View File

@ -6,7 +6,13 @@ import (
"github.com/jlaffaye/ftp"
)
var connMap map[string]*ftp.ServerConn
func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
conn, ok := connMap[account.Name]
if ok {
return conn, nil
}
conn, err := ftp.Connect(account.SiteUrl)
if err != nil {
return nil, err
@ -20,4 +26,4 @@ func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
func init() {
base.RegisterDriver(&FTP{})
}
}

View File

@ -67,6 +67,9 @@ func (driver GoogleDrive) Items() []base.Item {
}
func (driver GoogleDrive) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
account.Proxy = true
err := driver.RefreshToken(account)
if err != nil {

View File

@ -33,17 +33,19 @@ func (driver Lanzou) Items() []base.Item {
Label: "cookie",
Type: base.TypeString,
Description: "about 15 days valid",
Required: true,
},
{
Name: "site_url",
Label: "share url",
Type: base.TypeString,
Required: true,
},
{
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
},
{
Name: "site_url",
Label: "share url",
Type: base.TypeString,
},
{
Name: "password",
Label: "share password",
@ -53,6 +55,9 @@ func (driver Lanzou) Items() []base.Item {
}
func (driver Lanzou) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
if account.InternalType == "cookie" {
if account.RootFolder == "" {
account.RootFolder = "-1"
@ -127,7 +132,7 @@ func (driver Lanzou) Link(args base.Args, account *model.Account) (*base.Link, e
return nil, err
}
}
url, err := driver.GetLink(downId)
url, err := driver.GetLink(downId, account)
if err != nil {
return nil, err
}

View File

@ -8,6 +8,7 @@ import (
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"net/url"
"path/filepath"
"regexp"
"strconv"
@ -103,6 +104,10 @@ func (driver *Lanzou) GetFiles(folderId string, account *model.Account) ([]LanZo
func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error) {
files := make([]LanZouFile, 0)
shareUrl := account.SiteUrl
u, err := url.Parse(shareUrl)
if err != nil {
return nil, err
}
res, err := lanzouClient.R().Get(shareUrl)
if err != nil {
return nil, err
@ -135,7 +140,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
"up": up,
"ls": ls,
"pwd": account.Password,
}).Post("https://wwa.lanzouo.com/filemoreajax.php")
}).Post(fmt.Sprintf("https://%s/filemoreajax.php", u.Host))
if err != nil {
log.Debug(err)
break
@ -158,7 +163,7 @@ func (driver *Lanzou) GetFilesByUrl(account *model.Account) ([]LanZouFile, error
// IsNewd string `json:"is_newd"`
//}
// 获取下载页面的ID
// GetDownPageId 获取下载页面的ID
func (driver *Lanzou) GetDownPageId(fileId string, account *model.Account) (string, error) {
var resp LanZouFilesResp
res, err := lanzouClient.R().SetResult(&resp).SetHeader("Cookie", account.AccessToken).
@ -190,8 +195,13 @@ type LanzouLinkResp struct {
Zt int `json:"zt"`
}
func (driver *Lanzou) GetLink(downId string) (string, error) {
res, err := lanzouClient.R().Get("https://wwa.lanzouo.com/" + downId)
func (driver *Lanzou) GetLink(downId string, account *model.Account) (string, error) {
shareUrl := account.SiteUrl
u, err := url.Parse(shareUrl)
if err != nil {
return "", err
}
res, err := lanzouClient.R().Get(fmt.Sprintf("https://%s/%s", u.Host, downId))
if err != nil {
return "", err
}
@ -210,6 +220,7 @@ func (driver *Lanzou) GetLink(downId string) (string, error) {
}
signs := ajaxdata[1]
sign := regexp.MustCompile(`var ispostdowns = '(.+?)';`).FindStringSubmatch(res.String())[1]
websign := regexp.MustCompile(`'websign':'(.+?)'`).FindStringSubmatch(res.String())[1]
websignkey := regexp.MustCompile(`'websignkey':'(.+?)'`).FindStringSubmatch(res.String())[1]
var resp LanzouLinkResp
form := map[string]string{
@ -217,14 +228,18 @@ func (driver *Lanzou) GetLink(downId string) (string, error) {
"signs": signs,
"sign": sign,
"ves": "1",
"websign": "",
"websign": websign,
"websignkey": websignkey,
}
log.Debugf("form: %+v", form)
_, err = lanzouClient.R().SetResult(&resp).
SetHeader("origin", "https://wwa.lanzouo.com").
res, err = lanzouClient.R().SetResult(&resp).
SetHeader("origin", "https://"+u.Host).
SetHeader("referer", iframeUrl).
SetFormData(form).Post("https://wwa.lanzouo.com/ajaxm.php")
SetFormData(form).Post(fmt.Sprintf("https://%s/ajaxm.php", u.Host))
log.Debug(res.String())
if err != nil {
return "", err
}
if resp.Zt == 1 {
return resp.Dom + "/file/" + resp.Url, nil
}

View File

@ -36,11 +36,13 @@ func (driver MediaTrack) Items() []base.Item {
Label: "Token",
Type: base.TypeString,
Description: "Unknown expiration time",
Required: true,
},
{
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
Name: "root_folder",
Label: "root folder file_id",
Type: base.TypeString,
Required: true,
},
{
Name: "order_by",
@ -60,6 +62,9 @@ func (driver MediaTrack) Items() []base.Item {
}
func (driver MediaTrack) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
return nil
}
@ -151,11 +156,15 @@ func (driver MediaTrack) Preview(path string, account *model.Account) (interface
}
func (driver MediaTrack) MakeDir(path string, account *model.Account) error {
_, err := driver.File(path, account)
if err != base.ErrPathNotFound {
return nil
}
parentFile, err := driver.File(utils.Dir(path), account)
if err != nil {
return err
}
url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v4/assets/%s/children", parentFile.Id)
url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", parentFile.Id)
_, err = driver.Request(url, base.Post, nil, nil, nil, base.Json{
"type": 1,
"title": utils.Base(path),

View File

@ -8,6 +8,7 @@ import (
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"path"
"strconv"
"time"
@ -58,6 +59,7 @@ func (driver MediaTrack) Request(url string, method int, headers, query, form ma
if err != nil {
return nil, err
}
log.Debugln(res.String())
if e.Status != "SUCCESS" {
return nil, errors.New(e.Message)
}

View File

@ -39,6 +39,9 @@ func (driver Native) Items() []base.Item {
}
func (driver Native) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
log.Debugf("save a account: [%s]", account.Name)
if !utils.Exists(account.RootFolder) {
account.Status = fmt.Sprintf("[%s] not exist", account.RootFolder)
@ -107,7 +110,6 @@ func (driver Native) Files(path string, account *model.Account) ([]model.File, e
}
files = append(files, file)
}
model.SortFiles(files, account)
return files, nil
}

View File

@ -92,13 +92,16 @@ func (driver Onedrive) Items() []base.Item {
}
func (driver Onedrive) Save(account *model.Account, old *model.Account) error {
if old != nil {
conf.Cron.Remove(cron.EntryID(old.CronId))
}
if account == nil {
return nil
}
_, ok := onedriveHostMap[account.Zone]
if !ok {
return fmt.Errorf("no [%s] zone", account.Zone)
}
if old != nil {
conf.Cron.Remove(cron.EntryID(old.CronId))
}
account.RootFolder = utils.ParsePath(account.RootFolder)
err := driver.RefreshToken(account)
if err != nil {

View File

@ -4,17 +4,23 @@ import (
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
)
func MakeDir(driver base.Driver, account *model.Account, path string, clearCache bool) error {
log.Debugf("mkdir: %s", path)
err := driver.MakeDir(path, account)
if err == nil && clearCache {
_ = base.DeleteCache(utils.Dir(path), account)
}
if err != nil {
log.Errorf("mkdir error: %s", err.Error())
}
return err
}
func Move(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
log.Debugf("move %s to %s", src, dst)
rename := false
if utils.Dir(src) == utils.Dir(dst) {
rename = true
@ -31,22 +37,33 @@ func Move(driver base.Driver, account *model.Account, src, dst string, clearCach
_ = base.DeleteCache(utils.Dir(dst), account)
}
}
if err != nil {
log.Errorf("move error: %s", err.Error())
}
return err
}
func Copy(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
log.Debugf("copy %s to %s", src, dst)
err := driver.Copy(src, dst, account)
if err == nil && clearCache {
_ = base.DeleteCache(utils.Dir(dst), account)
}
if err != nil {
log.Errorf("copy error: %s", err.Error())
}
return err
}
func Delete(driver base.Driver, account *model.Account, path string, clearCache bool) error {
log.Debugf("delete %s", path)
err := driver.Delete(path, account)
if err == nil && clearCache {
_ = base.DeleteCache(utils.Dir(path), account)
}
if err != nil {
log.Errorf("delete error: %s", err.Error())
}
return err
}
@ -58,5 +75,8 @@ func Upload(driver base.Driver, account *model.Account, file *model.FileStream,
if err == nil && clearCache {
_ = base.DeleteCache(file.ParentPath, account)
}
if err != nil {
log.Errorf("upload error: %s", err.Error())
}
return err
}

View File

@ -51,6 +51,9 @@ func (driver PikPak) Items() []base.Item {
}
func (driver PikPak) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
err := driver.Login(account)
return err
}

View File

@ -12,7 +12,6 @@ import (
log "github.com/sirupsen/logrus"
"net/url"
"path/filepath"
"strings"
"time"
)
@ -71,14 +70,23 @@ func (driver S3) Items() []base.Item {
},
{
Name: "limit",
Label: "url expire time(hours)",
Label: "Sign url expire time(hours)",
Type: base.TypeNumber,
Description: "default 4 hours",
},
{
Name: "zone",
Label: "placeholder filename",
Type: base.TypeNumber,
Description: "default empty string",
},
}
}
func (driver S3) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
if account.Limit == 0 {
account.Limit = 4
}
@ -138,15 +146,24 @@ func (driver S3) Link(args base.Args, account *model.Account) (*base.Link, error
if err != nil {
return nil, err
}
path := strings.TrimPrefix(args.Path, "/")
path := driver.GetKey(args.Path, account, false)
disposition := fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(utils.Base(path)))
input := &s3.GetObjectInput{
Bucket: &account.Bucket,
Key: &path,
ResponseContentDisposition: &disposition,
Bucket: &account.Bucket,
Key: &path,
//ResponseContentDisposition: &disposition,
}
if account.CustomHost == "" {
input.ResponseContentDisposition = &disposition
}
req, _ := client.GetObjectRequest(input)
link, err := req.Presign(time.Hour * time.Duration(account.Limit))
var link string
if account.CustomHost != "" {
err = req.Build()
link = req.HTTPRequest.URL.String()
} else {
link, err = req.Presign(time.Hour * time.Duration(account.Limit))
}
if err != nil {
return nil, err
}

View File

@ -73,9 +73,10 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
return nil, err
}
for _, object := range listObjectsResult.CommonPrefixes {
name := utils.Base(strings.Trim(*object.Prefix, "/"))
file := model.File{
//Id: *object.Key,
Name: utils.Base(strings.Trim(*object.Prefix, "/")),
Name: name,
Driver: driver.Config().Name,
UpdatedAt: account.UpdatedAt,
TimeStr: "-",
@ -84,9 +85,13 @@ func (driver S3) List(prefix string, account *model.Account) ([]model.File, erro
files = append(files, file)
}
for _, object := range listObjectsResult.Contents {
name := utils.Base(*object.Key)
if name == account.Zone {
continue
}
file := model.File{
//Id: *object.Key,
Name: utils.Base(*object.Key),
Name: name,
Size: *object.Size,
Driver: driver.Config().Name,
UpdatedAt: object.LastModified,

View File

@ -48,6 +48,9 @@ func (driver Shandian) Items() []base.Item {
}
func (driver Shandian) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
if account.RootFolder == "" {
account.RootFolder = "0"
}

View File

@ -64,6 +64,9 @@ func (driver Teambition) Items() []base.Item {
}
func (driver Teambition) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
_, err := driver.Request("/api/v2/roles", base.Get, nil, nil, nil, nil, nil, account)
return err
}

View File

@ -45,6 +45,9 @@ func (driver WebDav) Items() []base.Item {
}
func (driver WebDav) Save(account *model.Account, old *model.Account) error {
if account == nil {
return nil
}
account.Status = "work"
_ = model.SaveAccount(account)
return nil

22
go.mod
View File

@ -15,10 +15,10 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/studio-b12/gowebdav v0.0.0-20211109083228-3f8721cd4b6f
golang.org/x/text v0.3.7
gorm.io/driver/mysql v1.1.2
gorm.io/driver/postgres v1.1.2
gorm.io/driver/sqlite v1.1.6
gorm.io/gorm v1.21.16
gorm.io/driver/mysql v1.2.3
gorm.io/driver/postgres v1.2.3
gorm.io/driver/sqlite v1.2.6
gorm.io/gorm v1.22.5
)
require (
@ -37,19 +37,19 @@ require (
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.0 // indirect
github.com/jackc/pgconn v1.10.1 // indirect
github.com/jackc/pgio v1.0.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgproto3/v2 v2.1.1 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.8.1 // indirect
github.com/jackc/pgx/v4 v4.13.0 // indirect
github.com/jackc/pgtype v1.9.1 // indirect
github.com/jackc/pgx/v4 v4.14.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.2 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.9 // indirect
github.com/mattn/go-sqlite3 v1.14.10 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@ -63,7 +63,7 @@ require (
go.opentelemetry.io/otel v0.20.0 // indirect
go.opentelemetry.io/otel/metric v0.20.0 // indirect
go.opentelemetry.io/otel/trace v0.20.0 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
golang.org/x/sys v0.0.0-20211023085530-d6a326fbbf70 // indirect
google.golang.org/protobuf v1.27.1 // indirect

28
go.sum
View File

@ -231,6 +231,8 @@ github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.10.0 h1:4EYhlDVEMsJ30nNj0mmgwIUXoq7e9sMJrVC2ED6QlCU=
github.com/jackc/pgconn v1.10.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
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=
@ -248,6 +250,8 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
@ -256,19 +260,29 @@ github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrU
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.8.1 h1:9k0IXtdJXHJbyAWQgbWr1lU+MEhPXZz6RIXxfR5oxXs=
github.com/jackc/pgtype v1.8.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0=
github.com/jackc/pgtype v1.9.1/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.13.0 h1:JCjhT5vmhMAf/YwBHLvrBn4OGdIQBiFG6ym8Zmdx570=
github.com/jackc/pgx/v4 v4.13.0/go.mod h1:9P4X524sErlaxj0XSGZk7s+LD0eOyu1ZDUrrpznYDF0=
github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
github.com/jackc/pgx/v4 v4.14.1 h1:71oo1KAGI6mXhLiTMn6iDFcp3e7+zon/capWjl2OEFU=
github.com/jackc/pgx/v4 v4.14.1/go.mod h1:RgDuE4Z34o7XE92RpLsvFiOEfrAUT0Xt2KxvX73W06M=
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.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.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b h1:Ur6QAxsHCK99Quj9PaWafoV4unb0DO/HWiKExD+TN5g=
github.com/jlaffaye/ftp v0.0.0-20211117213618-11820403398b/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
@ -331,6 +345,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9 h1:10HX2Td0ocZpYEjhilsuo6WWtUqttj2Kb0KtD86/KYA=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.10 h1:MLn+5bFRlWMGoSRmJour3CL1w/qL96mvipqpwQW/Sfk=
github.com/mattn/go-sqlite3 v1.14.10/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@ -541,6 +557,8 @@ golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -731,14 +749,24 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M=
gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM=
gorm.io/driver/mysql v1.2.3 h1:cZqzlOfg5Kf1VIdLC1D9hT6Cy9BgxhExLj/2tIgUe7Y=
gorm.io/driver/mysql v1.2.3/go.mod h1:qsiz+XcAyMrS6QY+X3M9R6b/lKM1imKmcuK9kac5LTo=
gorm.io/driver/postgres v1.1.2 h1:Amy3hCvLqM+/ICzjCnQr8wKFLVJTeOTdlMT7kCP+J1Q=
gorm.io/driver/postgres v1.1.2/go.mod h1:/AGV0zvqF3mt9ZtzLzQmXWQ/5vr+1V1TyHZGZVjzmwI=
gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
gorm.io/driver/sqlite v1.1.6 h1:p3U8WXkVFTOLPED4JjrZExfndjOtya3db8w9/vEMNyI=
gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
gorm.io/driver/sqlite v1.2.6 h1:SStaH/b+280M7C8vXeZLz/zo9cLQmIGwwj3cSj7p6l4=
gorm.io/driver/sqlite v1.2.6/go.mod h1:gyoX0vHiiwi0g49tv+x2E7l8ksauLK0U/gShcdUsjWY=
gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.21.16 h1:YBIQLtP5PLfZQz59qfrq7xbrK7KWQ+JsXXCH/THlMqs=
gorm.io/gorm v1.21.16/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -2,7 +2,6 @@ package model
import (
"github.com/Xhofe/alist/conf"
"github.com/robfig/cron/v3"
"time"
)
@ -37,12 +36,13 @@ type Account struct {
DownProxyUrl string `json:"down_proxy_url"` // 用于中转下载服务的URL 两处 1. path请求中返回的链接 2. down下载时进行302
APIProxyUrl string `json:"api_proxy_url"` // 用于中转api的地址
// for s3
Bucket string `json:"bucket"`
Endpoint string `json:"endpoint"`
Region string `json:"region"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
CustomHost string `json:"custom_host"`
Bucket string `json:"bucket"`
Endpoint string `json:"endpoint"`
Region string `json:"region"`
AccessKey string `json:"access_key"`
AccessSecret string `json:"access_secret"`
CustomHost string `json:"custom_host"`
ExtractFolder string `json:"extract_folder"`
}
var accountsMap = map[string]Account{}
@ -64,19 +64,18 @@ func CreateAccount(account *Account) error {
return nil
}
func DeleteAccount(id uint) error {
func DeleteAccount(id uint) (*Account, error) {
var account Account
account.ID = id
if err := conf.DB.First(&account).Error; err != nil {
return err
return nil, err
}
name := account.Name
conf.Cron.Remove(cron.EntryID(account.CronId))
if err := conf.DB.Delete(&account).Error; err != nil {
return err
return nil, err
}
delete(accountsMap, name)
return nil
return &account, nil
}
func DeleteAccountFromMap(name string) {

View File

@ -25,14 +25,6 @@ func SortFiles(files []File, account *Account) {
return
}
sort.Slice(files, func(i, j int) bool {
if files[i].IsDir() || files[j].IsDir() {
if !files[i].IsDir() {
return false
}
if !files[j].IsDir() {
return true
}
}
switch account.OrderBy {
case "name":
{
@ -59,6 +51,24 @@ func SortFiles(files []File, account *Account) {
})
}
func ExtractFolder(files []File, account *Account) {
if account.ExtractFolder == "" {
return
}
front := account.ExtractFolder == "front"
sort.Slice(files, func(i, j int) bool {
if files[i].IsDir() || files[j].IsDir() {
if !files[i].IsDir() {
return !front
}
if !files[j].IsDir() {
return front
}
}
return false
})
}
func (f File) GetSize() uint64 {
return uint64(f.Size)
}

View File

@ -5,6 +5,7 @@ import (
"fmt"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"strings"
@ -60,6 +61,16 @@ func ErrorResp(c *gin.Context, err error, code int) {
c.Abort()
}
func ErrorStrResp(c *gin.Context, str string, code int) {
log.Error(str)
c.JSON(200, Resp{
Code: code,
Message: str,
Data: nil,
})
c.Abort()
}
func SuccessResp(c *gin.Context, data ...interface{}) {
if len(data) == 0 {
c.JSON(200, Resp{
@ -75,3 +86,18 @@ func SuccessResp(c *gin.Context, data ...interface{}) {
Data: data[0],
})
}
func Hide(meta *model.Meta, files []model.File) []model.File {
//meta, _ := model.GetMetaByPath(path)
if meta != nil && meta.Hide != "" {
tmpFiles := make([]model.File, 0)
hideFiles := strings.Split(meta.Hide, ",")
for _, item := range files {
if !utils.IsContain(hideFiles, item.Name) {
tmpFiles = append(tmpFiles, item)
}
}
files = tmpFiles
}
return files
}

View File

@ -28,7 +28,7 @@ func CreateAccount(c *gin.Context) {
}
driver, ok := base.GetDriver(req.Type)
if !ok {
common.ErrorResp(c, fmt.Errorf("no [%s] driver", req.Type), 400)
common.ErrorStrResp(c, fmt.Sprintf("No [%s] driver", req.Type), 400)
return
}
now := time.Now()
@ -54,7 +54,7 @@ func SaveAccount(c *gin.Context) {
}
driver, ok := base.GetDriver(req.Type)
if !ok {
common.ErrorResp(c, fmt.Errorf("no [%s] driver", req.Type), 400)
common.ErrorStrResp(c, fmt.Sprintf("No [%s] driver", req.Type), 400)
return
}
old, err := model.GetAccountById(req.ID)
@ -87,9 +87,16 @@ func DeleteAccount(c *gin.Context) {
common.ErrorResp(c, err, 400)
return
}
if err := model.DeleteAccount(uint(id)); err != nil {
if account, err := model.DeleteAccount(uint(id)); err != nil {
common.ErrorResp(c, err, 500)
return
} else {
driver, ok := base.GetDriver(account.Type)
if ok {
_ = driver.Save(nil, account)
} else {
log.Errorf("no driver: %s", account.Type)
}
}
common.SuccessResp(c)
}

View File

@ -1,21 +1,11 @@
package controllers
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
"strings"
)
func Down(c *gin.Context) {
@ -39,129 +29,3 @@ func Down(c *gin.Context) {
c.Redirect(302, link.Url)
return
}
func Proxy(c *gin.Context) {
rawPath := c.Param("path")
rawPath = utils.ParsePath(rawPath)
log.Debugf("proxy: %s", rawPath)
account, path, driver, err := common.ParsePath(rawPath)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// 只有以下几种情况允许中转:
// 1. 账号开启中转
// 2. driver只能中转
// 3. 是文本类型文件
// 4. 开启webdav中转需要验证sign
if !account.Proxy && !driver.Config().OnlyProxy && utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT {
// 只开启了webdav中转验证sign
ok := false
if account.WebdavProxy {
_, ok = c.Get("sign")
}
if !ok {
common.ErrorResp(c, fmt.Errorf("[%s] not allowed proxy", account.Name), 403)
return
}
}
// 中转时有中转机器使用中转机器,若携带标志位则表明不能再走中转机器了
if account.DownProxyUrl != "" && c.Query("d") != "1" {
name := utils.Base(rawPath)
link := fmt.Sprintf("%s%s?sign=%s", account.DownProxyUrl, rawPath, utils.SignWithToken(name, conf.Token))
c.Redirect(302, link)
return
}
// 对于中转不需要重设IP
link, err := driver.Link(base.Args{Path: path}, account)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// 本机读取数据
if link.Data != nil {
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
defer func() {
_ = link.Data.Close()
}()
c.Status(http.StatusOK)
c.Header("content", "application/octet-stream")
_, err = io.Copy(c.Writer, link.Data)
if err != nil {
_, _ = c.Writer.WriteString(err.Error())
}
return
}
// 本机文件直接返回文件
if account.Type == "Native" {
// 对于名称为index.html的文件需要特殊处理
if utils.Base(rawPath) == "index.html" {
file, err := os.Open(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
defer func() {
_ = file.Close()
}()
fileStat, err := os.Stat(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
return
}
c.File(link.Url)
return
} else {
if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
Text(c, link)
return
}
driver.Proxy(c, account)
r := c.Request
w := c.Writer
target, err := url.Parse(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
protocol := "http://"
if strings.HasPrefix(link.Url, "https://") {
protocol = "https://"
}
targetHost, err := url.Parse(fmt.Sprintf("%s%s", protocol, target.Host))
proxy := httputil.NewSingleHostReverseProxy(targetHost)
r.URL = target
r.Host = target.Host
proxy.ServeHTTP(w, r)
}
}
var client *resty.Client
func init() {
client = resty.New()
client.SetRetryCount(3)
}
func Text(c *gin.Context, link *base.Link) {
res, err := client.R().Get(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
text := res.String()
t := utils.GetStrCoding(res.Body())
log.Debugf("text type: %s", t)
if t != utils.UTF8 {
body, err := utils.GbkToUtf8(res.Body())
if err != nil {
common.ErrorResp(c, err, 500)
return
}
text = string(body)
}
c.String(200, text)
}

View File

@ -1,7 +1,6 @@
package file
import (
"errors"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/drivers/operate"
"github.com/Xhofe/alist/server/common"
@ -21,7 +20,7 @@ func DeleteFiles(c *gin.Context) {
return
}
if len(req.Names) == 0 {
common.ErrorResp(c, errors.New("empty file names"), 400)
common.ErrorStrResp(c, "Empty file names", 400)
return
}
for i, name := range req.Names {
@ -30,6 +29,10 @@ func DeleteFiles(c *gin.Context) {
common.ErrorResp(c, err, 500)
return
}
if path_ == "/" {
common.ErrorStrResp(c, "Delete root folder is not allowed", 400)
return
}
clearCache := false
if i == len(req.Names)-1 {
clearCache = true

View File

@ -0,0 +1,52 @@
package file
import (
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/server/common"
"github.com/gin-gonic/gin"
)
type FolderReq struct {
Path string `json:"path"`
}
func Folder(c *gin.Context) {
var req FolderReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
var files = make([]model.File, 0)
var err error
if model.AccountsCount() > 1 && (req.Path == "/" || req.Path == "") {
files, err = model.GetAccountFiles()
if err != nil {
common.ErrorResp(c, err, 500)
return
}
} else {
account, path, driver, err := common.ParsePath(req.Path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
file, rawFiles, err := driver.Path(path, account)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
if file != nil {
common.ErrorStrResp(c, "Not folder", 400)
}
for _, file := range rawFiles {
if file.IsDir() {
files = append(files, file)
}
}
}
c.JSON(200, common.Resp{
Code: 200,
Message: "success",
Data: files,
})
}

View File

@ -0,0 +1,34 @@
package file
import (
"github.com/Xhofe/alist/drivers/operate"
"github.com/Xhofe/alist/server/common"
"github.com/gin-gonic/gin"
)
type MkdirReq struct {
Path string `json:"path"`
}
func Mkdir(c *gin.Context) {
var req MkdirReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
account, path_, driver, err := common.ParsePath(req.Path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
if path_ == "/" {
common.ErrorStrResp(c, "Folder name can't be empty", 400)
return
}
err = operate.MakeDir(driver, account, path_, true)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
common.SuccessResp(c)
}

View File

@ -0,0 +1,67 @@
package file
import (
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/drivers/operate"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
)
type MoveReq struct {
SrcDir string `json:"src_dir"`
DstDir string `json:"dst_dir"`
Names []string `json:"names"`
}
func Move(c *gin.Context) {
var req MoveReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
if len(req.Names) == 0 {
common.ErrorStrResp(c, "Empty file names", 400)
return
}
if model.AccountsCount() > 1 && (req.SrcDir == "/" || req.DstDir == "/") {
common.ErrorStrResp(c, "Can't operate root folder", 400)
return
}
srcAccount, srcPath, srcDriver, err := common.ParsePath(utils.Join(req.SrcDir, req.Names[0]))
if err != nil {
common.ErrorResp(c, err, 500)
return
}
dstAccount, dstPath, _, err := common.ParsePath(utils.Join(req.DstDir, req.Names[0]))
if err != nil {
common.ErrorResp(c, err, 500)
return
}
if srcAccount.Name != dstAccount.Name {
common.ErrorStrResp(c, "Can't move files between two accounts", 400)
return
}
if srcPath == "/" || dstPath == "/" {
common.ErrorStrResp(c, "Can't move root folder", 400)
return
}
srcDir, dstDir := utils.Dir(srcPath), utils.Dir(dstPath)
for i, name := range req.Names {
clearCache := false
if i == len(req.Names)-1 {
clearCache = true
}
err := operate.Move(srcDriver, srcAccount, utils.Join(srcDir, name), utils.Join(dstDir, name), clearCache)
if err != nil {
if i == 0 {
_ = base.DeleteCache(srcDir, srcAccount)
_ = base.DeleteCache(dstDir, dstAccount)
}
common.ErrorResp(c, err, 500)
return
}
}
common.SuccessResp(c)
}

View File

@ -0,0 +1,36 @@
package file
import (
"github.com/Xhofe/alist/drivers/operate"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
)
type RenameReq struct {
Path string `json:"path"`
Name string `json:"name"`
}
func Rename(c *gin.Context) {
var req RenameReq
if err := c.ShouldBind(&req); err != nil {
common.ErrorResp(c, err, 400)
return
}
account, path_, driver, err := common.ParsePath(req.Path)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
if path_ == "/" {
common.ErrorStrResp(c, "Can't account name here", 400)
return
}
err = operate.Move(driver, account, path_, utils.Join(utils.Dir(path_), req.Name), true)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
common.SuccessResp(c)
}

View File

@ -1,7 +1,6 @@
package file
import (
"errors"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/drivers/operate"
@ -19,11 +18,11 @@ func UploadFiles(c *gin.Context) {
password := c.PostForm("password")
meta, _ := model.GetMetaByPath(path)
if meta == nil || !meta.Upload {
common.ErrorResp(c, errors.New("not allow upload"), 403)
common.ErrorStrResp(c, "Not allow upload", 403)
return
}
if meta.Password != "" && meta.Password != password {
common.ErrorResp(c, errors.New("wrong password"), 403)
common.ErrorStrResp(c, "Wrong password", 403)
return
}
}

View File

@ -10,24 +10,8 @@ import (
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"strings"
)
func Hide(meta *model.Meta, files []model.File) []model.File {
//meta, _ := model.GetMetaByPath(path)
if meta != nil && meta.Hide != "" {
tmpFiles := make([]model.File, 0)
hideFiles := strings.Split(meta.Hide, ",")
for _, item := range files {
if !utils.IsContain(hideFiles, item.Name) {
tmpFiles = append(tmpFiles, item)
}
}
files = tmpFiles
}
return files
}
func Pagination(files []model.File, req *common.PathReq) (int, []model.File) {
pageNum, pageSize := req.PageNum, req.PageSize
total := len(files)
@ -87,18 +71,21 @@ type PathResp struct {
func Path(c *gin.Context) {
reqV, _ := c.Get("req")
req := reqV.(common.PathReq)
_, ok := c.Get("admin")
meta, _ := model.GetMetaByPath(req.Path)
upload := false
if meta != nil && meta.Upload {
upload = true
}
if model.AccountsCount() > 1 && req.Path == "/" {
if model.AccountsCount() > 1 && (req.Path == "/" || req.Path == "") {
files, err := model.GetAccountFiles()
if err != nil {
common.ErrorResp(c, err, 500)
return
}
files = Hide(meta, files)
if !ok {
files = common.Hide(meta, files)
}
c.JSON(200, common.Resp{
Code: 200,
Message: "success",
@ -155,10 +142,13 @@ func Path(c *gin.Context) {
},
})
} else {
files = Hide(meta, files)
if !ok {
files = common.Hide(meta, files)
}
if driver.Config().LocalSort {
model.SortFiles(files, account)
}
model.ExtractFolder(files, account)
total, files := Pagination(files, &req)
c.JSON(200, common.Resp{
Code: 200,

176
server/controllers/proxy.go Normal file
View File

@ -0,0 +1,176 @@
package controllers
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"io"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
)
func Proxy(c *gin.Context) {
rawPath := c.Param("path")
rawPath = utils.ParsePath(rawPath)
log.Debugf("proxy: %s", rawPath)
account, path, driver, err := common.ParsePath(rawPath)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// 只有以下几种情况允许中转:
// 1. 账号开启中转
// 2. driver只能中转
// 3. 是文本类型文件
// 4. 开启webdav中转需要验证sign
if !account.Proxy && !driver.Config().OnlyProxy && utils.GetFileType(filepath.Ext(rawPath)) != conf.TEXT {
// 只开启了webdav中转验证sign
ok := false
if account.WebdavProxy {
_, ok = c.Get("sign")
}
if !ok {
common.ErrorStrResp(c, fmt.Sprintf("[%s] not allowed proxy", account.Name), 403)
return
}
}
// 中转时有中转机器使用中转机器,若携带标志位则表明不能再走中转机器了
if account.DownProxyUrl != "" && c.Query("d") != "1" {
name := utils.Base(rawPath)
link := fmt.Sprintf("%s%s?sign=%s", account.DownProxyUrl, rawPath, utils.SignWithToken(name, conf.Token))
c.Redirect(302, link)
return
}
// 检查文件
file, err := driver.File(path, account)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// 对于中转不需要重设IP
link, err := driver.Link(base.Args{Path: path}, account)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
// 本机读取数据
if link.Data != nil {
//c.Data(http.StatusOK, "application/octet-stream", link.Data)
defer func() {
_ = link.Data.Close()
}()
c.Status(http.StatusOK)
c.Header("Content-Type", "application/octet-stream")
c.Header("Content-Disposition", fmt.Sprintf(`attachment; filename=%s`, url.QueryEscape(file.Name)))
c.Header("Content-Length", strconv.FormatInt(file.Size, 10))
_, err = io.Copy(c.Writer, link.Data)
if err != nil {
_, _ = c.Writer.WriteString(err.Error())
}
return
}
// 本机文件直接返回文件
if account.Type == "Native" {
// 对于名称为index.html的文件需要特殊处理
if utils.Base(rawPath) == "index.html" {
file, err := os.Open(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
defer func() {
_ = file.Close()
}()
fileStat, err := os.Stat(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
http.ServeContent(c.Writer, c.Request, utils.Base(rawPath), fileStat.ModTime(), file)
return
}
c.File(link.Url)
return
} else {
if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
Text(c, link)
return
}
driver.Proxy(c, account)
r := c.Request
w := c.Writer
//target, err := url.Parse(link.Url)
//if err != nil {
// common.ErrorResp(c, err, 500)
// return
//}
req, err := http.NewRequest("GET", link.Url, nil)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
for h, val := range r.Header {
req.Header[h] = val
}
res, err := HttpClient.Do(req)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
defer func() {
_ = res.Body.Close()
}()
log.Debugf("proxy status: %d", res.StatusCode)
w.WriteHeader(res.StatusCode)
for h, v := range res.Header {
w.Header()[h] = v
}
_, err = io.Copy(w, res.Body)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
}
}
var client *resty.Client
var HttpClient = &http.Client{}
func init() {
client = resty.New()
client.SetRetryCount(3)
}
func Text(c *gin.Context, link *base.Link) {
req := client.R()
if link.Headers != nil {
for _, header := range link.Headers {
req.SetHeader(header.Name, header.Value)
}
}
res, err := req.Get(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
text := res.String()
t := utils.GetStrCoding(res.Body())
log.Debugf("text type: %s", t)
if t != utils.UTF8 {
body, err := utils.GbkToUtf8(res.Body())
if err != nil {
common.ErrorResp(c, err, 500)
return
}
text = string(body)
}
c.String(200, text)
}

View File

@ -1,7 +1,6 @@
package middlewares
import (
"fmt"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/server/common"
"github.com/gin-gonic/gin"
@ -9,8 +8,8 @@ import (
func CheckAccount(c *gin.Context) {
if model.AccountsCount() == 0 {
common.ErrorResp(c, fmt.Errorf("no accounts,please add one first"), 1001)
common.ErrorStrResp(c, "No accounts,please add one first", 1001)
return
}
c.Next()
}
}

View File

@ -1,7 +1,6 @@
package middlewares
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/server/common"
"github.com/gin-gonic/gin"
@ -20,8 +19,8 @@ func Auth(c *gin.Context) {
//}
//if token != utils.GetMD5Encode(password.Value) {
if token != conf.Token {
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
common.ErrorStrResp(c, "Wrong password", 401)
return
}
c.Next()
}
}

View File

@ -1,7 +1,6 @@
package middlewares
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
@ -20,7 +19,7 @@ func DownCheck(c *gin.Context) {
}
pw := c.Query("pw")
if !common.CheckDownLink(utils.Dir(rawPath), pw, utils.Base(rawPath)) {
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
common.ErrorStrResp(c, "Wrong password", 401)
c.Abort()
return
}

View File

@ -1,7 +1,6 @@
package middlewares
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/server/common"
@ -19,19 +18,20 @@ func PathCheck(c *gin.Context) {
c.Set("req", req)
token := c.GetHeader("Authorization")
if token == conf.Token {
c.Set("admin", true)
c.Next()
return
}
meta, err := model.GetMetaByPath(req.Path)
if err == nil {
if meta.Password != "" && meta.Password != req.Password {
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
common.ErrorStrResp(c, "Wrong password", 401)
c.Abort()
return
}
} else if conf.GetBool("check parent folder") {
if !common.CheckParent(utils.Dir(req.Path), req.Password) {
common.ErrorResp(c, fmt.Errorf("wrong password"), 401)
common.ErrorStrResp(c, "Wrong password", 401)
c.Abort()
return
}

View File

@ -51,6 +51,10 @@ func InitApiRouter(r *gin.Engine) {
admin.POST("/link", controllers.Link)
admin.DELETE("/files", file.DeleteFiles)
admin.POST("/mkdir", file.Mkdir)
admin.POST("/rename", file.Rename)
admin.POST("/move", file.Move)
admin.POST("/folder", file.Folder)
}
WebDav(r)
Static(r)

View File

@ -1,6 +1,7 @@
package server
import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/public"
"github.com/gin-gonic/gin"
@ -14,14 +15,21 @@ import (
func InitIndex() {
var index fs.File
var err error
if conf.Conf.Local {
index, err = public.Public.Open("local.html")
} else {
index, err = public.Public.Open("index.html")
//if conf.Conf.Local {
// index, err = public.Public.Open("local.html")
//} else {
// index, err = public.Public.Open("index.html")
//}
if conf.Conf.Assets == "" {
conf.Conf.Assets = conf.DefaultConfig().Assets
}
index, err = public.Public.Open(fmt.Sprintf("%s.html", conf.Conf.Assets))
if err != nil {
//log.Fatalf(err.Error())
return
log.Error(err.Error())
index, err = public.Public.Open("index.html")
if err != nil {
log.Fatal(err.Error())
}
}
data, _ := ioutil.ReadAll(index)
conf.RawIndexHtml = string(data)

View File

@ -1,6 +1,7 @@
package server
import (
"context"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/server/webdav"
"github.com/Xhofe/alist/utils"
@ -58,6 +59,8 @@ func WebDAVAuth(c *gin.Context) {
(conf.GetStr("Visitor WebDAV username") == "" &&
conf.GetStr("Visitor WebDAV password") == "") {
if !utils.IsContain([]string{"PUT", "DELETE", "PROPPATCH", "MKCOL", "COPY", "MOVE"}, c.Request.Method) {
ctx := context.WithValue(c.Request.Context(), "visitor", true)
c.Request = c.Request.WithContext(ctx)
c.Next()
return
}

View File

@ -43,7 +43,7 @@ func (fs *FileSystem) File(rawPath string) (*model.File, error) {
return driver.File(path_, account)
}
func (fs *FileSystem) Files(rawPath string) ([]model.File, error) {
func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) {
rawPath = utils.ParsePath(rawPath)
if model.AccountsCount() > 1 && rawPath == "/" {
files, err := model.GetAccountFiles()
@ -56,7 +56,20 @@ func (fs *FileSystem) Files(rawPath string) ([]model.File, error) {
if err != nil {
return nil, err
}
return driver.Files(path_, account)
files, err := driver.Files(path_, account)
if err != nil {
return nil, err
}
meta, _ := model.GetMetaByPath(rawPath)
if visitor := ctx.Value("visitor"); visitor != nil {
if visitor.(bool) {
log.Debug("visitor")
files = common.Hide(meta, files)
}
} else {
log.Debug("admin")
}
return files, nil
}
func ClientIP(r *http.Request) string {
@ -97,7 +110,7 @@ func (fs *FileSystem) Link(r *http.Request, rawPath string) (string, error) {
link = fmt.Sprintf("%s://%s/p%s", protocol, r.Host, rawPath)
if conf.GetBool("check down link") {
sign := utils.SignWithToken(utils.Base(rawPath), conf.Token)
link += "?sign" + sign
link += "?sign=" + sign
}
} else {
link_, err := driver.Link(base.Args{Path: path_, IP: ClientIP(r)}, account)
@ -260,7 +273,7 @@ func walkFS(
depth = 0
}
files, err := fs.Files(name)
files, err := fs.Files(ctx, name)
if err != nil {
return err
}

View File

@ -46,7 +46,7 @@ func (h *Handler) stripPrefix(p string) (string, int, error) {
func isPathExist(ctx context.Context, fs *FileSystem, path string) (bool, FileInfo) {
file, err := fs.File(path)
if err != nil {
log.Debug(err)
log.Debug(path, err)
return false, nil
}
return true, file