Compare commits

...

48 Commits

Author SHA1 Message Date
6fd05d7d72 🐛 fix connMap not init 2022-01-30 00:55:12 +08:00
f26ac57569 🐛 fix ftp conn not store 2022-01-30 00:04:31 +08:00
2434ac54d0 🐛 fix webdav 2022-01-29 18:36:22 +08:00
f25b557327 📝 update readme 2022-01-29 15:31:06 +08:00
81a0706d01 189 chunk upload 2022-01-29 11:16:40 +08:00
5f6b576cbf 🔧 switch cdn to jsdelivr 2022-01-28 23:29:38 +08:00
549877f71e 🔥 Delete Text 2022-01-28 23:21:11 +08:00
c6a5ba9b91 🚧 189 chunk upload 2022-01-28 19:51:54 +08:00
1a69d80489 🎨 Pull away Path 2022-01-28 11:04:56 +08:00
b797f4302c 🎇 use tempFile to cal md5 2022-01-27 23:48:29 +08:00
bf9aa5c3d3 🔒 not allowed use relative path of native 2022-01-27 15:10:33 +08:00
7390e19a7a 🔒 not allowed down with relative path 2022-01-27 15:05:17 +08:00
b31a12a0cc 🔒 not allowed access using relative path for native 2022-01-27 14:54:20 +08:00
26ce001782 🎇 add ocr for 189 2022-01-27 12:34:49 +08:00
a2c7ff3262 ✏️ Invalid Token 2022-01-26 16:27:38 +08:00
8fc7c716c0 copy api 2022-01-26 14:07:51 +08:00
c70fc3fc4b finish teambition chunk upload 2022-01-26 14:05:35 +08:00
df513b7dc0 teambition upload (<= 20 MB) 2022-01-23 14:03:04 +08:00
2a9598f4c6 🐛 fix #407 189cloud use local sort 2022-01-21 19:01:33 +08:00
224c20779c 🔧 add webp to image types 2022-01-20 23:01:59 +08:00
5d722298cb 📝 update readme 2022-01-20 22:38:53 +08:00
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
64 changed files with 1599 additions and 531 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

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

@ -50,7 +50,7 @@ English | [中文](./README_cn.md)
- [x] Cloudflare workers proxy
- [x] File/Folder package download
- [x] Support video list playback and subtitles(ass,srt,vtt)
- [x] Web upload(Can allow visitors to upload) and delete
- [x] Web upload(Can allow visitors to upload), delete, mkdir, rename, move and copy
## Discussion

View File

@ -49,7 +49,7 @@
- [x] Cloudflare workers 中转
- [x] 文件/文件夹打包下载
- [x] 支持视频列表播放和字幕(ass,srt,vtt)
- [x] 网页上传(可以允许访客上传),删除
- [x] 网页上传(可以允许访客上传),删除,新建文件夹,重命名,移动,复制
## 讨论

View File

@ -19,6 +19,7 @@ func InitAccounts() {
if !ok {
log.Errorf("no [%s] driver", account.Type)
} else {
log.Infof("start init account: [%s], type: [%s]", account.Name, account.Type)
err := driver.Save(&accounts[i], nil)
if err != nil {
log.Errorf("init account [%s] error:[%s]", account.Name, err.Error())
@ -27,4 +28,4 @@ func InitAccounts() {
}
}
}
}
}

View File

@ -5,6 +5,7 @@ import (
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"io/ioutil"
"os"
)
// InitConf init config
@ -41,4 +42,8 @@ func InitConf() {
if err != nil {
log.Fatalf("update config struct error: %s", err.Error())
}
err = os.MkdirAll("data/temp", 0700)
if err != nil {
log.Fatalf("create temp dir 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

@ -219,6 +219,14 @@ func InitSettings() {
Access: model.PUBLIC,
Group: model.FRONT,
},
{
Key: "ocr api",
Value: "https://api.xhofe.top/ocr/file/json",
Description: "Used to identify verification codes",
Type: "string",
Access: model.PRIVATE,
Group: model.BACK,
},
}
for i, _ := range settings {
v := settings[i]

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 {
@ -35,12 +36,13 @@ func DefaultConfig() *Config {
return &Config{
Address: "0.0.0.0",
Port: 5244,
Assets: "zhimg",
Assets: "jsdelivr",
Database: Database{
Type: "sqlite3",
Port: 0,
TablePrefix: "x_",
DBFile: "data/data.db",
SslMode: "disable",
},
Cache: CacheConfig{
Expiration: 60,

View File

@ -36,10 +36,10 @@ 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", "webp"}
)
var settingsMap = make(map[string]string, 0)
var settingsMap = make(map[string]string)
func Set(key string, value string) {
settingsMap[key] = value
@ -78,6 +78,7 @@ var (
"check parent folder", "check down link", "WebDAV username", "WebDAV password",
"Visitor WebDAV username", "Visitor WebDAV password",
"default page size", "load type",
"ocr api",
}
)

View File

@ -1,6 +1,8 @@
package _23
import (
"crypto/md5"
"encoding/hex"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
@ -12,7 +14,10 @@ import (
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"strconv"
)
@ -65,6 +70,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 +242,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",
@ -291,10 +299,37 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
return base.ErrNotFolder
}
parentFileId, _ := strconv.Atoi(parentFile.Id)
tempFile, err := ioutil.TempFile("data/temp", "file-*")
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
_, err = io.Copy(tempFile, file)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
h := md5.New()
_, err = io.Copy(h, tempFile)
if err != nil {
return err
}
etag := hex.EncodeToString(h.Sum(nil))
log.Debugln("md5:", etag)
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
data := base.Json{
"driveId": 0,
"duplicate": true,
"etag": "836aae6cac845e17fce51919594737d0", //maybe file's md5
"etag": etag,
"fileName": file.GetFileName(),
"parentFileId": parentFileId,
"size": file.GetSize(),
@ -307,6 +342,9 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
if err != nil {
return err
}
if resp.Data.Key == "" {
return nil
}
cfg := &aws.Config{
Credentials: credentials.NewStaticCredentials(resp.Data.AccessKeyId, resp.Data.SecretAccessKey, resp.Data.SessionToken),
Region: aws.String("123pan"),
@ -321,7 +359,7 @@ func (driver Pan123) Upload(file *model.FileStream, account *model.Account) erro
input := &s3manager.UploadInput{
Bucket: &resp.Data.Bucket,
Key: &resp.Data.Key,
Body: file,
Body: tempFile,
}
_, err = uploader.Upload(input)
if err != nil {

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),

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{
@ -229,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,
},
},
@ -254,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,
},
}
@ -264,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,
},
}
@ -303,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,
},
},
@ -335,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,
},
},
@ -346,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,
@ -382,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

