Skip to content

Commit

Permalink
feat: add 123pan driver
Browse files Browse the repository at this point in the history
  • Loading branch information
xhofe committed Sep 1, 2022
1 parent 7290f9b commit 284274b
Show file tree
Hide file tree
Showing 23 changed files with 456 additions and 26 deletions.
227 changes: 227 additions & 0 deletions drivers/123/driver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
package _123

import (
"bytes"
"context"
"crypto/md5"
"encoding/binary"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"

"github.com/alist-org/alist/v3/drivers/base"
"github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/errs"
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/pkg/utils"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/go-resty/resty/v2"
)

type Pan123 struct {
model.Storage
Addition
AccessToken string
}

func (d *Pan123) Config() driver.Config {
return config
}

func (d *Pan123) GetAddition() driver.Additional {
return d.Addition
}

func (d *Pan123) Init(ctx context.Context, storage model.Storage) error {
d.Storage = storage
err := utils.Json.UnmarshalFromString(d.Storage.Addition, &d.Addition)
if err != nil {
return err
}
return d.login()
}

func (d *Pan123) Drop(ctx context.Context) error {
return nil
}

func (d *Pan123) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
files, err := d.getFiles(dir.GetID())
if err != nil {
return nil, err
}
return utils.SliceConvert(files, func(src File) (model.Obj, error) {
return src, nil
})
}

//func (d *Pan123) Get(ctx context.Context, path string) (model.Obj, error) {
// // TODO this is optional
// return nil, errs.NotImplement
//}

func (d *Pan123) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
if f, ok := file.(File); ok {
return &model.Link{
URL: f.DownloadUrl,
}, nil
} else {
return nil, fmt.Errorf("can't convert obj")
}
}

func (d *Pan123) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
data := base.Json{
"driveId": 0,
"etag": "",
"fileName": dirName,
"parentFileId": parentDir.GetID(),
"size": 0,
"type": 1,
}
_, err := d.request("https://www.123pan.com/api/file/upload_request", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
}

func (d *Pan123) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
data := base.Json{
"fileIdList": []base.Json{{"FileId": srcObj.GetID()}},
"parentFileId": dstDir.GetID(),
}
_, err := d.request("https://www.123pan.com/api/file/mod_pid", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
}

func (d *Pan123) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
data := base.Json{
"driveId": 0,
"fileId": srcObj.GetID(),
"fileName": newName,
}
_, err := d.request("https://www.123pan.com/api/file/rename", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
}

func (d *Pan123) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return errs.NotSupport
}

func (d *Pan123) Remove(ctx context.Context, obj model.Obj) error {
if f, ok := obj.(File); ok {
data := base.Json{
"driveId": 0,
"operation": true,
"fileTrashInfoList": []File{f},
}
_, err := d.request("https://www.123pan.com/a/api/file/trash", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
} else {
return fmt.Errorf("can't convert obj")
}
}

func (d *Pan123) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
const DEFAULT int64 = 10485760
var uploadFile io.Reader
h := md5.New()
if d.StreamUpload && stream.GetSize() > DEFAULT {
// 只计算前10MIB
buf := bytes.NewBuffer(make([]byte, 0, DEFAULT))
if n, err := io.CopyN(io.MultiWriter(buf, h), stream, DEFAULT); err != io.EOF && n == 0 {
return err
}
// 增加额外参数防止MD5碰撞
h.Write([]byte(stream.GetName()))
num := make([]byte, 8)
binary.BigEndian.PutUint64(num, uint64(stream.GetSize()))
h.Write(num)
// 拼装
uploadFile = io.MultiReader(buf, stream)
} else {
// 计算完整文件MD5
tempFile, err := os.CreateTemp(conf.Conf.TempDir, "file-*")
if err != nil {
return err
}
defer func() {
_ = tempFile.Close()
_ = os.Remove(tempFile.Name())
}()

if _, err = io.Copy(io.MultiWriter(tempFile, h), stream); err != nil {
return err
}

_, err = tempFile.Seek(0, io.SeekStart)
if err != nil {
return err
}
uploadFile = tempFile
}
etag := hex.EncodeToString(h.Sum(nil))
data := base.Json{
"driveId": 0,
"duplicate": 2, // 2->覆盖 1->重命名 0->默认
"etag": etag,
"fileName": stream.GetName(),
"parentFileId": dstDir.GetID(),
"size": stream.GetSize(),
"type": 0,
}
var resp UploadResp
_, err := d.request("https://www.123pan.com/api/file/upload_request", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, &resp)
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"),
Endpoint: aws.String("file.123pan.com"),
S3ForcePathStyle: aws.Bool(true),
}
s, err := session.NewSession(cfg)
if err != nil {
return err
}
uploader := s3manager.NewUploader(s)
input := &s3manager.UploadInput{
Bucket: &resp.Data.Bucket,
Key: &resp.Data.Key,
Body: uploadFile,
}
_, err = uploader.Upload(input)
if err != nil {
return err
}
_, err = d.request("https://www.123pan.com/api/file/upload_complete", http.MethodPost, func(req *resty.Request) {
req.SetBody(base.Json{
"fileId": resp.Data.FileId,
})
}, nil)
return err
}

