Skip to content

Commit e59d223

Browse files
CaspianGUANCopilotxrgzs
authored
feat(drivers): add Degoo driver (#1097)
* Create driver.go Signed-off-by: CaspianGUAN <[email protected]> * Create util.go Signed-off-by: CaspianGUAN <[email protected]> * Create types.go Signed-off-by: CaspianGUAN <[email protected]> * Create meta.go Signed-off-by: CaspianGUAN <[email protected]> * Update drivers/degoo/driver.go Co-authored-by: Copilot <[email protected]> Signed-off-by: CaspianGUAN <[email protected]> * Update drivers/degoo/driver.go Co-authored-by: Copilot <[email protected]> Signed-off-by: CaspianGUAN <[email protected]> * Update driver.go Signed-off-by: CaspianGUAN <[email protected]> * Update meta.go Signed-off-by: CaspianGUAN <[email protected]> * Update types.go Signed-off-by: CaspianGUAN <[email protected]> * Update util.go Signed-off-by: CaspianGUAN <[email protected]> * Update driver.go Signed-off-by: CaspianGUAN <[email protected]> * Update util.go Signed-off-by: CaspianGUAN <[email protected]> * Update drivers/degoo/util.go Co-authored-by: Copilot <[email protected]> Signed-off-by: CaspianGUAN <[email protected]> * Update util.go Signed-off-by: CaspianGUAN <[email protected]> * refactor(degoo): add Degoo driver integration and update API handling * fix(degoo): apply suggestions --------- Signed-off-by: CaspianGUAN <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: MadDogOwner <[email protected]>
1 parent 01914a0 commit e59d223

File tree

6 files changed

+785
-0
lines changed

6 files changed

+785
-0
lines changed

drivers/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
_ "github.com/OpenListTeam/OpenList/v4/drivers/cloudreve"
2424
_ "github.com/OpenListTeam/OpenList/v4/drivers/cloudreve_v4"
2525
_ "github.com/OpenListTeam/OpenList/v4/drivers/crypt"
26+
_ "github.com/OpenListTeam/OpenList/v4/drivers/degoo"
2627
_ "github.com/OpenListTeam/OpenList/v4/drivers/doubao"
2728
_ "github.com/OpenListTeam/OpenList/v4/drivers/doubao_share"
2829
_ "github.com/OpenListTeam/OpenList/v4/drivers/dropbox"

drivers/degoo/driver.go

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
package degoo
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"strconv"
8+
"time"
9+
10+
"github.com/OpenListTeam/OpenList/v4/drivers/base"
11+
"github.com/OpenListTeam/OpenList/v4/internal/driver"
12+
"github.com/OpenListTeam/OpenList/v4/internal/errs"
13+
"github.com/OpenListTeam/OpenList/v4/internal/model"
14+
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
15+
)
16+
17+
type Degoo struct {
18+
model.Storage
19+
Addition
20+
client *http.Client
21+
}
22+
23+
func (d *Degoo) Config() driver.Config {
24+
return config
25+
}
26+
27+
func (d *Degoo) GetAddition() driver.Additional {
28+
return &d.Addition
29+
}
30+
31+
func (d *Degoo) Init(ctx context.Context) error {
32+
33+
d.client = base.HttpClient
34+
35+
if d.Token == "" {
36+
err := d.login(ctx)
37+
if err != nil {
38+
return err
39+
}
40+
}
41+
42+
return d.getDevices(ctx)
43+
}
44+
45+
func (d *Degoo) Drop(ctx context.Context) error {
46+
return nil
47+
}
48+
49+
func (d *Degoo) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
50+
items, err := d.getAllFileChildren5(ctx, dir.GetID())
51+
if err != nil {
52+
return nil, err
53+
}
54+
return utils.MustSliceConvert(items, func(s DegooFileItem) model.Obj {
55+
isFolder := s.Category == 2 || s.Category == 1 || s.Category == 10
56+
57+
createTime, modTime, _ := humanReadableTimes(s.CreationTime, s.LastModificationTime, s.LastUploadTime)
58+
59+
size, err := strconv.ParseInt(s.Size, 10, 64)
60+
if err != nil {
61+
size = 0 // Default to 0 if size parsing fails
62+
}
63+
64+
return &model.Object{
65+
ID: s.ID,
66+
Path: s.FilePath,
67+
Name: s.Name,
68+
Size: size,
69+
Modified: modTime,
70+
Ctime: createTime,
71+
IsFolder: isFolder,
72+
}
73+
}), nil
74+
}
75+
76+
func (d *Degoo) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
77+
item, err := d.getOverlay4(ctx, file.GetID())
78+
if err != nil {
79+
return nil, err
80+
}
81+
82+
return &model.Link{URL: item.URL}, nil
83+
}
84+
85+
func (d *Degoo) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
86+
// This is done by calling the setUploadFile3 API with a special checksum and size.
87+
const query = `mutation SetUploadFile3($Token: String!, $FileInfos: [FileInfoUpload3]!) { setUploadFile3(Token: $Token, FileInfos: $FileInfos) }`
88+
89+
variables := map[string]interface{}{
90+
"Token": d.Token,
91+
"FileInfos": []map[string]interface{}{
92+
{
93+
"Checksum": folderChecksum,
94+
"Name": dirName,
95+
"CreationTime": time.Now().UnixMilli(),
96+
"ParentID": parentDir.GetID(),
97+
"Size": 0,
98+
},
99+
},
100+
}
101+
102+
_, err := d.apiCall(ctx, "SetUploadFile3", query, variables)
103+
if err != nil {
104+
return err
105+
}
106+
107+
return nil
108+
}
109+
110+
func (d *Degoo) Move(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
111+
const query = `mutation SetMoveFile($Token: String!, $Copy: Boolean, $NewParentID: String!, $FileIDs: [String]!) { setMoveFile(Token: $Token, Copy: $Copy, NewParentID: $NewParentID, FileIDs: $FileIDs) }`
112+
113+
variables := map[string]interface{}{
114+
"Token": d.Token,
115+
"Copy": false,
116+
"NewParentID": dstDir.GetID(),
117+
"FileIDs": []string{srcObj.GetID()},
118+
}
119+
120+
_, err := d.apiCall(ctx, "SetMoveFile", query, variables)
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
return srcObj, nil
126+
}
127+
128+
func (d *Degoo) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
129+
const query = `mutation SetRenameFile($Token: String!, $FileRenames: [FileRenameInfo]!) { setRenameFile(Token: $Token, FileRenames: $FileRenames) }`
130+
131+
variables := map[string]interface{}{
132+
"Token": d.Token,
133+
"FileRenames": []DegooFileRenameInfo{
134+
{
135+
ID: srcObj.GetID(),
136+
NewName: newName,
137+
},
138+
},
139+
}
140+
141+
_, err := d.apiCall(ctx, "SetRenameFile", query, variables)
142+
if err != nil {
143+
return err
144+
}
145+
return nil
146+
}
147+
148+
func (d *Degoo) Copy(ctx context.Context, srcObj, dstDir model.Obj) (model.Obj, error) {
149+
// Copy is not implemented, Degoo API does not support direct copy.
150+
return nil, errs.NotImplement
151+
}
152+
153+
func (d *Degoo) Remove(ctx context.Context, obj model.Obj) error {
154+
// Remove deletes a file or folder (moves to trash).
155+
const query = `mutation SetDeleteFile5($Token: String!, $IsInRecycleBin: Boolean!, $IDs: [IDType]!) { setDeleteFile5(Token: $Token, IsInRecycleBin: $IsInRecycleBin, IDs: $IDs) }`
156+
157+
variables := map[string]interface{}{
158+
"Token": d.Token,
159+
"IsInRecycleBin": false,
160+
"IDs": []map[string]string{{"FileID": obj.GetID()}},
161+
}
162+
163+
_, err := d.apiCall(ctx, "SetDeleteFile5", query, variables)
164+
return err
165+
}
166+
167+
func (d *Degoo) Put(ctx context.Context, dstDir model.Obj, file model.FileStreamer, up driver.UpdateProgress) error {
168+
tmpF, err := file.CacheFullAndWriter(&up, nil)
169+
if err != nil {
170+
return err
171+
}
172+
173+
parentID := dstDir.GetID()
174+
175+
// Calculate the checksum for the file.
176+
checksum, err := d.checkSum(tmpF)
177+
if err != nil {
178+
return err
179+
}
180+
181+
// 1. Get upload authorization via getBucketWriteAuth4.
182+
auths, err := d.getBucketWriteAuth4(ctx, file, parentID, checksum)
183+
if err != nil {
184+
return err
185+
}
186+
187+
// 2. Upload file.
188+
// support rapid upload
189+
if auths.GetBucketWriteAuth4[0].Error != "Already exist!" {
190+
err = d.uploadS3(ctx, auths, tmpF, file, checksum)
191+
if err != nil {
192+
return err
193+
}
194+
}
195+
196+
// 3. Register metadata with setUploadFile3.
197+
data, err := d.SetUploadFile3(ctx, file, parentID, checksum)
198+
if err != nil {
199+
return err
200+
}
201+
if !data.SetUploadFile3 {
202+
return fmt.Errorf("setUploadFile3 failed: %v", data)
203+
}
204+
return nil
205+
}

