feat(fs): custom properties for files (#2407)
This commit is contained in:
@@ -25,6 +25,7 @@ const (
|
||||
QuerySearchNameOpOr = "name_op_or"
|
||||
QuerySearchUseOr = "use_or"
|
||||
QuerySearchMetadataPrefix = "meta_"
|
||||
QuerySearchMetadataExact = "exact_meta_"
|
||||
QuerySearchCaseFolding = "case_folding"
|
||||
QuerySearchType = "type"
|
||||
QuerySearchTypeCategory = "category"
|
||||
@@ -218,7 +219,7 @@ func (u *URI) FileSystem() constants.FileSystemType {
|
||||
func (u *URI) SearchParameters() *inventory.SearchFileParameters {
|
||||
q := u.U.Query()
|
||||
res := &inventory.SearchFileParameters{
|
||||
Metadata: make(map[string]string),
|
||||
Metadata: make([]inventory.MetadataFilter, 0),
|
||||
}
|
||||
withSearch := false
|
||||
|
||||
@@ -252,7 +253,18 @@ func (u *URI) SearchParameters() *inventory.SearchFileParameters {
|
||||
|
||||
for k, v := range q {
|
||||
if strings.HasPrefix(k, QuerySearchMetadataPrefix) {
|
||||
res.Metadata[strings.TrimPrefix(k, QuerySearchMetadataPrefix)] = v[0]
|
||||
res.Metadata = append(res.Metadata, inventory.MetadataFilter{
|
||||
Key: strings.TrimPrefix(k, QuerySearchMetadataPrefix),
|
||||
Value: v[0],
|
||||
Exact: false,
|
||||
})
|
||||
withSearch = true
|
||||
} else if strings.HasPrefix(k, QuerySearchMetadataExact) {
|
||||
res.Metadata = append(res.Metadata, inventory.MetadataFilter{
|
||||
Key: strings.TrimPrefix(k, QuerySearchMetadataExact),
|
||||
Value: v[0],
|
||||
Exact: true,
|
||||
})
|
||||
withSearch = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,18 @@ import (
|
||||
"crypto/sha1"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/application/constants"
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory/types"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs/dbfs"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/go-playground/validator/v10"
|
||||
"strings"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -20,13 +24,14 @@ type (
|
||||
)
|
||||
|
||||
const (
|
||||
wildcardMetadataKey = "*"
|
||||
customizeMetadataSuffix = "customize"
|
||||
tagMetadataSuffix = "tag"
|
||||
iconColorMetadataKey = customizeMetadataSuffix + ":icon_color"
|
||||
emojiIconMetadataKey = customizeMetadataSuffix + ":emoji"
|
||||
shareOwnerMetadataKey = dbfs.MetadataSysPrefix + "shared_owner"
|
||||
shareRedirectMetadataKey = dbfs.MetadataSysPrefix + "shared_redirect"
|
||||
wildcardMetadataKey = "*"
|
||||
customizeMetadataSuffix = "customize"
|
||||
tagMetadataSuffix = "tag"
|
||||
customPropsMetadataSuffix = "props"
|
||||
iconColorMetadataKey = customizeMetadataSuffix + ":icon_color"
|
||||
emojiIconMetadataKey = customizeMetadataSuffix + ":emoji"
|
||||
shareOwnerMetadataKey = dbfs.MetadataSysPrefix + "shared_owner"
|
||||
shareRedirectMetadataKey = dbfs.MetadataSysPrefix + "shared_redirect"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -131,6 +136,126 @@ var (
|
||||
return nil
|
||||
},
|
||||
},
|
||||
customPropsMetadataSuffix: {
|
||||
wildcardMetadataKey: func(ctx context.Context, m *manager, patch *fs.MetadataPatch) error {
|
||||
if patch.Remove {
|
||||
return nil
|
||||
}
|
||||
|
||||
customProps := m.settings.CustomProps(ctx)
|
||||
propId := strings.TrimPrefix(patch.Key, customPropsMetadataSuffix+":")
|
||||
for _, prop := range customProps {
|
||||
if prop.ID == propId {
|
||||
switch prop.Type {
|
||||
case types.CustomPropsTypeText:
|
||||
if prop.Min > 0 && prop.Min > len(patch.Value) {
|
||||
return fmt.Errorf("value is too short")
|
||||
}
|
||||
if prop.Max > 0 && prop.Max < len(patch.Value) {
|
||||
return fmt.Errorf("value is too long")
|
||||
}
|
||||
|
||||
return nil
|
||||
case types.CustomPropsTypeRating:
|
||||
if patch.Value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate the value is a number
|
||||
rating, err := strconv.Atoi(patch.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("value is not a number")
|
||||
}
|
||||
|
||||
if prop.Max < rating {
|
||||
return fmt.Errorf("value is too large")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case types.CustomPropsTypeNumber:
|
||||
if patch.Value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
value, err := strconv.Atoi(patch.Value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("value is not a number")
|
||||
}
|
||||
|
||||
if prop.Min > value {
|
||||
return fmt.Errorf("value is too small")
|
||||
}
|
||||
if prop.Max > 0 && prop.Max < value {
|
||||
return fmt.Errorf("value is too large")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case types.CustomPropsTypeBoolean:
|
||||
if patch.Value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if patch.Value != "true" && patch.Value != "false" {
|
||||
return fmt.Errorf("value is not a boolean")
|
||||
}
|
||||
|
||||
return nil
|
||||
case types.CustomPropsTypeSelect:
|
||||
if patch.Value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, option := range prop.Options {
|
||||
if option == patch.Value {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("invalid option")
|
||||
case types.CustomPropsTypeMultiSelect:
|
||||
if patch.Value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var values []string
|
||||
if err := json.Unmarshal([]byte(patch.Value), &values); err != nil {
|
||||
return fmt.Errorf("invalid multi select value: %w", err)
|
||||
}
|
||||
|
||||
// make sure all values are in the options
|
||||
for _, value := range values {
|
||||
if !lo.Contains(prop.Options, value) {
|
||||
return fmt.Errorf("invalid option")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
case types.CustomPropsTypeLink:
|
||||
if patch.Value == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if prop.Min > 0 && len(patch.Value) < prop.Min {
|
||||
return fmt.Errorf("value is too small")
|
||||
}
|
||||
|
||||
if prop.Max > 0 && len(patch.Value) > prop.Max {
|
||||
return fmt.Errorf("value is too large")
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("unkown custom props")
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user