77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"kra/internal/biz/system"
|
|
"path"
|
|
"strings"
|
|
)
|
|
|
|
func deletePrefixViaList(ctx context.Context, prefix string, list func(context.Context, string, string, int) ([]*system.StoredFile, string, bool, error), remove func(context.Context, string) error) error {
|
|
prefix, err := normalizeDeletePrefix(prefix)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cursor := ""
|
|
for {
|
|
items, next, more, err := list(ctx, prefix+"/", cursor, 1000)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, item := range items {
|
|
if err := remove(ctx, item.Path); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if !more {
|
|
return nil
|
|
}
|
|
cursor, err = advanceDeletePrefixCursor(cursor, next, more)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
|
|
// normalizeDeletePrefix rejects requests that could accidentally target the
|
|
// storage root and returns the canonical key form used by all backends.
|
|
func normalizeDeletePrefix(prefix string) (string, error) {
|
|
prefix = strings.TrimSpace(strings.ReplaceAll(prefix, "\\", "/"))
|
|
prefix = strings.Trim(prefix, "/")
|
|
if prefix == "" {
|
|
return "", errors.New("storage delete prefix is required")
|
|
}
|
|
for _, part := range strings.Split(prefix, "/") {
|
|
if part == ".." {
|
|
return "", errors.New("invalid storage delete prefix")
|
|
}
|
|
}
|
|
clean := path.Clean(prefix)
|
|
if clean == "." || clean == ".." || strings.HasPrefix(clean, "../") {
|
|
return "", errors.New("invalid storage delete prefix")
|
|
}
|
|
return clean, nil
|
|
}
|
|
|
|
// advanceDeletePrefixCursor prevents a backend that reports a truncated page
|
|
// without a new cursor from making DeletePrefix loop forever.
|
|
func advanceDeletePrefixCursor(current, next string, more bool) (string, error) {
|
|
if !more {
|
|
return "", nil
|
|
}
|
|
current = strings.TrimSpace(current)
|
|
next = strings.TrimSpace(next)
|
|
if next == "" || next == current {
|
|
return "", errors.New("storage delete prefix pagination made no progress")
|
|
}
|
|
return next, nil
|
|
}
|
|
|
|
func boundedPrefix(key, prefix string) string {
|
|
if strings.HasSuffix(strings.ReplaceAll(prefix, "\\", "/"), "/") && !strings.HasSuffix(key, "/") {
|
|
return key + "/"
|
|
}
|
|
return key
|
|
}
|