浏览代码

event rules: allow to set min/max file size using "human" notation

10MB or 1GB instead of the size in bytes

Signed-off-by: Nicola Murino <[email protected]>
Nicola Murino 3 年之前
父节点
当前提交
15b4194e8f

+ 11 - 8
internal/common/eventmanager.go

@@ -836,8 +836,7 @@ func getMailAttachments(user dataprovider.User, attachments []string, replacer *
 	}
 	}
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
 	totalSize := int64(0)
 	totalSize := int64(0)
-	for _, virtualPath := range attachments {
-		virtualPath = util.CleanPath(replaceWithReplacer(virtualPath, replacer))
+	for _, virtualPath := range replacePathsPlaceholders(attachments, replacer) {
 		info, err := conn.DoStat(virtualPath, 0, false)
 		info, err := conn.DoStat(virtualPath, 0, false)
 		if err != nil {
 		if err != nil {
 			return nil, fmt.Errorf("unable to get info for file %q, user %q: %w", virtualPath, conn.User.Username, err)
 			return nil, fmt.Errorf("unable to get info for file %q, user %q: %w", virtualPath, conn.User.Username, err)
@@ -1203,6 +1202,13 @@ func getUserForEventAction(user dataprovider.User) (dataprovider.User, error) {
 	return user, nil
 	return user, nil
 }
 }
 
 
+func replacePathsPlaceholders(paths []string, replacer *strings.Replacer) []string {
+	for idx := range paths {
+		paths[idx] = util.CleanPath(replaceWithReplacer(paths[idx], replacer))
+	}
+	return util.RemoveDuplicates(paths, false)
+}
+
 func executeDeleteFileFsAction(conn *BaseConnection, item string, info os.FileInfo) error {
 func executeDeleteFileFsAction(conn *BaseConnection, item string, info os.FileInfo) error {
 	fs, fsPath, err := conn.GetFsAndResolvedPath(item)
 	fs, fsPath, err := conn.GetFsAndResolvedPath(item)
 	if err != nil {
 	if err != nil {
@@ -1223,8 +1229,7 @@ func executeDeleteFsActionForUser(deletes []string, replacer *strings.Replacer,
 		return fmt.Errorf("delete error, unable to check root fs for user %q: %w", user.Username, err)
 		return fmt.Errorf("delete error, unable to check root fs for user %q: %w", user.Username, err)
 	}
 	}
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
-	for _, item := range deletes {
-		item = util.CleanPath(replaceWithReplacer(item, replacer))
+	for _, item := range replacePathsPlaceholders(deletes, replacer) {
 		info, err := conn.DoStat(item, 0, false)
 		info, err := conn.DoStat(item, 0, false)
 		if err != nil {
 		if err != nil {
 			if conn.IsNotExistError(err) {
 			if conn.IsNotExistError(err) {
@@ -1298,8 +1303,7 @@ func executeMkDirsFsActionForUser(dirs []string, replacer *strings.Replacer, use
 		return fmt.Errorf("mkdir error, unable to check root fs for user %q: %w", user.Username, err)
 		return fmt.Errorf("mkdir error, unable to check root fs for user %q: %w", user.Username, err)
 	}
 	}
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
-	for _, item := range dirs {
-		item = util.CleanPath(replaceWithReplacer(item, replacer))
+	for _, item := range replacePathsPlaceholders(dirs, replacer) {
 		if err = conn.CheckParentDirs(path.Dir(item)); err != nil {
 		if err = conn.CheckParentDirs(path.Dir(item)); err != nil {
 			return fmt.Errorf("unable to check parent dirs for %q, user %q: %w", item, user.Username, err)
 			return fmt.Errorf("unable to check parent dirs for %q, user %q: %w", item, user.Username, err)
 		}
 		}
@@ -1389,8 +1393,7 @@ func executeExistFsActionForUser(exist []string, replacer *strings.Replacer,
 		return fmt.Errorf("existence check error, unable to check root fs for user %q: %w", user.Username, err)
 		return fmt.Errorf("existence check error, unable to check root fs for user %q: %w", user.Username, err)
 	}
 	}
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
 	conn := NewBaseConnection(connectionID, protocolEventAction, "", "", user)
-	for _, item := range exist {
-		item = util.CleanPath(replaceWithReplacer(item, replacer))
+	for _, item := range replacePathsPlaceholders(exist, replacer) {
 		if _, err = conn.DoStat(item, 0, false); err != nil {
 		if _, err = conn.DoStat(item, 0, false); err != nil {
 			return fmt.Errorf("error checking existence for path %q, user %q: %w", item, user.Username, err)
 			return fmt.Errorf("error checking existence for path %q, user %q: %w", item, user.Username, err)
 		}
 		}

+ 10 - 0
internal/common/eventmanager_test.go

@@ -1598,6 +1598,16 @@ func TestWriteHTTPPartsError(t *testing.T) {
 	assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
 	assert.ErrorIs(t, err, io.ErrUnexpectedEOF)
 }
 }
 
 
+func TestReplacePathsPlaceholders(t *testing.T) {
+	replacer := strings.NewReplacer("{{VirtualPath}}", "/path1")
+	paths := []string{"{{VirtualPath}}", "/path1"}
+	paths = replacePathsPlaceholders(paths, replacer)
+	assert.Equal(t, []string{"/path1"}, paths)
+	paths = []string{"{{VirtualPath}}", "/path2"}
+	paths = replacePathsPlaceholders(paths, replacer)
+	assert.Equal(t, []string{"/path1", "/path2"}, paths)
+}
+
 func getErrorString(err error) string {
 func getErrorString(err error) string {
 	if err == nil {
 	if err == nil {
 		return ""
 		return ""

+ 2 - 2
internal/dataprovider/eventrule.go

@@ -1077,8 +1077,8 @@ func (f *ConditionOptions) validate() error {
 	}
 	}
 	if f.MinFileSize > 0 && f.MaxFileSize > 0 {
 	if f.MinFileSize > 0 && f.MaxFileSize > 0 {
 		if f.MaxFileSize <= f.MinFileSize {
 		if f.MaxFileSize <= f.MinFileSize {
-			return util.NewValidationError(fmt.Sprintf("invalid max file size %d, it is lesser or equal than min file size %d",
-				f.MaxFileSize, f.MinFileSize))
+			return util.NewValidationError(fmt.Sprintf("invalid max file size %s, it is lesser or equal than min file size %s",
+				util.ByteCountSI(f.MaxFileSize), util.ByteCountSI(f.MinFileSize)))
 		}
 		}
 	}
 	}
 	if config.IsShared == 0 {
 	if config.IsShared == 0 {

+ 3 - 3
internal/httpd/webadmin.go

@@ -496,7 +496,7 @@ func loadAdminTemplates(templatesPath string) {
 	foldersTmpl := util.LoadTemplate(nil, foldersPaths...)
 	foldersTmpl := util.LoadTemplate(nil, foldersPaths...)
 	folderTmpl := util.LoadTemplate(fsBaseTpl, folderPaths...)
 	folderTmpl := util.LoadTemplate(fsBaseTpl, folderPaths...)
 	eventRulesTmpl := util.LoadTemplate(nil, eventRulesPaths...)
 	eventRulesTmpl := util.LoadTemplate(nil, eventRulesPaths...)
-	eventRuleTmpl := util.LoadTemplate(nil, eventRulePaths...)
+	eventRuleTmpl := util.LoadTemplate(fsBaseTpl, eventRulePaths...)
 	eventActionsTmpl := util.LoadTemplate(nil, eventActionsPaths...)
 	eventActionsTmpl := util.LoadTemplate(nil, eventActionsPaths...)
 	eventActionTmpl := util.LoadTemplate(nil, eventActionPaths...)
 	eventActionTmpl := util.LoadTemplate(nil, eventActionPaths...)
 	statusTmpl := util.LoadTemplate(nil, statusPaths...)
 	statusTmpl := util.LoadTemplate(nil, statusPaths...)
@@ -2138,11 +2138,11 @@ func getEventRuleConditionsFromPostFields(r *http.Request) (dataprovider.EventCo
 			}
 			}
 		}
 		}
 	}
 	}
-	minFileSize, err := strconv.ParseInt(r.Form.Get("fs_min_size"), 10, 64)
+	minFileSize, err := util.ParseBytes(r.Form.Get("fs_min_size"))
 	if err != nil {
 	if err != nil {
 		return dataprovider.EventConditions{}, fmt.Errorf("invalid min file size: %w", err)
 		return dataprovider.EventConditions{}, fmt.Errorf("invalid min file size: %w", err)
 	}
 	}
-	maxFileSize, err := strconv.ParseInt(r.Form.Get("fs_max_size"), 10, 64)
+	maxFileSize, err := util.ParseBytes(r.Form.Get("fs_max_size"))
 	if err != nil {
 	if err != nil {
 		return dataprovider.EventConditions{}, fmt.Errorf("invalid max file size: %w", err)
 		return dataprovider.EventConditions{}, fmt.Errorf("invalid max file size: %w", err)
 	}
 	}

+ 5 - 5
templates/webadmin/eventrule.html

@@ -347,20 +347,20 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 
             <div class="card bg-light mb-3 trigger trigger-fs">
             <div class="card bg-light mb-3 trigger trigger-fs">
                 <div class="card-header">
                 <div class="card-header">
-                    <b>File size limits. 0 means no limit</b>
+                    <b>File size limits. 0 means no limit. You can use MB/GB suffix</b>
                 </div>
                 </div>
                 <div class="card-body">
                 <div class="card-body">
                     <div class="form-group row">
                     <div class="form-group row">
                         <label for="idFsMinSize" class="col-sm-2 col-form-label">Min size</label>
                         <label for="idFsMinSize" class="col-sm-2 col-form-label">Min size</label>
                         <div class="col-sm-3">
                         <div class="col-sm-3">
-                            <input type="number" class="form-control" id="idFsMinSize" name="fs_min_size" placeholder=""
-                                value="{{.Rule.Conditions.Options.MinFileSize}}" min="0">
+                            <input type="text" class="form-control" id="idFsMinSize" name="fs_min_size" placeholder=""
+                                value="{{HumanizeBytes .Rule.Conditions.Options.MinFileSize}}">
                         </div>
                         </div>
                         <div class="col-sm-2"></div>
                         <div class="col-sm-2"></div>
                         <label for="idFsMaxSize" class="col-sm-2 col-form-label">Max size</label>
                         <label for="idFsMaxSize" class="col-sm-2 col-form-label">Max size</label>
                         <div class="col-sm-3">
                         <div class="col-sm-3">
-                            <input type="number" class="form-control" id="idFsMaxSize" name="fs_max_size" placeholder=""
-                                value="{{.Rule.Conditions.Options.MaxFileSize}}" min="0">
+                            <input type="text" class="form-control" id="idFsMaxSize" name="fs_max_size" placeholder=""
+                                value="{{HumanizeBytes .Rule.Conditions.Options.MaxFileSize}}">
                         </div>
                         </div>
                     </div>
                     </div>
                 </div>
                 </div>