@@ -33,6 +33,9 @@ type GuangYaPan struct {
3333
3434 accountClient * resty.Client
3535 apiClient * resty.Client
36+
37+ resolvedRootFolderID string
38+ rootFolderResolved bool
3639}
3740
3841func (d * GuangYaPan ) Config () driver.Config {
@@ -62,12 +65,15 @@ func (d *GuangYaPan) Init(ctx context.Context) error {
6265 d .SortType = 1
6366 }
6467
68+ d .RootPath = strings .TrimSpace (d .RootPath )
6569 d .AccessToken = strings .TrimSpace (d .AccessToken )
6670 d .RefreshToken = strings .TrimSpace (d .RefreshToken )
6771 d .PhoneNumber = strings .TrimSpace (d .PhoneNumber )
6872 d .VerifyCode = strings .TrimSpace (d .VerifyCode )
6973 d .CaptchaToken = strings .TrimSpace (d .CaptchaToken )
7074 d .VerificationID = strings .TrimSpace (d .VerificationID )
75+ d .resolvedRootFolderID = ""
76+ d .rootFolderResolved = false
7177
7278 d .accountClient = base .NewRestyClient ().
7379 SetBaseURL (accountBaseURL ).
@@ -99,14 +105,14 @@ func (d *GuangYaPan) Init(ctx context.Context) error {
99105 // Priority: access_token -> refresh_token -> sms login.
100106 if d .AccessToken != "" {
101107 if err := d .validateToken (ctx ); err == nil {
102- return nil
108+ return d . prepareRootFolder ( ctx )
103109 }
104110 d .AccessToken = ""
105111 }
106112 if d .RefreshToken != "" {
107113 if err := d .refreshToken (ctx ); err == nil {
108114 if err2 := d .validateToken (ctx ); err2 == nil {
109- return nil
115+ return d . prepareRootFolder ( ctx )
110116 }
111117 }
112118 }
@@ -118,7 +124,10 @@ func (d *GuangYaPan) Init(ctx context.Context) error {
118124 if err := d .loginBySMSCode (ctx ); err != nil {
119125 return err
120126 }
121- return d .validateToken (ctx )
127+ if err := d .validateToken (ctx ); err != nil {
128+ return err
129+ }
130+ return d .prepareRootFolder (ctx )
122131 }
123132 if d .SendCode {
124133 d .setTempStatus ("SMS sending in progress..." )
@@ -138,15 +147,27 @@ func (d *GuangYaPan) Drop(ctx context.Context) error {
138147 return nil
139148}
140149
150+ func (d * GuangYaPan ) GetRoot (ctx context.Context ) (model.Obj , error ) {
151+ rootID , err := d .getRootFolderID (ctx )
152+ if err != nil {
153+ return nil , err
154+ }
155+ return & model.Object {
156+ ID : rootID ,
157+ Path : "/" ,
158+ Name : "root" ,
159+ Size : 0 ,
160+ Modified : d .Modified ,
161+ IsFolder : true ,
162+ }, nil
163+ }
164+
141165func (d * GuangYaPan ) List (ctx context.Context , dir model.Obj , args model.ListArgs ) ([]model.Obj , error ) {
142166 if err := d .ensureAccessToken (ctx ); err != nil {
143167 return nil , err
144168 }
145169
146170 parentID := dir .GetID ()
147- if parentID == d .RootFolderID {
148- parentID = ""
149- }
150171
151172 res := make ([]model.Obj , 0 , d .PageSize )
152173 for page := 0 ; ; page ++ {
@@ -219,9 +240,6 @@ func (d *GuangYaPan) MakeDir(ctx context.Context, parentDir model.Obj, dirName s
219240 }
220241
221242 parentID := parentDir .GetID ()
222- if parentID == d .RootFolderID {
223- parentID = ""
224- }
225243
226244 var out createDirResp
227245 if err := d .postAPI (ctx , "/nd.bizuserres.s/v1/file/create_dir" , map [string ]any {
@@ -301,9 +319,6 @@ func (d *GuangYaPan) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
301319 return errors .New ("file id is empty" )
302320 }
303321 parentID := dstDir .GetID ()
304- if parentID == d .RootFolderID {
305- parentID = ""
306- }
307322
308323 var out deleteResp
309324 if err := d .postAPI (ctx , "/nd.bizuserres.s/v1/file/move_file" , map [string ]any {
@@ -332,9 +347,6 @@ func (d *GuangYaPan) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
332347 return errors .New ("file id is empty" )
333348 }
334349 parentID := dstDir .GetID ()
335- if parentID == d .RootFolderID {
336- parentID = ""
337- }
338350
339351 var out deleteResp
340352 if err := d .postAPI (ctx , "/nd.bizuserres.s/v1/file/copy_file" , map [string ]any {
@@ -369,9 +381,6 @@ func (d *GuangYaPan) Put(ctx context.Context, dstDir model.Obj, file model.FileS
369381 }
370382
371383 parentID := dstDir .GetID ()
372- if parentID == d .RootFolderID {
373- parentID = ""
374- }
375384
376385 token , code , err := d .getUploadToken (ctx , parentID , name , file .GetSize ())
377386 if err != nil {
@@ -415,6 +424,97 @@ func (d *GuangYaPan) Put(ctx context.Context, dstDir model.Obj, file model.FileS
415424 return d .waitUploadTaskInfo (ctx , taskID )
416425}
417426
427+ func (d * GuangYaPan ) getRootFolderID (ctx context.Context ) (string , error ) {
428+ if d .rootFolderResolved {
429+ return d .resolvedRootFolderID , nil
430+ }
431+ if err := d .ensureAccessToken (ctx ); err != nil {
432+ return "" , err
433+ }
434+ if err := d .prepareRootFolder (ctx ); err != nil {
435+ return "" , err
436+ }
437+ return d .resolvedRootFolderID , nil
438+ }
439+
440+ func (d * GuangYaPan ) prepareRootFolder (ctx context.Context ) error {
441+ rootID , err := d .resolveConfiguredRootFolderID (ctx )
442+ if err != nil {
443+ return err
444+ }
445+ d .resolvedRootFolderID = rootID
446+ d .rootFolderResolved = true
447+ return nil
448+ }
449+
450+ func (d * GuangYaPan ) resolveConfiguredRootFolderID (ctx context.Context ) (string , error ) {
451+ root := strings .TrimSpace (d .RootPath )
452+ if root == "" {
453+ return "" , nil
454+ }
455+ return d .resolveFolderPath (ctx , root )
456+ }
457+
458+ func (d * GuangYaPan ) resolveFolderPath (ctx context.Context , rootPath string ) (string , error ) {
459+ cleanPath := strings .Trim (strings .ReplaceAll (strings .TrimSpace (rootPath ), "\\ " , "/" ), "/" )
460+ if cleanPath == "" {
461+ return "" , nil
462+ }
463+
464+ parentID := ""
465+ for _ , name := range strings .Split (cleanPath , "/" ) {
466+ if name == "" {
467+ continue
468+ }
469+ childID , err := d .findChildFolderID (ctx , parentID , name )
470+ if err != nil {
471+ return "" , err
472+ }
473+ parentID = childID
474+ }
475+ return parentID , nil
476+ }
477+
478+ func (d * GuangYaPan ) findChildFolderID (ctx context.Context , parentID , name string ) (string , error ) {
479+ pageSize := d .PageSize
480+ if pageSize <= 0 {
481+ pageSize = 100
482+ }
483+
484+ seen := 0
485+ for page := 0 ; ; page ++ {
486+ var resp listResp
487+ body := map [string ]any {
488+ "parentId" : parentID ,
489+ "page" : page ,
490+ "pageSize" : pageSize ,
491+ "orderBy" : d .OrderBy ,
492+ "sortType" : d .SortType ,
493+ "fileTypes" : []int {},
494+ }
495+ if err := d .postAPI (ctx , "/nd.bizuserres.s/v1/file/get_file_list" , body , & resp ); err != nil {
496+ return "" , err
497+ }
498+ for _ , item := range resp .Data .List {
499+ seen ++
500+ if item .ResType == 2 && item .FileName == name {
501+ return item .FileID , nil
502+ }
503+ }
504+ if len (resp .Data .List ) < pageSize {
505+ break
506+ }
507+ if resp .Data .Total > 0 && seen >= resp .Data .Total {
508+ break
509+ }
510+ }
511+
512+ if parentID == "" {
513+ return "" , fmt .Errorf ("resolve root folder path failed: folder %q not found under /" , name )
514+ }
515+ return "" , fmt .Errorf ("resolve root folder path failed: folder %q not found under parent %s" , name , parentID )
516+ }
517+
418518func (d * GuangYaPan ) ensureAccessToken (ctx context.Context ) error {
419519 if strings .TrimSpace (d .AccessToken ) != "" {
420520 return nil
@@ -948,3 +1048,4 @@ func randomDeviceID() string {
9481048}
9491049
9501050var _ driver.Driver = (* GuangYaPan )(nil )
1051+ var _ driver.GetRooter = (* GuangYaPan )(nil )
0 commit comments