Skip to content

Commit

Permalink
added google drive
Browse files Browse the repository at this point in the history
  • Loading branch information
LinkLeong committed Jan 16, 2023
1 parent be50579 commit 87d8be8
Show file tree
Hide file tree
Showing 47 changed files with 3,711 additions and 67 deletions.
11 changes: 11 additions & 0 deletions drivers/all.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package drivers

import (
_ "github.com/IceWhaleTech/CasaOS/drivers/google_drive"
)

// All do nothing,just for import
// same as _ import
func All() {

}
30 changes: 30 additions & 0 deletions drivers/base/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package base

import (
"net/http"
"time"

"github.com/go-resty/resty/v2"
)

var NoRedirectClient *resty.Client
var RestyClient = NewRestyClient()
var HttpClient = &http.Client{}
var UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36"
var DefaultTimeout = time.Second * 30

func init() {
NoRedirectClient = resty.New().SetRedirectPolicy(
resty.RedirectPolicyFunc(func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}),
)
NoRedirectClient.SetHeader("user-agent", UserAgent)
}

func NewRestyClient() *resty.Client {
return resty.New().
SetHeader("user-agent", UserAgent).
SetRetryCount(3).
SetTimeout(DefaultTimeout)
}
12 changes: 12 additions & 0 deletions drivers/base/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package base

import "github.com/go-resty/resty/v2"

type Json map[string]interface{}

type TokenResp struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}

type ReqCallback func(req *resty.Request)
170 changes: 170 additions & 0 deletions drivers/google_drive/drive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
package google_drive

import (
"context"
"errors"
"fmt"
"net/http"
"strconv"

"github.com/IceWhaleTech/CasaOS/drivers/base"
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/model"
"github.com/IceWhaleTech/CasaOS/pkg/utils"
"github.com/go-resty/resty/v2"
)

type GoogleDrive struct {
model.Storage
Addition
AccessToken string
}

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

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

func (d *GoogleDrive) Init(ctx context.Context) error {
if d.ChunkSize == 0 {
d.ChunkSize = 5
}
if len(d.RefreshToken) == 0 {
return d.getRefreshToken()
}
return d.refreshToken()
}

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

func (d *GoogleDrive) 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 fileToObj(src), nil
})
}

func (d *GoogleDrive) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
url := fmt.Sprintf("https://www.googleapis.com/drive/v3/files/%s?includeItemsFromAllDrives=true&supportsAllDrives=true", file.GetID())
_, err := d.request(url, http.MethodGet, nil, nil)
if err != nil {
return nil, err
}
link := model.Link{
URL: url + "&alt=media",
Header: http.Header{
"Authorization": []string{"Bearer " + d.AccessToken},
},
}
return &link, nil
}

func (d *GoogleDrive) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
data := base.Json{
"name": dirName,
"parents": []string{parentDir.GetID()},
"mimeType": "application/vnd.google-apps.folder",
}
_, err := d.request("https://www.googleapis.com/drive/v3/files", http.MethodPost, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
}

func (d *GoogleDrive) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
query := map[string]string{
"addParents": dstDir.GetID(),
"removeParents": "root",
}
url := "https://www.googleapis.com/drive/v3/files/" + srcObj.GetID()
_, err := d.request(url, http.MethodPatch, func(req *resty.Request) {
req.SetQueryParams(query)
}, nil)
return err
}

func (d *GoogleDrive) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
data := base.Json{
"name": newName,
}
url := "https://www.googleapis.com/drive/v3/files/" + srcObj.GetID()
_, err := d.request(url, http.MethodPatch, func(req *resty.Request) {
req.SetBody(data)
}, nil)
return err
}

func (d *GoogleDrive) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
return errors.New("not support")
}

func (d *GoogleDrive) Remove(ctx context.Context, obj model.Obj) error {
url := "https://www.googleapis.com/drive/v3/files/" + obj.GetID()
_, err := d.request(url, http.MethodDelete, nil, nil)
return err
}