@ -2,16 +2,9 @@ package _89
import (
"bytes"
"crypto/aes"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"fmt"
"github.com/Xhofe/alist/conf"
@ -19,14 +12,12 @@ import (
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
"github.com/google/uuid"
jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"io"
"math"
mathRand "math/rand"
"net/http"
"net/url"
"path/filepath"
"regexp"
"strconv"
@ -35,6 +26,23 @@ import (
)
var client189Map map[string]*resty.Client
var infoMap = make(map[string]Rsa)
func (driver Cloud189) getClient(account *model.Account) (*resty.Client, error) {
client, ok := client189Map[account.Name]
if ok {
return client, nil
}
err := driver.Login(account)
if err != nil {
return nil, err
}
client, ok = client189Map[account.Name]
if !ok {
return nil, fmt.Errorf("can't find [%s] client", account.Name)
}
return client, nil
}
func (driver Cloud189) FormatFile(file *Cloud189File) *model.File {
f := &model.File{
@ -137,9 +145,33 @@ func (driver Cloud189) Login(account *model.Account) error {
vCodeRS := ""
if vCodeID != "" {
// need ValidateCode
log.Debugf("try to identify verification codes")
timeStamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)
u := "https://open.e.189.cn/api/logbox/oauth2/picCaptcha.do?token=" + vCodeID + timeStamp
imgRes, err := client.R().SetHeaders(map[string]string{
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/76.0",
"Referer": "https://open.e.189.cn/api/logbox/oauth2/unifyAccountLogin.do",
"Sec-Fetch-Dest": "image",
"Sec-Fetch-Mode": "no-cors",
"Sec-Fetch-Site": "same-origin",
}).Get(u)
if err != nil {
return err
}
vRes, err := client.R().SetMultipartField(
"image", "validateCode.png", "image/png", bytes.NewReader(imgRes.Body())).
Post(conf.GetStr("ocr api"))
if err != nil {
return err
}
if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
return errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
}
vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
log.Debugln("code: ", vCodeRS)
}
userRsa := RsaEncode([]byte(account.Username), jRsakey)
passwordRsa := RsaEncode([]byte(account.Password), jRsakey)
userRsa := RsaEncode([]byte(account.Username), jRsakey, true)
passwordRsa := RsaEncode([]byte(account.Password), jRsakey, true)
url = "https://open.e.189.cn/api/logbox/oauth2/loginSubmit.do"
var loginResp LoginResp
res, err := client.R().
@ -183,39 +215,6 @@ func (driver Cloud189) Login(account *model.Account) error {
return nil
}
type Cloud189Error struct {
ErrorCode string `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}
type Cloud189File struct {
Id int64 `json:"id"`
LastOpTime string `json:"lastOpTime"`
Name string `json:"name"`
Size int64 `json:"size"`
Icon struct {
SmallUrl string `json:"smallUrl"`
//LargeUrl string `json:"largeUrl"`
} `json:"icon"`
Url string `json:"url"`
}
type Cloud189Folder struct {
Id int64 `json:"id"`
LastOpTime string `json:"lastOpTime"`
Name string `json:"name"`
}
type Cloud189Files struct {
ResCode int `json:"res_code"`
ResMessage string `json:"res_message"`
FileListAO struct {
Count int `json:"count"`
FileList []Cloud189File `json:"fileList"`
FolderList []Cloud189Folder `json:"folderList"`
} `json:"fileListAO"`
}
func (driver Cloud189) isFamily(account *model.Account) bool {
return account.InternalType == "Family"
}
@ -233,8 +232,8 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
"mediaType": "0",
"folderId": fileId,
"iconOption": "5",
"orderBy": account.OrderBy,
"descending": account.OrderDirection,
"orderBy": "lastOpTime", //account.OrderBy
"descending": "true", //account.OrderDirection
}, nil, nil, account)
if err != nil {
return nil, err
@ -264,9 +263,9 @@ func (driver Cloud189) GetFiles(fileId string, account *model.Account) ([]Cloud1
}
func (driver Cloud189) Request(url string, method int, query, form map[string]string, headers map[string]string, account *model.Account) ([]byte, error) {
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 +292,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:
@ -306,7 +304,7 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
if err != nil {
return nil, err
}
log.Debug(res.String())
//log.Debug(res.String())
if e.ErrorCode != "" {
if e.ErrorCode == "InvalidSessionKey" {
err = driver.Login(account)
@ -323,61 +321,198 @@ func (driver Cloud189) Request(url string, method int, query, form map[string]st
}
func (driver Cloud189) GetSessionKey(account *model.Account) (string, error) {
//info, ok := infoMap[account.Name]
//if !ok {
// info = Info{}
// infoMap[account.Name] = info
//} else {
// log.Debugf("hit")
//}
//if info.SessionKey != "" {
// return info.SessionKey, nil
//}
resp, err := driver.Request("https://cloud.189.cn/v2/getUserBriefInfo.action", base.Get, nil, nil, nil, account)
if err != nil {
return "", err
}
return jsoniter.Get(resp, "sessionKey").ToString(), nil
sessionKey := jsoniter.Get(resp, "sessionKey").ToString()
//info.SessionKey = sessionKey
return sessionKey, nil
}
func (driver Cloud189) GetResKey(account *model.Account) (string, string, error) {
rsa, ok := infoMap[account.Name]
if !ok {
rsa = Rsa{}
infoMap[account.Name] = rsa
}
now := time.Now().UnixMilli()
if rsa.Expire > now {
return rsa.PubKey, rsa.PkId, nil
}
resp, err := driver.Request("https://cloud.189.cn/api/security/generateRsaKey.action", base.Get, nil, nil, nil, account)
if err != nil {
return "", "", err
}
return jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString(), nil
pubKey, pkId := jsoniter.Get(resp, "pubKey").ToString(), jsoniter.Get(resp, "pkId").ToString()
rsa.PubKey, rsa.PkId = pubKey, pkId
rsa.Expire = jsoniter.Get(resp, "expire").ToInt64()
return pubKey, pkId, nil
}
func (driver Cloud189) UploadRequest(url string, form map[string]string, account *model.Account) ([]byte, error) {
sessionKey, err := driver.GetSessionKey(account)
if err != nil {
return nil, err
}
//func (driver Cloud189) UploadRequest1(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
// //sessionKey, err := driver.GetSessionKey(account)
// //if err != nil {
// // return nil, err
// //}
// sessionKey := account.DriveId
// pubKey, pkId, err := driver.GetResKey(account)
// log.Debugln(sessionKey, pubKey, pkId)
// if err != nil {
// return nil, err
// }
// xRId := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
// pkey := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")[0 : 16+int(16*mathRand.Float32())]
// params := hex.EncodeToString(AesEncrypt([]byte(qs(form)), []byte(pkey[0:16])))
// date := strconv.FormatInt(time.Now().Unix(), 10)
// a := make(url.Values)
// a.Set("SessionKey", sessionKey)
// a.Set("Operate", http.MethodGet)
// a.Set("RequestURI", uri)
// a.Set("Date", date)
// a.Set("params", params)
// signature := hex.EncodeToString(SHA1(EncodeParam(a), pkey))
// encryptionText := RsaEncode([]byte(pkey), pubKey, false)
// headers := map[string]string{
// "signature": signature,
// "sessionKey": sessionKey,
// "encryptionText": encryptionText,
// "pkId": pkId,
// "x-request-id": xRId,
// "x-request-date": date,
// }
// req := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params)
// if resp != nil {
// req.SetResult(resp)
// }
// res, err := req.Get("https://upload.cloud.189.cn" + uri)
// if err != nil {
// return nil, err
// }
// //log.Debug(res.String())
// data := res.Body()
// if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
// return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
// }
// return data, nil
//}
//
//func (driver Cloud189) UploadRequest2(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
// c := strconv.FormatInt(time.Now().UnixMilli(), 10)
// r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
// l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
// l = l[0 : 16+int(16*mathRand.Float32())]
//
// e := qs(form)
// data := AesEncrypt([]byte(e), []byte(l[0:16]))
// h := hex.EncodeToString(data)
//
// sessionKey := account.DriveId
// a := make(url.Values)
// a.Set("SessionKey", sessionKey)
// a.Set("Operate", http.MethodGet)
// a.Set("RequestURI", uri)
// a.Set("Date", c)
// a.Set("params", h)
// g := SHA1(EncodeParam(a), l)
//
// pubKey, pkId, err := driver.GetResKey(account)
// if err != nil {
// return nil, err
// }
// b := RsaEncode([]byte(l), pubKey, false)
// client, err := driver.getClient(account)
// if err != nil {
// return nil, err
// }
// req := client.R()
// req.Header.Set("accept", "application/json;charset=UTF-8")
// req.Header.Set("SessionKey", sessionKey)
// req.Header.Set("Signature", hex.EncodeToString(g))
// req.Header.Set("X-Request-Date", c)
// req.Header.Set("X-Request-ID", r)
// req.Header.Set("EncryptionText", b)
// req.Header.Set("PkId", pkId)
// if resp != nil {
// req.SetResult(resp)
// }
// res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
// if err != nil {
// return nil, err
// }
// //log.Debug(res.String())
// data = res.Body()
// if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
// return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
// }
// return data, nil
//}
func (driver Cloud189) UploadRequest(uri string, form map[string]string, account *model.Account, resp interface{}) ([]byte, error) {
c := strconv.FormatInt(time.Now().UnixMilli(), 10)
r := Random("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx")
l := Random("xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx")
l = l[0 : 16+int(16*mathRand.Float32())]
e := qs(form)
data := AesEncrypt([]byte(e), []byte(l[0:16]))
h := hex.EncodeToString(data)
sessionKey := account.DriveId
signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s&params=%s", sessionKey, uri, c, h), l)
pubKey, pkId, err := driver.GetResKey(account)
if err != nil {
return nil, err
}
xRId := uuid.New().String()
pkey := strings.ReplaceAll(xRId, "-", "")[:mathRand.Intn(16)+16]
params := aesEncrypt(qs(form), pkey[:16])
date := strconv.FormatInt(time.Now().Unix(), 10)
signature := hmacSha1(fmt.Sprintf("SessionKey=%s&Operate=GET&RequestURI=%s&Date=%s&params=%s", sessionKey, url, date, params), pkey)
encryptionText := RsaEncode([]byte(pkey), pubKey)
headers := map[string]string{
"signature": signature,
"sessionKey": sessionKey,
"encryptionText": encryptionText,
"pkId": pkId,
"x-request-id": xRId,
"x-request-date": date,
"origin": "https://cloud.189.cn",
"referer": "https://cloud.189.cn/",
}
log.Debugf("%+v\n%s", headers, params)
res, err := base.RestyClient.R().SetHeaders(headers).SetQueryParam("params", params).Get("https://upload.cloud.189.cn" + url)
b := RsaEncode([]byte(l), pubKey, false)
client, err := driver.getClient(account)
if err != nil {
return nil, err
}
log.Debug(res.String())
data := res.Body()
req := client.R()
req.Header.Set("accept", "application/json;charset=UTF-8")
req.Header.Set("SessionKey", sessionKey)
req.Header.Set("Signature", signature)
req.Header.Set("X-Request-Date", c)
req.Header.Set("X-Request-ID", r)
req.Header.Set("EncryptionText", b)
req.Header.Set("PkId", pkId)
if resp != nil {
req.SetResult(resp)
}
res, err := req.Get("https://upload.cloud.189.cn" + uri + "?params=" + h)
if err != nil {
return nil, err
}
//log.Debug(res.String())
data = res.Body()
if jsoniter.Get(data, "code").ToString() != "SUCCESS" {
return nil, errors.New(jsoniter.Get(data, "msg").ToString())
return nil, errors.New(uri + "---" + jsoniter.Get(data, "msg").ToString())
}
return data, nil
}
// Upload Error: decrypt encryptionText failed
// NewUpload Error: signature check false
func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account) error {
sessionKey, err := driver.GetSessionKey(account)
if err != nil {
account.Status = err.Error()
} else {
account.Status = "work"
account.DriveId = sessionKey
}
_ = model.SaveAccount(account)
const DEFAULT uint64 = 10485760
var count = int64(math.Ceil(float64(file.GetSize()) / float64(DEFAULT)))
var finish uint64 = 0
@ -394,11 +529,14 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
"fileSize": strconv.FormatInt(int64(file.Size), 10),
"sliceSize": strconv.FormatInt(int64(DEFAULT), 10),
"lazyCheck": "1",
}, account)
}, account, nil)
if err != nil {
return err
}
uploadFileId := jsoniter.Get(res, "data.uploadFileId").ToString()
uploadFileId := jsoniter.Get(res, "data", "uploadFileId").ToString()
//_, err = driver.UploadRequest("/person/getUploadedPartsInfo", map[string]string{
// "uploadFileId": uploadFileId,
//}, account, nil)
var i int64
var byteSize uint64
md5s := make([]string, 0)
@ -408,180 +546,82 @@ func (driver Cloud189) NewUpload(file *model.FileStream, account *model.Account)
if DEFAULT < byteSize {
byteSize = DEFAULT
}
log.Debugf("%d,%d", byteSize, finish)
//log.Debugf("%d,%d", byteSize, finish)
byteData := make([]byte, byteSize)
n, err := io.ReadFull(file, byteData)
log.Debug(err, n)
//log.Debug(err, n)
if err != nil {
return err
}
finish += uint64(n)
md5Bytes := getMd5(byteData)
md5Str := hex.EncodeToString(md5Bytes)
md5Hex := hex.EncodeToString(md5Bytes)
md5Base64 := base64.StdEncoding.EncodeToString(md5Bytes)
md5s = append(md5s, md5Str)
md5s = append(md5s, strings.ToUpper(md5Hex))
md5Sum.Write(byteData)
//log.Debugf("md5Bytes: %+v,md5Str:%s,md5Base64:%s", md5Bytes, md5Hex, md5Base64)
var resp UploadUrlsResp
res, err = driver.UploadRequest("/person/getMultiUploadUrls", map[string]string{
"partInfo": fmt.Sprintf("%s-%s", strconv.FormatInt(i, 10), md5Base64),
"uploadFileId": uploadFileId,
}, account)
}, account, &resp)
if err != nil {
return err
}
uploadData := jsoniter.Get(res, "uploadUrls.partNumber_"+strconv.FormatInt(i, 10))
headers := strings.Split(uploadData.Get("requestHeader").ToString(), "&")
req, err := http.NewRequest("PUT", uploadData.Get("requestURL").ToString(), bytes.NewBuffer(byteData))
uploadData := resp.UploadUrls["partNumber_"+strconv.FormatInt(i, 10)]
log.Debugf("uploadData: %+v", uploadData)
requestURL := uploadData.RequestURL
uploadHeaders := strings.Split(decodeURIComponent(uploadData.RequestHeader), "&")
req, _ := http.NewRequest(http.MethodPut, requestURL, bytes.NewReader(byteData))
for _, v := range uploadHeaders {
i := strings.Index(v, "=")
req.Header.Set(v[0:i], v[i+1:])
}
r, err := base.HttpClient.Do(req)
log.Debugf("%+v %+v", r, r.Request.Header)
if err != nil {
return err
}
for _, header := range headers {
kv := strings.Split(header, "=")
req.Header.Set(kv[0], strings.Join(kv[1:], "="))
}
res, err := base.HttpClient.Do(req)
if err != nil {
return err
}
log.Debugf("%+v", res)
}
id := md5Sum.Sum(nil)
fileMd5 := hex.EncodeToString(md5Sum.Sum(nil))
sliceMd5 := fileMd5
if file.GetSize() > DEFAULT {
sliceMd5 = utils.GetMD5Encode(strings.Join(md5s, "\n"))
}
res, err = driver.UploadRequest("/person/commitMultiUploadFile", map[string]string{
"uploadFileId": uploadFileId,
"fileMd5": hex.EncodeToString(id),
"sliceMd5": utils.GetMD5Encode(strings.Join(md5s, "\n")),
"fileMd5": fileMd5,
"sliceMd5": sliceMd5,
"lazyCheck": "1",
}, account)
}, account, nil)
account.DriveId, _ = driver.GetSessionKey(account)
return err
}
func random() string {
return fmt.Sprintf("0.%17v", mathRand.New(mathRand.NewSource(time.Now().UnixNano())).Int63n(100000000000000000))
}
func RsaEncode(origData []byte, j_rsakey string) string {
publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
block, _ := pem.Decode(publicKey)
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
pub := pubInterface.(*rsa.PublicKey)
b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
func (driver Cloud189) OldUpload(file *model.FileStream, account *model.Account) error {
//return base.ErrNotImplement
client, err := driver.getClient(account)
if err != nil {
log.Errorf("err: %s", err.Error())
return err
}
return b64tohex(base64.StdEncoding.EncodeToString(b))
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
func int2char(a int) string {
return strings.Split(BI_RM, "")[a]
}
func b64tohex(a string) string {
d := ""
e := 0
c := 0
for i := 0; i < len(a); i++ {
m := strings.Split(a, "")[i]
if m != "=" {
v := strings.Index(b64map, m)
if 0 == e {
e = 1
d += int2char(v >> 2)
c = 3 & v
} else if 1 == e {
e = 2
d += int2char(c<<2 | v>>4)
c = 15 & v
} else if 2 == e {
e = 3
d += int2char(c)
d += int2char(v >> 2)
c = 3 & v
} else {
e = 0
d += int2char(c<<2 | v>>4)
d += int2char(15 & v)
}
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
if e == 1 {
d += int2char(c << 2)
// api refer to PanIndex
res, err := client.R().SetMultipartFormData(map[string]string{
"parentId": parentFile.Id,
"sessionKey": account.DriveId,
"opertype": "1",
"fname": file.GetFileName(),
}).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
if err != nil {
return err
}
return d
}
func qs(form map[string]string) string {
strList := make([]string, 0)
for k, v := range form {
strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
if jsoniter.Get(res.Body(), "MD5").ToString() != "" {
return nil
}
return strings.Join(strList, "&")
}
func aesEncrypt(data, key string) string {
encrypted := AesEncryptECB([]byte(data), []byte(key))
//return string(encrypted)
return hex.EncodeToString(encrypted)
}
func hmacSha1(data string, secret string) string {
h := hmac.New(sha1.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
cipher, _ := aes.NewCipher(generateKey(key))
length := (len(origData) + aes.BlockSize) / aes.BlockSize
plain := make([]byte, length*aes.BlockSize)
copy(plain, origData)
pad := byte(len(plain) - len(origData))
for i := len(origData); i < len(plain); i++ {
plain[i] = pad
}
encrypted = make([]byte, len(plain))
// 分组分块加密
for bs, be := 0, cipher.BlockSize(); bs <= len(origData); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Encrypt(encrypted[bs:be], plain[bs:be])
}
return encrypted
}
func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
cipher, _ := aes.NewCipher(generateKey(key))
decrypted = make([]byte, len(encrypted))
//
for bs, be := 0, cipher.BlockSize(); bs < len(encrypted); bs, be = bs+cipher.BlockSize(), be+cipher.BlockSize() {
cipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
}
trim := 0
if len(decrypted) > 0 {
trim = len(decrypted) - int(decrypted[len(decrypted)-1])
}
return decrypted[:trim]
}
func generateKey(key []byte) (genKey []byte) {
genKey = make([]byte, 16)
copy(genKey, key)
for i := 16; i < len(key); {
for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
genKey[j] ^= key[i]
}
}
return genKey
}
func getMd5(data []byte) []byte {
h := md5.New()
h.Write(data)
return h.Sum(nil)
}
func init() {
base.RegisterDriver(&Cloud189{})
client189Map = make(map[string]*resty.Client, 0)
log.Debugf(res.String())
return errors.New(res.String())
}

View File

@ -1,14 +1,12 @@
package _89
import (
"errors"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"path/filepath"
)
@ -17,7 +15,8 @@ type Cloud189 struct{}
func (driver Cloud189) Config() base.DriverConfig {
return base.DriverConfig{
Name: "189Cloud",
Name: "189Cloud",
LocalSort: true,
}
}
@ -55,27 +54,30 @@ func (driver Cloud189) Items() []base.Item {
// Label: "family id",
// Type: base.TypeString,
//},
{
Name: "order_by",
Label: "order_by",
Type: base.TypeSelect,
Values: "name,size,lastOpTime,createdDate",
Required: true,
},
{
Name: "order_direction",
Label: "desc",
Type: base.TypeSelect,
Values: "true,false",
Required: true,
},
//{
// Name: "order_by",
// Label: "order_by",
// Type: base.TypeSelect,
// Values: "name,size,lastOpTime,createdDate",
// Required: true,
//},
//{
// Name: "order_direction",
// Label: "desc",
// Type: base.TypeSelect,
// Values: "true,false",
// Required: true,
//},
}
}
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,29 +348,8 @@ 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)
}
parentFile, err := driver.File(file.ParentPath, account)
if err != nil {
return err
}
// api refer to PanIndex
res, err := client.R().SetMultipartFormData(map[string]string{
"parentId": parentFile.Id,
"sessionKey": account.DriveId,
"opertype": "1",
"fname": file.GetFileName(),
}).SetMultipartField("Filedata", file.GetFileName(), file.GetMIMEType(), file).Post("https://hb02.upload.cloud.189.cn/v1/DCIWebUploadAction")
if err != nil {
return err
}
if jsoniter.Get(res.Body(), "MD5").ToString() != "" {
return nil
}
log.Debugf(res.String())
return errors.New(res.String())
return driver.NewUpload(file, account)
//return driver.OldUpload(file, account)
}
var _ base.Driver = (*Cloud189)(nil)

55
drivers/189/types.go Normal file
View File

@ -0,0 +1,55 @@
package _89
type Cloud189Error struct {
ErrorCode string `json:"errorCode"`
ErrorMsg string `json:"errorMsg"`
}
type Cloud189File struct {
Id int64 `json:"id"`
LastOpTime string `json:"lastOpTime"`
Name string `json:"name"`
Size int64 `json:"size"`
Icon struct {
SmallUrl string `json:"smallUrl"`
//LargeUrl string `json:"largeUrl"`
} `json:"icon"`
Url string `json:"url"`
}
type Cloud189Folder struct {
Id int64 `json:"id"`
LastOpTime string `json:"lastOpTime"`
Name string `json:"name"`
}
type Cloud189Files struct {
ResCode int `json:"res_code"`
ResMessage string `json:"res_message"`
FileListAO struct {
Count int `json:"count"`
FileList []Cloud189File `json:"fileList"`
FolderList []Cloud189Folder `json:"folderList"`
} `json:"fileListAO"`
}
type UploadUrlsResp struct {
Code string `json:"code"`
UploadUrls map[string]Part `json:"uploadUrls"`
}
type Part struct {
RequestURL string `json:"requestURL"`
RequestHeader string `json:"requestHeader"`
}
//type Info struct {
// SessionKey string
// Rsa Rsa
//}
type Rsa struct {
Expire int64 `json:"expire"`
PkId string `json:"pkId"`
PubKey string `json:"pubKey"`
}

188
drivers/189/util.go Normal file
View File

@ -0,0 +1,188 @@
package _89
import (
"bytes"
"crypto/aes"
"crypto/hmac"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"fmt"
"github.com/Xhofe/alist/drivers/base"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
mathRand "math/rand"
"net/url"
"regexp"
"strconv"
"strings"
"time"
)
func random() string {
return fmt.Sprintf("0.%17v", mathRand.New(mathRand.NewSource(time.Now().UnixNano())).Int63n(100000000000000000))
}
func RsaEncode(origData []byte, j_rsakey string, hex bool) string {
publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + j_rsakey + "\n-----END PUBLIC KEY-----")
block, _ := pem.Decode(publicKey)
pubInterface, _ := x509.ParsePKIXPublicKey(block.Bytes)
pub := pubInterface.(*rsa.PublicKey)
b, err := rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
if err != nil {
log.Errorf("err: %s", err.Error())
}
res := base64.StdEncoding.EncodeToString(b)
if hex {
return b64tohex(res)
}
return res
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz"
func int2char(a int) string {
return strings.Split(BI_RM, "")[a]
}
func b64tohex(a string) string {
d := ""
e := 0
c := 0
for i := 0; i < len(a); i++ {
m := strings.Split(a, "")[i]
if m != "=" {
v := strings.Index(b64map, m)
if 0 == e {
e = 1
d += int2char(v >> 2)
c = 3 & v
} else if 1 == e {
e = 2
d += int2char(c<<2 | v>>4)
c = 15 & v
} else if 2 == e {
e = 3
d += int2char(c)
d += int2char(v >> 2)
c = 3 & v
} else {
e = 0
d += int2char(c<<2 | v>>4)
d += int2char(15 & v)
}
}
}
if e == 1 {
d += int2char(c << 2)
}
return d
}
func qs(form map[string]string) string {
f := make(url.Values)
for k, v := range form {
f.Set(k, v)
}
return EncodeParam(f)
//strList := make([]string, 0)
//for k, v := range form {
// strList = append(strList, fmt.Sprintf("%s=%s", k, url.QueryEscape(v)))
//}
//return strings.Join(strList, "&")
}
func EncodeParam(v url.Values) string {
if v == nil {
return ""
}
var buf strings.Builder
keys := make([]string, 0, len(v))
for k := range v {
keys = append(keys, k)
}
for _, k := range keys {
vs := v[k]
for _, v := range vs {
if buf.Len() > 0 {
buf.WriteByte('&')
}
buf.WriteString(k)
buf.WriteByte('=')
buf.WriteString(v)
}
}
return buf.String()
}
func AesEncrypt(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
if block == nil {
return []byte{}
}
data = PKCS7Padding(data, block.BlockSize())
decrypted := make([]byte, len(data))
size := block.BlockSize()
for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
block.Encrypt(decrypted[bs:be], data[bs:be])
}
return decrypted
}
func PKCS7Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
func hmacSha1(data string, secret string) string {
h := hmac.New(sha1.New, []byte(secret))
h.Write([]byte(data))
return hex.EncodeToString(h.Sum(nil))
}
func getMd5(data []byte) []byte {
h := md5.New()
h.Write(data)
return h.Sum(nil)
}
func init() {
base.RegisterDriver(&Cloud189{})
client189Map = make(map[string]*resty.Client)
}
func decodeURIComponent(str string) string {
r, _ := url.PathUnescape(str)
//r = strings.ReplaceAll(r, " ", "+")
return r
}
func Random(v string) string {
reg := regexp.MustCompilePOSIX("[xy]")
data := reg.ReplaceAllFunc([]byte(v), func(msg []byte) []byte {
var i int64
t := int64(16 * mathRand.Float32())
if msg[0] == 120 {
i = t
} else {
i = 3&t | 8
}
return []byte(strconv.FormatInt(i, 16))
})
return string(data)
}
//func SHA1(v, l string) []byte {
// key := []byte(l)
// mac := hmac.New(sha1.New, key)
// mac.Write([]byte(v))
// return mac.Sum(nil)
//}

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

@ -84,7 +84,7 @@ func GetDriversMap() map[string]Driver {
}
func GetDrivers() map[string][]Item {
res := make(map[string][]Item, 0)
res := make(map[string][]Item)
for k, v := range driversMap {
if v.Config().OnlyProxy {
res[k] = v.Items()
@ -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

@ -12,6 +12,7 @@ var (
ErrNotSupport = errors.New("not support")
ErrNotFolder = errors.New("not a folder")
ErrEmptyFile = errors.New("empty file")
ErrRelativePath = errors.New("access using relative path is not allowed")
)
const (

View File

@ -61,6 +61,9 @@ func (driver FTP) Save(account *model.Account, old *model.Account) error {
delete(connMap, old.Name)
}
}
if account == nil {
return nil
}
if account.RootFolder == "" {
account.RootFolder = "/"
}

View File

@ -21,9 +21,11 @@ func (driver FTP) Login(account *model.Account) (*ftp.ServerConn, error) {
if err != nil {
return nil, err
}
connMap[account.Name] = conn
return conn, nil
}
func init() {
base.RegisterDriver(&FTP{})
connMap = make(map[string]*ftp.ServerConn)
}

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

@ -1,7 +1,6 @@
package mediatrack
import (
"bytes"
"crypto/md5"
"encoding/hex"
"fmt"
@ -18,6 +17,8 @@ import (
jsoniter "github.com/json-iterator/go"
log "github.com/sirupsen/logrus"
"io"
"io/ioutil"
"os"
"path/filepath"
)
@ -62,6 +63,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
}
@ -153,6 +157,10 @@ 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
@ -258,21 +266,39 @@ func (driver MediaTrack) Upload(file *model.FileStream, account *model.Account)
if err != nil {
return err
}
var buf bytes.Buffer
read := io.TeeReader(file, &buf)
tempFile, err := ioutil.TempFile("data/temp", "file-*")
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()
_, err = io.Copy(tempFile, file)
if err != nil {
return err
}
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
uploader := s3manager.NewUploader(s)
input := &s3manager.UploadInput{
Bucket: &resp.Data.Bucket,
Key: &resp.Data.Object,
Body: read,
Body: tempFile,
}
_, err = uploader.Upload(input)
if err != nil {
return err
}
url := fmt.Sprintf("https://jayce.api.mediatrack.cn/v3/assets/%s/children", parentFile.Id)
_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
h := md5.New()
_, err = io.Copy(h, &buf)
_, err = io.Copy(h, tempFile)
if err != nil {
return err
}

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)
@ -55,6 +58,9 @@ func (driver Native) Save(account *model.Account, old *model.Account) error {
}
func (driver Native) File(path string, account *model.Account) (*model.File, error) {
if utils.IsContain(strings.Split(path, "/"), "..") {
return nil, base.ErrRelativePath
}
fullPath := filepath.Join(account.RootFolder, path)
if !utils.Exists(fullPath) {
return nil, base.ErrPathNotFound
@ -79,6 +85,9 @@ func (driver Native) File(path string, account *model.Account) (*model.File, err
}
func (driver Native) Files(path string, account *model.Account) ([]model.File, error) {
if utils.IsContain(strings.Split(path, "/"), "..") {
return nil, base.ErrRelativePath
}
fullPath := filepath.Join(account.RootFolder, path)
if !utils.Exists(fullPath) {
return nil, base.ErrPathNotFound
@ -107,11 +116,14 @@ func (driver Native) Files(path string, account *model.Account) ([]model.File, e
}
files = append(files, file)
}
model.SortFiles(files, account)
return files, nil
}
func (driver Native) Link(args base.Args, account *model.Account) (*base.Link, error) {
_, err := driver.File(args.Path, account)
if err != nil {
return nil, err
}
fullPath := filepath.Join(account.RootFolder, args.Path)
s, err := os.Stat(fullPath)
if err != nil {
@ -153,12 +165,18 @@ func (driver Native) Preview(path string, account *model.Account) (interface{},
}
func (driver Native) MakeDir(path string, account *model.Account) error {
if utils.IsContain(strings.Split(path, "/"), "..") {
return base.ErrRelativePath
}
fullPath := filepath.Join(account.RootFolder, path)
err := os.MkdirAll(fullPath, 0700)
return err
}
func (driver Native) Move(src string, dst string, account *model.Account) error {
if utils.IsContain(strings.Split(src+"/"+dst, "/"), "..") {
return base.ErrRelativePath
}
fullSrc := filepath.Join(account.RootFolder, src)
fullDst := filepath.Join(account.RootFolder, dst)
return os.Rename(fullSrc, fullDst)
@ -169,6 +187,9 @@ func (driver Native) Rename(src string, dst string, account *model.Account) erro
}
func (driver Native) Copy(src string, dst string, account *model.Account) error {
if utils.IsContain(strings.Split(src+"/"+dst, "/"), "..") {
return base.ErrRelativePath
}
fullSrc := filepath.Join(account.RootFolder, src)
fullDst := filepath.Join(account.RootFolder, dst)
srcFile, err := driver.File(src, account)
@ -188,6 +209,9 @@ func (driver Native) Copy(src string, dst string, account *model.Account) error
}
func (driver Native) Delete(path string, account *model.Account) error {
if utils.IsContain(strings.Split(path, "/"), "..") {
return base.ErrRelativePath
}
fullPath := filepath.Join(account.RootFolder, path)
file, err := driver.File(path, account)
if err != nil {
@ -203,6 +227,9 @@ func (driver Native) Upload(file *model.FileStream, account *model.Account) erro
if file == nil {
return base.ErrEmptyFile
}
if utils.IsContain(strings.Split(file.ParentPath, "/"), "..") {
return base.ErrRelativePath
}
fullPath := filepath.Join(account.RootFolder, file.ParentPath, file.Name)
_, err := driver.File(filepath.Join(file.ParentPath, file.Name), account)
if err == nil {
@ -222,6 +249,16 @@ func (driver Native) Upload(file *model.FileStream, account *model.Account) erro
defer func() {
_ = out.Close()
}()
//var buf bytes.Buffer
//reader := io.TeeReader(file, &buf)
//h := md5.New()
//_, err = io.Copy(h, reader)
//if err != nil {
// return err
//}
//hash := hex.EncodeToString(h.Sum(nil))
//log.Debugln("md5:", hash)
//_, err = io.Copy(out, &buf)
_, err = io.Copy(out, file)
return err
}

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

@ -5,9 +5,30 @@ import (
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
log "github.com/sirupsen/logrus"
"runtime/debug"
)
func Path(driver base.Driver, account *model.Account, path string) (*model.File, []model.File, error) {
return driver.Path(path, account)
}
func Files(driver base.Driver, account *model.Account, path string) ([]model.File, error) {
_, files, err := Path(driver, account, path)
if err != nil {
return nil, err
}
if files == nil {
return nil, base.ErrNotFolder
}
return files, nil
}
func File(driver base.Driver, account *model.Account, path string) (*model.File, error) {
return driver.File(path, account)
}
func MakeDir(driver base.Driver, account *model.Account, path string, clearCache bool) error {
log.Debugf("mkdir: %s", path)
err := driver.MakeDir(path, account)
if err == nil && clearCache {
_ = base.DeleteCache(utils.Dir(path), account)
@ -19,6 +40,7 @@ func MakeDir(driver base.Driver, account *model.Account, path string, clearCache
}
func Move(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
log.Debugf("move %s to %s", src, dst)
rename := false
if utils.Dir(src) == utils.Dir(dst) {
rename = true
@ -42,6 +64,7 @@ func Move(driver base.Driver, account *model.Account, src, dst string, clearCach
}
func Copy(driver base.Driver, account *model.Account, src, dst string, clearCache bool) error {
log.Debugf("copy %s to %s", src, dst)
err := driver.Copy(src, dst, account)
if err == nil && clearCache {
_ = base.DeleteCache(utils.Dir(dst), account)
@ -53,6 +76,7 @@ func Copy(driver base.Driver, account *model.Account, src, dst string, clearCach
}
func Delete(driver base.Driver, account *model.Account, path string, clearCache bool) error {
log.Debugf("delete %s", path)
err := driver.Delete(path, account)
if err == nil && clearCache {
_ = base.DeleteCache(utils.Dir(path), account)
@ -74,5 +98,6 @@ func Upload(driver base.Driver, account *model.Account, file *model.FileStream,
if err != nil {
log.Errorf("upload error: %s", err.Error())
}
debug.FreeOSMemory()
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,
@ -113,6 +118,6 @@ func (driver S3) GetKey(path string, account *model.Account, dir bool) string {
}
func init() {
sessionsMap = make(map[string]*session.Session, 0)
sessionsMap = make(map[string]*session.Session)
base.RegisterDriver(&S3{})
}

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
}
@ -249,7 +252,34 @@ func (driver Teambition) Delete(path string, account *model.Account) error {
}
func (driver Teambition) Upload(file *model.FileStream, account *model.Account) error {
return base.ErrNotImplement
if file == nil {
return base.ErrEmptyFile
}
parentFile, err := driver.File(file.ParentPath, account)
if !parentFile.IsDir() {
return base.ErrNotFolder
}
if err != nil {
return err
}
res, err := driver.Request("/projects", base.Get, nil, nil, nil, nil, nil, account)
if err != nil {
return err
}
token := GetBetweenStr(string(res), "strikerAuth&quot;:&quot;", "&quot;,&quot;phoneForLogin")
var newFile *FileUpload
if file.Size <= 20971520 {
// post upload
newFile, err = driver.upload(file, token, account)
} else {
// chunk upload
//err = base.ErrNotImplement
newFile, err = driver.chunkUpload(file, token, account)
}
if err != nil {
return err
}
return driver.finishUpload(newFile, parentFile.Id, account)
}
var _ base.Driver = (*Teambition)(nil)

View File

@ -2,11 +2,14 @@ package teambition
import (
"errors"
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/model"
"github.com/Xhofe/alist/utils"
"github.com/go-resty/resty/v2"
log "github.com/sirupsen/logrus"
"io"
"path"
"strconv"
"time"
@ -66,25 +69,6 @@ func (driver Teambition) Request(pathname string, method int, headers, query, fo
return res.Body(), nil
}
type Collection struct {
ID string `json:"_id"`
Title string `json:"title"`
Updated time.Time `json:"updated"`
}
type Work struct {
ID string `json:"_id"`
FileName string `json:"fileName"`
FileSize int64 `json:"fileSize"`
FileKey string `json:"fileKey"`
FileCategory string `json:"fileCategory"`
DownloadURL string `json:"downloadUrl"`
ThumbnailURL string `json:"thumbnailUrl"`
Thumbnail string `json:"thumbnail"`
Updated time.Time `json:"updated"`
PreviewURL string `json:"previewUrl"`
}
func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]model.File, error) {
files := make([]model.File, 0)
page := 1
@ -151,6 +135,101 @@ func (driver Teambition) GetFiles(parentId string, account *model.Account) ([]mo
return files, nil
}
func (driver Teambition) upload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) {
prefix := "tcs"
if account.InternalType == "International" {
prefix = "us-tcs"
}
var newFile FileUpload
_, err := base.RestyClient.R().SetResult(&newFile).SetHeader("Authorization", token).
SetMultipartFormData(map[string]string{
"name": file.GetFileName(),
"type": file.GetMIMEType(),
"size": strconv.FormatUint(file.GetSize(), 10),
//"lastModifiedDate": "",
}).SetMultipartField("file", file.GetFileName(), file.GetMIMEType(), file).
Post(fmt.Sprintf("https://%s.teambition.net/upload", prefix))
if err != nil {
return nil, err
}
return &newFile, nil
}
func (driver Teambition) chunkUpload(file *model.FileStream, token string, account *model.Account) (*FileUpload, error) {
prefix := "tcs"
if account.InternalType == "International" {
prefix = "us-tcs"
}
var newChunk ChunkUpload
_, err := base.RestyClient.R().SetResult(&newChunk).SetHeader("Authorization", token).
SetBody(base.Json{
"fileName": file.GetFileName(),
"fileSize": file.GetSize(),
"lastUpdated": time.Now(),
}).Post(fmt.Sprintf("https://%s.teambition.net/upload/chunk", prefix))
if err != nil {
return nil, err
}
for i := 0; i < newChunk.Chunks; i++ {
chunkSize := newChunk.ChunkSize
if i == newChunk.Chunks-1 {
chunkSize = int(file.GetSize()) - i*chunkSize
}
log.Debugf("%d : %d", i, chunkSize)
chunkData := make([]byte, chunkSize)
_, err = io.ReadFull(file, chunkData)
if err != nil {
return nil, err
}
u := fmt.Sprintf("https://%s.teambition.net/upload/chunk/%s?chunk=%d&chunks=%d",
prefix, newChunk.FileKey, i+1, newChunk.Chunks)
log.Debugf("url: %s", u)
res, err := base.RestyClient.R().SetHeaders(map[string]string{
"Authorization": token,
"Content-Type": "application/octet-stream",
"Referer": "https://www.teambition.com/",
}).SetBody(chunkData).Post(u)
if err != nil {
return nil, err
}
log.Debug(res.Status(), res.String())
//req, err := http.NewRequest("POST",
// u,
// bytes.NewBuffer(chunkData))
//if err != nil {
// return nil, err
//}
//req.Header.Set("Authorization", token)
//req.Header.Set("Content-Type", "application/octet-stream")
//req.Header.Set("Referer", "https://www.teambition.com/")
//resp, err := base.HttpClient.Do(req)
//res, _ := ioutil.ReadAll(resp.Body)
//log.Debugf("chunk upload status: %s, res: %s", resp.Status, string(res))
if err != nil {
return nil, err
}
}
res, err := base.RestyClient.R().SetHeader("Authorization", token).Post(
fmt.Sprintf("https://%s.teambition.net/upload/chunk/%s",
prefix, newChunk.FileKey))
log.Debug(res.Status(), res.String())
if err != nil {
return nil, err
}
return &newChunk.FileUpload, nil
}
func (driver Teambition) finishUpload(file *FileUpload, parentId string, account *model.Account) error {
file.InvolveMembers = []interface{}{}
file.Visible = "members"
file.ParentId = parentId
_, err := driver.Request("/api/works", base.Post, nil, nil, nil, base.Json{
"works": []FileUpload{*file},
"_parentId": parentId,
}, nil, account)
return err
}
func init() {
base.RegisterDriver(&Teambition{})
}

View File

@ -0,0 +1,63 @@
package teambition
import "time"
type Collection struct {
ID string `json:"_id"`
Title string `json:"title"`
Updated time.Time `json:"updated"`
}
type Work struct {
ID string `json:"_id"`
FileName string `json:"fileName"`
FileSize int64 `json:"fileSize"`
FileKey string `json:"fileKey"`
FileCategory string `json:"fileCategory"`
DownloadURL string `json:"downloadUrl"`
ThumbnailURL string `json:"thumbnailUrl"`
Thumbnail string `json:"thumbnail"`
Updated time.Time `json:"updated"`
PreviewURL string `json:"previewUrl"`
}
type FileUpload struct {
FileKey string `json:"fileKey"`
FileName string `json:"fileName"`
FileType string `json:"fileType"`
FileSize int `json:"fileSize"`
FileCategory string `json:"fileCategory"`
ImageWidth int `json:"imageWidth"`
ImageHeight int `json:"imageHeight"`
InvolveMembers []interface{} `json:"involveMembers"`
Source string `json:"source"`
Visible string `json:"visible"`
ParentId string `json:"_parentId"`
}
type ChunkUpload struct {
FileUpload
Storage string `json:"storage"`
MimeType string `json:"mimeType"`
Chunks int `json:"chunks"`
ChunkSize int `json:"chunkSize"`
Created time.Time `json:"created"`
FileMD5 string `json:"fileMD5"`
LastUpdated time.Time `json:"lastUpdated"`
UploadedChunks []interface{} `json:"uploadedChunks"`
Token struct {
AppID string `json:"AppID"`
OrganizationID string `json:"OrganizationID"`
UserID string `json:"UserID"`
Exp time.Time `json:"Exp"`
Storage string `json:"Storage"`
Resource string `json:"Resource"`
Speed int `json:"Speed"`
} `json:"token"`
DownloadUrl string `json:"downloadUrl"`
ThumbnailUrl string `json:"thumbnailUrl"`
PreviewUrl string `json:"previewUrl"`
ImmPreviewUrl string `json:"immPreviewUrl"`
PreviewExt string `json:"previewExt"`
LastUploadTime interface{} `json:"lastUploadTime"`
}

View File

@ -0,0 +1,18 @@
package teambition
import "strings"
func GetBetweenStr(str, start, end string) string {
n := strings.Index(str, start)
if n == -1 {
return ""
}
n = n + len(start)
str = string([]byte(str)[n:])
m := strings.Index(str, end)
if m == -1 {
return ""
}
str = string([]byte(str)[:m])
return str
}

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"
@ -85,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

@ -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

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

View File

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

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

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 edit account name here", 400)
return
}
err = operate.Move(driver, account, path_, utils.Join(utils.Dir(path_), req.Name), true)
if err != nil {
common.ErrorResp(c, err, 500)
return
}
common.SuccessResp(c)
}

View File

@ -5,29 +5,14 @@ import (
"fmt"
"github.com/Xhofe/alist/conf"
"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"
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)
@ -93,14 +78,14 @@ func Path(c *gin.Context) {
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
}
if !ok {
files = Hide(meta, files)
files = common.Hide(meta, files)
}
c.JSON(200, common.Resp{
Code: 200,
@ -125,7 +110,7 @@ func Path(c *gin.Context) {
common.ErrorResp(c, err, 500)
return
}
file, files, err := driver.Path(path, account)
file, files, err := operate.Path(driver, account, path)
if err != nil {
common.ErrorResp(c, err, 500)
return
@ -159,11 +144,12 @@ func Path(c *gin.Context) {
})
} else {
if !ok {
files = Hide(meta, files)
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,

View File

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/Xhofe/alist/conf"
"github.com/Xhofe/alist/drivers/base"
"github.com/Xhofe/alist/drivers/operate"
"github.com/Xhofe/alist/server/common"
"github.com/Xhofe/alist/utils"
"github.com/gin-gonic/gin"
@ -50,7 +51,7 @@ func Proxy(c *gin.Context) {
return
}
// 检查文件
file, err := driver.File(path, account)
file, err := operate.File(driver, account, path)
if err != nil {
common.ErrorResp(c, err, 500)
return
@ -100,10 +101,10 @@ func Proxy(c *gin.Context) {
c.File(link.Url)
return
} else {
if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
Text(c, link)
return
}
//if utils.GetFileType(filepath.Ext(rawPath)) == conf.TEXT {
// Text(c, link)
// return
//}
driver.Proxy(c, account)
r := c.Request
w := c.Writer
@ -125,6 +126,10 @@ func Proxy(c *gin.Context) {
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
@ -146,7 +151,13 @@ func init() {
}
func Text(c *gin.Context, link *base.Link) {
res, err := client.R().Get(link.Url)
req := client.R()
if link.Headers != nil {
for _, header := range link.Headers {
req.SetHeader(header.Name, header.Value)
}
}
res, err := req.Get(link.Url)
if err != nil {
common.ErrorResp(c, err, 500)
return

View File

@ -19,7 +19,7 @@ func Auth(c *gin.Context) {
//}
//if token != utils.GetMD5Encode(password.Value) {
if token != conf.Token {
common.ErrorStrResp(c, "Wrong password", 401)
common.ErrorStrResp(c, "Invalid token", 401)
return
}
c.Next()

View File

@ -51,6 +51,11 @@ 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("/copy", file.Copy)
admin.POST("/folder", file.Folder)
}
WebDav(r)
Static(r)

View File

@ -25,8 +25,11 @@ func InitIndex() {
}
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

@ -40,10 +40,10 @@ func (fs *FileSystem) File(rawPath string) (*model.File, error) {
if err != nil {
return nil, err
}
return driver.File(path_, account)
return operate.File(driver, account, path_)
}
func (fs *FileSystem) Files(rawPath string) ([]model.File, error) {
func (fs *FileSystem) Files(ctx context.Context, rawPath string) ([]model.File, error) {
rawPath = utils.ParsePath(rawPath)
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 := operate.Files(driver, account, path_)
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.Debugln(path, err)
return false, nil
}
return true, file