mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-05-12 15:18:38 -05:00
[server][core] Include links
This commit is contained in:
@@ -46,5 +46,6 @@ func ResourceFromFS(r fs.Resource) Resource {
|
||||
ContentSHA256: r.ContentSHA256(),
|
||||
Permissions: string(r.Permissions()),
|
||||
Grants: string(r.Grants()),
|
||||
Links: string(r.Links()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ type Resource struct {
|
||||
ContentSHA256 string `json:"c_sha256"`
|
||||
Permissions string `json:"permissions"`
|
||||
Grants string `json:"grants"`
|
||||
Links string `json:"links"`
|
||||
}
|
||||
|
||||
type ResourceFull struct {
|
||||
|
||||
@@ -9,23 +9,6 @@ import (
|
||||
"github.com/shroff/phylum/server/internal/core/fs"
|
||||
)
|
||||
|
||||
func formatRow(id string, dir bool, size int, sha256, name, permissions string, deleted bool) string {
|
||||
sizeStr := " -"
|
||||
if dir {
|
||||
sha256 = "- "
|
||||
} else {
|
||||
sizeStr = FormatSize(size)
|
||||
}
|
||||
if sha256 == "" {
|
||||
sha256 = "xxxxxxxx"
|
||||
}
|
||||
delStr := " "
|
||||
if deleted {
|
||||
delStr = "*"
|
||||
}
|
||||
return fmt.Sprintf("%s %s %s %s%-24s %s", id, sizeStr, sha256[0:8], delStr, name, permissions)
|
||||
}
|
||||
|
||||
func FormatResourceSummary(r fs.Resource, name string, deleted pgtype.Timestamp) string {
|
||||
if name == "" {
|
||||
name = r.Name()
|
||||
@@ -33,7 +16,21 @@ func FormatResourceSummary(r fs.Resource, name string, deleted pgtype.Timestamp)
|
||||
if r.Dir() {
|
||||
name += "/"
|
||||
}
|
||||
return formatRow(r.ID().String(), r.Dir(), int(r.ContentLength()), r.ContentSHA256(), name, formatGrantsJson(r.Grants()), deleted != r.Deleted())
|
||||
sha256 := r.ContentSHA256()
|
||||
sizeStr := " -"
|
||||
if r.Dir() {
|
||||
sha256 = "- "
|
||||
} else {
|
||||
sizeStr = FormatSize(r.ContentLength())
|
||||
}
|
||||
if sha256 == "" {
|
||||
sha256 = "xxxxxxxx"
|
||||
}
|
||||
delStr := " "
|
||||
if deleted != r.Deleted() {
|
||||
delStr = "*"
|
||||
}
|
||||
return fmt.Sprintf("%s %s %s %s%-24s %s %s", r.ID().String(), sizeStr, sha256[0:8], delStr, name, formatGrantsJson(r.Grants()), r.Links())
|
||||
}
|
||||
|
||||
func FormatSize(size int) string {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
CREATE VIEW linked_resources AS
|
||||
SELECT r.*, (SELECT jsonb_agg(l.name) FROM publinks l WHERE l.root = r.id AND l.deleted IS NULL) AS links
|
||||
FROM resources r;
|
||||
|
||||
---- create above / drop below ----
|
||||
|
||||
DROP VIEW linked_resources;
|
||||
@@ -116,7 +116,7 @@ func (f filesystem) createResource(
|
||||
case ResourceBindConflictResolutionError:
|
||||
err = ErrResourceNameConflict
|
||||
case ResourceBindConflictResolutionEnsure:
|
||||
res, err = f.ChildResourceByName(parent, name)
|
||||
res, err = f.childResourceByName(parent, name)
|
||||
if err == nil && res.dir != dir {
|
||||
err = ErrResourceNameConflict
|
||||
}
|
||||
@@ -150,7 +150,7 @@ func (f filesystem) createResource(
|
||||
}
|
||||
}
|
||||
case ResourceBindConflictResolutionOverwrite:
|
||||
res, err = f.ChildResourceByName(parent, name)
|
||||
res, err = f.childResourceByName(parent, name)
|
||||
if err == nil {
|
||||
deleted = true
|
||||
if res.dir == dir {
|
||||
@@ -185,7 +185,7 @@ func (f filesystem) createResource(
|
||||
}
|
||||
}
|
||||
case ResourceBindConflictResolutionDelete:
|
||||
res, err = f.ChildResourceByName(parent, name)
|
||||
res, err = f.childResourceByName(parent, name)
|
||||
if err == nil {
|
||||
deleted = true
|
||||
err = f.deleteRecursive(res.id, parent, true, false)
|
||||
@@ -229,6 +229,6 @@ func (f filesystem) insertResource(id, parent uuid.UUID, name string, dir bool,
|
||||
if rows, err := f.db.Query(query, args...); err != nil {
|
||||
return Resource{}, err
|
||||
} else {
|
||||
return f.collectResource(rows)
|
||||
return f.collectResource(rows, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,8 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
|
||||
Where(resources.Col("id").Eq(goqu.V(f.rootID))).
|
||||
UnionAll(sub)
|
||||
|
||||
q := pg.Select(resources.All()).
|
||||
l := goqu.T("publinks").As("l")
|
||||
q := pg.Select(resources.All(), pg.Select(goqu.L("jsonb_agg(?)", l.Col("name"))).From(l).Where(l.Col("root").Eq(resources.Col("id")), l.Col("deleted").IsNull())).
|
||||
From(resources).
|
||||
WithRecursive("nodes(id, parent, search, depth)", rec).
|
||||
Join(nodes, goqu.On(resources.Col("id").Eq(nodes.Col("id")))).
|
||||
@@ -34,25 +35,25 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
|
||||
if rows, err := f.db.Query(query, args...); err != nil {
|
||||
return Resource{}, err
|
||||
} else {
|
||||
return f.collectResource(rows)
|
||||
return f.collectResource(rows, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (f filesystem) ResourceByID(id uuid.UUID) (Resource, error) {
|
||||
const query = "SELECT * FROM resources WHERE id = $1::UUID"
|
||||
const query = "SELECT r.*, (SELECT jsonb_agg(l.name) FROM publinks l WHERE l.root = r.id AND l.deleted IS NULL) AS links FROM resources r WHERE id = $1::UUID"
|
||||
if rows, err := f.db.Query(query, id); err != nil {
|
||||
return Resource{}, err
|
||||
} else {
|
||||
return f.collectResource(rows)
|
||||
return f.collectResource(rows, true)
|
||||
}
|
||||
}
|
||||
|
||||
func (f filesystem) ChildResourceByName(parentID uuid.UUID, name string) (Resource, error) {
|
||||
func (f filesystem) childResourceByName(parentID uuid.UUID, name string) (Resource, error) {
|
||||
const query = "SELECT * FROM resources WHERE parent = $1::UUID AND name = $2::TEXT AND deleted IS NULL"
|
||||
if rows, err := f.db.Query(query, parentID, name); err != nil {
|
||||
return Resource{}, err
|
||||
} else {
|
||||
return f.collectResource(rows)
|
||||
return f.collectResource(rows, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -22,6 +23,7 @@ type Resource struct {
|
||||
contentSHA256 string
|
||||
permissions []byte
|
||||
grants []byte
|
||||
links []byte
|
||||
userPermission Permission
|
||||
}
|
||||
|
||||
@@ -37,13 +39,18 @@ func (r Resource) ContentSHA256() string { return r.contentSHA256 }
|
||||
func (r Resource) ContentType() string { return r.contentType }
|
||||
func (r Resource) Permissions() []byte { return r.permissions }
|
||||
func (r Resource) Grants() []byte { return r.grants }
|
||||
func (r Resource) Links() []byte { return r.links }
|
||||
|
||||
func (r Resource) hasPermission(p Permission) bool {
|
||||
return r.userPermission&p != 0
|
||||
}
|
||||
|
||||
func (f filesystem) collectResource(rows pgx.Rows) (Resource, error) {
|
||||
if r, err := pgx.CollectExactlyOneRow(rows, f.scanResource); err == nil {
|
||||
func (f filesystem) collectResource(rows pgx.Rows, includeLinks bool) (Resource, error) {
|
||||
scanner := f.scanLinkedResource
|
||||
if !includeLinks {
|
||||
scanner = f.scanResource
|
||||
}
|
||||
if r, err := pgx.CollectExactlyOneRow(rows, scanner); err == nil {
|
||||
return r, nil
|
||||
} else {
|
||||
if err == pgx.ErrNoRows {
|
||||
@@ -54,6 +61,41 @@ func (f filesystem) collectResource(rows pgx.Rows) (Resource, error) {
|
||||
|
||||
}
|
||||
|
||||
func (f filesystem) scanLinkedResource(row pgx.CollectableRow) (Resource, error) {
|
||||
r := Resource{f: f}
|
||||
err := row.Scan(
|
||||
&r.id,
|
||||
&r.name,
|
||||
&r.parentID,
|
||||
&r.dir,
|
||||
&r.created,
|
||||
&r.modified,
|
||||
&r.deleted,
|
||||
&r.contentLength,
|
||||
&r.contentType,
|
||||
&r.contentSHA256,
|
||||
&r.permissions,
|
||||
&r.grants,
|
||||
&r.links,
|
||||
)
|
||||
if err != nil {
|
||||
return r, err
|
||||
}
|
||||
permission := Permission(0)
|
||||
if f.fullAccess {
|
||||
permission = -1
|
||||
} else if p, err := readPermissionFromJson(r.permissions, f.username); err != nil {
|
||||
return Resource{}, err
|
||||
} else {
|
||||
permission = p
|
||||
}
|
||||
if permission&PermissionRead == 0 {
|
||||
return Resource{}, ErrResourceNotFound
|
||||
}
|
||||
r.userPermission = permission
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (f filesystem) scanResource(row pgx.CollectableRow) (Resource, error) {
|
||||
r := Resource{f: f}
|
||||
err := row.Scan(
|
||||
@@ -87,3 +129,18 @@ func (f filesystem) scanResource(row pgx.CollectableRow) (Resource, error) {
|
||||
r.userPermission = permission
|
||||
return r, err
|
||||
}
|
||||
|
||||
func readPermissionFromJson(j []byte, username string) (Permission, error) {
|
||||
if j == nil {
|
||||
return PermissionNone, nil
|
||||
}
|
||||
p := make(map[string]Permission)
|
||||
err := json.Unmarshal(j, &p)
|
||||
if err != nil {
|
||||
return PermissionNone, err
|
||||
}
|
||||
if p, ok := p[username]; ok {
|
||||
return p, nil
|
||||
}
|
||||
return PermissionNone, nil
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ func (r Resource) DeleteChildRecursive(name string) (Resource, error) {
|
||||
return Resource{}, ErrInsufficientPermissions
|
||||
}
|
||||
|
||||
c, err := r.f.ChildResourceByName(r.ID(), name)
|
||||
c, err := r.f.childResourceByName(r.ID(), name)
|
||||
if err != nil {
|
||||
if errors.Is(err, pgx.ErrNoRows) {
|
||||
err = ErrResourceNotFound
|
||||
@@ -90,14 +90,14 @@ func (r Resource) RestoreDeleted(parentPathOrUUID string, name string, autoRenam
|
||||
if name == "" {
|
||||
name = r.name
|
||||
}
|
||||
_, err = r.f.ChildResourceByName(p.id, name)
|
||||
_, err = r.f.childResourceByName(p.id, name)
|
||||
if autoRename && err == nil {
|
||||
ext := path.Ext(name)
|
||||
basename := name[:len(name)-len(ext)]
|
||||
counter := 1
|
||||
for {
|
||||
name = fmt.Sprintf("%s (%d)%s", basename, counter, ext)
|
||||
if _, err = r.f.ChildResourceByName(p.id, name); err == nil {
|
||||
if _, err = r.f.childResourceByName(p.id, name); err == nil {
|
||||
counter++
|
||||
} else {
|
||||
break
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -70,21 +69,6 @@ func selectDirectChildren(id uuid.UUID, deleted pgtype.Timestamp, includeDeleted
|
||||
return r, q
|
||||
}
|
||||
|
||||
func readPermissionFromJson(j []byte, username string) (Permission, error) {
|
||||
if j == nil {
|
||||
return PermissionNone, nil
|
||||
}
|
||||
p := make(map[string]Permission)
|
||||
err := json.Unmarshal(j, &p)
|
||||
if err != nil {
|
||||
return PermissionNone, err
|
||||
}
|
||||
if p, ok := p[username]; ok {
|
||||
return p, nil
|
||||
}
|
||||
return PermissionNone, nil
|
||||
}
|
||||
|
||||
func collectNonDirResourceIDs(rows pgx.Rows, e error) (total int, ids []uuid.UUID, err error) {
|
||||
if e != nil {
|
||||
return 0, nil, e
|
||||
|
||||
Reference in New Issue
Block a user