func (d *GoogleDrive) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
obj := stream.GetOld()
var (
e Error
url string
data base.Json
res *resty.Response
err error
)
if obj != nil {
url = fmt.Sprintf("https://www.googleapis.com/upload/drive/v3/files/%s?uploadType=resumable&supportsAllDrives=true", obj.GetID())
data = base.Json{}
} else {
data = base.Json{
"name": stream.GetName(),
"parents": []string{dstDir.GetID()},
}
url = "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable&supportsAllDrives=true"
}
req := base.NoRedirectClient.R().
SetHeaders(map[string]string{
"Authorization": "Bearer " + d.AccessToken,
"X-Upload-Content-Type": stream.GetMimetype(),
"X-Upload-Content-Length": strconv.FormatInt(stream.GetSize(), 10),
}).
SetError(&e).SetBody(data).SetContext(ctx)
if obj != nil {
res, err = req.Patch(url)
} else {
res, err = req.Post(url)
}
if err != nil {
return err
}
if e.Error.Code != 0 {
if e.Error.Code == 401 {
err = d.refreshToken()
if err != nil {
return err
}
return d.Put(ctx, dstDir, stream, up)
}
return fmt.Errorf("%s: %v", e.Error.Message, e.Error.Errors)
}
putUrl := res.Header().Get("location")
if stream.GetSize() < d.ChunkSize*1024*1024 {
_, err = d.request(putUrl, http.MethodPut, func(req *resty.Request) {
req.SetHeader("Content-Length", strconv.FormatInt(stream.GetSize(), 10)).SetBody(stream.GetReadCloser())
}, nil)
} else {
err = d.chunkUpload(ctx, stream, putUrl)
}
return err
}

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

import (
"github.com/IceWhaleTech/CasaOS/internal/driver"
"github.com/IceWhaleTech/CasaOS/internal/op"
)

type Addition struct {
driver.RootID
RefreshToken string `json:"refresh_token" required:"true"`
OrderBy string `json:"order_by" type:"string" help:"such as: folder,name,modifiedTime"`
OrderDirection string `json:"order_direction" type:"select" options:"asc,desc"`
ClientID string `json:"client_id" required:"true" default:"865173455964-4ce3gdl73ak5s15kn1vkn73htc8tant2.apps.googleusercontent.com"`
ClientSecret string `json:"client_secret" required:"true" default:"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"`
ChunkSize int64 `json:"chunk_size" type:"number" default:"5" help:"chunk size while uploading (unit: MB)"`
AuthUrl string `json:"auth_url" type:"string" default:"https://accounts.google.com/o/oauth2/auth/oauthchooseaccount?response_type=code&client_id=865173455964-4ce3gdl73ak5s15kn1vkn73htc8tant2.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Ftest-get.casaos.io&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdrive&access_type=offline&approval_prompt=force&state=${HOST}%2Fv1%2Frecover%2FGoogleDrive&service=lso&o2v=1&flowName=GeneralOAuthFlow"`
Icon string `json:"icon" type:"string" default:"https://i.pcmag.com/imagery/reviews/02PHW91bUvLOs36qNbBzOiR-12.fit_scale.size_760x427.v1569471162.png"`
Code string `json:"code" type:"string" help:"code from auth_url"`
}

var config = driver.Config{
Name: "GoogleDrive",
OnlyProxy: true,
DefaultRoot: "root",
}

func init() {
op.RegisterDriver(func() driver.Driver {
return &GoogleDrive{}
})
}
66 changes: 66 additions & 0 deletions drivers/google_drive/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package google_drive

import (
"strconv"
"time"

"github.com/IceWhaleTech/CasaOS/model"
log "github.com/sirupsen/logrus"
)

type TokenError struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}

type Files struct {
NextPageToken string `json:"nextPageToken"`
Files []File `json:"files"`
}

type File struct {
Id string `json:"id"`
Name string `json:"name"`
MimeType string `json:"mimeType"`
ModifiedTime time.Time `json:"modifiedTime"`
Size string `json:"size"`
ThumbnailLink string `json:"thumbnailLink"`
ShortcutDetails struct {
TargetId string `json:"targetId"`
TargetMimeType string `json:"targetMimeType"`
} `json:"shortcutDetails"`
}

func fileToObj(f File) *model.ObjThumb {
log.Debugf("google file: %+v", f)
size, _ := strconv.ParseInt(f.Size, 10, 64)
obj := &model.ObjThumb{
Object: model.Object{
ID: f.Id,
Name: f.Name,
Size: size,
Modified: f.ModifiedTime,
IsFolder: f.MimeType == "application/vnd.google-apps.folder",
},
Thumbnail: model.Thumbnail{},
}
if f.MimeType == "application/vnd.google-apps.shortcut" {
obj.ID = f.ShortcutDetails.TargetId
obj.IsFolder = f.ShortcutDetails.TargetMimeType == "application/vnd.google-apps.folder"
}
return obj
}

type Error 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"`
}
Loading

0 comments on commit 87d8be8

Please sign in to comment.