mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-05-09 05:39:35 -05:00
[server] db support for file versions
This commit is contained in:
@@ -50,6 +50,7 @@ func ResourceFromFS(r fs.Resource) Resource {
|
||||
ContentSHA256: r.ContentSHA256(),
|
||||
InheritedPermissions: r.InheritedPermissions(),
|
||||
Grants: r.Grants(),
|
||||
Versions: r.Versions(),
|
||||
Links: r.Links(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ type Resource struct {
|
||||
ContentSHA256 string `json:"c_sha256"`
|
||||
InheritedPermissions json.RawMessage `json:"inherited_permissions,omitempty"`
|
||||
Grants json.RawMessage `json:"grants"`
|
||||
Versions json.RawMessage `json:"versions"`
|
||||
Links json.RawMessage `json:"publinks"`
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,6 @@ CREATE TABLE resources (
|
||||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
modified TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted TIMESTAMP,
|
||||
content_length BIGINT NOT NULL DEFAULT 0,
|
||||
content_type TEXT NOT NULL DEFAULT '',
|
||||
content_sha256 TEXT NOT NULL DEFAULT '',
|
||||
permissions JSONB NOT NULL DEFAULT '{}',
|
||||
grants JSONB NOT NULL DEFAULT '{}'
|
||||
);
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
CREATE TABLE resource_versions(
|
||||
id UUID PRIMARY KEY,
|
||||
resource_id UUID NOT NULL REFERENCES resources(id),
|
||||
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted TIMESTAMP,
|
||||
size BIGINT NOT NULL,
|
||||
mime_type TEXT NOT NULL,
|
||||
sha256 TEXT NOT NULL
|
||||
);
|
||||
|
||||
ALTER TABLE resources DROP COLUMN content_length;
|
||||
ALTER TABLE resources DROP COLUMN content_type;
|
||||
ALTER TABLE resources DROP COLUMN content_sha256;
|
||||
|
||||
|
||||
|
||||
---- create above / drop below ----
|
||||
|
||||
DROP TABLE resource_versions;
|
||||
|
||||
ALTER TABLE resources ADD COLUMN content_length BIGINT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE resources ADD COLUMN content_type TEXT NOT NULL DEFAULT '';
|
||||
ALTER TABLE resources ADD COLUMN content_sha256 TEXT NOT NULL DEFAULT '';
|
||||
|
||||
@@ -46,7 +46,6 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
|
||||
nodes := goqu.T("nodes").As("n")
|
||||
r := goqu.T("resources").As("r")
|
||||
p := goqu.T("resources").As("p")
|
||||
l := goqu.T("publinks").As("l")
|
||||
sub := pg.
|
||||
Select(r.Col("id"), r.Col("parent"), nodes.Col("search"), goqu.L("n.depth + 1")).
|
||||
From(r).
|
||||
@@ -62,8 +61,11 @@ func (f filesystem) ResourceByPath(path string) (Resource, error) {
|
||||
Where(r.Col("id").Eq(goqu.V(f.pathRoot))).
|
||||
UnionAll(sub)
|
||||
|
||||
l := goqu.T("publinks").As("l")
|
||||
v := goqu.T("resource_versions").As("v")
|
||||
q := pg.Select(r.All(),
|
||||
pg.Select(goqu.L(publinkFieldsQuery)).From(l).Where(l.Col("root").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L(versionsQuery)).From(v).Where(v.Col("resource_id").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L(publinksQuery)).From(l).Where(l.Col("root").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L("CASE WHEN COALESCE(p.permissions[?::INT]::INTEGER, 0) <> 0 THEN p.id ELSE NULL END AS visible_parent", f.userID)),
|
||||
pg.Select(goqu.L("COALESCE(p.permissions, '{}'::JSONB)")),
|
||||
).
|
||||
|
||||
@@ -23,6 +23,7 @@ type Resource struct {
|
||||
contentSHA256 string
|
||||
permissions []byte
|
||||
grants []byte
|
||||
versions []byte
|
||||
links []byte
|
||||
inheritedPermissions []byte
|
||||
visibleParent pgtype.UUID
|
||||
@@ -39,6 +40,7 @@ func (r Resource) ContentLength() int { return r.contentLength }
|
||||
func (r Resource) ContentSHA256() string { return r.contentSHA256 }
|
||||
func (r Resource) ContentType() string { return r.contentType }
|
||||
func (r Resource) Grants() []byte { return r.grants }
|
||||
func (r Resource) Versions() []byte { return r.versions }
|
||||
func (r Resource) Links() []byte { return r.links }
|
||||
func (r Resource) VisibleParentID() pgtype.UUID { return r.visibleParent }
|
||||
func (r Resource) InheritedPermissions() []byte { return r.inheritedPermissions }
|
||||
@@ -69,11 +71,9 @@ func (f filesystem) scanFullResource(row pgx.CollectableRow) (Resource, error) {
|
||||
&r.created,
|
||||
&r.modified,
|
||||
&r.deleted,
|
||||
&r.contentLength,
|
||||
&r.contentType,
|
||||
&r.contentSHA256,
|
||||
&r.permissions,
|
||||
&r.grants,
|
||||
&r.versions,
|
||||
&r.links,
|
||||
&r.visibleParent,
|
||||
&r.inheritedPermissions,
|
||||
@@ -106,11 +106,9 @@ func (f filesystem) scanResourceWithoutParent(row pgx.CollectableRow) (Resource,
|
||||
&r.created,
|
||||
&r.modified,
|
||||
&r.deleted,
|
||||
&r.contentLength,
|
||||
&r.contentType,
|
||||
&r.contentSHA256,
|
||||
&r.permissions,
|
||||
&r.grants,
|
||||
&r.versions,
|
||||
&r.links,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,21 +11,32 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const publinkFieldsQuery = "COALESCE(JSONB_AGG(JSONB_BUILD_OBJECT(" +
|
||||
const publinksQuery = "COALESCE(JSONB_AGG(JSONB_BUILD_OBJECT(" +
|
||||
"'id',l.id," +
|
||||
"'expires',EXTRACT(EPOCH FROM l.expires)::integer," +
|
||||
"'protected',l.password_hash <> ''," +
|
||||
"'access_limit',l.access_limit," +
|
||||
"'accessed',l.accessed)), '[]'::JSONB)"
|
||||
"'accessed',l.accessed" +
|
||||
")), '[]'::JSONB)"
|
||||
|
||||
const versionsQuery = "COALESCE(JSONB_AGG(JSONB_BUILD_OBJECT(" +
|
||||
"'id',v.id," +
|
||||
"'created',EXTRACT(EPOCH FROM v.created)::integer," +
|
||||
"'deleted',EXTRACT(EPOCH FROM v.deleted)::integer," +
|
||||
"'mime_type',v.mime_type," +
|
||||
"'size',v.size," +
|
||||
"'sha256',v.sha256," +
|
||||
")), '[]'::JSONB)"
|
||||
|
||||
const fullResourceQuery = `SELECT r.*,
|
||||
(SELECT ` + publinkFieldsQuery + ` FROM publinks l WHERE l.root = r.id) AS links,
|
||||
(SELECT ` + versionsQuery + ` FROM resource_versions v WHERE v.resource_id = r.id) AS versions,
|
||||
(SELECT ` + publinksQuery + ` FROM publinks l WHERE l.root = r.id) AS links,
|
||||
CASE WHEN COALESCE(p.permissions[@user_id::INT]::INTEGER, 0) <> 0 THEN p.id ELSE NULL END AS visible_parent,
|
||||
COALESCE(p.permissions, '{}'::JSONB) AS inherited_permissions
|
||||
FROM resources r LEFT JOIN resources p ON p.id = r.parent
|
||||
`
|
||||
|
||||
func selectResourceTree(id uuid.UUID, excludeTreeRoot, includeDeleted, includeLinks bool, extraCols ...string) (exp.IdentifierExpression, exp.AliasedExpression, *goqu.SelectDataset) {
|
||||
func selectResourceTree(id uuid.UUID, excludeTreeRoot, includeDeleted, includeSubQueries bool, extraCols ...string) (exp.IdentifierExpression, exp.AliasedExpression, *goqu.SelectDataset) {
|
||||
t := goqu.T("resources")
|
||||
r := t.As("r")
|
||||
n := goqu.T("nodes").As("n")
|
||||
@@ -64,11 +75,16 @@ func selectResourceTree(id uuid.UUID, excludeTreeRoot, includeDeleted, includeLi
|
||||
idSelect = idSelect.Where(n.Col("depth").Gt(0))
|
||||
}
|
||||
|
||||
l := goqu.T("publinks").As("l")
|
||||
q := pg.
|
||||
From(t)
|
||||
if includeLinks {
|
||||
q = q.Select(t.All(), pg.Select(goqu.L(publinkFieldsQuery)).From(l).Where(l.Col("root").Eq(t.Col("id"))))
|
||||
if includeSubQueries {
|
||||
v := goqu.T("resource_versions").As("v")
|
||||
l := goqu.T("publinks").As("l")
|
||||
q = q.Select(
|
||||
r.All(),
|
||||
pg.Select(goqu.L(versionsQuery)).From(v).Where(v.Col("resource_id").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L(publinksQuery)).From(l).Where(l.Col("root").Eq(r.Col("id"))),
|
||||
)
|
||||
}
|
||||
q = q.WithRecursive("nodes("+strings.Join(cols, ",")+")", base.UnionAll(rec)).
|
||||
Where(t.Col("id").Eq(idSelect))
|
||||
@@ -76,11 +92,16 @@ func selectResourceTree(id uuid.UUID, excludeTreeRoot, includeDeleted, includeLi
|
||||
}
|
||||
|
||||
func selectDirectChildren(id uuid.UUID, deleted pgtype.Timestamp, includeDeleted bool) (exp.AliasedExpression, *goqu.SelectDataset) {
|
||||
v := goqu.T("resource_versions").As("v")
|
||||
l := goqu.T("publinks").As("l")
|
||||
r := goqu.T("resources").As("r")
|
||||
q := pg.
|
||||
From(r).
|
||||
Select(r.All(), pg.Select(goqu.L(publinkFieldsQuery)).From(l).Where(l.Col("root").Eq(r.Col("id")))).
|
||||
Select(
|
||||
r.All(),
|
||||
pg.Select(goqu.L(versionsQuery)).From(v).Where(v.Col("resource_id").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L(publinksQuery)).From(l).Where(l.Col("root").Eq(r.Col("id"))),
|
||||
).
|
||||
Where(r.Col("parent").Eq(goqu.V(id)))
|
||||
if !includeDeleted {
|
||||
q = q.Where(goqu.L("? IS NOT DISTINCT FROM ?", r.Col("deleted"), goqu.V(deleted)))
|
||||
|
||||
@@ -15,13 +15,15 @@ func (f filesystem) TrashList(cursor string, n uint) ([]Resource, string, error)
|
||||
t := goqu.T("trash")
|
||||
r := goqu.T("resources").As("r")
|
||||
p := goqu.T("resources").As("p")
|
||||
v := goqu.T("resource_versions").As("v")
|
||||
l := goqu.T("publinks").As("l")
|
||||
q := pg.From(t).
|
||||
Join(r, goqu.On(t.Col("id").Eq(r.Col("id")))).
|
||||
Join(p, goqu.On(r.Col("parent").Eq(p.Col("id")))).
|
||||
Select(
|
||||
r.All(),
|
||||
pg.Select(goqu.L(publinkFieldsQuery)).From(l).Where(l.Col("root").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L(versionsQuery)).From(v).Where(v.Col("resource_id").Eq(r.Col("id"))),
|
||||
pg.Select(goqu.L(publinksQuery)).From(l).Where(l.Col("root").Eq(r.Col("id"))),
|
||||
// TODO: Always select p.id when fullAccess
|
||||
pg.Select(goqu.L("CASE WHEN COALESCE(p.permissions[?::INT]::INTEGER, 0) <> 0 THEN p.id ELSE NULL END AS visible_parent", f.userID)),
|
||||
pg.Select(goqu.L("COALESCE(p.permissions, '{}'::JSONB)")),
|
||||
|
||||
Reference in New Issue
Block a user