mirror of https://github.com/Xhofe/alist
commit
bf73ea7f5d
|
@ -69,6 +69,10 @@ Available at: <https://alist.nn.ci>.
|
|||
|
||||
<https://alist-doc.nn.ci/en/>
|
||||
|
||||
## Special sponsors
|
||||
- [Find Resources - Aliyundrive Resource Search Engine](https://zhaoziyuan.la/)
|
||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||
|
||||
## License
|
||||
|
||||
The `AList` is open-source software licensed under the AGPL-3.0 license.
|
||||
|
|
|
@ -69,6 +69,10 @@
|
|||
|
||||
<https://alist-doc.nn.ci/>
|
||||
|
||||
## 特别赞助
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [JetBrains: Essential tools for software developers and teams](https://www.jetbrains.com/)
|
||||
|
||||
## 许可
|
||||
|
||||
`AList` 是在 AGPL-3.0 许可下许可的开源软件。
|
||||
|
|
|
@ -50,8 +50,8 @@ func InitModel() {
|
|||
}
|
||||
case "mysql":
|
||||
{
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name)
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local&tls=%s",
|
||||
databaseConfig.User, databaseConfig.Password, databaseConfig.Host, databaseConfig.Port, databaseConfig.Name, databaseConfig.SslMode)
|
||||
db, err := gorm.Open(mysql.Open(dsn), gormConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to connect database:%s", err.Error())
|
||||
|
|
|
@ -45,7 +45,6 @@ func DefaultConfig() *Config {
|
|||
Port: 0,
|
||||
TablePrefix: "x_",
|
||||
DBFile: "data/data.db",
|
||||
SslMode: "disable",
|
||||
},
|
||||
Cache: CacheConfig{
|
||||
Expiration: 60,
|
||||
|
|
|
@ -140,7 +140,7 @@ func (driver Pan123) Link(args base.Args, account *model.Account) (*base.Link, e
|
|||
}
|
||||
var resp Pan123DownResp
|
||||
var headers map[string]string
|
||||
if args.IP != "" && args.IP != "::1" {
|
||||
if !utils.IsLocalIPAddr(args.IP) {
|
||||
headers = map[string]string{
|
||||
//"X-Real-IP": "1.1.1.1",
|
||||
"X-Forwarded-For": args.IP,
|
||||
|
|
|
@ -17,6 +17,7 @@ import (
|
|||
"io"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
|
@ -105,6 +106,9 @@ func (driver Cloud189) Login(account *model.Account) error {
|
|||
client.SetRetryCount(3)
|
||||
client.SetHeader("Referer", "https://cloud.189.cn/")
|
||||
}
|
||||
// clear cookie
|
||||
jar, _ := cookiejar.New(nil)
|
||||
client.SetCookieJar(jar)
|
||||
url := "https://cloud.189.cn/api/portal/loginUrl.action?redirectURL=https%3A%2F%2Fcloud.189.cn%2Fmain.action"
|
||||
b := ""
|
||||
lt := ""
|
||||
|
|
|
@ -16,7 +16,6 @@ import (
|
|||
"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"
|
||||
)
|
||||
|
||||
|
@ -110,7 +109,7 @@ func (s *State) login(account *model.Account) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
toUrl := jsoniter.Get(res.Body(), "toUrl").ToString()
|
||||
toUrl := utils.Json.Get(res.Body(), "toUrl").ToString()
|
||||
if toUrl == "" {
|
||||
log.Error(res.String())
|
||||
return fmt.Errorf(res.String())
|
||||
|
@ -179,10 +178,10 @@ func (s *State) getLoginParam() (*LoginParam, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if jsoniter.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||
return nil, errors.New("ocr error:" + jsoniter.Get(vRes.Body(), "msg").ToString())
|
||||
if utils.Json.Get(vRes.Body(), "status").ToInt() != 200 {
|
||||
return nil, errors.New("ocr error:" + utils.Json.Get(vRes.Body(), "msg").ToString())
|
||||
}
|
||||
param.vCodeRS = jsoniter.Get(vRes.Body(), "result").ToString()
|
||||
param.vCodeRS = utils.Json.Get(vRes.Body(), "result").ToString()
|
||||
log.Debugln("code: ", param.vCodeRS)
|
||||
}
|
||||
return param, nil
|
||||
|
@ -308,9 +307,9 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba
|
|||
}
|
||||
|
||||
if account != nil {
|
||||
switch jsoniter.Get(res.Body(), "res_code").ToInt64() {
|
||||
switch utils.Json.Get(res.Body(), "res_code").ToInt64() {
|
||||
case 11, 18:
|
||||
if err := s.refreshSession(account); err != nil {
|
||||
if err := s.RefreshSession(account); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.Request(method, fullUrl, params, callback, account)
|
||||
|
@ -324,8 +323,8 @@ func (s *State) Request(method string, fullUrl string, params url.Values, callba
|
|||
}
|
||||
}
|
||||
|
||||
if jsoniter.Get(res.Body(), "res_code").ToInt64() != 0 {
|
||||
return res, fmt.Errorf(jsoniter.Get(res.Body(), "res_message").ToString())
|
||||
if utils.Json.Get(res.Body(), "res_code").ToInt64() != 0 {
|
||||
return res, fmt.Errorf(utils.Json.Get(res.Body(), "res_message").ToString())
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package _189
|
|||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
|
@ -67,6 +66,7 @@ func (driver Cloud189) Items() []base.Item {
|
|||
Name: "site_id",
|
||||
Label: "family id",
|
||||
Type: base.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "order_by",
|
||||
|
@ -96,13 +96,41 @@ func (driver Cloud189) Save(account *model.Account, old *model.Account) error {
|
|||
|
||||
state := GetState(account)
|
||||
if !state.IsLogin() {
|
||||
return state.Login(account)
|
||||
if err := state.Login(account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if isFamily(account) {
|
||||
list, err := driver.getFamilyInfoList(account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, l := range list {
|
||||
if account.SiteId == "" {
|
||||
account.SiteId = fmt.Sprint(l.FamilyID)
|
||||
}
|
||||
log.Infof("天翼家庭云 用户名:%s FamilyID %d\n", l.RemarkName, l.FamilyID)
|
||||
}
|
||||
}
|
||||
|
||||
account.Status = "work"
|
||||
model.SaveAccount(account)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver Cloud189) getFamilyInfoList(account *model.Account) ([]FamilyInfoResp, error) {
|
||||
var resp FamilyInfoListResp
|
||||
_, err := GetState(account).Request("GET", API_URL+"/family/manage/getFamilyList.action", nil, func(r *resty.Request) {
|
||||
r.SetQueryParams(clientSuffix())
|
||||
r.SetResult(&resp)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.FamilyInfoResp, nil
|
||||
}
|
||||
|
||||
func (driver Cloud189) File(path string, account *model.Account) (*model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
if path == "/" {
|
||||
|
@ -329,7 +357,7 @@ func (driver Cloud189) Move(src string, dst string, account *model.Account) erro
|
|||
_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
|
||||
r.SetFormData(clientSuffix()).SetFormData(map[string]string{
|
||||
"type": "MOVE",
|
||||
"taskInfos": string(MustToBytes(json.Marshal(
|
||||
"taskInfos": string(MustToBytes(utils.Json.Marshal(
|
||||
[]*BatchTaskInfo{
|
||||
{
|
||||
FileId: srcFile.Id,
|
||||
|
@ -445,7 +473,7 @@ func (driver Cloud189) Copy(src string, dst string, account *model.Account) erro
|
|||
_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
|
||||
r.SetFormData(clientSuffix()).SetFormData(map[string]string{
|
||||
"type": "COPY",
|
||||
"taskInfos": string(MustToBytes(json.Marshal(
|
||||
"taskInfos": string(MustToBytes(utils.Json.Marshal(
|
||||
[]*BatchTaskInfo{
|
||||
{
|
||||
FileId: srcFile.Id,
|
||||
|
@ -475,7 +503,7 @@ func (driver Cloud189) Delete(path string, account *model.Account) error {
|
|||
_, err = GetState(account).Request("POST", API_URL+"/batch/createBatchTask.action", nil, func(r *resty.Request) {
|
||||
r.SetFormData(clientSuffix()).SetFormData(map[string]string{
|
||||
"type": "DELETE",
|
||||
"taskInfos": string(MustToBytes(json.Marshal(
|
||||
"taskInfos": string(MustToBytes(utils.Json.Marshal(
|
||||
[]*BatchTaskInfo{
|
||||
{
|
||||
FileId: srcFile.Id,
|
||||
|
@ -546,7 +574,7 @@ func (driver Cloud189) uploadFamily(file *model.FileStream, parentFile *model.Fi
|
|||
}
|
||||
|
||||
if createUpload.FileDataExists != 1 {
|
||||
if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -601,7 +629,7 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
|
|||
}
|
||||
|
||||
if createUpload.FileDataExists != 1 {
|
||||
if err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
if createUpload.UploadFileId, err = driver.uploadFileData(file, tempFile, createUpload, account); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -618,40 +646,39 @@ func (driver Cloud189) uploadPerson(file *model.FileStream, parentFile *model.Fi
|
|||
return err
|
||||
}
|
||||
|
||||
func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) error {
|
||||
var uploadFileState *UploadFileStatusResult
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
if uploadFileState, err = driver.getUploadFileState(createUpload.UploadFileId, account); err != nil {
|
||||
return err
|
||||
func (driver Cloud189) uploadFileData(file *model.FileStream, tempFile *os.File, createUpload CreateUploadFileResult, account *model.Account) (int64, error) {
|
||||
uploadFileState, err := driver.getUploadFileState(createUpload.UploadFileId, account)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if uploadFileState.FileDataExists == 1 || uploadFileState.DataSize == int64(file.Size) {
|
||||
return nil
|
||||
return uploadFileState.UploadFileId, nil
|
||||
}
|
||||
|
||||
if _, err = tempFile.Seek(uploadFileState.DataSize, io.SeekStart); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
_, err = GetState(account).Request("PUT", uploadFileState.FileUploadUrl, nil, func(r *resty.Request) {
|
||||
r.SetQueryParams(clientSuffix())
|
||||
r.SetHeaders(map[string]string{
|
||||
"Content-Type": "application/octet-stream",
|
||||
"ResumePolicy": "1",
|
||||
"Edrive-UploadFileId": fmt.Sprint(createUpload.UploadFileId),
|
||||
"Edrive-UploadFileRange": fmt.Sprintf("bytes=%d-%d", uploadFileState.DataSize, file.Size),
|
||||
"Expect": "100-continue",
|
||||
})
|
||||
if isFamily(account) {
|
||||
r.SetHeader("FamilyId", account.SiteId)
|
||||
r.SetHeaders(map[string]string{
|
||||
"familyId": account.SiteId,
|
||||
"UploadFileId": fmt.Sprint(uploadFileState.UploadFileId),
|
||||
})
|
||||
} else {
|
||||
r.SetHeader("Edrive-UploadFileId", fmt.Sprint(uploadFileState.UploadFileId))
|
||||
}
|
||||
r.SetBody(tempFile)
|
||||
}, account)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
return uploadFileState.UploadFileId, err
|
||||
}
|
||||
|
||||
func (driver Cloud189) getUploadFileState(uploadFileId int64, account *model.Account) (*UploadFileStatusResult, error) {
|
||||
|
|
|
@ -60,6 +60,19 @@ type appSessionResp struct {
|
|||
RefreshToken string `json:"refreshToken"`
|
||||
}
|
||||
|
||||
type FamilyInfoListResp struct {
|
||||
FamilyInfoResp []FamilyInfoResp `json:"familyInfoResp"`
|
||||
}
|
||||
type FamilyInfoResp struct {
|
||||
Count int `json:"count"`
|
||||
CreateTime string `json:"createTime"`
|
||||
FamilyID int `json:"familyId"`
|
||||
RemarkName string `json:"remarkName"`
|
||||
Type int `json:"type"`
|
||||
UseFlag int `json:"useFlag"`
|
||||
UserRole int `json:"userRole"`
|
||||
}
|
||||
|
||||
/*文件部分*/
|
||||
// 文件
|
||||
type Cloud189File struct {
|
||||
|
|
|
@ -412,7 +412,7 @@ func (driver AliDrive) Upload(file *model.FileStream, account *model.Account) er
|
|||
buf := make([]byte, 1024)
|
||||
n, _ := file.Read(buf[:])
|
||||
reqBody := base.Json{
|
||||
"check_name_mode": "auto_rename",
|
||||
"check_name_mode": "overwrite",
|
||||
"drive_id": account.DriveId,
|
||||
"name": file.GetFileName(),
|
||||
"parent_file_id": parentFile.Id,
|
||||
|
|
|
@ -76,6 +76,9 @@ func (driver Baidu) Items() []base.Item {
|
|||
}
|
||||
|
||||
func (driver Baidu) Save(account *model.Account, old *model.Account) error {
|
||||
if account == nil {
|
||||
return nil
|
||||
}
|
||||
return driver.RefreshToken(account)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||
"github.com/go-resty/resty/v2"
|
||||
|
||||
"github.com/Xhofe/alist/conf"
|
||||
"github.com/Xhofe/alist/drivers/base"
|
||||
|
@ -95,12 +95,13 @@ func (driver XunLeiCloud) File(path string, account *model.Account) (*model.File
|
|||
}
|
||||
|
||||
func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.File, error) {
|
||||
path = utils.ParsePath(path)
|
||||
cache, err := base.GetCache(path, account)
|
||||
if err == nil {
|
||||
files, _ := cache.([]model.File)
|
||||
return files, nil
|
||||
}
|
||||
file, err := driver.File(utils.ParsePath(path), account)
|
||||
file, err := driver.File(path, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -108,8 +109,16 @@ func (driver XunLeiCloud) Files(path string, account *model.Account) ([]model.Fi
|
|||
files := make([]model.File, 0)
|
||||
for {
|
||||
var fileList FileList
|
||||
u := fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files?parent_id=%s&page_token=%s&with_audit=true&filters=%s", file.Id, fileList.NextPageToken, url.QueryEscape(`{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`))
|
||||
if err = GetState(account).Request("GET", u, nil, &fileList, account); err != nil {
|
||||
_, err = GetState(account).Request("GET", FILE_API_URL, func(r *resty.Request) {
|
||||
r.SetQueryParams(map[string]string{
|
||||
"parent_id": file.Id,
|
||||
"page_token": fileList.NextPageToken,
|
||||
"with_audit": "true",
|
||||
"filters": `{"phase": {"eq": "PHASE_TYPE_COMPLETE"}, "trashed":{"eq":false}}`,
|
||||
})
|
||||
r.SetResult(&fileList)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, file := range fileList.Files {
|
||||
|
@ -153,7 +162,12 @@ func (driver XunLeiCloud) Link(args base.Args, account *model.Account) (*base.Li
|
|||
return nil, base.ErrNotFile
|
||||
}
|
||||
var lFile Files
|
||||
if err = GetState(account).Request("GET", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s?&with_audit=true", file.Id), nil, &lFile, account); err != nil {
|
||||
_, err = GetState(account).Request("GET", FILE_API_URL+"/{id}", func(r *resty.Request) {
|
||||
r.SetPathParam("id", file.Id)
|
||||
r.SetQueryParam("with_audit", "true")
|
||||
r.SetResult(&lFile)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &base.Link{
|
||||
|
@ -194,7 +208,14 @@ func (driver XunLeiCloud) MakeDir(path string, account *model.Account) error {
|
|||
if !parentFile.IsDir() {
|
||||
return base.ErrNotFolder
|
||||
}
|
||||
return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{"kind": FOLDER, "name": name, "parent_id": parentFile.Id}, nil, account)
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"kind": FOLDER,
|
||||
"name": name,
|
||||
"parent_id": parentFile.Id,
|
||||
})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) error {
|
||||
|
@ -207,7 +228,14 @@ func (driver XunLeiCloud) Move(src string, dst string, account *model.Account) e
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchMove", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account)
|
||||
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL+":batchMove", func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"to": base.Json{"parent_id": dstDirFile.Id},
|
||||
"ids": []string{srcFile.Id},
|
||||
})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) error {
|
||||
|
@ -220,7 +248,13 @@ func (driver XunLeiCloud) Copy(src string, dst string, account *model.Account) e
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files:batchCopy", &base.Json{"to": base.Json{"parent_id": dstDirFile.Id}, "ids": []string{srcFile.Id}}, nil, account)
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL+":batchCopy", func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"to": base.Json{"parent_id": dstDirFile.Id},
|
||||
"ids": []string{srcFile.Id},
|
||||
})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
|
||||
|
@ -228,7 +262,11 @@ func (driver XunLeiCloud) Delete(path string, account *model.Account) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s/trash", srcFile.Id), &base.Json{}, nil, account)
|
||||
_, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}/trash", func(r *resty.Request) {
|
||||
r.SetPathParam("id", srcFile.Id)
|
||||
r.SetBody(&base.Json{})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account) error {
|
||||
|
@ -246,7 +284,6 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
|
|||
return err
|
||||
}
|
||||
|
||||
defer tempFile.Close()
|
||||
defer os.Remove(tempFile.Name())
|
||||
|
||||
gcid, err := getGcid(io.TeeReader(file, tempFile), int64(file.Size))
|
||||
|
@ -254,21 +291,27 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
|
|||
return err
|
||||
}
|
||||
|
||||
var rep UploadTaskResponse
|
||||
err = GetState(account).Request("POST", "https://api-pan.xunlei.com/drive/v1/files", &base.Json{
|
||||
tempFile.Close()
|
||||
|
||||
var resp UploadTaskResponse
|
||||
_, err = GetState(account).Request("POST", FILE_API_URL, func(r *resty.Request) {
|
||||
r.SetBody(&base.Json{
|
||||
"kind": FILE,
|
||||
"parent_id": parentFile.Id,
|
||||
"name": file.Name,
|
||||
"size": fmt.Sprint(file.Size),
|
||||
"hash": gcid,
|
||||
"upload_type": UPLOAD_TYPE_RESUMABLE,
|
||||
}, &rep, account)
|
||||
})
|
||||
r.SetResult(&resp)
|
||||
}, account)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
param := rep.Resumable.Params
|
||||
client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true), oss.HTTPClient(xunleiClient.GetClient()))
|
||||
param := resp.Resumable.Params
|
||||
if resp.UploadType == UPLOAD_TYPE_RESUMABLE {
|
||||
client, err := oss.New(param.Endpoint, param.AccessKeyID, param.AccessKeySecret, oss.SecurityToken(param.SecurityToken), oss.EnableMD5(true))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -276,7 +319,9 @@ func (driver XunLeiCloud) Upload(file *model.FileStream, account *model.Account)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return bucket.UploadFile(param.Key, tempFile.Name(), 4*1024*1024, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration))
|
||||
return bucket.UploadFile(param.Key, tempFile.Name(), 1<<22, oss.Routines(3), oss.Checkpoint(true, ""), oss.Expires(param.Expiration))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account) error {
|
||||
|
@ -285,8 +330,11 @@ func (driver XunLeiCloud) Rename(src string, dst string, account *model.Account)
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return GetState(account).Request("PATCH", fmt.Sprintf("https://api-pan.xunlei.com/drive/v1/files/%s", srcFile.Id), &base.Json{"name": dstName}, nil, account)
|
||||
_, err = GetState(account).Request("PATCH", FILE_API_URL+"/{id}", func(r *resty.Request) {
|
||||
r.SetPathParam("id", srcFile.Id)
|
||||
r.SetBody(&base.Json{"name": dstName})
|
||||
}, account)
|
||||
return err
|
||||
}
|
||||
|
||||
var _ base.Driver = (*XunLeiCloud)(nil)
|
||||
|
|
|
@ -37,6 +37,12 @@ var Algorithms = []string{
|
|||
"T78dnANexYRbiZy",
|
||||
}
|
||||
|
||||
const (
|
||||
API_URL = "https://api-pan.xunlei.com/drive/v1"
|
||||
FILE_API_URL = API_URL + "/files"
|
||||
XLUSER_API_URL = "https://xluser-ssl.xunlei.com/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
FOLDER = "drive#folder"
|
||||
FILE = "drive#file"
|
||||
|
|
|
@ -2,6 +2,7 @@ package xunlei
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var xunleiClient = resty.New().SetTimeout(120 * time.Second)
|
||||
var xunleiClient = resty.New().SetHeaders(map[string]string{"Accept": "application/json;charset=UTF-8"})
|
||||
|
||||
// 一个账户只允许登陆一次
|
||||
var userStateCache = struct {
|
||||
|
@ -102,16 +103,16 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account *
|
|||
var e Erron
|
||||
var resp CaptchaTokenResponse
|
||||
_, err := xunleiClient.R().
|
||||
SetHeader("X-Device-Id", driverID).
|
||||
SetBody(&creq).
|
||||
SetError(&e).
|
||||
SetResult(&resp).
|
||||
Post("https://xluser-ssl.xunlei.com/v1/shield/captcha/init?client_id=" + CLIENT_ID)
|
||||
SetHeader("X-Device-Id", driverID).
|
||||
SetQueryParam("client_id", CLIENT_ID).
|
||||
Post(XLUSER_API_URL + "/shield/captcha/init")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if e.ErrorCode != 0 {
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return "", fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
if resp.Url != "" {
|
||||
|
@ -120,7 +121,6 @@ func (s *State) newCaptchaToken(action string, meta map[string]string, account *
|
|||
|
||||
s.captchaTokenExpiresTime = (ctime + resp.ExpiresIn*1000) - 30000
|
||||
s.captchaToken = resp.CaptchaToken
|
||||
log.Debugf("%+v\n %+v", s.captchaToken, account)
|
||||
return s.captchaToken, nil
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ func (s *State) refreshToken_(account *model.Account) error {
|
|||
"client_secret": CLIENT_SECRET,
|
||||
}).
|
||||
SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).SetQueryParam("client_id", CLIENT_ID).
|
||||
Post("https://xluser-ssl.xunlei.com/v1/auth/token")
|
||||
Post(XLUSER_API_URL + "/auth/token")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -152,7 +152,6 @@ func (s *State) refreshToken_(account *model.Account) error {
|
|||
s.userID = resp.UserID
|
||||
return nil
|
||||
default:
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +159,7 @@ func (s *State) refreshToken_(account *model.Account) error {
|
|||
func (s *State) login(account *model.Account) error {
|
||||
s.init()
|
||||
ctime := time.Now().UnixMilli()
|
||||
url := "https://xluser-ssl.xunlei.com/v1/auth/signin"
|
||||
url := XLUSER_API_URL + "/auth/signin"
|
||||
captchaToken, err := s.newCaptchaToken(getAction("POST", url), map[string]string{"username": account.Username}, account)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -190,7 +189,6 @@ func (s *State) login(account *model.Account) error {
|
|||
defer model.SaveAccount(account)
|
||||
if e.ErrorCode != 0 {
|
||||
account.Status = e.Error
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
account.Status = "work"
|
||||
|
@ -199,67 +197,68 @@ func (s *State) login(account *model.Account) error {
|
|||
s.accessToken = resp.AccessToken
|
||||
s.refreshToken = resp.RefreshToken
|
||||
s.userID = resp.UserID
|
||||
log.Debugf("%+v\n %+v", resp, account)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *State) Request(method string, url string, body interface{}, resp interface{}, account *model.Account) error {
|
||||
func (s *State) Request(method string, url string, callback func(*resty.Request), account *model.Account) (*resty.Response, error) {
|
||||
s.Lock()
|
||||
token, err := s.getToken(account)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
captchaToken, err := s.getCaptchaToken(getAction(method, url), account)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
s.Unlock()
|
||||
var e Erron
|
||||
|
||||
req := xunleiClient.R().
|
||||
SetError(&e).
|
||||
SetHeader("X-Device-Id", utils.GetMD5Encode(account.Username)).
|
||||
SetHeader("Authorization", token).
|
||||
SetHeader("X-Captcha-Token", captchaToken).
|
||||
SetHeaders(map[string]string{
|
||||
"X-Device-Id": utils.GetMD5Encode(account.Username),
|
||||
"Authorization": token,
|
||||
"X-Captcha-Token": captchaToken,
|
||||
}).
|
||||
SetQueryParam("client_id", CLIENT_ID)
|
||||
|
||||
if body != nil {
|
||||
req.SetBody(body)
|
||||
}
|
||||
if resp != nil {
|
||||
req.SetResult(resp)
|
||||
}
|
||||
callback(req)
|
||||
s.Unlock()
|
||||
|
||||
var res *resty.Response
|
||||
switch method {
|
||||
case "GET":
|
||||
_, err = req.Get(url)
|
||||
res, err = req.Get(url)
|
||||
case "POST":
|
||||
_, err = req.Post(url)
|
||||
res, err = req.Post(url)
|
||||
case "DELETE":
|
||||
_, err = req.Delete(url)
|
||||
res, err = req.Delete(url)
|
||||
case "PATCH":
|
||||
_, err = req.Patch(url)
|
||||
res, err = req.Patch(url)
|
||||
case "PUT":
|
||||
_, err = req.Put(url)
|
||||
res, err = req.Put(url)
|
||||
default:
|
||||
return base.ErrNotSupport
|
||||
return nil, base.ErrNotSupport
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
log.Debug(res.String())
|
||||
|
||||
var e Erron
|
||||
utils.Json.Unmarshal(res.Body(), &e)
|
||||
switch e.ErrorCode {
|
||||
case 0:
|
||||
return nil
|
||||
case 9:
|
||||
s.newCaptchaToken(getAction(method, url), nil, account)
|
||||
fallthrough
|
||||
case 4122, 4121:
|
||||
return s.Request(method, url, body, resp, account)
|
||||
return s.Request(method, url, callback, account)
|
||||
case 0:
|
||||
if res.StatusCode() == http.StatusOK {
|
||||
return res, nil
|
||||
}
|
||||
return nil, fmt.Errorf(res.String())
|
||||
default:
|
||||
log.Debugf("%+v\n %+v", e, account)
|
||||
return fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
return nil, fmt.Errorf("%s : %s", e.Error, e.ErrorDescription)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -156,29 +156,27 @@ func (fs *FileSystem) CreateDirectory(ctx context.Context, rawPath string) error
|
|||
return operate.MakeDir(driver, account, path_, true)
|
||||
}
|
||||
|
||||
func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) error {
|
||||
func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath string) (FileInfo, error) {
|
||||
rawPath = utils.ParsePath(rawPath)
|
||||
if model.AccountsCount() > 1 && rawPath == "/" {
|
||||
return ErrNotImplemented
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
account, path_, driver, err := common.ParsePath(rawPath)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
//fileSize, err := strconv.ParseUint(r.Header.Get("Content-Length"), 10, 64)
|
||||
fileSize := uint64(r.ContentLength)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
filePath, fileName := filepath.Split(path_)
|
||||
now := time.Now()
|
||||
if fileSize == 0 {
|
||||
upFileMap[rawPath] = &model.File{
|
||||
fi := &model.File{
|
||||
Name: fileName,
|
||||
Size: 0,
|
||||
UpdatedAt: &now,
|
||||
}
|
||||
return nil
|
||||
if fileSize == 0 {
|
||||
// 如果文件大小为0,默认成功
|
||||
upFileMap[rawPath] = fi
|
||||
return fi, nil
|
||||
} else {
|
||||
delete(upFileMap, rawPath)
|
||||
}
|
||||
|
@ -189,7 +187,7 @@ func (fs *FileSystem) Upload(ctx context.Context, r *http.Request, rawPath strin
|
|||
Name: fileName,
|
||||
ParentPath: filePath,
|
||||
}
|
||||
return operate.Upload(driver, account, &fileData, true)
|
||||
return fi, operate.Upload(driver, account, &fileData, true)
|
||||
}
|
||||
|
||||
func (fs *FileSystem) Delete(rawPath string) error {
|
||||
|
|
|
@ -94,7 +94,6 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
|||
}
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string, status int, err error) {
|
||||
token, err = h.LockSystem.Create(now, LockDetails{
|
||||
Root: root,
|
||||
|
@ -110,7 +109,6 @@ func (h *Handler) lock(now time.Time, root string, fs *FileSystem) (token string
|
|||
return token, 0, nil
|
||||
}
|
||||
|
||||
// ok
|
||||
func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem) (release func(), status int, err error) {
|
||||
hdr := r.Header.Get("If")
|
||||
if hdr == "" {
|
||||
|
@ -189,7 +187,6 @@ func (h *Handler) confirmLocks(r *http.Request, src, dst string, fs *FileSystem)
|
|||
return nil, http.StatusPreconditionFailed, ErrLocked
|
||||
}
|
||||
|
||||
//OK
|
||||
func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
|
@ -213,7 +210,6 @@ func (h *Handler) handleOptions(w http.ResponseWriter, r *http.Request, fs *File
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
|
@ -248,7 +244,6 @@ func (h *Handler) handleGetHeadPost(w http.ResponseWriter, r *http.Request, fs *
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
|
@ -267,7 +262,6 @@ func (h *Handler) handleDelete(w http.ResponseWriter, r *http.Request, fs *FileS
|
|||
return http.StatusNoContent, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
|
@ -283,12 +277,13 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
|||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
err = fs.Upload(ctx, r, reqPath)
|
||||
fi, err := fs.Upload(ctx, r, reqPath)
|
||||
if err != nil {
|
||||
return http.StatusMethodNotAllowed, err
|
||||
}
|
||||
|
||||
_, fi := isPathExist(ctx, fs, reqPath)
|
||||
//_, fi := isPathExist(ctx, fs, reqPath)
|
||||
// 为防止有些网盘上传有延时,这里认为上传没有报错就已经成功了,不再去判断是否存在
|
||||
etag, err := findETag(ctx, fs, h.LockSystem, reqPath, fi)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
|
@ -297,7 +292,6 @@ func (h *Handler) handlePut(w http.ResponseWriter, r *http.Request, fs *FileSyst
|
|||
return http.StatusCreated, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
|
@ -325,7 +319,6 @@ func (h *Handler) handleMkcol(w http.ResponseWriter, r *http.Request, fs *FileSy
|
|||
return http.StatusCreated, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
hdr := r.Header.Get("Destination")
|
||||
|
@ -410,7 +403,6 @@ func (h *Handler) handleCopyMove(w http.ResponseWriter, r *http.Request, fs *Fil
|
|||
return moveFiles(ctx, fs, src, dst, r.Header.Get("Overwrite") == "T")
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (retStatus int, retErr error) {
|
||||
|
||||
duration, err := parseTimeout(r.Header.Get("Timeout"))
|
||||
|
@ -506,7 +498,6 @@ func (h *Handler) handleLock(w http.ResponseWriter, r *http.Request, fs *FileSys
|
|||
return 0, nil
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
|
||||
// http://www.webdav.org/specs/rfc4918.html#HEADER_Lock-Token says that the
|
||||
|
@ -531,7 +522,6 @@ func (h *Handler) handleUnlock(w http.ResponseWriter, r *http.Request, fs *FileS
|
|||
}
|
||||
}
|
||||
|
||||
// OK
|
||||
func (h *Handler) handlePropfind(w http.ResponseWriter, r *http.Request, fs *FileSystem) (status int, err error) {
|
||||
reqPath, status, err := h.stripPrefix(r.URL.Path)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
func IsLocalIPAddr(ip string) bool {
|
||||
return IsLocalIP(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
func IsLocalIP(ip net.IP) bool {
|
||||
if ip == nil {
|
||||
return false
|
||||
}
|
||||
if ip.IsLoopback() {
|
||||
return true
|
||||
}
|
||||
|
||||
ip4 := ip.To4()
|
||||
if ip4 == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return ip4[0] == 10 || // 10.0.0.0/8
|
||||
(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
|
||||
(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
|
||||
(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
|
||||
}
|
Loading…
Reference in New Issue