feat: add query param to include dir path in query

This commit is contained in:
2025-12-05 00:38:05 +00:00
parent 1bedafd3de
commit 3ea12cf51a
4 changed files with 64 additions and 10 deletions

View File

@@ -16,12 +16,13 @@ const (
) )
type DirectoryInfo struct { type DirectoryInfo struct {
Kind string `json:"kind"` Kind string `json:"kind"`
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Path virtualfs.Path `json:"path,omitempty"`
CreatedAt time.Time `json:"createdAt"` Name string `json:"name"`
UpdatedAt time.Time `json:"updatedAt"` CreatedAt time.Time `json:"createdAt"`
DeletedAt *time.Time `json:"deletedAt,omitempty"` UpdatedAt time.Time `json:"updatedAt"`
DeletedAt *time.Time `json:"deletedAt,omitempty"`
} }
type createDirectoryRequest struct { type createDirectoryRequest struct {
@@ -93,6 +94,7 @@ func (h *HTTPHandler) createDirectory(c *fiber.Ctx) error {
func (h *HTTPHandler) fetchDirectory(c *fiber.Ctx) error { func (h *HTTPHandler) fetchDirectory(c *fiber.Ctx) error {
node := mustCurrentDirectoryNode(c) node := mustCurrentDirectoryNode(c)
i := DirectoryInfo{ i := DirectoryInfo{
Kind: DirItemKindDirectory, Kind: DirItemKindDirectory,
ID: node.PublicID, ID: node.PublicID,
@@ -101,6 +103,16 @@ func (h *HTTPHandler) fetchDirectory(c *fiber.Ctx) error {
UpdatedAt: node.UpdatedAt, UpdatedAt: node.UpdatedAt,
DeletedAt: node.DeletedAt, DeletedAt: node.DeletedAt,
} }
include := c.Query("include")
if include == "path" {
p, err := h.vfs.RealPath(c.Context(), h.db, node)
if err != nil {
return httperr.Internal(err)
}
i.Path = p
}
return c.JSON(i) return c.JSON(i)
} }

View File

@@ -23,7 +23,7 @@ func (r *HierarchicalKeyResolver) ShouldPersistKey() bool {
} }
func (r *HierarchicalKeyResolver) Resolve(ctx context.Context, db bun.IDB, node *Node) (blob.Key, error) { func (r *HierarchicalKeyResolver) Resolve(ctx context.Context, db bun.IDB, node *Node) (blob.Key, error) {
path, err := buildNodeAbsolutePath(ctx, db, node) path, err := buildNodeAbsolutePathString(ctx, db, node)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -32,7 +32,7 @@ func (r *HierarchicalKeyResolver) Resolve(ctx context.Context, db bun.IDB, node
} }
func (r *HierarchicalKeyResolver) ResolveDeletionKeys(ctx context.Context, node *Node, allKeys []blob.Key) (*DeletionPlan, error) { func (r *HierarchicalKeyResolver) ResolveDeletionKeys(ctx context.Context, node *Node, allKeys []blob.Key) (*DeletionPlan, error) {
path, err := buildNodeAbsolutePath(ctx, r.db, node) path, err := buildNodeAbsolutePathString(ctx, r.db, node)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -23,11 +23,34 @@ SELECT name FROM path
WHERE EXISTS (SELECT 1 FROM path WHERE parent_id IS NULL) WHERE EXISTS (SELECT 1 FROM path WHERE parent_id IS NULL)
ORDER BY depth DESC;` ORDER BY depth DESC;`
const absolutePathWithPublicIDQuery = `WITH RECURSIVE path AS (
SELECT id, parent_id, name, public_id, 1 as depth
FROM vfs_nodes WHERE id = ? AND deleted_at IS NULL
UNION ALL
SELECT n.id, n.parent_id, n.name, n.public_id, p.depth + 1
FROM vfs_nodes n
JOIN path p ON n.id = p.parent_id
)
SELECT name, public_id FROM path
WHERE EXISTS (SELECT 1 FROM path WHERE parent_id IS NULL)
ORDER BY depth DESC;`
// Path represents a path to a node.
type Path []PathSegment
// PathSegment represents a segment of a path, containing the name and public ID of the node.
type PathSegment struct {
Name string `bun:"name" json:"name"`
PublicID string `bun:"public_id" json:"id"`
}
func JoinPath(parts ...string) string { func JoinPath(parts ...string) string {
return strings.Join(parts, "/") return strings.Join(parts, "/")
} }
func buildNodeAbsolutePath(ctx context.Context, db bun.IDB, node *Node) (string, error) { func buildNodeAbsolutePathString(ctx context.Context, db bun.IDB, node *Node) (string, error) {
var path []string var path []string
err := db.NewRaw(absolutePathQuery, node.ID).Scan(ctx, &path) err := db.NewRaw(absolutePathQuery, node.ID).Scan(ctx, &path)
if err != nil { if err != nil {
@@ -38,3 +61,15 @@ func buildNodeAbsolutePath(ctx context.Context, db bun.IDB, node *Node) (string,
} }
return JoinPath(path...), nil return JoinPath(path...), nil
} }
func buildNoteAbsolutePath(ctx context.Context, db bun.IDB, node *Node) (Path, error) {
var segments []PathSegment
err := db.NewRaw(absolutePathWithPublicIDQuery, node.ID).Scan(ctx, &segments)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, ErrNodeNotFound
}
return nil, err
}
return segments, nil
}

View File

@@ -434,7 +434,7 @@ func (vfs *VirtualFS) AbsolutePath(ctx context.Context, db bun.IDB, node *Node)
if !node.IsAccessible() { if !node.IsAccessible() {
return "", ErrNodeNotFound return "", ErrNodeNotFound
} }
return buildNodeAbsolutePath(ctx, db, node) return buildNodeAbsolutePathString(ctx, db, node)
} }
func (vfs *VirtualFS) PermanentlyDeleteNode(ctx context.Context, db bun.IDB, node *Node) error { func (vfs *VirtualFS) PermanentlyDeleteNode(ctx context.Context, db bun.IDB, node *Node) error {
@@ -564,3 +564,10 @@ func (vfs *VirtualFS) generatePublicID() (string, error) {
n := binary.BigEndian.Uint64(b[:]) n := binary.BigEndian.Uint64(b[:])
return vfs.sqid.Encode([]uint64{n}) return vfs.sqid.Encode([]uint64{n})
} }
func (vfs *VirtualFS) RealPath(ctx context.Context, db bun.IDB, node *Node) (Path, error) {
if !node.IsAccessible() {
return nil, ErrNodeNotFound
}
return buildNoteAbsolutePath(ctx, db, node)
}