diff --git a/docs/eventmanager.md b/docs/eventmanager.md index 91bf9569..a0c4f152 100644 --- a/docs/eventmanager.md +++ b/docs/eventmanager.md @@ -28,10 +28,13 @@ The following placeholders are supported: - `{{StatusString}}`. Status as string. Possible values "OK", "KO". - `{{ErrorString}}`. Error details. Replaced with an empty string if no errors occur. - `{{VirtualPath}}`. Path seen by SFTPGo users, for example `/adir/afile.txt`. +- `{{VirtualDirPath}}`. Parent directory for VirtualPath, for example if VirtualPath is "/adir/afile.txt", VirtualDirPath is "/adir". - `{{FsPath}}`. Full filesystem path, for example `/user/homedir/adir/afile.txt` or `C:/data/user/homedir/adir/afile.txt` on Windows. - `{{ObjectName}}`. File/directory name, for example `afile.txt` or provider object name. - `{{ObjectType}}`. Object type for provider events: `user`, `group`, `admin`, etc. - `{{VirtualTargetPath}}`. Virtual target path for renames. +- `{{VirtualTargetDirPath}}`. Parent directory for VirtualTargetPath. +- `{{TargetName}}`. Target object name for renames. - `{{FsTargetPath}}`. Full filesystem target path for renames. - `{{FileSize}}`. File size. - `{{Protocol}}`. Used protocol, for example `SFTP`, `FTP`. diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go index 63f070ad..58214e93 100644 --- a/internal/common/eventmanager.go +++ b/internal/common/eventmanager.go @@ -483,7 +483,13 @@ func (p *EventParams) setBackupParams(backupPath string) { } p.sender = dataprovider.ActionExecutorSystem p.FsPath = backupPath - p.VirtualPath = filepath.Base(backupPath) + p.ObjectName = filepath.Base(backupPath) + p.VirtualPath = "/" + p.ObjectName + p.Timestamp = time.Now().UnixNano() + info, err := os.Stat(backupPath) + if err == nil { + p.FileSize = info.Size() + } } func (p *EventParams) getStatusString() string { @@ -607,6 +613,13 @@ func (p *EventParams) getStringReplacements(addObjectData bool) []string { "{{Timestamp}}", fmt.Sprintf("%d", p.Timestamp), "{{StatusString}}", p.getStatusString(), } + if p.VirtualPath != "" { + replacements = append(replacements, "{{VirtualDirPath}}", path.Dir(p.VirtualPath)) + } + if p.VirtualTargetPath != "" { + replacements = append(replacements, "{{VirtualTargetDirPath}}", path.Dir(p.VirtualTargetPath)) + replacements = append(replacements, "{{TargetName}}", path.Base(p.VirtualTargetPath)) + } if len(p.errors) > 0 { replacements = append(replacements, "{{ErrorString}}", strings.Join(p.errors, ", ")) } else { diff --git a/internal/common/protocol_test.go b/internal/common/protocol_test.go index 66b4c7b6..a0534b84 100644 --- a/internal/common/protocol_test.go +++ b/internal/common/protocol_test.go @@ -3868,7 +3868,7 @@ func TestEventRuleFsActions(t *testing.T) { Type: dataprovider.FilesystemActionRename, Renames: []dataprovider.KeyValue{ { - Key: "/{{VirtualPath}}", + Key: "/{{VirtualDirPath}}/{{ObjectName}}", Value: "/{{ObjectName}}_renamed", }, }, @@ -5294,6 +5294,86 @@ func TestEventRuleFirstUploadDownloadActions(t *testing.T) { require.NoError(t, err) } +func TestEventRuleRenameEvent(t *testing.T) { + smtpCfg := smtp.Config{ + Host: "127.0.0.1", + Port: 2525, + From: "notify@example.com", + TemplatesPath: "templates", + } + err := smtpCfg.Initialize(configDir) + require.NoError(t, err) + + a1 := dataprovider.BaseEventAction{ + Name: "action1", + Type: dataprovider.ActionTypeEmail, + Options: dataprovider.BaseEventActionOptions{ + EmailConfig: dataprovider.EventActionEmailConfig{ + Recipients: []string{"test@example.com"}, + Subject: `"{{Event}}" from "{{Name}}"`, + Body: `Fs path {{FsPath}}, Target path "{{VirtualTargetDirPath}}/{{TargetName}}", size: {{FileSize}}`, + }, + }, + } + action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated) + assert.NoError(t, err) + r1 := dataprovider.EventRule{ + Name: "test rename rule", + Trigger: dataprovider.EventTriggerFsEvent, + Conditions: dataprovider.EventConditions{ + FsEvents: []string{"rename"}, + }, + Actions: []dataprovider.EventAction{ + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action1.Name, + }, + Order: 1, + }, + }, + } + rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated) + assert.NoError(t, err) + + user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated) + assert.NoError(t, err) + conn, client, err := getSftpClient(user) + if assert.NoError(t, err) { + defer conn.Close() + defer client.Close() + + testFileSize := int64(32768) + lastReceivedEmail.reset() + err = writeSFTPFileNoCheck(testFileName, testFileSize, client) + assert.NoError(t, err) + err = client.Mkdir("subdir") + assert.NoError(t, err) + err = client.Rename(testFileName, path.Join("/subdir", testFileName)) + assert.NoError(t, err) + assert.Eventually(t, func() bool { + return lastReceivedEmail.get().From != "" + }, 1500*time.Millisecond, 100*time.Millisecond) + email := lastReceivedEmail.get() + assert.Len(t, email.To, 1) + assert.True(t, util.Contains(email.To, "test@example.com")) + assert.Contains(t, email.Data, fmt.Sprintf(`Subject: "rename" from "%s"`, user.Username)) + assert.Contains(t, email.Data, fmt.Sprintf("Target path %q", path.Join("/subdir", testFileName))) + } + + _, err = httpdtest.RemoveEventRule(rule1, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventAction(action1, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveUser(user, http.StatusOK) + assert.NoError(t, err) + err = os.RemoveAll(user.GetHomeDir()) + assert.NoError(t, err) + + smtpCfg = smtp.Config{} + err = smtpCfg.Initialize(configDir) + require.NoError(t, err) +} + func TestEventRuleCertificate(t *testing.T) { smtpCfg := smtp.Config{ Host: "127.0.0.1", diff --git a/templates/webadmin/eventaction.html b/templates/webadmin/eventaction.html index 3a31dad1..eed0d461 100644 --- a/templates/webadmin/eventaction.html +++ b/templates/webadmin/eventaction.html @@ -669,6 +669,9 @@ along with this program. If not, see .

{{`{{VirtualPath}}`}} => Path seen by SFTPGo users, for example "/adir/afile.txt".

+

+ {{`{{VirtualDirPath}}`}} => Parent directory for VirtualPath, for example if VirtualPath is "/adir/afile.txt", VirtualDirPath is "/adir". +

{{`{{FsPath}}`}} => Full filesystem path, for example "/user/homedir/adir/afile.txt" or "C:/data/user/homedir/adir/afile.txt" on Windows.

@@ -681,6 +684,12 @@ along with this program. If not, see .

{{`{{VirtualTargetPath}}`}} => Virtual target path for renames.

+

+ {{`{{VirtualTargetDirPath}}`}} => Parent directory for VirtualTargetPath. +

+

+ {{`{{TargetName}}`}} => Target object name for renames. +

{{`{{FsTargetPath}}`}} => Full filesystem target path for renames.