浏览代码

eventmanager: add placeholder to get the parent directory

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
Nicola Murino 2 年之前
父节点
当前提交
bf45d04600
共有 4 个文件被更改,包括 107 次插入2 次删除
  1. 3 0
      docs/eventmanager.md
  2. 14 1
      internal/common/eventmanager.go
  3. 81 1
      internal/common/protocol_test.go
  4. 9 0
      templates/webadmin/eventaction.html

+ 3 - 0
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`.

+ 14 - 1
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 {

+ 81 - 1
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",

+ 9 - 0
templates/webadmin/eventaction.html

@@ -669,6 +669,9 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
                 <p>
                     <span class="shortcut"><b>{{`{{VirtualPath}}`}}</b></span> => Path seen by SFTPGo users, for example "/adir/afile.txt".
                 </p>
+                <p>
+                    <span class="shortcut"><b>{{`{{VirtualDirPath}}`}}</b></span> => Parent directory for VirtualPath, for example if VirtualPath is "/adir/afile.txt", VirtualDirPath is "/adir".
+                </p>
                 <p>
                     <span class="shortcut"><b>{{`{{FsPath}}`}}</b></span> => Full filesystem path, for example "/user/homedir/adir/afile.txt" or "C:/data/user/homedir/adir/afile.txt" on Windows.
                 </p>
@@ -681,6 +684,12 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
                 <p>
                     <span class="shortcut"><b>{{`{{VirtualTargetPath}}`}}</b></span> => Virtual target path for renames.
                 </p>
+                <p>
+                    <span class="shortcut"><b>{{`{{VirtualTargetDirPath}}`}}</b></span> => Parent directory for VirtualTargetPath.
+                </p>
+                <p>
+                    <span class="shortcut"><b>{{`{{TargetName}}`}}</b></span> => Target object name for renames.
+                </p>
                 <p>
                     <span class="shortcut"><b>{{`{{FsTargetPath}}`}}</b></span> => Full filesystem target path for renames.
                 </p>