diff --git a/internal/command/album.go b/internal/command/album.go index 2a0325c..03325e3 100644 --- a/internal/command/album.go +++ b/internal/command/album.go @@ -303,7 +303,7 @@ func CmdAlbum() cli.Command { func RunAlbumList() { activeUser := GetActiveUser() - records, err := activeUser.PanClient().AlbumListGetAll(&aliyunpan.AlbumListParam{}) + records, err := activeUser.PanClient().WebapiPanClient().AlbumListGetAll(&aliyunpan.AlbumListParam{}) if err != nil { fmt.Printf("获取相簿列表失败: %s\n", err) return @@ -326,7 +326,7 @@ func RunAlbumCreate(name, description string) { } activeUser := GetActiveUser() - _, err := activeUser.PanClient().AlbumCreate(&aliyunpan.AlbumCreateParam{ + _, err := activeUser.PanClient().WebapiPanClient().AlbumCreate(&aliyunpan.AlbumCreateParam{ Name: name, Description: description, }) @@ -344,7 +344,7 @@ func RunAlbumDelete(nameList []string) { } activeUser := GetActiveUser() - records, err := activeUser.PanClient().AlbumListGetAll(&aliyunpan.AlbumListParam{}) + records, err := activeUser.PanClient().WebapiPanClient().AlbumListGetAll(&aliyunpan.AlbumListParam{}) if err != nil { fmt.Printf("获取相簿列表失败: %s\n", err) return @@ -354,7 +354,7 @@ func RunAlbumDelete(nameList []string) { for i, name := range nameList { if name == record.Name { nameList = append(nameList[:i], nameList[i+1:]...) - _, err := activeUser.PanClient().AlbumDelete(&aliyunpan.AlbumDeleteParam{ + _, err := activeUser.PanClient().WebapiPanClient().AlbumDelete(&aliyunpan.AlbumDeleteParam{ AlbumId: record.AlbumId, }) if err != nil { @@ -370,7 +370,7 @@ func RunAlbumDelete(nameList []string) { } func getAlbumFromName(activeUser *config.PanUser, name string) *aliyunpan.AlbumEntity { - records, err := activeUser.PanClient().AlbumListGetAll(&aliyunpan.AlbumListParam{}) + records, err := activeUser.PanClient().WebapiPanClient().AlbumListGetAll(&aliyunpan.AlbumListParam{}) if err != nil { fmt.Printf("获取相簿列表失败: %s\n", err) return nil @@ -399,7 +399,7 @@ func RunAlbumRename(name, newName string) { if record == nil { return } - _, err := activeUser.PanClient().AlbumEdit(&aliyunpan.AlbumEditParam{ + _, err := activeUser.PanClient().WebapiPanClient().AlbumEdit(&aliyunpan.AlbumEditParam{ AlbumId: record.AlbumId, Description: record.Description, Name: newName, @@ -424,7 +424,7 @@ func RunAlbumListFile(name string) { return } - fileList, er := activeUser.PanClient().AlbumListFileGetAll(&aliyunpan.AlbumListFileParam{ + fileList, er := activeUser.PanClient().WebapiPanClient().AlbumListFileGetAll(&aliyunpan.AlbumListFileParam{ AlbumId: record.AlbumId, }) if er != nil { @@ -450,7 +450,7 @@ func RunAlbumRmFile(name string, nameList []string) { return } - fileList, er := activeUser.PanClient().AlbumListFileGetAll(&aliyunpan.AlbumListFileParam{ + fileList, er := activeUser.PanClient().WebapiPanClient().AlbumListFileGetAll(&aliyunpan.AlbumListFileParam{ AlbumId: album.AlbumId, }) if er != nil { @@ -480,7 +480,7 @@ func RunAlbumRmFile(name string, nameList []string) { return } // delete file - _, e := activeUser.PanClient().AlbumDeleteFile(param) + _, e := activeUser.PanClient().WebapiPanClient().AlbumDeleteFile(param) if e != nil { fmt.Printf("删除相簿文件失败:%s\n", e) return @@ -519,7 +519,7 @@ func RunAlbumAddFile(albumName string, filePathList []string, filterOption Album } for k := range paths { filePath := paths[k] - fileInfo, apierr := activeUser.PanClient().FileInfoByPath(activeUser.ActiveDriveId, filePath) + fileInfo, apierr := activeUser.PanClient().WebapiPanClient().FileInfoByPath(activeUser.ActiveDriveId, filePath) if apierr != nil { fmt.Printf("获取文件信息失败: %s\n", filePath) continue @@ -531,7 +531,7 @@ func RunAlbumAddFile(albumName string, filePathList []string, filterOption Album } } else { // folder - activeUser.PanClient().FilesDirectoriesRecurseList(activeUser.ActiveDriveId, fileInfo.Path, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { + activeUser.PanClient().WebapiPanClient().FilesDirectoriesRecurseList(activeUser.ActiveDriveId, fileInfo.Path, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { if apiError != nil { logger.Verbosef("%s\n", apiError) return true @@ -553,7 +553,7 @@ func RunAlbumAddFile(albumName string, filePathList []string, filterOption Album return } // add file - _, e := activeUser.PanClient().AlbumAddFile(param) + _, e := activeUser.PanClient().WebapiPanClient().AlbumAddFile(param) if e != nil { fmt.Printf("增加相簿文件失败:%s\n", e) return @@ -584,9 +584,9 @@ func RunAlbumDownloadFile(albumNames []string, options *DownloadOptions) { } activeUser := GetActiveUser() - activeUser.PanClient().EnableCache() - activeUser.PanClient().ClearCache() - defer activeUser.PanClient().DisableCache() + activeUser.PanClient().WebapiPanClient().EnableCache() + activeUser.PanClient().WebapiPanClient().ClearCache() + defer activeUser.PanClient().WebapiPanClient().DisableCache() // pan token expired checker continueFlag := int32(0) atomic.StoreInt32(&continueFlag, 0) @@ -681,7 +681,7 @@ func RunAlbumDownloadFile(albumNames []string, options *DownloadOptions) { continue } // 获取相簿下的所有文件 - fileList, er := activeUser.PanClient().AlbumListFileGetAll(&aliyunpan.AlbumListFileParam{ + fileList, er := activeUser.PanClient().WebapiPanClient().AlbumListFileGetAll(&aliyunpan.AlbumListFileParam{ AlbumId: record.AlbumId, }) if er != nil { @@ -700,7 +700,7 @@ func RunAlbumDownloadFile(albumNames []string, options *DownloadOptions) { newCfg := *cfg unit := pandownload.DownloadTaskUnit{ Cfg: &newCfg, // 复制一份新的cfg - PanClient: panClient, + PanClient: panClient.WebapiPanClient(), VerbosePrinter: panCommandVerbose, PrintFormat: downloadPrintFormat(options.Load), ParentTaskExecutor: &executor, diff --git a/internal/command/cd.go b/internal/command/cd.go index 6c8ff1d..cc700af 100644 --- a/internal/command/cd.go +++ b/internal/command/cd.go @@ -96,7 +96,7 @@ func RunChangeDirectory(driveId, targetPath string) { user := config.Config.ActiveUser() targetPath = user.PathJoin(driveId, targetPath) - //targetPathInfo, err := user.PanClient().FileInfoByPath(driveId, targetPath) + //targetPathInfo, err := user.PanClient().WebapiPanClient().FileInfoByPath(driveId, targetPath) files, err := matchPathByShellPattern(driveId, targetPath) if err != nil { fmt.Println(err) diff --git a/internal/command/command.go b/internal/command/command.go index d38da3c..b8e8bdb 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -58,7 +58,7 @@ const ( var ErrBadArgs = errors.New("参数错误") var ErrNotLogined = errors.New("未登录账号") -func GetActivePanClient() *aliyunpan.PanClient { +func GetActivePanClient() *config.PanClient { return config.Config.ActiveUser().PanClient() } diff --git a/internal/command/download.go b/internal/command/download.go index 7f91397..7a60e6a 100644 --- a/internal/command/download.go +++ b/internal/command/download.go @@ -219,9 +219,9 @@ func downloadPrintFormat(load int) string { // RunDownload 执行下载网盘内文件 func RunDownload(paths []string, options *DownloadOptions) { activeUser := GetActiveUser() - activeUser.PanClient().EnableCache() - activeUser.PanClient().ClearCache() - defer activeUser.PanClient().DisableCache() + activeUser.PanClient().WebapiPanClient().EnableCache() + activeUser.PanClient().WebapiPanClient().ClearCache() + defer activeUser.PanClient().WebapiPanClient().DisableCache() // pan token expired checker continueFlag := int32(0) atomic.StoreInt32(&continueFlag, 0) @@ -347,7 +347,7 @@ func RunDownload(paths []string, options *DownloadOptions) { // 匹配的文件 unit := pandownload.DownloadTaskUnit{ Cfg: &newCfg, // 复制一份新的cfg - PanClient: panClient, + PanClient: panClient.WebapiPanClient(), VerbosePrinter: panCommandVerbose, PrintFormat: downloadPrintFormat(options.Load), ParentTaskExecutor: &executor, diff --git a/internal/command/export_file.go b/internal/command/export_file.go index 8316f3e..8eb3588 100644 --- a/internal/command/export_file.go +++ b/internal/command/export_file.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -117,7 +117,7 @@ func RunExportFiles(driveId string, overwrite bool, panPaths []string, saveLocal for _, panPath := range panPaths { panPath = activeUser.PathJoin(driveId, panPath) - panClient.FilesDirectoriesRecurseList(driveId, panPath, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { + panClient.WebapiPanClient().FilesDirectoriesRecurseList(driveId, panPath, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { if apiError != nil { logger.Verbosef("%s\n", apiError) return true diff --git a/internal/command/import_file.go b/internal/command/import_file.go index 50b6b45..58ad0a8 100644 --- a/internal/command/import_file.go +++ b/internal/command/import_file.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -199,7 +199,7 @@ func processOneImport(driveId string, isOverwrite bool, dirMap map[string]*dirFi } if efi != nil && efi.FileId != "" { // existed, delete it - fdr, err := panClient.FileDelete([]*aliyunpan.FileBatchActionParam{ + fdr, err := panClient.WebapiPanClient().FileDelete([]*aliyunpan.FileBatchActionParam{ { DriveId: driveId, FileId: efi.FileId, @@ -221,7 +221,7 @@ func processOneImport(driveId string, isOverwrite bool, dirMap map[string]*dirFi ContentHash: item.FileSha1, ParentFileId: dataItem.Dir.FileId, } - uploadOpEntity, apierr := panClient.CreateUploadFile(appCreateUploadFileParam) + uploadOpEntity, apierr := panClient.WebapiPanClient().CreateUploadFile(appCreateUploadFileParam) if apierr != nil { fmt.Println("创建秒传任务失败:" + apierr.Error()) return false, true @@ -247,7 +247,7 @@ func prepareMkdir(driveId string, importFileItems []RapidUploadItem) map[string] continue } if panDir != "/" { - rs, apierr = panClient.MkdirRecursive(driveId, "", "", 0, strings.Split(path.Clean(panDir), "/")) + rs, apierr = panClient.WebapiPanClient().MkdirRecursive(driveId, "", "", 0, strings.Split(path.Clean(panDir), "/")) if apierr != nil || rs.FileId == "" { logger.Verboseln("创建云盘文件夹失败") continue @@ -263,7 +263,7 @@ func prepareMkdir(driveId string, importFileItems []RapidUploadItem) map[string] param := &aliyunpan.FileListParam{} param.DriveId = driveId param.ParentFileId = rs.FileId - allFileInfo, err1 := panClient.FileListGetAll(param, 0) + allFileInfo, err1 := panClient.WebapiPanClient().FileListGetAll(param, 0) if err1 != nil { logger.Verboseln("获取文件信息出错") continue diff --git a/internal/command/locate.go b/internal/command/locate.go index 48d2edf..fa0cf42 100644 --- a/internal/command/locate.go +++ b/internal/command/locate.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -80,9 +80,9 @@ func CmdLocateUrl() cli.Command { func RunLocateUrl(driveId string, paths []string, saveFilePath string) { useInternalUrl := config.Config.TransferUrlType == 2 activeUser := GetActiveUser() - activeUser.PanClient().EnableCache() - activeUser.PanClient().ClearCache() - defer activeUser.PanClient().DisableCache() + activeUser.PanClient().WebapiPanClient().EnableCache() + activeUser.PanClient().WebapiPanClient().ClearCache() + defer activeUser.PanClient().WebapiPanClient().DisableCache() paths, err := makePathAbsolute(driveId, paths...) if err != nil { @@ -116,7 +116,7 @@ func RunLocateUrl(driveId string, paths []string, saveFilePath string) { } fileInfo := item.(*aliyunpan.FileEntity) if fileInfo.IsFolder() { // 文件夹,获取下面所有文件 - allFilesInFolder, er := activeUser.PanClient().FileListGetAll(&aliyunpan.FileListParam{ + allFilesInFolder, er := activeUser.PanClient().WebapiPanClient().FileListGetAll(&aliyunpan.FileListParam{ DriveId: driveId, ParentFileId: fileInfo.FileId, }, 300) @@ -131,7 +131,7 @@ func RunLocateUrl(driveId string, paths []string, saveFilePath string) { fileEntityQueue.Push(f) continue } - durl, apierr := activeUser.PanClient().GetFileDownloadUrl(&aliyunpan.GetFileDownloadUrlParam{ + durl, apierr := activeUser.PanClient().WebapiPanClient().GetFileDownloadUrl(&aliyunpan.GetFileDownloadUrlParam{ DriveId: driveId, FileId: f.FileId, }) @@ -151,7 +151,7 @@ func RunLocateUrl(driveId string, paths []string, saveFilePath string) { } } } else { // 文件 - durl, apierr := activeUser.PanClient().GetFileDownloadUrl(&aliyunpan.GetFileDownloadUrlParam{ + durl, apierr := activeUser.PanClient().WebapiPanClient().GetFileDownloadUrl(&aliyunpan.GetFileDownloadUrlParam{ DriveId: driveId, FileId: fileInfo.FileId, }) diff --git a/internal/command/login.go b/internal/command/login.go index 67c4e44..0be28b5 100644 --- a/internal/command/login.go +++ b/internal/command/login.go @@ -15,14 +15,12 @@ package command import ( "fmt" - "github.com/tickstep/aliyunpan-api/aliyunpan" "github.com/tickstep/aliyunpan/cmder/cmdliner" "github.com/tickstep/aliyunpan/internal/config" "github.com/tickstep/aliyunpan/internal/functions/panlogin" - "github.com/tickstep/library-go/logger" _ "github.com/tickstep/library-go/requester" "github.com/urfave/cli" - "time" + "strings" ) func CmdLogin() cli.Command { @@ -34,55 +32,36 @@ func CmdLogin() cli.Command { 1.常规登录,按提示一步一步来即可 aliyunpan login - 2.直接指定RefreshToken进行登录 - aliyunpan login -RefreshToken=8B12CBBCE89CA8DFC3445985B63B511B5E7EC7... - - 3.使用二维码方式进行登录 - aliyunpan login -QrCode `, Category: "阿里云盘账号", Before: ReloadConfigFunc, // 每次进行登录动作的时候需要调用刷新配置 After: SaveConfigFunc, // 登录完成需要调用保存配置 Action: func(c *cli.Context) error { - refreshTokenStr := "" - if refreshTokenStr == "" { - refreshTokenStr = c.String("RefreshToken") - } - useQrCode := c.Bool("QrCode") - - tokenId := "" - webToken := aliyunpan.WebLoginToken{} - refreshToken := "" + ticketId := "" + openToken := &config.PanClientToken{} + webToken := &config.PanClientToken{} var err error - tokenId, refreshToken, webToken, err = RunLogin(useQrCode, refreshTokenStr) + ticketId, openToken, webToken, err = RunLogin() if err != nil { fmt.Println(err) return err } - cloudUser, err := config.SetupUserByCookie(&webToken, config.Config.DeviceId, config.Config.DeviceName) + cloudUser, err := config.SetupUserByCookie(openToken, webToken, + ticketId, "", + config.Config.DeviceId, config.Config.DeviceName, + config.Config.ClientId, config.Config.ClientSecret) if cloudUser == nil { fmt.Println("登录失败: ", err) return nil } - cloudUser.RefreshToken = refreshToken - cloudUser.TokenId = tokenId + cloudUser.TicketId = ticketId config.Config.SetActiveUser(cloudUser) fmt.Println("阿里云盘登录成功: ", cloudUser.Nickname) return nil }, // 命令的附加options参数说明,使用 help panlogin 命令即可查看 - Flags: []cli.Flag{ - // aliyunpan panlogin -RefreshToken=8B12CBBCE89CA8DFC3445985B63B511B5E7EC7... - cli.StringFlag{ - Name: "RefreshToken", - Usage: "使用RefreshToken Cookie来登录帐号", - }, - cli.BoolFlag{ - Name: "QrCode", - Usage: "使用二维码登录", - }, - }, + Flags: []cli.Flag{}, } } @@ -117,7 +96,7 @@ func CmdLogout() cli.Command { } // 云端注销登录 - //if _, e := activeUser.PanClient().DeviceLogout(); e != nil { + //if _, e := activeUser.PanClient().WebapiPanClient().DeviceLogout(); e != nil { // fmt.Printf("登出设备失败,请手动在网页端进行登出: %s\n", e.String()) //} @@ -139,55 +118,40 @@ func CmdLogout() cli.Command { } } -func RunLogin(useQrCodeLogin bool, refreshToken string) (tokenId, refreshTokenStr string, webToken aliyunpan.WebLoginToken, error error) { - if useQrCodeLogin { - h := panlogin.NewLoginHelper(config.DefaultTokenServiceWebHost) - qrCodeUrlResult, err := h.GetQRCodeLoginUrl("") - if err != nil { - fmt.Println("二维码登录错误:", err) - return "", "", aliyunpan.WebLoginToken{}, err - } - fmt.Printf("请在浏览器打开以下链接进行扫码登录,链接有效时间为5分钟\n%s\n\n", qrCodeUrlResult.TokenUrl+"&deviceId="+config.Config.DeviceId) +func RunLogin() (ticketId string, openapiToken, webapiToken *config.PanClientToken, error error) { + h := panlogin.NewLoginHelper(config.DefaultTokenServiceWebHost) - // handler waiting - line := cmdliner.NewLiner() - var qrCodeLoginResult *panlogin.QRCodeLoginResult - queryResult := true - defer line.Close() + // web login request + qrCodeUrlResult, err := h.GetQRCodeLoginUrl("") + if err != nil { + fmt.Println("登录出错:", err) + return "", nil, nil, err + } + ticketId = qrCodeUrlResult.TokenId + loginUrl := &strings.Builder{} + fmt.Fprintf(loginUrl, "https://openapi.alipan.com/oauth/authorize?client_id=%s&redirect_uri=https%%3A%%2F%%2Fapi.tickstep.com%%2Fauth%%2Ftickstep%%2Faliyunpan%%2Ftoken%%2Fopenapi%%2F%s%%2Fauth&scope=user:base,file:all:read,file:all:write", + config.Config.ClientId, ticketId) + fmt.Printf("请在浏览器打开以下链接进行扫码登录,链接有效时间为5分钟\n%s\n\n", loginUrl) - go func() { - for queryResult { - time.Sleep(3 * time.Second) - qr, er := h.GetQRCodeLoginResult(qrCodeUrlResult.TokenId) - if er != nil { - continue - } - logger.Verboseln(qr) - if qr.QrCodeStatus == "CONFIRMED" { - // login successfully - qrCodeLoginResult = qr - break - } else if qr.QrCodeStatus == "EXPIRED" { - break - } - } - }() + // handler waiting + line := cmdliner.NewLiner() + defer line.Close() + line.State.Prompt("请在浏览器里面完成扫码登录,然后再按Enter键继续...") - line.State.Prompt("请在浏览器里面完成扫码登录,然后再按Enter键继续...") - if qrCodeLoginResult == nil { - queryResult = false - return "", "", aliyunpan.WebLoginToken{}, fmt.Errorf("二维码登录失败") - } - - tokenStr, er := h.ParseSecureRefreshToken("", qrCodeLoginResult.SecureRefreshToken) - if er != nil { - fmt.Println("解析Token错误:", er) - return "", "", aliyunpan.WebLoginToken{}, er - } - refreshToken = tokenStr - tokenId = qrCodeUrlResult.TokenId + // get login token + comToken, er := h.GetLoginToken(ticketId) + if er != nil { + return ticketId, nil, nil, fmt.Errorf("登录失败,请稍后尝试重新登录") } - refreshTokenStr, webToken, error = DoLoginHelper(refreshToken) - return + return ticketId, + &config.PanClientToken{ + AccessToken: comToken.Openapi.AccessToken, + Expired: comToken.Openapi.Expired, + }, + &config.PanClientToken{ + AccessToken: comToken.Webapi.AccessToken, + Expired: comToken.Webapi.Expired, + }, + nil } diff --git a/internal/command/ls_search.go b/internal/command/ls_search.go index 7ba6cac..ea4ebe2 100644 --- a/internal/command/ls_search.go +++ b/internal/command/ls_search.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -169,7 +169,7 @@ func RunLs(driveId, targetPath string, lsOptions *LsOptions, fileListParam.OrderBy = orderBy fileListParam.OrderDirection = orderDirection if targetPathInfo.IsFolder() { - fileResult, err1 := activeUser.PanClient().FileListGetAll(fileListParam, 200) + fileResult, err1 := activeUser.PanClient().WebapiPanClient().FileListGetAll(fileListParam, 200) if err1 != nil { fmt.Println(err1) return diff --git a/internal/command/mkdir.go b/internal/command/mkdir.go index 3925d40..e97b4bc 100644 --- a/internal/command/mkdir.go +++ b/internal/command/mkdir.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -56,7 +56,7 @@ func RunMkdir(driveId, name string) { fullpath := activeUser.PathJoin(driveId, name) rs := &aliyunpan.MkdirResult{} err := apierror.NewFailedApiError("") - rs, err = activeUser.PanClient().Mkdir(driveId, "", fullpath) + rs, err = activeUser.PanClient().WebapiPanClient().Mkdir(driveId, "", fullpath) if err != nil { fmt.Println("创建文件夹失败:" + err.Error()) diff --git a/internal/command/mv.go b/internal/command/mv.go index 4beaffd..7710952 100644 --- a/internal/command/mv.go +++ b/internal/command/mv.go @@ -98,7 +98,7 @@ func RunMove(driveId string, paths ...string) { }) cacheCleanPaths = append(cacheCleanPaths, path.Dir(mfi.Path)) } - fmr, er := activeUser.PanClient().FileMove(moveFileParamList) + fmr, er := activeUser.PanClient().WebapiPanClient().FileMove(moveFileParamList) for _, rs := range fmr { if !rs.Success { @@ -138,7 +138,7 @@ func getFileInfo(driveId string, paths ...string) (opFileList []*aliyunpan.FileE // the last one is the target file path targetFilePath := path.Clean(paths[len(paths)-1]) absolutePath := activeUser.PathJoin(driveId, targetFilePath) - targetFile, err := activeUser.PanClient().FileInfoByPath(driveId, absolutePath) + targetFile, err := activeUser.PanClient().WebapiPanClient().FileInfoByPath(driveId, absolutePath) if err != nil || !targetFile.IsFolder() { return nil, nil, nil, fmt.Errorf("指定目标文件夹不存在") } diff --git a/internal/command/quota.go b/internal/command/quota.go index c3893fc..b78e252 100644 --- a/internal/command/quota.go +++ b/internal/command/quota.go @@ -52,7 +52,7 @@ func CmdQuota() cli.Command { } func RunGetQuotaInfo() (quotaInfo *QuotaInfo, error error) { - user, err := GetActivePanClient().GetUserInfo() + user, err := GetActivePanClient().WebapiPanClient().GetUserInfo() if err != nil { return nil, err } diff --git a/internal/command/recycle.go b/internal/command/recycle.go index 025267a..8dcd819 100644 --- a/internal/command/recycle.go +++ b/internal/command/recycle.go @@ -132,7 +132,7 @@ func CmdRecycle() cli.Command { // RunRecycleList 执行列出回收站文件列表 func RunRecycleList(driveId string) { panClient := GetActivePanClient() - fdl, err := panClient.RecycleBinFileListGetAll(&aliyunpan.RecycleBinFileListParam{ + fdl, err := panClient.WebapiPanClient().RecycleBinFileListGetAll(&aliyunpan.RecycleBinFileListParam{ DriveId: driveId, Limit: 100, }) @@ -174,7 +174,7 @@ func RunRecycleRestore(driveId string, fidStrList ...string) { return } - rbfr, err := panClient.RecycleBinFileRestore(restoreFileList) + rbfr, err := panClient.WebapiPanClient().RecycleBinFileRestore(restoreFileList) if rbfr != nil && len(rbfr) > 0 { fmt.Printf("还原文件成功\n") return @@ -203,7 +203,7 @@ func RunRecycleDelete(driveId string, fidStrList ...string) { return } - rbfr, err := panClient.RecycleBinFileDelete(deleteFileList) + rbfr, err := panClient.WebapiPanClient().RecycleBinFileDelete(deleteFileList) if rbfr != nil && len(rbfr) > 0 { fmt.Printf("彻底删除文件成功\n") return @@ -220,7 +220,7 @@ func RunRecycleClear(driveId string) { panClient := GetActivePanClient() // 提交清空回收站异步任务 - r, err := panClient.RecycleBinFileClear(&aliyunpan.RecycleBinFileClearParam{ + r, err := panClient.WebapiPanClient().RecycleBinFileClear(&aliyunpan.RecycleBinFileClearParam{ DriveId: driveId, }) if err != nil { @@ -229,7 +229,7 @@ func RunRecycleClear(driveId string) { } for i := 0; i < 10; i++ { - ar, err1 := panClient.AsyncTaskQueryStatus(&aliyunpan.AsyncTaskQueryStatusParam{ + ar, err1 := panClient.WebapiPanClient().AsyncTaskQueryStatus(&aliyunpan.AsyncTaskQueryStatusParam{ AsyncTaskId: r.AsyncTaskId, }) if err1 != nil { diff --git a/internal/command/rename.go b/internal/command/rename.go index b4362b2..b123b85 100644 --- a/internal/command/rename.go +++ b/internal/command/rename.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -136,14 +136,14 @@ func RunRename(driveId string, oldName string, newName string) { } fileId := "" - r, err := GetActivePanClient().FileInfoByPath(driveId, activeUser.PathJoin(driveId, oldName)) + r, err := GetActivePanClient().WebapiPanClient().FileInfoByPath(driveId, activeUser.PathJoin(driveId, oldName)) if err != nil { fmt.Printf("原文件不存在: %s, %s\n", oldName, err) return } fileId = r.FileId - b, e := activeUser.PanClient().FileRename(driveId, fileId, path.Base(newName)) + b, e := activeUser.PanClient().WebapiPanClient().FileRename(driveId, fileId, path.Base(newName)) if e != nil { fmt.Println(e.Err) return @@ -234,7 +234,7 @@ func RunRenameBatch(skipConfirm bool, driveId string, expression, replacement, f // 重命名 for _, file := range files { - b, e := activeUser.PanClient().FileRename(driveId, file.file.FileId, file.newFileName) + b, e := activeUser.PanClient().WebapiPanClient().FileRename(driveId, file.file.FileId, file.newFileName) if e != nil { fmt.Println(e.Err) return diff --git a/internal/command/rm.go b/internal/command/rm.go index cf3f728..2ff551d 100644 --- a/internal/command/rm.go +++ b/internal/command/rm.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -106,7 +106,7 @@ func RunRemove(driveId string, paths ...string) { // delete successDelFileEntity := []*aliyunpan.FileEntity{} - fdr, err := activeUser.PanClient().FileDelete(delFileInfos) + fdr, err := activeUser.PanClient().WebapiPanClient().FileDelete(delFileInfos) if fdr != nil { for _, item := range fdr { if !item.Success { diff --git a/internal/command/save.go b/internal/command/save.go index ff979d8..7bbcb43 100644 --- a/internal/command/save.go +++ b/internal/command/save.go @@ -72,7 +72,7 @@ func RunSave(driveId string, args ...string) { targetFilePath := path.Clean(args[len(args)-1]) absolutePath := activeUser.PathJoin(driveId, targetFilePath) - targetFile, err := activeUser.PanClient().FileInfoByPath(driveId, absolutePath) + targetFile, err := activeUser.PanClient().WebapiPanClient().FileInfoByPath(driveId, absolutePath) if err != nil || !targetFile.IsFolder() { fmt.Println("指定目标文件夹不存在") return @@ -88,19 +88,19 @@ func RunSave(driveId string, args ...string) { sharePwd = args[1] } - token, err := activeUser.PanClient().GetShareToken(shareID, sharePwd) + token, err := activeUser.PanClient().WebapiPanClient().GetShareToken(shareID, sharePwd) if err != nil { fmt.Println("读取分享链接失败:", err) return } - list, err := activeUser.PanClient().GetListByShare(token.ShareToken, shareID, "") + list, err := activeUser.PanClient().WebapiPanClient().GetListByShare(token.ShareToken, shareID, "") if err != nil { fmt.Println("读取分享文件列表失败:", err) return } for list.NextMarker != "" { - list2, err := activeUser.PanClient().GetListByShare(token.ShareToken, shareID, "") + list2, err := activeUser.PanClient().WebapiPanClient().GetListByShare(token.ShareToken, shareID, "") if err != nil { fmt.Println("读取分享文件列表失败:", err) return @@ -129,7 +129,7 @@ func RunSave(driveId string, args ...string) { } fmt.Println() - result, err := activeUser.PanClient().FileCopy(token.ShareToken, params) + result, err := activeUser.PanClient().WebapiPanClient().FileCopy(token.ShareToken, params) if err != nil { fmt.Println("保存分享文件失败:", err) return @@ -148,7 +148,7 @@ func RunSave(driveId string, args ...string) { } } if ids != nil { - result2, err := activeUser.PanClient().AsyncTaskGet(token.ShareToken, ids) + result2, err := activeUser.PanClient().WebapiPanClient().AsyncTaskGet(token.ShareToken, ids) if err != nil { fmt.Println("读取保存结果失败:", err) } diff --git a/internal/command/share.go b/internal/command/share.go index 04b3ec8..b61871a 100644 --- a/internal/command/share.go +++ b/internal/command/share.go @@ -286,7 +286,7 @@ func RunShareSet(modeFlag, driveId string, paths []string, expiredTime string, s if modeFlag == "3" { // 快传 - r, err1 := panClient.FastShareLinkCreate(aliyunpan.FastShareCreateParam{ + r, err1 := panClient.WebapiPanClient().FastShareLinkCreate(aliyunpan.FastShareCreateParam{ DriveId: driveId, FileIdList: fidList, }) @@ -303,7 +303,7 @@ func RunShareSet(modeFlag, driveId string, paths []string, expiredTime string, s fmt.Printf("链接:%s\n", r.ShareUrl) } else { // 分享 - r, err1 := panClient.ShareLinkCreate(aliyunpan.ShareCreateParam{ + r, err1 := panClient.WebapiPanClient().ShareLinkCreate(aliyunpan.ShareCreateParam{ DriveId: driveId, SharePwd: sharePwd, Expiration: expiredTime, @@ -330,7 +330,7 @@ func RunShareSet(modeFlag, driveId string, paths []string, expiredTime string, s // RunShareList 执行列出分享列表 func RunShareList() { activeUser := GetActiveUser() - records, err := activeUser.PanClient().ShareLinkList(activeUser.UserId) + records, err := activeUser.PanClient().WebapiPanClient().ShareLinkList(activeUser.UserId) if err != nil { fmt.Printf("获取分享列表失败: %s\n", err) return @@ -378,7 +378,7 @@ func RunShareCancel(shareIdList []string) { } activeUser := GetActiveUser() - r, err := activeUser.PanClient().ShareLinkCancel(shareIdList) + r, err := activeUser.PanClient().WebapiPanClient().ShareLinkCancel(shareIdList) if err != nil { fmt.Printf("取消分享操作失败: %s\n", err) return @@ -393,7 +393,7 @@ func RunShareCancel(shareIdList []string) { func RunShareExport(option, saveFilePath string) { activeUser := GetActiveUser() - records, err := activeUser.PanClient().ShareLinkList(activeUser.UserId) + records, err := activeUser.PanClient().WebapiPanClient().ShareLinkList(activeUser.UserId) if err != nil { fmt.Printf("获取分享列表失败: %s\n", err) return @@ -467,7 +467,7 @@ func RunShareMc(driveId string, hideRelativePath bool, panPaths []string) { totalCount := 0 for _, panPath := range panPaths { panPath = activeUser.PathJoin(driveId, panPath) - panClient.FilesDirectoriesRecurseList(driveId, panPath, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { + panClient.WebapiPanClient().FilesDirectoriesRecurseList(driveId, panPath, func(depth int, _ string, fd *aliyunpan.FileEntity, apiError *apierror.ApiError) bool { if apiError != nil { logger.Verbosef("%s\n", apiError) return true diff --git a/internal/command/sync.go b/internal/command/sync.go index 2f59409..b31b3e0 100644 --- a/internal/command/sync.go +++ b/internal/command/sync.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -18,6 +18,7 @@ import ( "github.com/tickstep/aliyunpan-api/aliyunpan" "github.com/tickstep/aliyunpan/cmder" "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/aliyunpan/internal/global" "github.com/tickstep/aliyunpan/internal/log" "github.com/tickstep/aliyunpan/internal/syncdrive" "github.com/tickstep/aliyunpan/internal/utils" @@ -302,7 +303,7 @@ func RunSync(defaultTask *syncdrive.SyncTask, fileDownloadParallel, fileUploadPa maxUploadRate := config.Config.MaxUploadRate activeUser := GetActiveUser() panClient := activeUser.PanClient() - panClient.DisableCache() + panClient.WebapiPanClient().DisableCache() // pan token expired checker continueFlag := int32(0) @@ -315,7 +316,8 @@ func RunSync(defaultTask *syncdrive.SyncTask, fileDownloadParallel, fileUploadPa time.Sleep(time.Duration(1) * time.Minute) if RefreshTokenInNeed(activeUser, config.Config.DeviceName) { logger.Verboseln("update access token for sync task") - panClient.UpdateToken(activeUser.WebToken) + userWebToken := NewWebLoginToken(activeUser.WebapiToken.AccessToken, activeUser.WebapiToken.Expired) + panClient.WebapiPanClient().UpdateToken(userWebToken) } } }(&continueFlag) @@ -354,7 +356,7 @@ func RunSync(defaultTask *syncdrive.SyncTask, fileDownloadParallel, fileUploadPa LocalFileModifiedCheckIntervalSec: localDelayTime, FileRecorder: fileRecorder, } - syncMgr := syncdrive.NewSyncTaskManager(activeUser, activeUser.DriveList.GetFileDriveId(), panClient, syncFolderRootPath, option) + syncMgr := syncdrive.NewSyncTaskManager(activeUser, activeUser.DriveList.GetFileDriveId(), panClient.WebapiPanClient(), syncFolderRootPath, option) syncConfigFile := syncMgr.ConfigFilePath() if tasks != nil { syncConfigFile = "(使用命令行配置)" @@ -377,7 +379,7 @@ func RunSync(defaultTask *syncdrive.SyncTask, fileDownloadParallel, fileUploadPa time.Sleep(60 * time.Second) } } else { - if config.IsAppInCliMode { + if global.IsAppInCliMode { // in cmd mode c := "" fmt.Println("本命令不会退出,如需要结束同步备份进程请输入y,然后按Enter键进行停止。") diff --git a/internal/command/token.go b/internal/command/token.go deleted file mode 100644 index 9214381..0000000 --- a/internal/command/token.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2020 tickstep. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -package command - -import ( - "fmt" - "github.com/tickstep/aliyunpan-api/aliyunpan" - "github.com/tickstep/aliyunpan/cmder" - "github.com/tickstep/aliyunpan/internal/config" - "github.com/tickstep/aliyunpan/internal/plugins" - "github.com/tickstep/aliyunpan/internal/utils" - "github.com/tickstep/library-go/logger" - "github.com/urfave/cli" -) - -func CmdToken() cli.Command { - return cli.Command{ - Name: "token", - Usage: "Token相关操作", - UsageText: cmder.App().Name + " token", - Category: "阿里云盘账号", - Before: ReloadConfigFunc, - Action: func(c *cli.Context) error { - cli.ShowCommandHelp(c, c.Command.Name) - return nil - }, - Subcommands: []cli.Command{ - { - Name: "update", - Usage: "更新Token", - UsageText: cmder.App().Name + " token update", - Description: ` -示例: - - 更新当前登录用户的Token - aliyunpan token update -mode 1 - - 更新全部登录用户的Token - aliyunpan token update -mode 2 -`, - Action: func(c *cli.Context) error { - if config.Config.ActiveUser() == nil { - fmt.Println("未登录账号,无需刷新Token") - return nil - } - - modeFlag := "1" - if c.IsSet("mode") { - modeFlag = c.String("mode") - } - RunTokenUpdate(modeFlag) - return nil - }, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "mode", - Usage: "模式,1-登录用户,2-全部用户", - Value: "1", - }, - }, - }, - }, - } -} - -// RunTokenUpdate 执行Token更新 -func RunTokenUpdate(modeFlag string) { - ReloadConfigFunc(nil) - - // 获取当前插件 - pluginManger := plugins.NewPluginManager(config.GetPluginDir()) - plugin, _ := pluginManger.GetPlugin() - params := &plugins.UserTokenRefreshFinishParams{ - Result: "success", - Message: "", - OldToken: "", - NewToken: "", - UpdatedAt: utils.NowTimeStr(), - } - - userList := config.Config.UserList - if userList == nil || len(userList) == 0 { - fmt.Printf("没有登录用户,无需刷新Token\n") - return - } - for _, user := range userList { - params.Result = "success" - - if modeFlag == "1" { - if user.UserId != config.Config.ActiveUID { - continue - } - } - newToken, e := aliyunpan.GetAccessTokenFromRefreshToken(user.RefreshToken) - if e != nil { - params.Result = "fail" - params.Message = e.Error() - fmt.Printf("无法为%s用户获取新的RefreshToken,可能需要重新登录\n", user.Nickname) - continue - } - if newToken != nil && newToken.RefreshToken != "" { - params.OldToken = user.RefreshToken - params.NewToken = newToken.RefreshToken - - user.RefreshToken = newToken.RefreshToken - user.WebToken = *newToken - fmt.Printf("成功刷新%s用户的RefreshToken\n", user.Nickname) - } else { - params.Result = "fail" - } - - // plugin callback - if er := plugin.UserTokenRefreshFinishCallback(plugins.GetContext(user), params); er != nil { - logger.Verbosef("UserTokenRefreshFinishCallback error: " + er.Error()) - } - } - SaveConfigFunc(nil) -} diff --git a/internal/command/tree.go b/internal/command/tree.go index adf28f4..d57c5af 100644 --- a/internal/command/tree.go +++ b/internal/command/tree.go @@ -146,7 +146,7 @@ func getTree(driveId, pathStr string, depth int, statistic *treeStatistic, setti fileListParam.OrderBy = aliyunpan.FileOrderByName fileListParam.OrderDirection = aliyunpan.FileOrderDirectionAsc if targetPathInfo.IsFolder() { - fileResult, err := activeUser.PanClient().FileListGetAll(fileListParam, 500) + fileResult, err := activeUser.PanClient().WebapiPanClient().FileListGetAll(fileListParam, 500) if err != nil { fmt.Println(err) return @@ -214,9 +214,9 @@ func getTree(driveId, pathStr string, depth int, statistic *treeStatistic, setti // RunTree 列出树形图 func RunTree(driveId, pathStr string, showFullPath, showFileSize bool, minSize, maxSize int64) { activeUser := config.Config.ActiveUser() - activeUser.PanClient().ClearCache() - activeUser.PanClient().EnableCache() - defer activeUser.PanClient().DisableCache() + activeUser.PanClient().WebapiPanClient().ClearCache() + activeUser.PanClient().WebapiPanClient().EnableCache() + defer activeUser.PanClient().WebapiPanClient().DisableCache() pathStr = activeUser.PathJoin(driveId, pathStr) statistic := &treeStatistic{ CountOfDir: 0, diff --git a/internal/command/upload.go b/internal/command/upload.go index 449a6f8..27ca48d 100644 --- a/internal/command/upload.go +++ b/internal/command/upload.go @@ -273,9 +273,9 @@ func CmdRapidUpload() cli.Command { // RunUpload 执行文件上传 func RunUpload(localPaths []string, savePath string, opt *UploadOptions) { activeUser := GetActiveUser() - activeUser.PanClient().EnableCache() - activeUser.PanClient().ClearCache() - defer activeUser.PanClient().DisableCache() + activeUser.PanClient().WebapiPanClient().EnableCache() + activeUser.PanClient().WebapiPanClient().ClearCache() + defer activeUser.PanClient().WebapiPanClient().DisableCache() // pan token expired checker continueFlag := int32(0) atomic.StoreInt32(&continueFlag, 0) @@ -316,13 +316,13 @@ func RunUpload(localPaths []string, savePath string, opt *UploadOptions) { // 超时时间 if opt.MaxTimeoutSec > 0 { - activeUser.PanClient().SetTimeout(time.Duration(opt.MaxTimeoutSec) * time.Second) + activeUser.PanClient().WebapiPanClient().SetTimeout(time.Duration(opt.MaxTimeoutSec) * time.Second) } fmt.Printf("\n[0] 当前文件上传最大并发量为: %d, 上传分片大小为: %s\n", opt.AllParallel, converter.ConvertFileSize(opt.BlockSize, 2)) savePath = activeUser.PathJoin(opt.DriveId, savePath) - _, err1 := activeUser.PanClient().FileInfoByPath(opt.DriveId, savePath) + _, err1 := activeUser.PanClient().WebapiPanClient().FileInfoByPath(opt.DriveId, savePath) if err1 != nil { fmt.Printf("警告: 上传文件, 获取云盘路径 %s 错误, %s\n", savePath, err1) } @@ -441,7 +441,7 @@ func RunUpload(localPaths []string, savePath string, opt *UploadOptions) { LocalFileChecksum: localfile.NewLocalSymlinkFileEntity(file), SavePath: subSavePath, DriveId: opt.DriveId, - PanClient: activeUser.PanClient(), + PanClient: activeUser.PanClient().WebapiPanClient(), UploadingDatabase: uploadDatabase, FolderCreateMutex: folderCreateMutex, Parallel: opt.Parallel, @@ -463,11 +463,11 @@ func RunUpload(localPaths []string, savePath string, opt *UploadOptions) { if saveFilePath != "/" { folderCreateMutex.Lock() fmt.Printf("正在检测和创建云盘文件夹: %s\n", saveFilePath) - _, apierr1 := activeUser.PanClient().FileInfoByPath(opt.DriveId, saveFilePath) + _, apierr1 := activeUser.PanClient().WebapiPanClient().FileInfoByPath(opt.DriveId, saveFilePath) time.Sleep(1 * time.Second) if apierr1 != nil && apierr1.Code == apierror.ApiCodeFileNotFoundCode { logger.Verbosef("%s 创建云盘文件夹: %s\n", utils.NowTimeStr(), saveFilePath) - rs, apierr := activeUser.PanClient().Mkdir(opt.DriveId, "root", saveFilePath) + rs, apierr := activeUser.PanClient().WebapiPanClient().Mkdir(opt.DriveId, "root", saveFilePath) if apierr != nil || rs.FileId == "" { fmt.Printf("创建云盘文件夹失败: %s\n", saveFilePath) } @@ -565,7 +565,7 @@ func doRapidUpload(driveId string, isOverwrite bool, item *RapidUploadItem) erro panDir, panFileName := path.Split(item.FilePath) saveFilePath = item.FilePath if panDir != "/" { - rs, apierr = panClient.MkdirRecursive(driveId, "", "", 0, strings.Split(path.Clean(panDir), "/")) + rs, apierr = panClient.WebapiPanClient().MkdirRecursive(driveId, "", "", 0, strings.Split(path.Clean(panDir), "/")) if apierr != nil || rs.FileId == "" { return fmt.Errorf("创建云盘文件夹失败") } @@ -578,13 +578,13 @@ func doRapidUpload(driveId string, isOverwrite bool, item *RapidUploadItem) erro if isOverwrite { // 标记覆盖旧同名文件 // 检查同名文件是否存在 - efi, apierr := panClient.FileInfoByPath(driveId, saveFilePath) + efi, apierr := panClient.WebapiPanClient().FileInfoByPath(driveId, saveFilePath) if apierr != nil && apierr.Code != apierror.ApiCodeFileNotFoundCode { return fmt.Errorf("检测同名文件失败,请稍后重试") } if efi != nil && efi.FileId != "" { // existed, delete it - fileDeleteResult, err1 := panClient.FileDelete([]*aliyunpan.FileBatchActionParam{{DriveId: efi.DriveId, FileId: efi.FileId}}) + fileDeleteResult, err1 := panClient.WebapiPanClient().FileDelete([]*aliyunpan.FileBatchActionParam{{DriveId: efi.DriveId, FileId: efi.FileId}}) if err1 != nil || len(fileDeleteResult) == 0 { return fmt.Errorf("无法删除文件,请稍后重试") } @@ -604,7 +604,7 @@ func doRapidUpload(driveId string, isOverwrite bool, item *RapidUploadItem) erro ContentHash: item.FileSha1, ParentFileId: rs.FileId, } - uploadOpEntity, apierr := panClient.CreateUploadFile(appCreateUploadFileParam) + uploadOpEntity, apierr := panClient.WebapiPanClient().CreateUploadFile(appCreateUploadFileParam) if apierr != nil { return fmt.Errorf("创建秒传任务失败:" + apierr.Error()) } diff --git a/internal/command/user_info.go b/internal/command/user_info.go index 0439259..6a1bae9 100644 --- a/internal/command/user_info.go +++ b/internal/command/user_info.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -137,5 +137,5 @@ func CmdWho() cli.Command { } func RunGetUserInfo() (userInfo *aliyunpan.UserInfo, error error) { - return GetActivePanClient().GetUserInfo() + return GetActivePanClient().WebapiPanClient().GetUserInfo() } diff --git a/internal/command/utils.go b/internal/command/utils.go index a658382..bab9dd5 100644 --- a/internal/command/utils.go +++ b/internal/command/utils.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -16,8 +16,6 @@ package command import ( "fmt" "github.com/tickstep/aliyunpan-api/aliyunpan" - "github.com/tickstep/aliyunpan-api/aliyunpan/apierror" - "github.com/tickstep/aliyunpan/cmder/cmdliner" "github.com/tickstep/aliyunpan/internal/config" "github.com/tickstep/aliyunpan/internal/functions/panlogin" "github.com/tickstep/aliyunpan/internal/plugins" @@ -60,7 +58,7 @@ var ( // RunTestShellPattern 执行测试通配符 func RunTestShellPattern(driveId string, pattern string) { acUser := GetActiveUser() - files, err := acUser.PanClient().MatchPathByShellPattern(driveId, GetActiveUser().PathJoin(driveId, pattern)) + files, err := acUser.PanClient().WebapiPanClient().MatchPathByShellPattern(driveId, GetActiveUser().PathJoin(driveId, pattern)) if err != nil { fmt.Println(err) return @@ -75,7 +73,7 @@ func RunTestShellPattern(driveId string, pattern string) { func matchPathByShellPattern(driveId string, patterns ...string) (files []*aliyunpan.FileEntity, e error) { acUser := GetActiveUser() for k := range patterns { - ps, err := acUser.PanClient().MatchPathByShellPattern(driveId, acUser.PathJoin(driveId, patterns[k])) + ps, err := acUser.PanClient().WebapiPanClient().MatchPathByShellPattern(driveId, acUser.PathJoin(driveId, patterns[k])) if err != nil { return nil, err } @@ -128,7 +126,21 @@ func UnescapeStr(s string) string { return r } -// RefreshTokenInNeed 刷新refresh token +func NewWebLoginToken(accessToken string, expired int64) aliyunpan.WebLoginToken { + webapiToken := &config.PanClientToken{ + AccessToken: accessToken, + Expired: expired, + } + return aliyunpan.WebLoginToken{ + AccessTokenType: "Bearer", + AccessToken: webapiToken.AccessToken, + RefreshToken: "", + ExpiresIn: 7200, + ExpireTime: webapiToken.GetExpiredTimeCstStr(), + } +} + +// RefreshTokenInNeed 刷新webapi access token func RefreshTokenInNeed(activeUser *config.PanUser, deviceName string) bool { if activeUser == nil { return false @@ -136,11 +148,11 @@ func RefreshTokenInNeed(activeUser *config.PanUser, deviceName string) bool { // refresh expired token if activeUser.PanClient() != nil { - if len(activeUser.WebToken.RefreshToken) > 0 { + if len(activeUser.WebapiToken.AccessToken) > 0 { cz := time.FixedZone("CST", 8*3600) // 东8区 - expiredTime, _ := time.ParseInLocation("2006-01-02 15:04:05", activeUser.WebToken.ExpireTime, cz) + expiredTime := time.Unix(activeUser.WebapiToken.Expired, 0).In(cz) now := time.Now() - if (expiredTime.Unix() - now.Unix()) <= (20 * 60) { // 20min + if (expiredTime.Unix() - now.Unix()) <= (5 * 60) { // 有效期小于5min就刷新 pluginManger := plugins.NewPluginManager(config.GetPluginDir()) plugin, _ := pluginManger.GetPlugin() params := &plugins.UserTokenRefreshFinishParams{ @@ -152,15 +164,23 @@ func RefreshTokenInNeed(activeUser *config.PanUser, deviceName string) bool { } // need update refresh token - logger.Verboseln("access token expired, get new from refresh token") - if wt, er := aliyunpan.GetAccessTokenFromRefreshToken(activeUser.RefreshToken); er == nil { + logger.Verboseln("access token expired, get new from server") + loginHelper := panlogin.NewLoginHelper(config.DefaultTokenServiceWebHost) + wt, e := loginHelper.GetWebapiNewToken(activeUser.TicketId, activeUser.UserId) + if e != nil { + logger.Verboseln("get web token from server error: ", e) + } + if wt != nil { params.Result = "success" - params.OldToken = activeUser.RefreshToken - params.NewToken = wt.RefreshToken + params.OldToken = activeUser.WebapiToken.AccessToken + params.NewToken = wt.AccessToken - activeUser.RefreshToken = wt.RefreshToken - activeUser.WebToken = *wt - activeUser.PanClient().UpdateToken(*wt) + userWebToken := NewWebLoginToken(wt.AccessToken, wt.Expired) + activeUser.WebapiToken = &config.PanClientToken{ + AccessToken: wt.AccessToken, + Expired: wt.Expired, + } + activeUser.PanClient().WebapiPanClient().UpdateToken(userWebToken) logger.Verboseln("get new access token success") // plugin callback @@ -169,7 +189,7 @@ func RefreshTokenInNeed(activeUser *config.PanUser, deviceName string) bool { } // create new signature - _, e := activeUser.PanClient().CreateSession(nil) + _, e := activeUser.PanClient().WebapiPanClient().CreateSession(nil) if e != nil { logger.Verboseln("call CreateSession error in RefreshTokenInNeed: " + e.Error()) } @@ -179,8 +199,8 @@ func RefreshTokenInNeed(activeUser *config.PanUser, deviceName string) bool { // if token has expired, callback plugin api for notify if now.Unix() >= expiredTime.Unix() { params.Result = "fail" - params.Message = er.Error() - params.OldToken = activeUser.RefreshToken + params.Message = e.Error() + params.OldToken = activeUser.WebapiToken.AccessToken if er1 := plugin.UserTokenRefreshFinishCallback(plugins.GetContext(activeUser), params); er1 != nil { logger.Verbosef("UserTokenRefreshFinishCallback error: " + er1.Error()) } @@ -205,110 +225,26 @@ func isMatchWildcardPattern(name string) bool { return strings.ContainsAny(name, "*") || strings.ContainsAny(name, "?") || strings.ContainsAny(name, "[") } -// DoLoginHelper 登录助手,使用token进行登录 -func DoLoginHelper(refreshToken string) (refreshTokenStr string, webToken aliyunpan.WebLoginToken, error error) { - line := cmdliner.NewLiner() - defer line.Close() - - if refreshToken == "" { - refreshToken, error = line.State.Prompt("请输入RefreshToken, 回车键提交 > ") - if error != nil { - return - } - } - - // app login - atoken, apperr := aliyunpan.GetAccessTokenFromRefreshToken(refreshToken) - if apperr != nil { - if apperr.Code == apierror.ApiCodeTokenExpiredCode || apperr.Code == apierror.ApiCodeRefreshTokenExpiredCode { - fmt.Println("Token过期,需要重新登录") - } else { - fmt.Println("Token登录失败:", apperr) - } - return "", webToken, fmt.Errorf("登录失败") - } - refreshTokenStr = refreshToken - return refreshTokenStr, *atoken, nil -} - // TryLogin 尝试登录,基础应用支持的各类登录方式进行尝试成功登录 func TryLogin() *config.PanUser { - // 获取当前插件 - pluginManger := plugins.NewPluginManager(config.GetPluginDir()) - plugin, _ := pluginManger.GetPlugin() - params := &plugins.UserTokenRefreshFinishParams{ - Result: "success", - Message: "", - OldToken: "", - NewToken: "", - UpdatedAt: utils.NowTimeStr(), - } - // can do automatically login? for _, u := range config.Config.UserList { if u.UserId == config.Config.ActiveUID { // login - _, webToken, err := DoLoginHelper(u.RefreshToken) - if err != nil { - logger.Verboseln("automatically login use saved refresh token error ", err) - if u.TokenId != "" { - logger.Verboseln("try to login use tokenId") - h := panlogin.NewLoginHelper(config.DefaultTokenServiceWebHost) - r, e := h.GetRefreshToken(u.TokenId) - if e != nil { - logger.Verboseln("try to login use tokenId error", e) - // login fail plugin callback - params.Result = "fail" - params.OldToken = u.RefreshToken - params.NewToken = "" - if er := plugin.UserTokenRefreshFinishCallback(plugins.GetContext(u), params); er != nil { - logger.Verbosef("UserTokenRefreshFinishCallback error: " + er.Error()) - } - break - } - refreshToken, e := h.ParseSecureRefreshToken("", r.SecureRefreshToken) - if e != nil { - logger.Verboseln("try to parse refresh token error", e) - break - } - _, webToken, err = DoLoginHelper(refreshToken) - if err != nil { - logger.Verboseln("try to use refresh token from tokenId error", e) - break - } - fmt.Println("Token重新自动登录成功") - // save new refresh token - u.RefreshToken = refreshToken - } else { - // login fail plugin callback - params.Result = "fail" - params.OldToken = u.RefreshToken - params.NewToken = "" - if er := plugin.UserTokenRefreshFinishCallback(plugins.GetContext(u), params); er != nil { - logger.Verbosef("UserTokenRefreshFinishCallback error: " + er.Error()) - } - } - break + cloudUser, err := config.SetupUserByCookie(u.OpenapiToken, u.WebapiToken, + u.TicketId, u.UserId, + config.Config.DeviceId, config.Config.DeviceName, + config.Config.ClientId, config.Config.ClientSecret) + if cloudUser == nil { + fmt.Println("尝试登录失败: ", err) + return nil } - // plugin param - params.Result = "success" - params.OldToken = u.RefreshToken - params.NewToken = webToken.RefreshToken - - // success login, save new token and access token - u.RefreshToken = webToken.RefreshToken - u.WebToken = webToken // save SaveConfigFunc(nil) // reload ReloadConfigFunc(nil) - // do plugin callback - if er := plugin.UserTokenRefreshFinishCallback(plugins.GetContext(u), params); er != nil { - logger.Verbosef("UserTokenRefreshFinishCallback error: " + er.Error()) - } - return config.Config.ActiveUser() } } diff --git a/internal/command/xcp.go b/internal/command/xcp.go index 0f559f5..b8b1431 100644 --- a/internal/command/xcp.go +++ b/internal/command/xcp.go @@ -106,7 +106,7 @@ func RunXCopy(srcDriveId, dstDriveId string, paths ...string) { copyFileParamList = append(copyFileParamList, mfi.FileId) cacheCleanPaths = append(cacheCleanPaths, path.Dir(mfi.Path)) } - fccr, er := activeUser.PanClient().FileCrossDriveCopy(&aliyunpan.FileCrossCopyParam{ + fccr, er := activeUser.PanClient().WebapiPanClient().FileCrossDriveCopy(&aliyunpan.FileCrossCopyParam{ FromDriveId: srcDriveId, FromFileIds: copyFileParamList, ToDriveId: dstDriveId, @@ -151,7 +151,7 @@ func getCrossCopyFileInfo(srcDriveId, dstDriveId string, paths ...string) (opFil // the last one is the target file path targetFilePath := path.Clean(paths[len(paths)-1]) absolutePath := activeUser.PathJoin(dstDriveId, targetFilePath) - targetFile, err := activeUser.PanClient().FileInfoByPath(dstDriveId, absolutePath) + targetFile, err := activeUser.PanClient().WebapiPanClient().FileInfoByPath(dstDriveId, absolutePath) if err != nil || !targetFile.IsFolder() { return nil, nil, nil, fmt.Errorf("指定目标文件夹不存在") } diff --git a/internal/config/cache.go b/internal/config/cache.go index cd3df5b..14ae59d 100644 --- a/internal/config/cache.go +++ b/internal/config/cache.go @@ -30,7 +30,7 @@ func (pu *PanUser) DeleteOneCache(dirPath string) { func (pu *PanUser) CacheFilesDirectoriesList(pathStr string) (fdl aliyunpan.FileList, apiError *apierror.ApiError) { data := pu.cacheOpMap.CacheOperation(pu.ActiveDriveId, pathStr+"_OrderByName", func() expires.DataExpires { var fi *aliyunpan.FileEntity - fi, apiError = pu.panClient.FileInfoByPath(pu.ActiveDriveId, pathStr) + fi, apiError = pu.panClient.WebapiPanClient().FileInfoByPath(pu.ActiveDriveId, pathStr) if apiError != nil { return nil } @@ -38,7 +38,7 @@ func (pu *PanUser) CacheFilesDirectoriesList(pathStr string) (fdl aliyunpan.File DriveId: pu.ActiveDriveId, ParentFileId: fi.FileId, } - fdl, apiError = pu.panClient.FileListGetAll(fileListParam, 100) + fdl, apiError = pu.panClient.WebapiPanClient().FileListGetAll(fileListParam, 100) if apiError != nil { return nil } diff --git a/internal/config/pan_client.go b/internal/config/pan_client.go new file mode 100644 index 0000000..a258217 --- /dev/null +++ b/internal/config/pan_client.go @@ -0,0 +1,31 @@ +package config + +import ( + "github.com/tickstep/aliyunpan-api/aliyunpan" + "github.com/tickstep/aliyunpan-api/aliyunpan_open" +) + +type ( + // PanClient 云盘客户端 + PanClient struct { + // 网页WEB接口客户端 + webapiPanClient *aliyunpan.PanClient + // 阿里openapi接口客户端 + openapiPanClient *aliyunpan_open.OpenPanClient + } +) + +func NewPanClient(webClient *aliyunpan.PanClient, openClient *aliyunpan_open.OpenPanClient) *PanClient { + return &PanClient{ + webapiPanClient: webClient, + openapiPanClient: openClient, + } +} + +func (p *PanClient) WebapiPanClient() *aliyunpan.PanClient { + return p.webapiPanClient +} + +func (p *PanClient) OpenapiPanClient() *aliyunpan_open.OpenPanClient { + return p.openapiPanClient +} diff --git a/internal/config/pan_config.go b/internal/config/pan_config.go index 071273a..56dd543 100644 --- a/internal/config/pan_config.go +++ b/internal/config/pan_config.go @@ -61,6 +61,9 @@ const ( // DefaultDeviceName 默认客户端名称 DefaultDeviceName = "Chrome浏览器" + + // DefaultClientId 默认的clientId + DefaultClientId = "cf9f70e8fc61430f8ec5ab5cadf31375" ) var ( @@ -69,11 +72,6 @@ var ( // Config 配置信息, 由外部调用 Config = NewConfig(configFilePath) - - AppVersion string - - // IsAppInCliMode 是否在交互模式 - IsAppInCliMode = false ) type UpdateCheckInfo struct { @@ -110,6 +108,10 @@ type PanConfig struct { DeviceId string `json:"deviceId"` // 客户端ID,用于标识登录客户端,阿里单个账号最多允许10个客户端同时登录 DeviceName string `json:"deviceName"` // 客户端名称,默认为:Chrome浏览器 + // Openapi客户端信息 + ClientId string `json:"clientId"` + ClientSecret string `json:"clientSecret"` + configFilePath string configFile *os.File fileMu sync.Mutex @@ -270,6 +272,9 @@ func (c *PanConfig) loadConfigFromFile() (err error) { if c.DeviceName == "" { c.DeviceName = DefaultDeviceName } + if c.ClientId == "" { + c.ClientId = DefaultClientId + } return nil } @@ -294,6 +299,7 @@ func (c *PanConfig) initDefaultConfig() { c.VideoFileExtensions = DefaultVideoFileExtensions c.DeviceId = RandomDeviceId() // 生成默认客户端ID c.DeviceName = DefaultDeviceName + c.ClientId = DefaultClientId c.FileRecordConfig = "1" // 默认开启 c.PreferIPType = "ipv4" // 默认优先IPv4 } @@ -390,7 +396,10 @@ func (c *PanConfig) ActiveUser() *PanUser { if u.UserId == c.ActiveUID { if u.PanClient() == nil { // restore client - user, err := SetupUserByCookie(&u.WebToken, c.DeviceId, c.DeviceName) + user, err := SetupUserByCookie(u.OpenapiToken, u.WebapiToken, + u.TicketId, u.UserId, + c.DeviceId, c.DeviceName, + c.ClientId, c.ClientSecret) if err != nil { logger.Verboseln("setup user error") return nil @@ -401,7 +410,7 @@ func (c *PanConfig) ActiveUser() *PanUser { u.DriveList = user.DriveList // check workdir valid or not if user.IsFileDriveActive() { - fe, err1 := u.PanClient().FileInfoByPath(u.ActiveDriveId, u.Workdir) + fe, err1 := u.PanClient().WebapiPanClient().FileInfoByPath(u.ActiveDriveId, u.Workdir) if err1 != nil { // default to root u.Workdir = "/" @@ -410,7 +419,7 @@ func (c *PanConfig) ActiveUser() *PanUser { u.WorkdirFileEntity = *fe } } else if user.IsResourceDriveActive() { - fe, err1 := u.PanClient().FileInfoByPath(u.ActiveDriveId, u.ResourceWorkdir) + fe, err1 := u.PanClient().WebapiPanClient().FileInfoByPath(u.ActiveDriveId, u.ResourceWorkdir) if err1 != nil { // default to root u.ResourceWorkdir = "/" @@ -419,7 +428,7 @@ func (c *PanConfig) ActiveUser() *PanUser { u.ResourceWorkdirFileEntity = *fe } } else if user.IsAlbumDriveActive() { - fe, err1 := u.PanClient().FileInfoByPath(u.ActiveDriveId, u.AlbumWorkdir) + fe, err1 := u.PanClient().WebapiPanClient().FileInfoByPath(u.ActiveDriveId, u.AlbumWorkdir) if err1 != nil { // default to root u.AlbumWorkdir = "/" @@ -444,9 +453,9 @@ func (c *PanConfig) SetActiveUser(user *PanUser) *PanUser { if u.UserId == user.UserId { // update user info u.Nickname = user.Nickname - u.WebToken = user.WebToken - u.RefreshToken = user.RefreshToken - u.TokenId = user.TokenId + u.WebapiToken = user.WebapiToken + u.OpenapiToken = user.OpenapiToken + u.TicketId = user.TicketId needToInsert = false break } diff --git a/internal/config/pan_user.go b/internal/config/pan_user.go index 7d50e8c..46b9926 100644 --- a/internal/config/pan_user.go +++ b/internal/config/pan_user.go @@ -17,10 +17,14 @@ import ( "fmt" "github.com/tickstep/aliyunpan-api/aliyunpan" "github.com/tickstep/aliyunpan-api/aliyunpan/apierror" + "github.com/tickstep/aliyunpan-api/aliyunpan_open" + "github.com/tickstep/aliyunpan-api/aliyunpan_open/openapi" + "github.com/tickstep/aliyunpan/internal/functions/panlogin" "github.com/tickstep/library-go/expires/cachemap" "github.com/tickstep/library-go/logger" "path" "path/filepath" + "time" ) type DriveInfo struct { @@ -57,12 +61,28 @@ func (d DriveInfoList) GetResourceDriveId() string { return "" } +// PanClientToken 授权Token +type PanClientToken struct { + // AccessToken AccessToken + AccessToken string `json:"accessToken"` + // Expired 过期时间戳,单位秒 + Expired int64 `json:"expired"` +} + +// GetExpiredTimeCstStr 获取东八区时间字符串 +func (t *PanClientToken) GetExpiredTimeCstStr() string { + cz := time.FixedZone("CST", 8*3600) // 东8区 + tm := time.Unix(t.Expired, 0).In(cz) + return tm.Format("2006-01-02 15:04:05") +} + type PanUser struct { + // 用户信息 UserId string `json:"userId"` Nickname string `json:"nickname"` AccountName string `json:"accountName"` - // 文件/备份盘 + // 文件(备份盘) Workdir string `json:"workdir"` WorkdirFileEntity aliyunpan.FileEntity `json:"workdirFileEntity"` @@ -74,89 +94,115 @@ type PanUser struct { AlbumWorkdir string `json:"albumWorkdir"` AlbumWorkdirFileEntity aliyunpan.FileEntity `json:"albumWorkdirFileEntity"` + // 用户网盘信息 ActiveDriveId string `json:"activeDriveId"` DriveList DriveInfoList `json:"driveList"` - RefreshToken string `json:"refreshToken"` - WebToken aliyunpan.WebLoginToken `json:"webToken"` - TokenId string `json:"tokenId"` + // 授权Token信息 + TicketId string `json:"ticketId"` + WebapiToken *PanClientToken `json:"webapiToken"` + OpenapiToken *PanClientToken `json:"openapiToken"` - panClient *aliyunpan.PanClient - cacheOpMap cachemap.CacheOpMap + // API客户端 + //panClient *aliyunpan.PanClient `json:"-"` + panClient *PanClient `json:"-"` + cacheOpMap cachemap.CacheOpMap `json:"-"` } type PanUserList []*PanUser -func SetupUserByCookie(webToken *aliyunpan.WebLoginToken, deviceId, deviceName string) (user *PanUser, err *apierror.ApiError) { +func SetupUserByCookie(openapiToken, webapiToken *PanClientToken, ticketId, userId, deviceId, deviceName, clientId, clientSecret string) (user *PanUser, err *apierror.ApiError) { tryRefreshWebToken := true + tryRefreshOpenToken := true + loginHelper := panlogin.NewLoginHelper(DefaultTokenServiceWebHost) - if webToken == nil { - return nil, apierror.NewFailedApiError("web token is empty") + if openapiToken == nil { + return nil, apierror.NewFailedApiError("openapi token is empty") + } + if webapiToken == nil { + return nil, apierror.NewFailedApiError("webapi token is empty") } -doLoginAct: - appConfig := aliyunpan.AppConfig{ - AppId: "25dzX3vbYqktVxyX", - DeviceId: deviceId, - UserId: "", - Nonce: 0, - PublicKey: "", - } - panClient := aliyunpan.NewPanClient(*webToken, aliyunpan.AppLoginToken{}, appConfig, aliyunpan.SessionConfig{ - DeviceName: deviceName, - ModelName: "Windows网页版", +doOpenLoginAct: + // setup openapi client + openPanClient := aliyunpan_open.NewOpenPanClient(openapi.ApiConfig{ + TicketId: ticketId, + UserId: userId, + ClientId: clientId, + ClientSecret: clientSecret, + }, openapi.ApiToken{ + AccessToken: openapiToken.AccessToken, + ExpiredAt: openapiToken.Expired, + }, func(newToken openapi.ApiToken) error { + // TODO: save & refresh new token + return nil }) - u := &PanUser{ - WebToken: *webToken, - panClient: panClient, - Workdir: "/", - WorkdirFileEntity: *aliyunpan.NewFileEntityForRootDir(), - } - - // web api token maybe expired - userInfo, err := panClient.GetUserInfo() + // open api token maybe expired + // check & refresh new one + openUserInfo, err := openPanClient.GetUserInfo() if err != nil { - if err.Code == apierror.ApiCodeTokenExpiredCode && tryRefreshWebToken { - tryRefreshWebToken = false - webCookie, _ := aliyunpan.GetAccessTokenFromRefreshToken(webToken.RefreshToken) - if webCookie != nil { - webToken = webCookie - goto doLoginAct + if err.Code == apierror.ApiCodeTokenExpiredCode && tryRefreshOpenToken { + tryRefreshOpenToken = false + wt, e := loginHelper.GetOpenapiNewToken(ticketId, userId) + if e != nil { + logger.Verboseln("get openapi token from server error: ", e) + return nil, apierror.NewFailedApiError("get new openapi token error, try login again") + } + if wt != nil { + openapiToken = &PanClientToken{ + AccessToken: wt.AccessToken, + Expired: wt.Expired, + } + goto doOpenLoginAct } } return nil, err } - name := "Unknown" - if userInfo != nil { - if userInfo.Nickname != "" { - name = userInfo.Nickname - } - // update user - u.UserId = userInfo.UserId - u.Nickname = name - u.AccountName = userInfo.UserName - - // default file drive - if u.ActiveDriveId == "" { - u.ActiveDriveId = userInfo.FileDriveId +doWebLoginAct: + // setup webapi client + appConfig := aliyunpan.AppConfig{ + AppId: "25dzX3vbYqktVxyX", + DeviceId: deviceId, + UserId: userId, + Nonce: 0, + PublicKey: "", + } + webPanClient := aliyunpan.NewPanClient(aliyunpan.WebLoginToken{ + AccessTokenType: "Bearer", + AccessToken: webapiToken.AccessToken, + RefreshToken: "", + ExpiresIn: 7200, + ExpireTime: webapiToken.GetExpiredTimeCstStr(), + }, aliyunpan.AppLoginToken{}, appConfig, aliyunpan.SessionConfig{ + DeviceName: deviceName, + ModelName: "Windows网页版", + }) + // web api token maybe expired + // check & refresh new one + webUserInfo, err2 := webPanClient.GetUserInfo() + if err != nil { + if err2.Code == apierror.ApiCodeTokenExpiredCode && tryRefreshWebToken { + tryRefreshWebToken = false + wt, e := loginHelper.GetWebapiNewToken(ticketId, userId) + if e != nil { + logger.Verboseln("get web token from server error: ", e) + } + if wt != nil { + webapiToken = &PanClientToken{ + AccessToken: wt.AccessToken, + Expired: wt.Expired, + } + goto doWebLoginAct + } } - - // drive list - u.DriveList = DriveInfoList{ - {DriveId: userInfo.FileDriveId, DriveTag: "File", DriveName: "备份盘"}, - {DriveId: userInfo.ResourceDriveId, DriveTag: "Resource", DriveName: "资源库"}, - {DriveId: userInfo.AlbumDriveId, DriveTag: "Album", DriveName: "相册"}, - } - } else { - // error, maybe the token has expired - return nil, apierror.NewFailedApiError("cannot get user info, the token has expired") + return nil, err } - // create session - appConfig.UserId = u.UserId - panClient.UpdateAppConfig(appConfig) - r, e := panClient.CreateSession(nil) + // web create session + appConfig.UserId = webUserInfo.UserId + webPanClient.UpdateAppConfig(appConfig) + r, e := webPanClient.CreateSession(nil) if e != nil { logger.Verboseln("call CreateSession error in SetupUserByCookie: " + e.Error()) } @@ -164,10 +210,56 @@ doLoginAct: logger.Verboseln("上传签名秘钥失败,可能是你账号登录的设备已超最大数量") } + // + // setup PanUser + // + u := &PanUser{ + WebapiToken: webapiToken, + OpenapiToken: openapiToken, + panClient: NewPanClient(webPanClient, openPanClient), + Workdir: "/", + WorkdirFileEntity: *aliyunpan.NewFileEntityForRootDir(), + } + + // setup user info + name := "Unknown" + if openUserInfo != nil { + // fill userId for client + u.PanClient().OpenapiPanClient().UpdateUserId(openUserInfo.UserId) + u.PanClient().WebapiPanClient().UpdateUserId(openUserInfo.UserId) + + // update user + if openUserInfo.Nickname != "" { + name = openUserInfo.Nickname + } + u.UserId = openUserInfo.UserId + u.Nickname = name + u.AccountName = openUserInfo.UserName + + // default file drive + if u.ActiveDriveId == "" { + u.ActiveDriveId = openUserInfo.FileDriveId + } + + // drive list + u.DriveList = DriveInfoList{ + {DriveId: openUserInfo.FileDriveId, DriveTag: "File", DriveName: "备份盘"}, + {DriveId: openUserInfo.ResourceDriveId, DriveTag: "Resource", DriveName: "资源库"}, + } + if webUserInfo != nil { + u.AccountName = webUserInfo.UserName + u.DriveList = append(u.DriveList, &DriveInfo{DriveId: webUserInfo.AlbumDriveId, DriveTag: "Album", DriveName: "相册"}) + } + } else { + // error, maybe the token has expired + return nil, apierror.NewFailedApiError("cannot get user info, the token has expired. please login again") + } + + // return user return u, nil } -func (pu *PanUser) PanClient() *aliyunpan.PanClient { +func (pu *PanUser) PanClient() *PanClient { return pu.panClient } @@ -192,21 +284,20 @@ func (pu *PanUser) PathJoin(driveId, p string) string { func (pu *PanUser) FreshWorkdirInfo() { if pu.IsFileDriveActive() { - fe, err := pu.PanClient().FileInfoById(pu.ActiveDriveId, pu.WorkdirFileEntity.FileId) + fe, err := pu.PanClient().WebapiPanClient().FileInfoById(pu.ActiveDriveId, pu.WorkdirFileEntity.FileId) if err != nil { logger.Verboseln("刷新工作目录信息失败") return } pu.WorkdirFileEntity = *fe } else if pu.IsAlbumDriveActive() { - fe, err := pu.PanClient().FileInfoById(pu.ActiveDriveId, pu.AlbumWorkdirFileEntity.FileId) + fe, err := pu.PanClient().WebapiPanClient().FileInfoById(pu.ActiveDriveId, pu.AlbumWorkdirFileEntity.FileId) if err != nil { logger.Verboseln("刷新工作目录信息失败") return } pu.AlbumWorkdirFileEntity = *fe } - } // GetSavePath 根据提供的网盘文件路径 panpath, 返回本地储存路径, diff --git a/internal/functions/panlogin/login_helper.go b/internal/functions/panlogin/login_helper.go index e543ab6..d97ecfc 100644 --- a/internal/functions/panlogin/login_helper.go +++ b/internal/functions/panlogin/login_helper.go @@ -4,7 +4,7 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/aliyunpan/internal/global" "github.com/tickstep/aliyunpan/internal/utils" "github.com/tickstep/library-go/crypto" "github.com/tickstep/library-go/getip" @@ -40,6 +40,17 @@ type QRCodeLoginResult struct { SecureRefreshToken string `json:"secureRefreshToken"` } +// LoginTokenResult Token结果 +type LoginTokenResult struct { + AccessToken string `json:"accessToken"` + Expired int64 `json:"expired"` +} + +type CommonTokenEntity struct { + Openapi *LoginTokenResult `json:"openapi"` + Webapi *LoginTokenResult `json:"webapi"` +} + func NewLoginHelper(webHost string) *LoginHelper { return &LoginHelper{ webHost: webHost, @@ -57,13 +68,13 @@ func (h *LoginHelper) GetQRCodeLoginUrl(keyStr string) (*QRCodeUrlResult, error) ipAddr = "127.0.0.1" } fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/qrcode/create?ip=%s&os=%s&arch=%s&version=%s&key=%s", - h.webHost, ipAddr, runtime.GOOS, runtime.GOARCH, config.AppVersion, keyStr) + h.webHost, ipAddr, runtime.GOOS, runtime.GOARCH, global.AppVersion, keyStr) logger.Verboseln("do request url: " + fullUrl.String()) header := map[string]string{ "accept": "application/json, text/plain, */*", "content-type": "application/json;charset=UTF-8", - "user-agent": "aliyunpan/" + config.AppVersion, + "user-agent": "aliyunpan/" + global.AppVersion, } // request client := requester.NewHTTPClient() @@ -114,7 +125,7 @@ func (h *LoginHelper) GetQRCodeLoginResult(tokenId string) (*QRCodeLoginResult, header := map[string]string{ "accept": "application/json, text/plain, */*", "content-type": "application/json;charset=UTF-8", - "user-agent": "aliyunpan/" + config.AppVersion, + "user-agent": "aliyunpan/" + global.AppVersion, } // request client := requester.NewHTTPClient() @@ -154,7 +165,7 @@ func (h *LoginHelper) GetRefreshToken(tokenId string) (*QRCodeLoginResult, error header := map[string]string{ "accept": "application/json, text/plain, */*", "content-type": "application/json;charset=UTF-8", - "user-agent": "aliyunpan/" + config.AppVersion, + "user-agent": "aliyunpan/" + global.AppVersion, } // request client := requester.NewHTTPClient() @@ -220,3 +231,123 @@ func (h *LoginHelper) ParseSecureRefreshToken(keyStr, secureRefreshToken string) } return refreshToken, nil } + +// GetWebapiNewToken 获取Webapi Token +func (h *LoginHelper) GetWebapiNewToken(ticketId, userId string) (*LoginTokenResult, error) { + fullUrl := strings.Builder{} + fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/webapi/%s/refresh?userId=%s", + h.webHost, ticketId, userId) + logger.Verboseln("do request url: " + fullUrl.String()) + header := map[string]string{ + "accept": "application/json, text/plain, */*", + "content-type": "application/json;charset=UTF-8", + "user-agent": "aliyunpan/" + global.AppVersion, + } + // request + client := requester.NewHTTPClient() + client.SetTimeout(20 * time.Second) + client.SetKeepAlive(false) + body, err := client.Fetch("GET", fullUrl.String(), nil, header) + if err != nil { + logger.Verboseln("get web token result error ", err) + return nil, err + } + + errResp := &LoginHttpResult{} + if err1 := json.Unmarshal(body, errResp); err1 != nil { + logger.Verboseln("parse result json error ", err1) + return nil, err1 + } + if errResp.Code != 0 { + return nil, fmt.Errorf(errResp.Msg) + } + + // parse result + r := &LoginHttpResult{} + r.Data = &LoginTokenResult{} + if err2 := json.Unmarshal(body, r); err2 != nil { + logger.Verboseln("parse web token result json error ", err2) + return nil, err2 + } + return r.Data.(*LoginTokenResult), nil +} + +// GetOpenapiNewToken 获取Openapi Token +func (h *LoginHelper) GetOpenapiNewToken(ticketId, userId string) (*LoginTokenResult, error) { + fullUrl := strings.Builder{} + fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/openapi/%s/refresh?userId=%s", + h.webHost, ticketId, userId) + logger.Verboseln("do request url: " + fullUrl.String()) + header := map[string]string{ + "accept": "application/json, text/plain, */*", + "content-type": "application/json;charset=UTF-8", + "user-agent": "aliyunpan/" + global.AppVersion, + } + // request + client := requester.NewHTTPClient() + client.SetTimeout(20 * time.Second) + client.SetKeepAlive(false) + body, err := client.Fetch("GET", fullUrl.String(), nil, header) + if err != nil { + logger.Verboseln("get openapi token result error ", err) + return nil, err + } + + errResp := &LoginHttpResult{} + if err1 := json.Unmarshal(body, errResp); err1 != nil { + logger.Verboseln("parse result json error ", err1) + return nil, err1 + } + if errResp.Code != 0 { + return nil, fmt.Errorf(errResp.Msg) + } + + // parse result + r := &LoginHttpResult{} + r.Data = &LoginTokenResult{} + if err2 := json.Unmarshal(body, r); err2 != nil { + logger.Verboseln("parse openapi token result json error ", err2) + return nil, err2 + } + return r.Data.(*LoginTokenResult), nil +} + +// GetLoginToken 获取登录后的Token +func (h *LoginHelper) GetLoginToken(ticketId string) (*CommonTokenEntity, error) { + fullUrl := strings.Builder{} + fmt.Fprintf(&fullUrl, "%s/auth/tickstep/aliyunpan/token/common/%s/login", + h.webHost, ticketId) + logger.Verboseln("do request url: " + fullUrl.String()) + header := map[string]string{ + "accept": "application/json, text/plain, */*", + "content-type": "application/json;charset=UTF-8", + "user-agent": "aliyunpan/" + global.AppVersion, + } + // request + client := requester.NewHTTPClient() + client.SetTimeout(20 * time.Second) + client.SetKeepAlive(false) + body, err := client.Fetch("GET", fullUrl.String(), nil, header) + if err != nil { + logger.Verboseln("get login token result error ", err) + return nil, err + } + + errResp := &LoginHttpResult{} + if err1 := json.Unmarshal(body, errResp); err1 != nil { + logger.Verboseln("parse result json error ", err1) + return nil, err1 + } + if errResp.Code != 0 { + return nil, fmt.Errorf(errResp.Msg) + } + + // parse result + r := &LoginHttpResult{} + r.Data = &CommonTokenEntity{} + if err2 := json.Unmarshal(body, r); err2 != nil { + logger.Verboseln("parse login token result json error ", err2) + return nil, err2 + } + return r.Data.(*CommonTokenEntity), nil +} diff --git a/internal/global/vars.go b/internal/global/vars.go new file mode 100644 index 0000000..6806442 --- /dev/null +++ b/internal/global/vars.go @@ -0,0 +1,9 @@ +package global + +var ( + // AppVersion 应用版本 + AppVersion string + + // IsAppInCliMode 是否在交互模式 + IsAppInCliMode = false +) diff --git a/internal/panupdate/panupdate.go b/internal/panupdate/panupdate.go index 466638e..1748199 100644 --- a/internal/panupdate/panupdate.go +++ b/internal/panupdate/panupdate.go @@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -21,6 +21,7 @@ import ( "github.com/tickstep/aliyunpan/cmder/cmdliner" "github.com/tickstep/aliyunpan/cmder/cmdutil" "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/aliyunpan/internal/global" "github.com/tickstep/aliyunpan/internal/utils" "github.com/tickstep/aliyunpan/library/requester/transfer" "github.com/tickstep/library-go/cachepool" @@ -66,7 +67,7 @@ func getReleaseFromTicstep(client *requester.HTTPClient, showPrompt bool) *Relea ipAddr = "127.0.0.1" } fmt.Fprintf(&fullUrl, "http://api.tickstep.com/update/tickstep/aliyunpan/releases/latest?ip=%s&os=%s&arch=%s&version=%s", - ipAddr, runtime.GOOS, runtime.GOARCH, config.AppVersion) + ipAddr, runtime.GOOS, runtime.GOARCH, global.AppVersion) resp, err := client.Req(http.MethodGet, fullUrl.String(), nil, nil) if resp != nil { defer resp.Body.Close() diff --git a/internal/plugins/plugin_manager.go b/internal/plugins/plugin_manager.go index c10b97e..6c2749f 100644 --- a/internal/plugins/plugin_manager.go +++ b/internal/plugins/plugin_manager.go @@ -3,6 +3,7 @@ package plugins import ( "fmt" "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/aliyunpan/internal/global" "github.com/tickstep/library-go/logger" "io/ioutil" "os" @@ -21,7 +22,7 @@ func GetContext(user *config.PanUser) *Context { if user == nil { return &Context{ AppName: "aliyunpan", - Version: config.AppVersion, + Version: global.AppVersion, UserId: "", Nickname: "", FileDriveId: "", @@ -30,7 +31,7 @@ func GetContext(user *config.PanUser) *Context { } return &Context{ AppName: "aliyunpan", - Version: config.AppVersion, + Version: global.AppVersion, UserId: user.UserId, Nickname: user.Nickname, FileDriveId: user.DriveList.GetFileDriveId(), diff --git a/main.go b/main.go index 31e5614..c90b4b4 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,7 @@ package main import ( "fmt" + "github.com/tickstep/aliyunpan/internal/global" "io/ioutil" "os" "os/exec" @@ -61,7 +62,7 @@ var ( ) func init() { - config.AppVersion = Version + global.AppVersion = Version cmdutil.ChWorkDir() err := config.Config.Init() @@ -147,7 +148,7 @@ func main() { os.Setenv(config.EnvVerbose, c.String("verbose")) isCli = true - config.IsAppInCliMode = true + global.IsAppInCliMode = true logger.Verbosef("提示: 你已经开启VERBOSE调试日志\n\n") var ( @@ -309,9 +310,9 @@ func main() { // check update command.ReloadConfigFunc(c) if config.Config.UpdateCheckInfo.LatestVer != "" { - if utils.ParseVersionNum(config.Config.UpdateCheckInfo.LatestVer) > utils.ParseVersionNum(config.AppVersion) { + if utils.ParseVersionNum(config.Config.UpdateCheckInfo.LatestVer) > utils.ParseVersionNum(global.AppVersion) { fmt.Printf("\n当前的软件版本为:%s, 现在有新版本 %s 可供更新,强烈推荐进行更新!(可以输入 update 命令进行更新)\n\n", - config.AppVersion, config.Config.UpdateCheckInfo.LatestVer) + global.AppVersion, config.Config.UpdateCheckInfo.LatestVer) } } go func() { @@ -415,7 +416,7 @@ func main() { command.CmdQuota(), // Token操作 - command.CmdToken(), + //command.CmdToken(), // 切换工作目录 cd command.CmdCd(),