mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-21 09:19:16 +08:00
505 lines
12 KiB
Go
505 lines
12 KiB
Go
package client
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"strings"
|
|
|
|
"github.com/go-resty/resty/v2"
|
|
)
|
|
|
|
type aliClient struct {
|
|
token string
|
|
driveID string
|
|
}
|
|
|
|
func NewALIClient(vars map[string]interface{}) (*aliClient, error) {
|
|
refresh_token := loadParamFromVars("refresh_token", vars)
|
|
drive_id := loadParamFromVars("drive_id", vars)
|
|
|
|
token, err := loadToken(refresh_token)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &aliClient{token: token, driveID: drive_id}, nil
|
|
}
|
|
|
|
func (a aliClient) ListBuckets() ([]interface{}, error) {
|
|
return nil, nil
|
|
}
|
|
|
|
func (a aliClient) Exist(pathItem string) (bool, error) {
|
|
pathItem = path.Join("root", pathItem)
|
|
if _, err := a.loadFileWithName(pathItem); err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (a aliClient) Size(pathItem string) (int64, error) {
|
|
pathItem = path.Join("root", pathItem)
|
|
fileInfo, err := a.loadFileWithName(pathItem)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return int64(fileInfo.Size), nil
|
|
}
|
|
|
|
func (a aliClient) Delete(pathItem string) (bool, error) {
|
|
pathItem = path.Join("root", pathItem)
|
|
fileInfo, err := a.loadFileWithName(pathItem)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
client := resty.New()
|
|
data := map[string]interface{}{
|
|
"drive_id": a.driveID,
|
|
"file_id": fileInfo.FileID,
|
|
}
|
|
url := "https://api.alipan.com/v2/file/delete"
|
|
resp, err := client.R().
|
|
SetHeader("Authorization", a.token).
|
|
SetBody(data).
|
|
Post(url)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if resp.StatusCode() != 204 {
|
|
return false, fmt.Errorf("delete file %s failed, err: %v", pathItem, string(resp.Body()))
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (a aliClient) Upload(src, target string) (bool, error) {
|
|
target = path.Join("/root", target)
|
|
parentID := "root"
|
|
var err error
|
|
if path.Dir(target) != "/root" {
|
|
parentID, err = a.mkdirWithPath(path.Dir(target))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
file, err := os.Open(src)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
defer file.Close()
|
|
fileInfo, err := file.Stat()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
data := map[string]interface{}{
|
|
"drive_id": a.driveID,
|
|
"part_info_list": makePartInfoList(fileInfo.Size()),
|
|
"parent_file_id": parentID,
|
|
"name": path.Base(src),
|
|
"type": "file",
|
|
"size": fileInfo.Size(),
|
|
"check_name_mode": "auto_rename",
|
|
}
|
|
client := resty.New()
|
|
url := "https://api.alipan.com/v2/file/create"
|
|
|
|
resp, err := client.R().
|
|
SetHeader("Authorization", a.token).
|
|
SetBody(data).
|
|
Post(url)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
var createResp createFileResp
|
|
if err := json.Unmarshal(resp.Body(), &createResp); err != nil {
|
|
return false, err
|
|
}
|
|
for _, part := range createResp.PartInfoList {
|
|
err = a.uploadPart(part.UploadURL, io.LimitReader(file, 1024*1024*1024))
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
|
|
if err := a.completeUpload(createResp.UploadID, createResp.FileID); err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (a aliClient) Download(src, target string) (bool, error) {
|
|
src = path.Join("/root", src)
|
|
fileInfo, err := a.loadFileWithName(src)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
client := resty.New()
|
|
if fileInfo.Size > 100*1024*1024 {
|
|
return false, fmt.Errorf("The translation file %s exceeds 100MB, please download it through the client.", src)
|
|
}
|
|
data := map[string]interface{}{
|
|
"drive_id": a.driveID,
|
|
"file_id": fileInfo.FileID,
|
|
}
|
|
url := "https://api.aliyundrive.com/v2/file/get_download_url"
|
|
resp, err := client.R().
|
|
SetHeader("Authorization", a.token).
|
|
SetBody(data).
|
|
Post(url)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if resp.StatusCode() != 200 {
|
|
return false, fmt.Errorf("download file %s failed, err: %v", src, string(resp.Body()))
|
|
}
|
|
var respItem downloadResp
|
|
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
|
return false, err
|
|
}
|
|
if err := a.handleDownload(respItem.URL, target); err != nil {
|
|
return false, err
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (a *aliClient) ListObjects(src string) ([]string, error) {
|
|
if len(src) == 0 || src == "root" || src == "/root" {
|
|
src = "root"
|
|
} else {
|
|
src = path.Join("/root", src)
|
|
}
|
|
fileInfos, err := a.loadDirWithPath(src)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var names []string
|
|
for _, item := range fileInfos {
|
|
names = append(names, item.Name)
|
|
}
|
|
return names, nil
|
|
}
|
|
|
|
func (a aliClient) loadFileWithName(pathItem string) (fileInfo, error) {
|
|
pathItems := strings.Split(pathItem, "/")
|
|
var (
|
|
fileInfos []fileInfo
|
|
err error
|
|
)
|
|
parentID := "root"
|
|
for i := 0; i < len(pathItems); i++ {
|
|
if len(pathItems[i]) == 0 {
|
|
continue
|
|
}
|
|
fileInfos, err = a.loadFileWithParentID(parentID)
|
|
if err != nil {
|
|
return fileInfo{}, err
|
|
}
|
|
isEnd := false
|
|
if i == len(pathItems)-2 {
|
|
isEnd = true
|
|
}
|
|
exist := false
|
|
for _, item := range fileInfos {
|
|
if item.Name == pathItems[i+1] {
|
|
if isEnd {
|
|
return item, nil
|
|
} else {
|
|
parentID = item.FileID
|
|
exist = true
|
|
}
|
|
}
|
|
}
|
|
if !exist {
|
|
return fileInfo{}, errors.New("no such file or dir")
|
|
}
|
|
|
|
}
|
|
return fileInfo{}, errors.New("no such file or dir")
|
|
}
|
|
|
|
func (a aliClient) loadDirWithPath(path string) ([]fileInfo, error) {
|
|
pathItems := strings.Split(path, "/")
|
|
var (
|
|
fileInfos []fileInfo
|
|
err error
|
|
)
|
|
parentID := "root"
|
|
for i := 0; i < len(pathItems); i++ {
|
|
if len(pathItems[i]) == 0 {
|
|
continue
|
|
}
|
|
fileInfos, err = a.loadFileWithParentID(parentID)
|
|
if err != nil {
|
|
return fileInfos, err
|
|
}
|
|
if i == len(pathItems)-1 {
|
|
return fileInfos, nil
|
|
}
|
|
exist := false
|
|
for _, item := range fileInfos {
|
|
if item.Name == pathItems[i+1] {
|
|
parentID = item.FileID
|
|
exist = true
|
|
}
|
|
}
|
|
if !exist {
|
|
return nil, errors.New("no such file or dir")
|
|
}
|
|
}
|
|
return fileInfos, errors.New("no such file or dir")
|
|
}
|
|
|
|
func (a aliClient) loadFileWithParentID(parentID string) ([]fileInfo, error) {
|
|
client := resty.New()
|
|
data := map[string]interface{}{
|
|
"drive_id": a.driveID,
|
|
"fields": "*",
|
|
"limit": 100,
|
|
"parent_file_id": parentID,
|
|
}
|
|
url := "https://api.aliyundrive.com/adrive/v3/file/list"
|
|
resp, err := client.R().
|
|
SetHeader("Authorization", a.token).
|
|
SetBody(data).
|
|
Post(url)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode() != 200 {
|
|
return nil, fmt.Errorf("load file list failed, code: %v, err: %v", resp.StatusCode(), string(resp.Body()))
|
|
}
|
|
var fileResp fileResp
|
|
if err := json.Unmarshal(resp.Body(), &fileResp); err != nil {
|
|
return nil, err
|
|
}
|
|
return fileResp.Items, nil
|
|
}
|
|
|
|
func (a aliClient) mkdirWithPath(target string) (string, error) {
|
|
pathItems := strings.Split(target, "/")
|
|
var (
|
|
fileInfos []fileInfo
|
|
err error
|
|
)
|
|
parentID := "root"
|
|
for i := 0; i < len(pathItems); i++ {
|
|
if len(pathItems[i]) == 0 {
|
|
continue
|
|
}
|
|
fileInfos, err = a.loadFileWithParentID(parentID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
isEnd := false
|
|
if i == len(pathItems)-2 {
|
|
isEnd = true
|
|
}
|
|
exist := false
|
|
for _, item := range fileInfos {
|
|
if item.Name == pathItems[i+1] {
|
|
parentID = item.FileID
|
|
if isEnd {
|
|
return item.FileID, nil
|
|
} else {
|
|
exist = true
|
|
}
|
|
}
|
|
}
|
|
if !exist {
|
|
parentID, err = a.mkdir(parentID, pathItems[i+1])
|
|
if err != nil {
|
|
return parentID, err
|
|
}
|
|
if isEnd {
|
|
return parentID, nil
|
|
}
|
|
}
|
|
}
|
|
return "", errors.New("mkdir failed.")
|
|
}
|
|
|
|
func (a aliClient) mkdir(parentID, name string) (string, error) {
|
|
client := resty.New()
|
|
data := map[string]interface{}{
|
|
"drive_id": a.driveID,
|
|
"name": name,
|
|
"type": "folder",
|
|
"limit": 100,
|
|
"parent_file_id": parentID,
|
|
}
|
|
url := "https://api.aliyundrive.com/adrive/v2/file/createWithFolders"
|
|
resp, err := client.R().
|
|
SetHeader("Authorization", a.token).
|
|
SetBody(data).
|
|
Post(url)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if resp.StatusCode() != 201 {
|
|
return "", fmt.Errorf("mkdir %s failed, code: %v, err: %v", name, resp.StatusCode(), string(resp.Body()))
|
|
}
|
|
var mkdirResp mkdirResp
|
|
if err := json.Unmarshal(resp.Body(), &mkdirResp); err != nil {
|
|
return "", err
|
|
}
|
|
return mkdirResp.FileID, nil
|
|
}
|
|
|
|
type fileResp struct {
|
|
Items []fileInfo `json:"items"`
|
|
}
|
|
type fileInfo struct {
|
|
FileID string `json:"file_id"`
|
|
Name string `json:"name"`
|
|
Size int `json:"size"`
|
|
}
|
|
|
|
type mkdirResp struct {
|
|
FileID string `json:"file_id"`
|
|
}
|
|
|
|
type partInfo struct {
|
|
PartNumber int `json:"part_number"`
|
|
UploadURL string `json:"upload_url"`
|
|
InternalUploadURL string `json:"internal_upload_url"`
|
|
ContentType string `json:"content_type"`
|
|
}
|
|
|
|
func makePartInfoList(size int64) []*partInfo {
|
|
var res []*partInfo
|
|
maxPartSize := int64(1024 * 1024 * 1024)
|
|
partInfoNum := int(size / maxPartSize)
|
|
if size%maxPartSize > 0 {
|
|
partInfoNum += 1
|
|
}
|
|
|
|
for i := 0; i < partInfoNum; i++ {
|
|
res = append(res, &partInfo{PartNumber: i + 1})
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
type createFileResp struct {
|
|
Type string `json:"type"`
|
|
RapidUpload bool `json:"rapid_upload"`
|
|
DomainId string `json:"domain_id"`
|
|
DriveId string `json:"drive_id"`
|
|
FileName string `json:"file_name"`
|
|
EncryptMode string `json:"encrypt_mode"`
|
|
Location string `json:"location"`
|
|
UploadID string `json:"upload_id"`
|
|
FileID string `json:"file_id"`
|
|
PartInfoList []*partInfo `json:"part_info_list,omitempty"`
|
|
}
|
|
|
|
func (a aliClient) uploadPart(uri string, reader io.Reader) error {
|
|
req, err := http.NewRequest(http.MethodPut, uri, reader)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
client := &http.Client{}
|
|
response, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer response.Body.Close()
|
|
if response.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("handle upload park file with url failed, code: %v", response.StatusCode)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type downloadResp struct {
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
func (a aliClient) handleDownload(uri string, target string) error {
|
|
req, err := http.NewRequest(http.MethodGet, uri, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req.Header.Add("Authorization", a.token)
|
|
req.Header.Add("origin", "https://www.aliyundrive.com")
|
|
req.Header.Add("referer", "https://www.aliyundrive.com/")
|
|
client := &http.Client{}
|
|
response, err := client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer response.Body.Close()
|
|
if response.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("handle download with url failed, code: %v", response.StatusCode)
|
|
}
|
|
if _, err := os.Stat(path.Dir(target)); err != nil {
|
|
_ = os.MkdirAll(path.Dir(target), os.ModePerm)
|
|
}
|
|
out, err := os.Create(target)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
if _, err = io.Copy(out, response.Body); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *aliClient) completeUpload(uploadID, fileID string) error {
|
|
client := resty.New()
|
|
data := map[string]interface{}{
|
|
"drive_id": a.driveID,
|
|
"upload_id": uploadID,
|
|
"file_id": fileID,
|
|
}
|
|
|
|
url := "https://api.aliyundrive.com/v2/file/complete"
|
|
resp, err := client.R().
|
|
SetHeader("Authorization", a.token).
|
|
SetBody(data).
|
|
Post(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if resp.StatusCode() != 200 {
|
|
return fmt.Errorf("complete upload failed, err: %v", string(resp.Body()))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type tokenResp struct {
|
|
AccessToken string `json:"access_token"`
|
|
}
|
|
|
|
func loadToken(refresh_token string) (string, error) {
|
|
client := resty.New()
|
|
data := map[string]interface{}{
|
|
"grant_type": "refresh_token",
|
|
"refresh_token": refresh_token,
|
|
}
|
|
|
|
url := "https://api.aliyundrive.com/token/refresh"
|
|
resp, err := client.R().
|
|
SetBody(data).
|
|
Post(url)
|
|
|
|
if err != nil {
|
|
return "", fmt.Errorf("load account token failed, err: %v", err)
|
|
}
|
|
if resp.StatusCode() != 200 {
|
|
return "", fmt.Errorf("load account token failed, code: %v", resp.StatusCode())
|
|
}
|
|
var respItem tokenResp
|
|
if err := json.Unmarshal(resp.Body(), &respItem); err != nil {
|
|
return "", err
|
|
}
|
|
return respItem.AccessToken, nil
|
|
}
|