From 87f951b5f5a95605190695d843bb328d8ecd58d8 Mon Sep 17 00:00:00 2001 From: tickstep Date: Mon, 3 Mar 2025 11:46:08 +0800 Subject: [PATCH] add remove file plugin callback #70 --- assets/plugin/js/remove_handler.js.sample | 105 ++++++++++++++++++++++ internal/command/rm.go | 51 ++++++++++- internal/plugins/idle_plugin.go | 4 + internal/plugins/js_plugin.go | 19 ++++ internal/plugins/plugin.go | 39 ++++++++ 5 files changed, 217 insertions(+), 1 deletion(-) create mode 100755 assets/plugin/js/remove_handler.js.sample diff --git a/assets/plugin/js/remove_handler.js.sample b/assets/plugin/js/remove_handler.js.sample new file mode 100755 index 0000000..18eb326 --- /dev/null +++ b/assets/plugin/js/remove_handler.js.sample @@ -0,0 +1,105 @@ +// ========================================================================================== +// aliyunpan JS插件回调处理函数 +// 支持 JavaScript ECMAScript 5.1 语言规范 +// +// 更多内容请查看官方文档:https://github.com/tickstep/aliyunpan +// ========================================================================================== + + +// ------------------------------------------------------------------------------------------ +// 函数说明:删除文件前的回调函数 +// +// 参数说明 +// context - 当前调用的上下文信息 +// { +// "appName": "aliyunpan", +// "version": "v0.1.3", +// "userId": "11001d48564f43b3bc5662874f04bb11", +// "nickname": "tickstep", +// "fileDriveId": "19519111", +// "albumDriveId": "29519122" +// } +// appName - 应用名称,当前固定为aliyunpan +// version - 版本号 +// userId - 当前登录用户的ID +// nickname - 用户昵称 +// fileDriveId - 用户文件网盘ID +// albumDriveId - 用户相册网盘ID +// +// params - 删除文件前的调用参数 +// { +// "count": 10, +// "items": [{}, {}, ...], +// } +// count - 文件总数 +// items - 文件列表,文件详情请查看下面 RemoveFilePrepareItem 定义 +// +// RemoveFilePrepareItem - 文件详情参数 +// { +// "driveId": "19519221", +// "driveFileId": "65f6c5161ee4a1b9a02b41399282f281c6e6df21", +// "driveFileName": "token.bat", +// "driveFilePath": "/aliyunpan/Downloads/token.bat", +// "driveFileSize": 125330, +// "driveFileType": "file", +// "driveFileUpdatedAt": "2025-03-02 10:39:14", +// "driveFileCreatedAt": "2025-03-02 10:39:14" +// } +// driveId - 网盘ID +// driveFileId - 网盘文件的ID +// driveFileName - 网盘文件名 +// driveFilePath - 网盘文件绝对完整路径 +// driveFileSize - 网盘文件大小,单位B +// driveFileType - 网盘文件类型,file-文件,folder-文件夹 +// driveFileUpdatedAt - 网盘文件修改时间 +// driveFileCreatedAt - 网盘文件创建时间 +// +// 返回值说明 +// { +// "result": [{}, {}, ...], +// } +// result - 结果列表,每个文件的确认结果,文件详细定义请查看 RemoveFilePrepareResultItem 定义 +// +// RemoveFilePrepareResultItem - 每项文件确认结果 +// { +// "driveId": "19519221", +// "driveFileId": "65f6c5161ee4a1b9a02b41399282f281c6e6df21", +// "removeApproved": "yes" +// } +// driveId - 网盘ID +// driveFileId - 网盘文件的ID +// removeApproved - 该文件是否能删除,yes-确认删除,no-不能删除 +// ------------------------------------------------------------------------------------------ +function removeFilePrepareCallback(context, params) { + console.log(params) + var result = {"result":[]}; + for(var i = 0; i < params.items.length; i++) { + var fileItem = params.items[i] + console.log(fileItem); + var fileItemApprovedResult = { + "driveId": fileItem["driveId"], + "driveFileId": fileItem["driveFileId"], + "removeApproved": "yes" + } + + // 可以通过名称过滤 + if (fileItem["driveFileType"] == "file") { // 文件 + if (fileItem["driveFileName"] == "password.key") { + fileItemApprovedResult["removeApproved"] = "no"; // 禁止删除password.key文件 + } + } else if (params["driveFileType"] == "folder") { // 文件夹 + // 对文件夹进行处理 + } + + // 可以通过时间过滤,例如:保留最近10天的文件 + // ... + + // 可以通过数量过滤,例如:保留最新5个文件 + // ... + + // 放入结果列表 + result["result"].push(fileItemApprovedResult) + } + // 返回 + return result; +} diff --git a/internal/command/rm.go b/internal/command/rm.go index ac4b5f4..b85683b 100644 --- a/internal/command/rm.go +++ b/internal/command/rm.go @@ -19,10 +19,12 @@ import ( "github.com/tickstep/aliyunpan/cmder" "github.com/tickstep/aliyunpan/cmder/cmdtable" "github.com/tickstep/aliyunpan/internal/config" + "github.com/tickstep/aliyunpan/internal/plugins" "github.com/urfave/cli" "os" "path" "strconv" + "strings" ) func CmdRm() cli.Command { @@ -75,6 +77,8 @@ func CmdRm() cli.Command { // RunRemove 执行 批量删除文件/目录 func RunRemove(driveId string, paths ...string) { activeUser := GetActiveUser() + pluginManger := plugins.NewPluginManager(config.GetPluginDir()) + plugin, _ := pluginManger.GetPlugin() cacheCleanDirs := []string{} failedRmPaths := make([]string, 0, len(paths)) @@ -88,11 +92,54 @@ func RunRemove(driveId string, paths ...string) { continue } if fileList == nil || len(fileList) == 0 { - // 失败,文件不存在 + // 跳过,没有满足条件的文件 failedRmPaths = append(failedRmPaths, absolutePath) continue } + // 调用插件 + approvedToRemoveFiles := []*aliyunpan.FileEntity{} + pluginParam := &plugins.RemoveFilePrepareParams{ + Count: len(fileList), + Items: make([]*plugins.RemoveFilePrepareItem, 0), + } for _, f := range fileList { + pluginParam.Items = append(pluginParam.Items, &plugins.RemoveFilePrepareItem{ + DriveId: f.DriveId, + DriveFileId: f.FileId, + DriveFileName: f.FileName, + DriveFilePath: f.Path, + DriveFileSize: f.FileSize, + DriveFileType: f.FileType, + DriveFileUpdatedAt: f.UpdatedAt, + DriveFileCreatedAt: f.CreatedAt, + }) + } + if removeFilePrepareResult, er := plugin.RemoveFilePrepareCallback(plugins.GetContext(config.Config.ActiveUser()), pluginParam); er == nil && removeFilePrepareResult != nil { + for _, f := range fileList { + matchResult := false + for _, r := range removeFilePrepareResult.Result { + if strings.Compare(f.FileId, r.DriveFileId) == 0 { + matchResult = true + if strings.Compare("yes", r.RemoveApproved) != 0 { + // skip this file + fmt.Printf("插件不允许删除该文件: %s\n", f.Path) + } else { + approvedToRemoveFiles = append(approvedToRemoveFiles, f) + } + break + } + } + if !matchResult { + // 该文件没有确认结果,则默认删除 + approvedToRemoveFiles = append(approvedToRemoveFiles, f) + } + } + } else { + // 默认删除全部文件 + approvedToRemoveFiles = fileList + } + + for _, f := range approvedToRemoveFiles { // 删除匹配的文件 fdr, err := activeUser.PanClient().OpenapiPanClient().FileDelete(&aliyunpan.FileBatchActionParam{ DriveId: driveId, @@ -127,5 +174,7 @@ func RunRemove(driveId string, paths ...string) { fmt.Println("操作成功, 以下文件/目录已删除, 可在云盘文件回收站找回: ") pnt() activeUser.DeleteCache(cacheCleanDirs) + } else { + fmt.Println("本次操作没有删除任何文件") } } diff --git a/internal/plugins/idle_plugin.go b/internal/plugins/idle_plugin.go index 4f52810..6ae620a 100644 --- a/internal/plugins/idle_plugin.go +++ b/internal/plugins/idle_plugin.go @@ -51,6 +51,10 @@ func (p *IdlePlugin) UserTokenRefreshFinishCallback(context *Context, params *Us return nil } +func (p *IdlePlugin) RemoveFilePrepareCallback(context *Context, params *RemoveFilePrepareParams) (*RemoveFilePrepareResult, error) { + return nil, nil +} + func (p *IdlePlugin) Stop() error { return nil } diff --git a/internal/plugins/js_plugin.go b/internal/plugins/js_plugin.go index 5f35020..c8e63ee 100644 --- a/internal/plugins/js_plugin.go +++ b/internal/plugins/js_plugin.go @@ -261,6 +261,25 @@ func (js *JsPlugin) UserTokenRefreshFinishCallback(context *Context, params *Use return nil } +// RemoveFilePrepareCallback 删除文件前的回调函数 +func (js *JsPlugin) RemoveFilePrepareCallback(context *Context, params *RemoveFilePrepareParams) (*RemoveFilePrepareResult, error) { + var fn func(*Context, *RemoveFilePrepareParams) (*RemoveFilePrepareResult, error) + if !js.isHandlerFuncExisted("removeFilePrepareCallback") { + return nil, nil + } + err := js.vm.ExportTo(js.vm.Get("removeFilePrepareCallback"), &fn) + if err != nil { + logger.Verboseln("Js函数映射到 Go 函数失败!") + return nil, nil + } + r, er := fn(context, params) + if er != nil { + logger.Verboseln(er) + return nil, er + } + return r, nil +} + func (js *JsPlugin) Stop() error { return nil } diff --git a/internal/plugins/plugin.go b/internal/plugins/plugin.go index 5b846f5..313110b 100644 --- a/internal/plugins/plugin.go +++ b/internal/plugins/plugin.go @@ -166,6 +166,42 @@ type ( UpdatedAt string `json:"updatedAt"` } + // RemoveFilePrepareParams 删除文件前的回调函数-参数 + RemoveFilePrepareParams struct { + Count int `json:"count"` + Items []*RemoveFilePrepareItem `json:"items"` + } + RemoveFilePrepareItem struct { + // DriveId 网盘ID + DriveId string `json:"driveId"` + // DriveFileId 网盘文件的ID + DriveFileId string `json:"driveFileId"` + // DriveFileName 网盘文件名 + DriveFileName string `json:"driveFileName"` + // DriveFilePath 网盘文件路径 + DriveFilePath string `json:"driveFilePath"` + // DriveFileSize 网盘文件大小 + DriveFileSize int64 `json:"driveFileSize"` + // DriveFileType 网盘文件类型,file-文件,folder-文件夹 + DriveFileType string `json:"driveFileType"` + // DriveFileUpdatedAt 网盘文件修改时间,格式:2025-03-03 10:39:14 + DriveFileUpdatedAt string `json:"driveFileUpdatedAt"` + // DriveFileCreatedAt 网盘文件创建时间,格式:2025-03-03 10:39:14 + DriveFileCreatedAt string `json:"driveFileCreatedAt"` + } + // RemoveFilePrepareResult 删除文件前的回调函数-返回结果 + RemoveFilePrepareResult struct { + Result []*RemoveFilePrepareResultItem `json:"result"` + } + RemoveFilePrepareResultItem struct { + // DriveId 网盘ID + DriveId string `json:"driveId"` + // DriveFileId 网盘文件的ID + DriveFileId string `json:"driveFileId"` + // RemoveApproved 确认该文件是否删除。yes-删除 no-不删除 + RemoveApproved string `json:"removeApproved"` + } + // Plugin 插件接口 Plugin interface { // Start 启动 @@ -198,6 +234,9 @@ type ( // UserTokenRefreshFinishCallback 用户Token刷新完成后回调函数 UserTokenRefreshFinishCallback(context *Context, params *UserTokenRefreshFinishParams) error + // RemoveFilePrepareCallback 删除文件前的回调函数 + RemoveFilePrepareCallback(context *Context, params *RemoveFilePrepareParams) (*RemoveFilePrepareResult, error) + // Stop 停止 Stop() error }