diff --git a/drivers/all.go b/drivers/all.go index 40666028..599820c2 100644 --- a/drivers/all.go +++ b/drivers/all.go @@ -25,6 +25,7 @@ import ( _ "github.com/alist-org/alist/v3/drivers/ftp" _ "github.com/alist-org/alist/v3/drivers/google_drive" _ "github.com/alist-org/alist/v3/drivers/google_photo" + _ "github.com/alist-org/alist/v3/drivers/ilanzou" _ "github.com/alist-org/alist/v3/drivers/ipfs_api" _ "github.com/alist-org/alist/v3/drivers/lanzou" _ "github.com/alist-org/alist/v3/drivers/local" diff --git a/drivers/ilanzou/driver.go b/drivers/ilanzou/driver.go new file mode 100644 index 00000000..7a6e609f --- /dev/null +++ b/drivers/ilanzou/driver.go @@ -0,0 +1,82 @@ +package template + +import ( + "context" + "net/http" + + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/errs" + "github.com/alist-org/alist/v3/internal/model" + log "github.com/sirupsen/logrus" +) + +type ILanZou struct { + model.Storage + Addition +} + +func (d *ILanZou) Config() driver.Config { + return config +} + +func (d *ILanZou) GetAddition() driver.Additional { + return &d.Addition +} + +func (d *ILanZou) Init(ctx context.Context) error { + res, err := d.proved("/user/info/map", http.MethodGet, nil) + if err != nil { + return err + } + log.Debugf("[ilanzou] init response: %s", res) + return nil +} + +func (d *ILanZou) Drop(ctx context.Context) error { + return nil +} + +func (d *ILanZou) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) { + +} + +func (d *ILanZou) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) { + // TODO return link of file, required + return nil, errs.NotImplement +} + +func (d *ILanZou) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) (model.Obj, error) { + // TODO create folder, optional + return nil, errs.NotImplement +} + +func (d *ILanZou) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) { + // TODO move obj, optional + return nil, errs.NotImplement +} + +func (d *ILanZou) Rename(ctx context.Context, srcObj model.Obj, newName string) (model.Obj, error) { + // TODO rename obj, optional + return nil, errs.NotImplement +} + +func (d *ILanZou) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) { + // TODO copy obj, optional + return nil, errs.NotImplement +} + +func (d *ILanZou) Remove(ctx context.Context, obj model.Obj) error { + // TODO remove obj, optional + return errs.NotImplement +} + +func (d *ILanZou) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) (model.Obj, error) { + // TODO upload file, optional + return nil, errs.NotImplement +} + +//func (d *ILanZou) Other(ctx context.Context, args model.OtherArgs) (interface{}, error) { +// return nil, errs.NotSupport +//} + +var _ driver.Driver = (*ILanZou)(nil) diff --git a/drivers/ilanzou/meta.go b/drivers/ilanzou/meta.go new file mode 100644 index 00000000..c1ba4c8d --- /dev/null +++ b/drivers/ilanzou/meta.go @@ -0,0 +1,34 @@ +package template + +import ( + "github.com/alist-org/alist/v3/internal/driver" + "github.com/alist-org/alist/v3/internal/op" +) + +type Addition struct { + driver.RootID + Username string `json:"username" type:"string" required:"true"` + Password string `json:"password" type:"string" required:"true"` + + Token string +} + +var config = driver.Config{ + Name: "ILanZou", + LocalSort: false, + OnlyLocal: false, + OnlyProxy: false, + NoCache: false, + NoUpload: false, + NeedMs: false, + DefaultRoot: "0", + CheckStatus: false, + Alert: "", + NoOverwriteUpload: false, +} + +func init() { + op.RegisterDriver(func() driver.Driver { + return &ILanZou{} + }) +} diff --git a/drivers/ilanzou/types.go b/drivers/ilanzou/types.go new file mode 100644 index 00000000..238a618b --- /dev/null +++ b/drivers/ilanzou/types.go @@ -0,0 +1,27 @@ +package template + +type ListResp struct { + Msg string `json:"msg"` + Total int `json:"total"` + Code int `json:"code"` + Offset int `json:"offset"` + TotalPage int `json:"totalPage"` + Limit int `json:"limit"` + List []ListItem `json:"list"` +} + +type ListItem struct { + IconId int `json:"iconId"` + IsAmt int `json:"isAmt"` + FolderDesc string `json:"folderDesc"` + AddTime string `json:"addTime"` + FolderId int `json:"folderId"` + ParentId int `json:"parentId"` + NoteType int `json:"noteType"` + UpdTime string `json:"updTime"` + IsShare int `json:"isShare"` + FolderIcon string `json:"folderIcon"` + FolderName string `json:"folderName"` + FileType int `json:"fileType"` + Status int `json:"status"` +} diff --git a/drivers/ilanzou/util.go b/drivers/ilanzou/util.go new file mode 100644 index 00000000..d17c9350 --- /dev/null +++ b/drivers/ilanzou/util.go @@ -0,0 +1,105 @@ +package template + +import ( + "encoding/hex" + "fmt" + "net/http" + "strconv" + "time" + + "github.com/alist-org/alist/v3/drivers/base" + "github.com/alist-org/alist/v3/pkg/utils" + "github.com/foxxorcat/mopan-sdk-go" + "github.com/go-resty/resty/v2" + log "github.com/sirupsen/logrus" +) + +const ( + Base = "https://api.ilanzou.com" +) + +var ( + AesSecret = []byte("lanZouY-disk-app") +) + +func (d *ILanZou) login() error { + res, err := d.unproved("/login", http.MethodPost, func(req *resty.Request) { + req.SetBody(base.Json{ + "loginName": d.Username, + "loginPwd": d.Password, + }) + }) + if err != nil { + return err + } + d.Token = utils.Json.Get(res, "data", "appToken").ToString() + if d.Token == "" { + return fmt.Errorf("failed to login: token is empty, resp: %s", res) + } + return nil +} + +func getTimestamp() (string, error) { + ts := time.Now().UnixMilli() + tsStr := strconv.FormatInt(ts, 10) + res, err := mopan.AesEncrypt([]byte(tsStr), AesSecret) + if err != nil { + return "", err + } + return hex.EncodeToString(res), nil +} + +func (d *ILanZou) request(pathname, method string, callback base.ReqCallback, proved bool, retry ...bool) ([]byte, error) { + req := base.RestyClient.R() + ts, err := getTimestamp() + if err != nil { + return nil, err + } + req.SetQueryParams(map[string]string{ + //"uuid": "32dcf64b-e314-4c99-b192-a0a98c5c1e5e", + "devType": "6", + //"devCode": "32dcf64b-e314-4c99-b192-a0a98c5c1e5e", + "devModel": "chrome", + "devVersion": "120", + "appVersion": "", + "timestamp": ts, + //"appToken": d.Token, + "extra": "2", + }) + if proved { + req.SetQueryParam("appToken", d.Token) + } + if callback != nil { + callback(req) + } + res, err := req.Execute(method, Base+pathname) + if err != nil { + if res != nil { + log.Errorf("[iLanZou] request error: %s", res.String()) + } + return nil, err + } + isRetry := len(retry) > 0 && retry[0] + body := res.Body() + code := utils.Json.Get(body, "code").ToInt() + msg := utils.Json.Get(body, "msg").ToString() + if code != 200 { + if !isRetry && proved && (utils.SliceContains([]int{-1, -2}, code) || d.Token == "") { + err = d.login() + if err != nil { + return nil, err + } + return d.request(pathname, method, callback, proved, true) + } + return nil, fmt.Errorf("%d: %s", code, msg) + } + return body, nil +} + +func (d *ILanZou) unproved(pathname, method string, callback base.ReqCallback) ([]byte, error) { + return d.request("/unproved"+pathname, method, callback, false) +} + +func (d *ILanZou) proved(pathname, method string, callback base.ReqCallback) ([]byte, error) { + return d.request("/proved"+pathname, method, callback, true) +}