func (d *Pan123) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) {
return nil, errs.NotSupport
}

var _ driver.Driver = (*Pan123)(nil)
30 changes: 30 additions & 0 deletions drivers/123/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package _123

import (
"github.com/alist-org/alist/v3/internal/driver"
"github.com/alist-org/alist/v3/internal/op"
)

type Addition struct {
Username string `json:"username" required:"true"`
Password string `json:"password" required:"true"`
OrderBy string `json:"order_by" type:"select" options:"name,fileId,updateAt,createAt" default:"name"`
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc" default:"asc"`
driver.RootFolderId
// define other
StreamUpload bool `json:"stream_upload"`
//Field string `json:"field" type:"select" required:"true" options:"a,b,c" default:"a"`
}

var config = driver.Config{
Name: "123Pan",
DefaultRoot: "0",
}

func New() driver.Driver {
return &Pan123{}
}

func init() {
op.RegisterDriver(config, New)
}
85 changes: 85 additions & 0 deletions drivers/123/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package _123

import (
"strconv"
"time"

"github.com/alist-org/alist/v3/internal/model"
)

type BaseResp struct {
Code int `json:"code"`
Message string `json:"message"`
}

type TokenResp struct {
BaseResp
Data struct {
Token string `json:"token"`
} `json:"data"`
}

type File struct {
FileName string `json:"FileName"`
Size int64 `json:"Size"`
UpdateAt time.Time `json:"UpdateAt"`
FileId int64 `json:"FileId"`
Type int `json:"Type"`
Etag string `json:"Etag"`
S3KeyFlag string `json:"S3KeyFlag"`
DownloadUrl string `json:"DownloadUrl"`
}

func (f File) GetSize() int64 {
return f.Size
}

func (f File) GetName() string {
return f.FileName
}

func (f File) ModTime() time.Time {
return f.UpdateAt
}

func (f File) IsDir() bool {
return f.Type == 1
}

func (f File) GetID() string {
return strconv.FormatInt(f.FileId, 10)
}

var _ model.Obj = (*File)(nil)

//func (f File) Thumb() string {
//
//}
//var _ model.Thumb = (*File)(nil)

type Files struct {
BaseResp
Data struct {
InfoList []File `json:"InfoList"`
Next string `json:"Next"`
} `json:"data"`
}

type DownResp struct {
BaseResp
Data struct {
DownloadUrl string `json:"DownloadUrl"`
} `json:"data"`
}

type UploadResp struct {
BaseResp
Data struct {
AccessKeyId string `json:"AccessKeyId"`
Bucket string `json:"Bucket"`
Key string `json:"Key"`
SecretAccessKey string `json:"SecretAccessKey"`
SessionToken string `json:"SessionToken"`
FileId int64 `json:"FileId"`
} `json:"data"`
}
Loading

0 comments on commit 284274b

Please sign in to comment.