From eb21b87020a07fb4dac6f248f9cb2479fbf3be9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=AE=E5=87=89?= <927625802@qq.com> Date: Sat, 27 Nov 2021 19:52:38 +0800 Subject: [PATCH] :hammer: refactor driver: google --- bootstrap/drivers.go | 1 + drivers/googledrive.go | 298 ----------------------------- drivers/googledrive/driver.go | 159 +++++++++++++++ drivers/googledrive/googledrive.go | 160 ++++++++++++++++ 4 files changed, 320 insertions(+), 298 deletions(-) delete mode 100644 drivers/googledrive.go create mode 100644 drivers/googledrive/driver.go create mode 100644 drivers/googledrive/googledrive.go diff --git a/bootstrap/drivers.go b/bootstrap/drivers.go index aa00cd5c..e09f9016 100644 --- a/bootstrap/drivers.go +++ b/bootstrap/drivers.go @@ -4,4 +4,5 @@ import ( _ "github.com/Xhofe/alist/drivers/123pan" _ "github.com/Xhofe/alist/drivers/189cloud" _ "github.com/Xhofe/alist/drivers/alidrive" + _ "github.com/Xhofe/alist/drivers/googledrive" ) \ No newline at end of file diff --git a/drivers/googledrive.go b/drivers/googledrive.go deleted file mode 100644 index cfa5201d..00000000 --- a/drivers/googledrive.go +++ /dev/null @@ -1,298 +0,0 @@ -package drivers - -import ( - "fmt" - "github.com/Xhofe/alist/conf" - "github.com/Xhofe/alist/model" - "github.com/Xhofe/alist/utils" - "github.com/gin-gonic/gin" - "github.com/go-resty/resty/v2" - log "github.com/sirupsen/logrus" - "path/filepath" - "strconv" - "time" -) - -type GoogleDrive struct { -} - -func (g GoogleDrive) File(path string, account *model.Account) (*model.File, error) { - panic("implement me") -} - -func (g GoogleDrive) Files(path string, account *model.Account) ([]model.File, error) { - panic("implement me") -} - -var googleClient = resty.New() - -func (g GoogleDrive) Items() []Item { - return []Item{ - { - Name: "client_id", - Label: "client id", - Type: "string", - Required: true, - }, - { - Name: "client_secret", - Label: "client secret", - Type: "string", - Required: true, - }, - { - Name: "refresh_token", - Label: "refresh token", - Type: "string", - Required: true, - }, - { - Name: "root_folder", - Label: "root folder file_id", - Type: "string", - Required: true, - }, - } -} - -type GoogleTokenError struct { - Error string `json:"error"` - ErrorDescription string `json:"error_description"` -} - -func (g GoogleDrive) RefreshToken(account *model.Account) error { - url := "https://www.googleapis.com/oauth2/v4/token" - var resp TokenResp - var e GoogleTokenError - _, err := googleClient.R().SetResult(&resp).SetError(&e). - SetFormData(map[string]string{ - "client_id": account.ClientId, - "client_secret": account.ClientSecret, - "refresh_token": account.RefreshToken, - "grant_type": "refresh_token", - }).Post(url) - if err != nil { - return err - } - if e.Error != "" { - return fmt.Errorf(e.Error) - } - account.AccessToken = resp.AccessToken - account.Status = "work" - return nil -} - -func (g GoogleDrive) Save(account *model.Account, old *model.Account) error { - account.Proxy = true - err := g.RefreshToken(account) - if err != nil { - account.Status = err.Error() - _ = model.SaveAccount(account) - return err - } - account.Status = "work" - _ = model.SaveAccount(account) - return nil -} - -type GoogleFile struct { - Id string `json:"id"` - Name string `json:"name"` - MimeType string `json:"mimeType"` - ModifiedTime *time.Time `json:"modifiedTime"` - Size string `json:"size"` -} - -func (g GoogleDrive) IsDir(mimeType string) bool { - return mimeType == "application/vnd.google-apps.folder" || mimeType == "application/vnd.google-apps.shortcut" -} - -func (g GoogleDrive) FormatFile(file *GoogleFile) *model.File { - f := &model.File{ - Name: file.Name, - Driver: "GoogleDrive", - UpdatedAt: file.ModifiedTime, - Thumbnail: "", - Url: "", - } - if g.IsDir(file.MimeType) { - f.Type = conf.FOLDER - } else { - size, _ := strconv.ParseInt(file.Size, 10, 64) - f.Size = size - f.Type = utils.GetFileType(filepath.Ext(file.Name)) - } - return f -} - -type GoogleFiles struct { - NextPageToken string `json:"nextPageToken"` - Files []GoogleFile `json:"files"` -} - -type GoogleError struct { - Error struct { - Errors []struct { - Domain string `json:"domain"` - Reason string `json:"reason"` - Message string `json:"message"` - LocationType string `json:"location_type"` - Location string `json:"location"` - } - Code int `json:"code"` - Message string `json:"message"` - } `json:"error"` -} - -func (g GoogleDrive) GetFiles(id string, account *model.Account) ([]GoogleFile, error) { - pageToken := "first" - res := make([]GoogleFile, 0) - for pageToken != "" { - if pageToken == "first" { - pageToken = "" - } - var resp GoogleFiles - var e GoogleError - _, err := googleClient.R().SetResult(&resp).SetError(&e). - SetHeader("Authorization", "Bearer "+account.AccessToken). - SetQueryParams(map[string]string{ - "orderBy": "folder,name,modifiedTime desc", - "fields": "files(id,name,mimeType,size,modifiedTime),nextPageToken", - "pageSize": "1000", - "q": fmt.Sprintf("'%s' in parents and trashed = false", id), - "includeItemsFromAllDrives": "true", - "supportsAllDrives": "true", - "pageToken": pageToken, - }).Get("https://www.googleapis.com/drive/v3/files") - if err != nil { - return nil, err - } - if e.Error.Code != 0 { - if e.Error.Code == 401 { - err = g.RefreshToken(account) - if err != nil { - _ = model.SaveAccount(account) - return nil, err - } - return g.GetFiles(id, account) - } - return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) - } - pageToken = resp.NextPageToken - res = append(res, resp.Files...) - } - return res, nil -} - -func (g GoogleDrive) GetFile(path string, account *model.Account) (*GoogleFile, error) { - dir, name := filepath.Split(path) - dir = utils.ParsePath(dir) - _, _, err := g.Path(dir, account) - if err != nil { - return nil, err - } - parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir)) - parentFiles, _ := parentFiles_.([]GoogleFile) - for _, file := range parentFiles { - if file.Name == name { - if !g.IsDir(file.MimeType) { - return &file, err - } else { - return nil, fmt.Errorf("not file") - } - } - } - return nil, fmt.Errorf("path not found") -} - -func (g GoogleDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) { - path = utils.ParsePath(path) - log.Debugf("google path: %s", path) - cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path)) - if err == nil { - files, _ := cache.([]GoogleFile) - if len(files) != 0 { - res := make([]model.File, 0) - for _, file := range files { - res = append(res, *g.FormatFile(&file)) - } - return nil, res, nil - } - } - // no cache or len(files) == 0 - fileId := account.RootFolder - if path != "/" { - dir, name := filepath.Split(path) - dir = utils.ParsePath(dir) - _, _, err = g.Path(dir, account) - if err != nil { - return nil, nil, err - } - parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir)) - parentFiles, _ := parentFiles_.([]GoogleFile) - found := false - for _, file := range parentFiles { - if file.Name == name { - found = true - if !g.IsDir(file.MimeType) { - return g.FormatFile(&file), nil, nil - } else { - fileId = file.Id - break - } - } - } - if !found { - return nil, nil, fmt.Errorf("path not found") - } - } - files, err := g.GetFiles(fileId, account) - if err != nil { - return nil, nil, err - } - _ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), files, nil) - res := make([]model.File, 0) - for _, file := range files { - res = append(res, *g.FormatFile(&file)) - } - return nil, res, nil -} - -func (g GoogleDrive) Link(path string, account *model.Account) (string, error) { - file, err := g.GetFile(utils.ParsePath(path), account) - if err != nil { - return "", err - } - link := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.Id) - var e GoogleError - _, _ = googleClient.R().SetError(&e). - SetHeader("Authorization", "Bearer "+account.AccessToken). - Get(link) - if e.Error.Code != 0 { - if e.Error.Code == 401 { - err = g.RefreshToken(account) - if err != nil { - _ = model.SaveAccount(account) - return "", err - } - return g.Link(path, account) - } - return "", fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) - } - return link + "&alt=media", nil -} - -func (g GoogleDrive) Proxy(c *gin.Context, account *model.Account) { - c.Request.Header.Add("Authorization", "Bearer "+account.AccessToken) -} - -func (g GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) { - return nil, nil -} - -var _ Driver = (*GoogleDrive)(nil) - -func init() { - RegisterDriver("GoogleDrive", &GoogleDrive{}) - googleClient.SetRetryCount(3) -} diff --git a/drivers/googledrive/driver.go b/drivers/googledrive/driver.go new file mode 100644 index 00000000..8e646fd4 --- /dev/null +++ b/drivers/googledrive/driver.go @@ -0,0 +1,159 @@ +package googledrive + +import ( + "fmt" + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + "github.com/gin-gonic/gin" + log "github.com/sirupsen/logrus" + "path/filepath" +) + +type GoogleDrive struct {} + +var driverName = "GoogleDrive" + +func (driver GoogleDrive) Items() []drivers.Item { + return []drivers.Item{ + { + Name: "client_id", + Label: "client id", + Type: "string", + Required: true, + }, + { + Name: "client_secret", + Label: "client secret", + Type: "string", + Required: true, + }, + { + Name: "refresh_token", + Label: "refresh token", + Type: "string", + Required: true, + }, + { + Name: "root_folder", + Label: "root folder file_id", + Type: "string", + Required: true, + }, + } +} + +func (driver GoogleDrive) Save(account *model.Account, old *model.Account) error { + account.Proxy = true + err := driver.RefreshToken(account) + if err != nil { + account.Status = err.Error() + _ = model.SaveAccount(account) + return err + } + account.Status = "work" + _ = model.SaveAccount(account) + return nil +} + +func (driver GoogleDrive) File(path string, account *model.Account) (*model.File, error) { + path = utils.ParsePath(path) + if path == "/" { + return &model.File{ + Id: account.RootFolder, + Name: account.Name, + Size: 0, + Type: conf.FOLDER, + Driver: driverName, + UpdatedAt: account.UpdatedAt, + }, nil + } + dir, name := filepath.Split(path) + files, err := driver.Files(dir, account) + if err != nil { + return nil, err + } + for _, file := range files { + if file.Name == name { + return &file, nil + } + } + return nil, drivers.PathNotFound +} + +func (driver GoogleDrive) Files(path string, account *model.Account) ([]model.File, error) { + path = utils.ParsePath(path) + var rawFiles []GoogleFile + cache, err := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path)) + if err == nil { + rawFiles, _ = cache.([]GoogleFile) + } else { + file, err := driver.File(path, account) + if err != nil { + return nil, err + } + rawFiles, err = driver.GetFiles(file.Id, account) + if err != nil { + return nil, err + } + if len(rawFiles) > 0 { + _ = conf.Cache.Set(conf.Ctx, fmt.Sprintf("%s%s", account.Name, path), rawFiles, nil) + } + } + files := make([]model.File, 0) + for _, file := range rawFiles { + files = append(files, *driver.FormatFile(&file)) + } + return files, nil +} + +func (driver GoogleDrive) Link(path string, account *model.Account) (string, error) { + file, err := driver.GetFile(utils.ParsePath(path), account) + if err != nil { + return "", err + } + link := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.Id) + var e GoogleError + _, _ = googleClient.R().SetError(&e). + SetHeader("Authorization", "Bearer "+account.AccessToken). + Get(link) + if e.Error.Code != 0 { + if e.Error.Code == 401 { + err = driver.RefreshToken(account) + if err != nil { + _ = model.SaveAccount(account) + return "", err + } + return driver.Link(path, account) + } + return "", fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) + } + return link + "&alt=media", nil +} + +func (driver GoogleDrive) Path(path string, account *model.Account) (*model.File, []model.File, error) { + path = utils.ParsePath(path) + log.Debugf("google path: %s", path) + file, err := driver.File(path, account) + if err != nil { + return nil, nil, err + } + if file.Type != conf.FOLDER { + //file.Url, _ = driver.Link(path, account) + return file, nil, nil + } + files, err := driver.Files(path, account) + if err != nil { + return nil, nil, err + } + return nil, files, nil +} + +func (driver GoogleDrive) Proxy(c *gin.Context, account *model.Account) { + c.Request.Header.Add("Authorization", "Bearer "+account.AccessToken) +} + +func (driver GoogleDrive) Preview(path string, account *model.Account) (interface{}, error) { + return nil, nil +} \ No newline at end of file diff --git a/drivers/googledrive/googledrive.go b/drivers/googledrive/googledrive.go new file mode 100644 index 00000000..729f88d4 --- /dev/null +++ b/drivers/googledrive/googledrive.go @@ -0,0 +1,160 @@ +package googledrive + +import ( + "fmt" + "github.com/Xhofe/alist/conf" + "github.com/Xhofe/alist/drivers" + "github.com/Xhofe/alist/model" + "github.com/Xhofe/alist/utils" + "github.com/go-resty/resty/v2" + "path/filepath" + "strconv" + "time" +) + +var googleClient = resty.New() + +type GoogleTokenError struct { + Error string `json:"error"` + ErrorDescription string `json:"error_description"` +} + +func (driver GoogleDrive) RefreshToken(account *model.Account) error { + url := "https://www.googleapis.com/oauth2/v4/token" + var resp drivers.TokenResp + var e GoogleTokenError + _, err := googleClient.R().SetResult(&resp).SetError(&e). + SetFormData(map[string]string{ + "client_id": account.ClientId, + "client_secret": account.ClientSecret, + "refresh_token": account.RefreshToken, + "grant_type": "refresh_token", + }).Post(url) + if err != nil { + return err + } + if e.Error != "" { + return fmt.Errorf(e.Error) + } + account.AccessToken = resp.AccessToken + account.Status = "work" + return nil +} + +type GoogleFile struct { + Id string `json:"id"` + Name string `json:"name"` + MimeType string `json:"mimeType"` + ModifiedTime *time.Time `json:"modifiedTime"` + Size string `json:"size"` +} + +func (driver GoogleDrive) IsDir(mimeType string) bool { + return mimeType == "application/vnd.google-apps.folder" || mimeType == "application/vnd.google-apps.shortcut" +} + +func (driver GoogleDrive) FormatFile(file *GoogleFile) *model.File { + f := &model.File{ + Id: file.Id, + Name: file.Name, + Driver: driverName, + UpdatedAt: file.ModifiedTime, + Thumbnail: "", + Url: "", + } + if driver.IsDir(file.MimeType) { + f.Type = conf.FOLDER + } else { + size, _ := strconv.ParseInt(file.Size, 10, 64) + f.Size = size + f.Type = utils.GetFileType(filepath.Ext(file.Name)) + } + return f +} + +type GoogleFiles struct { + NextPageToken string `json:"nextPageToken"` + Files []GoogleFile `json:"files"` +} + +type GoogleError struct { + Error struct { + Errors []struct { + Domain string `json:"domain"` + Reason string `json:"reason"` + Message string `json:"message"` + LocationType string `json:"location_type"` + Location string `json:"location"` + } + Code int `json:"code"` + Message string `json:"message"` + } `json:"error"` +} + +func (driver GoogleDrive) GetFiles(id string, account *model.Account) ([]GoogleFile, error) { + pageToken := "first" + res := make([]GoogleFile, 0) + for pageToken != "" { + if pageToken == "first" { + pageToken = "" + } + var resp GoogleFiles + var e GoogleError + _, err := googleClient.R().SetResult(&resp).SetError(&e). + SetHeader("Authorization", "Bearer "+account.AccessToken). + SetQueryParams(map[string]string{ + "orderBy": "folder,name,modifiedTime desc", + "fields": "files(id,name,mimeType,size,modifiedTime),nextPageToken", + "pageSize": "1000", + "q": fmt.Sprintf("'%s' in parents and trashed = false", id), + "includeItemsFromAllDrives": "true", + "supportsAllDrives": "true", + "pageToken": pageToken, + }).Get("https://www.googleapis.com/drive/v3/files") + if err != nil { + return nil, err + } + if e.Error.Code != 0 { + if e.Error.Code == 401 { + err = driver.RefreshToken(account) + if err != nil { + _ = model.SaveAccount(account) + return nil, err + } + return driver.GetFiles(id, account) + } + return nil, fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors) + } + pageToken = resp.NextPageToken + res = append(res, resp.Files...) + } + return res, nil +} + +func (driver GoogleDrive) GetFile(path string, account *model.Account) (*GoogleFile, error) { + dir, name := filepath.Split(path) + dir = utils.ParsePath(dir) + _, _, err := driver.Path(dir, account) + if err != nil { + return nil, err + } + parentFiles_, _ := conf.Cache.Get(conf.Ctx, fmt.Sprintf("%s%s", account.Name, dir)) + parentFiles, _ := parentFiles_.([]GoogleFile) + for _, file := range parentFiles { + if file.Name == name { + if !driver.IsDir(file.MimeType) { + return &file, err + } else { + return nil, fmt.Errorf("not file") + } + } + } + return nil, fmt.Errorf("path not found") +} + +var _ drivers.Driver = (*GoogleDrive)(nil) + +func init() { + drivers.RegisterDriver(driverName, &GoogleDrive{}) + googleClient.SetRetryCount(3) +}