drivers/degoo/meta.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package degoo
2+
3+
import (
4+
"github.com/OpenListTeam/OpenList/v4/internal/driver"
5+
"github.com/OpenListTeam/OpenList/v4/internal/op"
6+
)
7+
8+
type Addition struct {
9+
driver.RootID
10+
Username string `json:"username" required:"true" help:"Your Degoo account email"`
11+
Password string `json:"password" required:"true" help:"Your Degoo account password"`
12+
Token string `json:"token" help:"Access token for Degoo API, will be obtained automatically if not provided"`
13+
}
14+
15+
var config = driver.Config{
16+
Name: "Degoo",
17+
LocalSort: true,
18+
DefaultRoot: "0",
19+
NoOverwriteUpload: true,
20+
}
21+
22+
func init() {
23+
op.RegisterDriver(func() driver.Driver {
24+
return &Degoo{}
25+
})
26+
}

drivers/degoo/types.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package degoo
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// DegooLoginRequest represents the login request body.
8+
type DegooLoginRequest struct {
9+
GenerateToken bool `json:"GenerateToken"`
10+
Username string `json:"Username"`
11+
Password string `json:"Password"`
12+
}
13+
14+
// DegooLoginResponse represents a successful login response.
15+
type DegooLoginResponse struct {
16+
Token string `json:"Token"`
17+
RefreshToken string `json:"RefreshToken"`
18+
}
19+
20+
// DegooAccessTokenRequest represents the token refresh request body.
21+
type DegooAccessTokenRequest struct {
22+
RefreshToken string `json:"RefreshToken"`
23+
}
24+
25+
// DegooAccessTokenResponse represents the token refresh response.
26+
type DegooAccessTokenResponse struct {
27+
AccessToken string `json:"AccessToken"`
28+
}
29+
30+
// DegooFileItem represents a Degoo file or folder.
31+
type DegooFileItem struct {
32+
ID string `json:"ID"`
33+
ParentID string `json:"ParentID"`
34+
Name string `json:"Name"`
35+
Category int `json:"Category"`
36+
Size string `json:"Size"`
37+
URL string `json:"URL"`
38+
CreationTime string `json:"CreationTime"`
39+
LastModificationTime string `json:"LastModificationTime"`
40+
LastUploadTime string `json:"LastUploadTime"`
41+
MetadataID string `json:"MetadataID"`
42+
DeviceID int64 `json:"DeviceID"`
43+
FilePath string `json:"FilePath"`
44+
IsInRecycleBin bool `json:"IsInRecycleBin"`
45+
}
46+
47+
type DegooErrors struct {
48+
Path []string `json:"path"`
49+
Data interface{} `json:"data"`
50+
ErrorType string `json:"errorType"`
51+
ErrorInfo interface{} `json:"errorInfo"`
52+
Message string `json:"message"`
53+
}
54+
55+
// DegooGraphqlResponse is the common structure for GraphQL API responses.
56+
type DegooGraphqlResponse struct {
57+
Data json.RawMessage `json:"data"`
58+
Errors []DegooErrors `json:"errors,omitempty"`
59+
}
60+
61+
// DegooGetChildren5Data is the data field for getFileChildren5.
62+
type DegooGetChildren5Data struct {
63+
GetFileChildren5 struct {
64+
Items []DegooFileItem `json:"Items"`
65+
NextToken string `json:"NextToken"`
66+
} `json:"getFileChildren5"`
67+
}
68+
69+
// DegooGetOverlay4Data is the data field for getOverlay4.
70+
type DegooGetOverlay4Data struct {
71+
GetOverlay4 DegooFileItem `json:"getOverlay4"`
72+
}
73+
74+
// DegooFileRenameInfo represents a file rename operation.
75+
type DegooFileRenameInfo struct {
76+
ID string `json:"ID"`
77+
NewName string `json:"NewName"`
78+
}
79+
80+
// DegooFileIDs represents a list of file IDs for move operations.
81+
type DegooFileIDs struct {
82+
FileIDs []string `json:"FileIDs"`
83+
}
84+
85+
// DegooGetBucketWriteAuth4Data is the data field for GetBucketWriteAuth4.
86+
type DegooGetBucketWriteAuth4Data struct {
87+
GetBucketWriteAuth4 []struct {
88+
AuthData struct {
89+
PolicyBase64 string `json:"PolicyBase64"`
90+
Signature string `json:"Signature"`
91+
BaseURL string `json:"BaseURL"`
92+
KeyPrefix string `json:"KeyPrefix"`
93+
AccessKey struct {
94+
Key string `json:"Key"`
95+
Value string `json:"Value"`
96+
} `json:"AccessKey"`
97+
ACL string `json:"ACL"`
98+
AdditionalBody []struct {
99+
Key string `json:"Key"`
100+
Value string `json:"Value"`
101+
} `json:"AdditionalBody"`
102+
} `json:"AuthData"`
103+
Error interface{} `json:"Error"`
104+
} `json:"getBucketWriteAuth4"`
105+
}
106+
107+
// DegooSetUploadFile3Data is the data field for SetUploadFile3.
108+
type DegooSetUploadFile3Data struct {
109+
SetUploadFile3 bool `json:"setUploadFile3"`
110+
}

0 commit comments

Comments
 (0)