mirror of
https://github.com/get-drexa/drive.git
synced 2026-02-02 13:21:17 +00:00
RenameNode was calling Resolve() which generated blob keys for directories that don't have blobs, causing 'key not found' errors. Added ResolveRenameOp to BlobKeyResolver interface: - FlatKeyResolver returns nil (UUIDs don't change on rename) - HierarchicalKeyResolver returns move op for files and directories This allows directory renames to work correctly with flat storage, and leverages os.Rename for atomic directory moves with hierarchical. Co-authored-by: Ona <no-reply@ona.com>
111 lines
3.1 KiB
Go
111 lines
3.1 KiB
Go
package virtualfs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/get-drexa/drexa/internal/blob"
|
|
"github.com/google/uuid"
|
|
"github.com/uptrace/bun"
|
|
)
|
|
|
|
type HierarchicalKeyResolver struct {
|
|
db *bun.DB
|
|
}
|
|
|
|
var _ BlobKeyResolver = &HierarchicalKeyResolver{}
|
|
|
|
func NewHierarchicalKeyResolver(db *bun.DB) *HierarchicalKeyResolver {
|
|
return &HierarchicalKeyResolver{db: db}
|
|
}
|
|
|
|
func (r *HierarchicalKeyResolver) ShouldPersistKey() bool {
|
|
return false
|
|
}
|
|
|
|
func (r *HierarchicalKeyResolver) Resolve(ctx context.Context, db bun.IDB, node *Node) (blob.Key, error) {
|
|
path, err := buildNodeAbsolutePathString(ctx, db, node)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return blob.Key(fmt.Sprintf("%s/%s", node.DriveID, path)), nil
|
|
}
|
|
|
|
func (r *HierarchicalKeyResolver) ResolveDeletionKeys(ctx context.Context, node *Node, allKeys []blob.Key) (*DeletionPlan, error) {
|
|
path, err := buildNodeAbsolutePathString(ctx, r.db, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &DeletionPlan{Prefix: blob.Key(fmt.Sprintf("%s/%s", node.DriveID, path))}, nil
|
|
}
|
|
|
|
// ResolveBulkMoveOps computes blob move operations for nodes being moved to a new parent.
|
|
// This implementation optimizes by computing parent paths only once (2 queries total),
|
|
// rather than computing the full path for each node individually (N queries).
|
|
func (r *HierarchicalKeyResolver) ResolveBulkMoveOps(ctx context.Context, db bun.IDB, nodes []*Node, newParentID uuid.UUID) ([]BlobMoveOp, error) {
|
|
if len(nodes) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
driveID := nodes[0].DriveID
|
|
oldParentID := nodes[0].ParentID
|
|
|
|
for _, node := range nodes[1:] {
|
|
if node.ParentID != oldParentID {
|
|
return nil, ErrUnsupportedOperation
|
|
}
|
|
}
|
|
|
|
oldParentPath, err := buildPathFromNodeID(ctx, db, oldParentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newParentPath, err := buildPathFromNodeID(ctx, db, newParentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// For each node, construct old and new keys using the precomputed parent paths
|
|
ops := make([]BlobMoveOp, len(nodes))
|
|
for i, node := range nodes {
|
|
oldKey := blob.Key(fmt.Sprintf("%s/%s/%s", driveID, oldParentPath, node.Name))
|
|
newKey := blob.Key(fmt.Sprintf("%s/%s/%s", driveID, newParentPath, node.Name))
|
|
ops[i] = BlobMoveOp{Node: node, OldKey: oldKey, NewKey: newKey}
|
|
}
|
|
|
|
return ops, nil
|
|
}
|
|
|
|
// ResolveRenameOp returns the blob move operation for renaming a node.
|
|
// For hierarchical storage, both files and directories need blob moves since
|
|
// the key is path-based. os.Rename handles directory moves atomically.
|
|
func (r *HierarchicalKeyResolver) ResolveRenameOp(ctx context.Context, db bun.IDB, node *Node, newName string) (*BlobMoveOp, error) {
|
|
oldPath, err := buildNodeAbsolutePathString(ctx, db, node)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
parentPath, err := buildPathFromNodeID(ctx, db, node.ParentID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var newPath string
|
|
if parentPath == "" {
|
|
newPath = newName
|
|
} else {
|
|
newPath = fmt.Sprintf("%s/%s", parentPath, newName)
|
|
}
|
|
|
|
oldKey := blob.Key(fmt.Sprintf("%s/%s", node.DriveID, oldPath))
|
|
newKey := blob.Key(fmt.Sprintf("%s/%s", node.DriveID, newPath))
|
|
|
|
return &BlobMoveOp{
|
|
Node: node,
|
|
OldKey: oldKey,
|
|
NewKey: newKey,
|
|
}, nil
|
|
}
|