From f90829aaab7910f9e1858291c29391deb3063c4f Mon Sep 17 00:00:00 2001 From: xiaoyaofenfen <1254525673@qq.com> Date: Mon, 10 Oct 2022 11:35:40 +0800 Subject: [PATCH] add sync file finish callback for plugin #166 --- assets/plugin/js/sync_handler.js.sample | 55 ++++++++++++++++ internal/plugins/idle_plugin.go | 4 ++ internal/plugins/js_plugin.go | 19 ++++++ internal/plugins/plugin.go | 16 +++++ internal/syncdrive/file_action_task.go | 5 +- internal/syncdrive/file_action_task_mgr.go | 75 ++++++++++++++++++++++ 6 files changed, 173 insertions(+), 1 deletion(-) diff --git a/assets/plugin/js/sync_handler.js.sample b/assets/plugin/js/sync_handler.js.sample index 1b3adb2..38b9fb4 100644 --- a/assets/plugin/js/sync_handler.js.sample +++ b/assets/plugin/js/sync_handler.js.sample @@ -151,4 +151,59 @@ function syncScanPanFilePrepareCallback(context, params) { // } return result; +} + +// ------------------------------------------------------------------------------------------ +// 函数说明:同步备份-同步文件后的回调函数 +// +// 参数说明 +// 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 - 同步文件后的参数 +// { +// "action": "upload", +// "actionResult": "success", +// "driveId": "19519221", +// "fileName": "1.txt", +// "filePath": "D:/goprojects/dev/upload/1.txt", +// "fileSha1": "294C8F24C56042710813E95C55A0B018299BA9A7", +// "fileSize": "28077", +// "fileType": "file", +// "fileUpdatedAt": "2022-03-04 15:19:47" +// } +// action - 同步动作, +// download-下载文件, +// upload-上传文件, +// delete_local-删除本地文件, +// delete_pan-删除云盘文件, +// create_local_folder-创建本地文件夹, +// create_pan_folder-创建云盘文件夹 +// actionResult - 同步结果,success-成功,fail-失败 +// driveId - 网盘ID +// fileName - 文件名称 +// filePath - 文件完整路径 +// fileSha1 - 文件SHA1 +// fileSize - 文件大小,单位B +// fileType - 文件类型,file-文件,folder-文件夹 +// fileUpdatedAt - 文件修改时间 +// +// 返回值说明 +// (无) +// ------------------------------------------------------------------------------------------ +function syncFileFinishCallback(context, params) { + console.log(params) } \ No newline at end of file diff --git a/internal/plugins/idle_plugin.go b/internal/plugins/idle_plugin.go index 0ad90e5..d8beb14 100644 --- a/internal/plugins/idle_plugin.go +++ b/internal/plugins/idle_plugin.go @@ -40,6 +40,10 @@ func (p *IdlePlugin) SyncScanPanFilePrepareCallback(context *Context, params *Sy return nil, nil } +func (p *IdlePlugin) SyncFileFinishCallback(context *Context, params *SyncFileFinishParams) error { + return nil +} + func (p *IdlePlugin) Stop() error { return nil } diff --git a/internal/plugins/js_plugin.go b/internal/plugins/js_plugin.go index a9eb062..68418fe 100644 --- a/internal/plugins/js_plugin.go +++ b/internal/plugins/js_plugin.go @@ -190,6 +190,25 @@ func (js *JsPlugin) SyncScanPanFilePrepareCallback(context *Context, params *Syn return r, nil } +// SyncFileFinishCallback 同步备份-同步文件完成的回调函数 +func (js *JsPlugin) SyncFileFinishCallback(context *Context, params *SyncFileFinishParams) error { + var fn func(*Context, *SyncFileFinishParams) error + if !js.isHandlerFuncExisted("syncFileFinishCallback") { + return nil + } + err := js.vm.ExportTo(js.vm.Get("syncFileFinishCallback"), &fn) + if err != nil { + logger.Verboseln("Js函数映射到 Go 函数失败!") + return nil + } + er := fn(context, params) + if er != nil { + logger.Verboseln(er) + return nil + } + return nil +} + func (js *JsPlugin) Stop() error { return nil } diff --git a/internal/plugins/plugin.go b/internal/plugins/plugin.go index cd0d44d..ff83da9 100644 --- a/internal/plugins/plugin.go +++ b/internal/plugins/plugin.go @@ -122,6 +122,19 @@ type ( SyncScanPanApproved string `json:"syncScanPanApproved"` } + // SyncFileFinishParams 同步备份-文件同步结束-回调参数 + SyncFileFinishParams struct { + Action string `json:"action"` + ActionResult string `json:"actionResult"` + DriveId string `json:"driveId"` + FileName string `json:"fileName"` + FilePath string `json:"filePath"` + FileSha1 string `json:"fileSha1"` + FileSize int64 `json:"fileSize"` + FileType string `json:"fileType"` + FileUpdatedAt string `json:"fileUpdatedAt"` + } + // Plugin 插件接口 Plugin interface { // Start 启动 @@ -145,6 +158,9 @@ type ( // SyncScanPanFilePrepareCallback 同步备份-扫描本地文件的回调函数 SyncScanPanFilePrepareCallback(context *Context, params *SyncScanPanFilePrepareParams) (*SyncScanPanFilePrepareResult, error) + // SyncFileFinishCallback 同步备份-同步文件完成的回调函数 + SyncFileFinishCallback(context *Context, params *SyncFileFinishParams) error + // Stop 停止 Stop() error } diff --git a/internal/syncdrive/file_action_task.go b/internal/syncdrive/file_action_task.go index 765de50..eb25d73 100644 --- a/internal/syncdrive/file_action_task.go +++ b/internal/syncdrive/file_action_task.go @@ -637,7 +637,7 @@ func (f *FileActionTask) deletePanFile(ctx context.Context) error { // 删除 var fileDeleteResult []*aliyunpan.FileBatchActionResult - var err *apierror.ApiError + var err *apierror.ApiError = nil fileDeleteResult, err = f.panClient.FileDelete([]*aliyunpan.FileBatchActionParam{{DriveId: driveId, FileId: panFileId}}) time.Sleep(1 * time.Second) if err != nil || len(fileDeleteResult) == 0 { @@ -647,6 +647,9 @@ func (f *FileActionTask) deletePanFile(ctx context.Context) error { } f.syncItem.StatusUpdateTime = utils.NowTimeStr() f.syncFileDb.Update(f.syncItem) + if err == nil { + return nil + } return err } diff --git a/internal/syncdrive/file_action_task_mgr.go b/internal/syncdrive/file_action_task_mgr.go index 5f1cda7..154927b 100644 --- a/internal/syncdrive/file_action_task_mgr.go +++ b/internal/syncdrive/file_action_task_mgr.go @@ -5,7 +5,9 @@ import ( "fmt" mapset "github.com/deckarep/golang-set" "github.com/tickstep/aliyunpan-api/aliyunpan" + "github.com/tickstep/aliyunpan/internal/config" "github.com/tickstep/aliyunpan/internal/localfile" + "github.com/tickstep/aliyunpan/internal/plugins" "github.com/tickstep/aliyunpan/internal/waitgroup" "github.com/tickstep/aliyunpan/library/collection" "github.com/tickstep/library-go/logger" @@ -36,6 +38,12 @@ type ( panFolderModifyCount int // 云盘文件扫描变更记录次数,作为后续文件对比进程的参考以节省CPU资源 syncActionModifyCount int // 文件对比进程检测的文件上传下载删除变更记录次数,作为后续文件上传下载处理进程的参考以节省CPU资源 resourceModifyMutex *sync.Mutex + + panUser *config.PanUser + + // 插件 + plugin plugins.Plugin + pluginMutex *sync.Mutex } localFileSet struct { @@ -62,6 +70,8 @@ func NewFileActionTaskManager(task *SyncTask) *FileActionTaskManager { panFolderModifyCount: 1, syncActionModifyCount: 1, resourceModifyMutex: &sync.Mutex{}, + + panUser: task.panUser, } } @@ -140,6 +150,14 @@ func (f *FileActionTaskManager) Start() error { f.ctx, cancel = context.WithCancel(context.Background()) f.cancelFunc = cancel + if f.plugin == nil { + pluginManger := plugins.NewPluginManager(config.GetPluginDir()) + f.plugin, _ = pluginManger.GetPlugin() + } + if f.pluginMutex == nil { + f.pluginMutex = &sync.Mutex{} + } + go f.doLocalFileDiffRoutine(f.ctx) go f.doPanFileDiffRoutine(f.ctx) go f.fileActionTaskExecutor(f.ctx) @@ -798,9 +816,11 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { if e := uploadItem.DoAction(ctx); e == nil { // success f.fileInProcessQueue.Remove(uploadItem.syncItem) + f.doPluginCallback(uploadItem.syncItem, "success") } else { // retry? f.fileInProcessQueue.Remove(uploadItem.syncItem) + f.doPluginCallback(uploadItem.syncItem, "fail") } uploadWaitGroup.Done() }() @@ -818,9 +838,11 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { if e := downloadItem.DoAction(ctx); e == nil { // success f.fileInProcessQueue.Remove(downloadItem.syncItem) + f.doPluginCallback(downloadItem.syncItem, "success") } else { // retry? f.fileInProcessQueue.Remove(downloadItem.syncItem) + f.doPluginCallback(downloadItem.syncItem, "fail") } downloadWaitGroup.Done() }() @@ -838,9 +860,11 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { if e := deleteLocalItem.DoAction(ctx); e == nil { // success f.fileInProcessQueue.Remove(deleteLocalItem.syncItem) + f.doPluginCallback(deleteLocalItem.syncItem, "success") } else { // retry? f.fileInProcessQueue.Remove(deleteLocalItem.syncItem) + f.doPluginCallback(deleteLocalItem.syncItem, "fail") } localFileWaitGroup.Done() }() @@ -858,9 +882,11 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { if e := deletePanItem.DoAction(ctx); e == nil { // success f.fileInProcessQueue.Remove(deletePanItem.syncItem) + f.doPluginCallback(deletePanItem.syncItem, "success") } else { // retry? f.fileInProcessQueue.Remove(deletePanItem.syncItem) + f.doPluginCallback(deletePanItem.syncItem, "fail") } panFileWaitGroup.Done() }() @@ -878,9 +904,11 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { if e := createLocalFolderItem.DoAction(ctx); e == nil { // success f.fileInProcessQueue.Remove(createLocalFolderItem.syncItem) + f.doPluginCallback(createLocalFolderItem.syncItem, "success") } else { // retry? f.fileInProcessQueue.Remove(createLocalFolderItem.syncItem) + f.doPluginCallback(createLocalFolderItem.syncItem, "fail") } localFileWaitGroup.Done() }() @@ -898,9 +926,11 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { if e := createPanFolderItem.DoAction(ctx); e == nil { // success f.fileInProcessQueue.Remove(createPanFolderItem.syncItem) + f.doPluginCallback(createPanFolderItem.syncItem, "success") } else { // retry? f.fileInProcessQueue.Remove(createPanFolderItem.syncItem) + f.doPluginCallback(createPanFolderItem.syncItem, "fail") } panFileWaitGroup.Done() }() @@ -920,6 +950,51 @@ func (f *FileActionTaskManager) fileActionTaskExecutor(ctx context.Context) { } } +func (f *FileActionTaskManager) doPluginCallback(syncFile *SyncFileItem, actionResult string) bool { + // 插件回调 + var pluginParam *plugins.SyncFileFinishParams + if syncFile.Action == SyncFileActionUpload || + syncFile.Action == SyncFileActionCreatePanFolder || + syncFile.Action == SyncFileActionDeletePan { + file := syncFile.LocalFile + pluginParam = &plugins.SyncFileFinishParams{ + Action: string(syncFile.Action), + ActionResult: actionResult, + DriveId: syncFile.DriveId, + FileName: file.FileName, + FilePath: syncFile.getPanFileFullPath(), + FileSha1: file.Sha1Hash, + FileSize: file.FileSize, + FileType: file.FileType, + FileUpdatedAt: file.UpdatedAt, + } + } else if syncFile.Action == SyncFileActionDownload || + syncFile.Action == SyncFileActionCreateLocalFolder || + syncFile.Action == SyncFileActionDeleteLocal { + file := syncFile.PanFile + pluginParam = &plugins.SyncFileFinishParams{ + Action: string(syncFile.Action), + ActionResult: actionResult, + DriveId: syncFile.DriveId, + FileName: file.FileName, + FilePath: syncFile.getLocalFileFullPath(), + FileSha1: file.Sha1Hash, + FileSize: file.FileSize, + FileType: file.FileType, + FileUpdatedAt: file.UpdatedAt, + } + } else { + return false + } + + f.pluginMutex.Lock() + defer f.pluginMutex.Unlock() + if er := f.plugin.SyncFileFinishCallback(plugins.GetContext(f.panUser), pluginParam); er == nil { + return true + } + return false +} + // getRelativePath 获取文件的相对路径 func (l *localFileSet) getRelativePath(localPath string) string { localPath = strings.ReplaceAll(localPath, "\\", "/")