mirror of
https://codeberg.org/shroff/phylum.git
synced 2026-01-05 11:10:47 -06:00
Consistent Naming
This commit is contained in:
@@ -14,44 +14,44 @@ func setupAdminCommand() *cobra.Command {
|
||||
Short: "Server Administration",
|
||||
}
|
||||
cmd.AddCommand([]*cobra.Command{
|
||||
setupAdminVfsCommand(),
|
||||
setupAdminLibraryCommand(),
|
||||
}...)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func setupAdminVfsCommand() *cobra.Command {
|
||||
func setupAdminLibraryCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "vfs",
|
||||
Short: "VFS Management",
|
||||
Use: "library",
|
||||
Short: "Library Management",
|
||||
}
|
||||
cmd.AddCommand([]*cobra.Command{
|
||||
setupAdminVfsCreateCommand(),
|
||||
setupAdminVfsDeleteCommand(),
|
||||
setupAdminLibraryCreateCommand(),
|
||||
setupAdminLibraryDeleteCommand(),
|
||||
}...)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func setupAdminVfsCreateCommand() *cobra.Command {
|
||||
func setupAdminLibraryCreateCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "create name",
|
||||
Short: "Create Root Folder",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := vfsManager.CreateVfs(context.Background(), uuid.New(), args[0]); err != nil {
|
||||
if err := libraryManager.Create(context.Background(), uuid.New(), args[0]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setupAdminVfsDeleteCommand() *cobra.Command {
|
||||
func setupAdminLibraryDeleteCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "delete name",
|
||||
Short: "Delete Root Folder",
|
||||
Args: cobra.ExactArgs(1),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if err := vfsManager.DeleteVfs(context.Background(), args[0]); err != nil {
|
||||
if err := libraryManager.Delete(context.Background(), args[0]); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -6,14 +6,14 @@ import (
|
||||
"path"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/shroff/phylum/server/internal/vfs"
|
||||
library "github.com/shroff/phylum/server/internal/library"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var debug bool = false
|
||||
var vfsManager vfs.VfsManager
|
||||
var libraryManager library.Manager
|
||||
|
||||
func SetupCommand() {
|
||||
viper.SetEnvPrefix("phylum")
|
||||
@@ -62,7 +62,7 @@ func SetupCommand() {
|
||||
if err != nil {
|
||||
logrus.Fatal("Unable to connect to database: " + err.Error())
|
||||
}
|
||||
vfsManager = vfs.NewManager(conn)
|
||||
libraryManager = library.NewManager(conn)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
|
||||
@@ -54,7 +54,7 @@ func setupServeCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
func setupWebdav(r *gin.RouterGroup) {
|
||||
handler := webdav.NewHandler(vfsManager, r.BasePath())
|
||||
handler := webdav.NewHandler(libraryManager, r.BasePath())
|
||||
r.Handle("OPTIONS", "/*path", handler.HandleRequest)
|
||||
r.Handle("GET", "/*path", handler.HandleRequest)
|
||||
r.Handle("PUT", "/*path", handler.HandleRequest)
|
||||
|
||||
90
internal/library/library.go
Normal file
90
internal/library/library.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"hash"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/sql"
|
||||
"github.com/shroff/phylum/server/internal/storage"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Library struct {
|
||||
queries sql.Queries
|
||||
root uuid.UUID
|
||||
cs storage.Storage
|
||||
}
|
||||
|
||||
func (l Library) Open(id uuid.UUID, flag int) (io.ReadWriteCloser, error) {
|
||||
return l.cs.Open(id, flag, nil)
|
||||
}
|
||||
|
||||
func (l Library) ReadDir(ctx context.Context, id uuid.UUID, includeRoot bool, recursive bool) ([]sql.ReadDirRow, error) {
|
||||
return l.queries.ReadDir(ctx, sql.ReadDirParams{ID: id, IncludeRoot: includeRoot, Recursive: recursive})
|
||||
}
|
||||
|
||||
func (l Library) DeleteRecursive(ctx context.Context, id uuid.UUID, hardDelete bool) error {
|
||||
query := l.queries.DeleteRecursive
|
||||
if hardDelete {
|
||||
query = l.queries.HardDeleteRecursive
|
||||
}
|
||||
deleted, err := query(ctx, id)
|
||||
if err == nil && hardDelete {
|
||||
for _, res := range deleted {
|
||||
if !res.Dir {
|
||||
l.cs.Delete(res.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (l Library) CreateResource(ctx context.Context, id uuid.UUID, parent uuid.UUID, name string, dir bool) error {
|
||||
return l.queries.CreateResource(ctx, sql.CreateResourceParams{ID: id, Parent: &parent, Name: name, Dir: dir})
|
||||
}
|
||||
|
||||
func (l Library) Move(ctx context.Context, id uuid.UUID, parent uuid.UUID, name string) error {
|
||||
return l.queries.Rename(ctx, sql.RenameParams{ID: id, Parent: parent, Name: name})
|
||||
}
|
||||
|
||||
func (l Library) UpdateContents(ctx context.Context, id uuid.UUID) (io.WriteCloser, error) {
|
||||
return l.cs.Open(id, os.O_CREATE|os.O_RDWR|os.O_TRUNC, func(h hash.Hash, len int, err error) {
|
||||
if err == nil {
|
||||
etag := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
err = l.queries.UpdateResourceContents(ctx, sql.UpdateResourceContentsParams{
|
||||
ID: id,
|
||||
Size: pgtype.Int4{Int32: int32(len), Valid: true},
|
||||
Etag: pgtype.Text{String: string(etag), Valid: true},
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warn("Unable to update contents: " + err.Error())
|
||||
l.cs.Delete(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (l Library) ResourceByPath(ctx context.Context, path string) (sql.ResourceByPathRow, error) {
|
||||
path = strings.Trim(path, "/")
|
||||
segments := strings.Split(path, "/")
|
||||
if path == "" {
|
||||
// Calling strings.Split on an empty string returns a slice of length 1. That breaks the query
|
||||
segments = []string{}
|
||||
}
|
||||
|
||||
res, err := l.queries.ResourceByPath(ctx, sql.ResourceByPathParams{Search: segments, Root: l.root})
|
||||
if err != nil {
|
||||
return sql.ResourceByPathRow{}, fs.ErrNotExist
|
||||
}
|
||||
|
||||
//TODO: Permissions checks
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package vfs
|
||||
package library
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -10,41 +10,41 @@ import (
|
||||
"github.com/shroff/phylum/server/internal/storage"
|
||||
)
|
||||
|
||||
type VfsManager struct {
|
||||
type Manager struct {
|
||||
queries sql.Queries
|
||||
cs storage.Storage
|
||||
}
|
||||
|
||||
func NewManager(conn *pgx.Conn) VfsManager {
|
||||
func NewManager(conn *pgx.Conn) Manager {
|
||||
queries := *sql.New(conn)
|
||||
cs, err := storage.NewLocalStorage("srv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return VfsManager{queries: queries, cs: cs}
|
||||
return Manager{queries: queries, cs: cs}
|
||||
}
|
||||
|
||||
func (b VfsManager) GetVfs(ctx context.Context, name string) (*Vfs, error) {
|
||||
func (b Manager) Get(ctx context.Context, name string) (*Library, error) {
|
||||
// TODO: Permissions checks
|
||||
root, err := b.queries.FindRoot(ctx, name)
|
||||
lib, err := b.queries.LibraryByName(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Vfs{queries: b.queries, root: root.ID, cs: b.cs}, nil
|
||||
return &Library{queries: b.queries, root: lib.ID, cs: b.cs}, nil
|
||||
}
|
||||
|
||||
func (b VfsManager) CreateVfs(ctx context.Context, id uuid.UUID, name string) error {
|
||||
if _, err := b.queries.FindRoot(ctx, name); err == nil {
|
||||
func (b Manager) Create(ctx context.Context, id uuid.UUID, name string) error {
|
||||
if _, err := b.queries.LibraryByName(ctx, name); err == nil {
|
||||
return fmt.Errorf("root directory already exists: %s", name)
|
||||
} else {
|
||||
return b.queries.CreateResource(ctx, sql.CreateResourceParams{ID: id, Parent: nil, Name: name, Dir: true})
|
||||
}
|
||||
}
|
||||
|
||||
func (b VfsManager) DeleteVfs(ctx context.Context, name string) error {
|
||||
if fs, err := b.GetVfs(ctx, name); err == nil {
|
||||
return fs.DeleteRecursive(ctx, fs.root, true)
|
||||
func (b Manager) Delete(ctx context.Context, name string) error {
|
||||
if lib, err := b.Get(ctx, name); err == nil {
|
||||
return lib.DeleteRecursive(ctx, lib.root, true)
|
||||
} else {
|
||||
return fmt.Errorf("root directory does not exist: %s", name)
|
||||
}
|
||||
@@ -82,48 +82,6 @@ func (q *Queries) DeleteRecursive(ctx context.Context, id uuid.UUID) ([]Resource
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const findResource = `-- name: FindResource :one
|
||||
SELECT id, parent, name, dir, created, modified, deleted, size, etag from resources WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) FindResource(ctx context.Context, id uuid.UUID) (Resource, error) {
|
||||
row := q.db.QueryRow(ctx, findResource, id)
|
||||
var i Resource
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Parent,
|
||||
&i.Name,
|
||||
&i.Dir,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.Deleted,
|
||||
&i.Size,
|
||||
&i.Etag,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const findRoot = `-- name: FindRoot :one
|
||||
SELECT id, parent, name, dir, created, modified, deleted, size, etag from resources WHERE deleted IS NULL AND parent IS NULL AND name = $1
|
||||
`
|
||||
|
||||
func (q *Queries) FindRoot(ctx context.Context, name string) (Resource, error) {
|
||||
row := q.db.QueryRow(ctx, findRoot, name)
|
||||
var i Resource
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Parent,
|
||||
&i.Name,
|
||||
&i.Dir,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.Deleted,
|
||||
&i.Size,
|
||||
&i.Etag,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const hardDeleteRecursive = `-- name: HardDeleteRecursive :many
|
||||
WITH RECURSIVE nodes(id, parent) AS (
|
||||
SELECT r.id, r.parent
|
||||
@@ -168,6 +126,27 @@ func (q *Queries) HardDeleteRecursive(ctx context.Context, id uuid.UUID) ([]Reso
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const libraryByName = `-- name: LibraryByName :one
|
||||
SELECT id, parent, name, dir, created, modified, deleted, size, etag from resources WHERE deleted IS NULL AND parent IS NULL AND name = $1
|
||||
`
|
||||
|
||||
func (q *Queries) LibraryByName(ctx context.Context, name string) (Resource, error) {
|
||||
row := q.db.QueryRow(ctx, libraryByName, name)
|
||||
var i Resource
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Parent,
|
||||
&i.Name,
|
||||
&i.Dir,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.Deleted,
|
||||
&i.Size,
|
||||
&i.Etag,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const readDir = `-- name: ReadDir :many
|
||||
WITH RECURSIVE nodes(id, parent, name, dir, created, modified, size, etag, depth, path) AS (
|
||||
SELECT r.id, r.parent, r.name, r.dir, r.created, r.modified, r.size, r.etag, 0, ''::text
|
||||
@@ -252,6 +231,27 @@ func (q *Queries) Rename(ctx context.Context, arg RenameParams) error {
|
||||
return err
|
||||
}
|
||||
|
||||
const resourceById = `-- name: ResourceById :one
|
||||
SELECT id, parent, name, dir, created, modified, deleted, size, etag from resources WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) ResourceById(ctx context.Context, id uuid.UUID) (Resource, error) {
|
||||
row := q.db.QueryRow(ctx, resourceById, id)
|
||||
var i Resource
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Parent,
|
||||
&i.Name,
|
||||
&i.Dir,
|
||||
&i.Created,
|
||||
&i.Modified,
|
||||
&i.Deleted,
|
||||
&i.Size,
|
||||
&i.Etag,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const resourceByPath = `-- name: ResourceByPath :one
|
||||
WITH RECURSIVE nodes(id, parent, name, dir, created, modified, size, etag, depth, path, search) AS (
|
||||
SELECT r.id, r.parent, r.name, r.dir, r.created, r.modified, r.size, r.etag, 0, ''::text, $1::text[]
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"hash"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
"github.com/shroff/phylum/server/internal/sql"
|
||||
"github.com/shroff/phylum/server/internal/storage"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Vfs struct {
|
||||
queries sql.Queries
|
||||
root uuid.UUID
|
||||
cs storage.Storage
|
||||
}
|
||||
|
||||
func (fs Vfs) Open(id uuid.UUID, flag int) (io.ReadWriteCloser, error) {
|
||||
return fs.cs.Open(id, flag, nil)
|
||||
}
|
||||
|
||||
func (fs Vfs) ReadDir(ctx context.Context, id uuid.UUID, includeRoot bool, recursive bool) ([]sql.ReadDirRow, error) {
|
||||
return fs.queries.ReadDir(ctx, sql.ReadDirParams{ID: id, IncludeRoot: includeRoot, Recursive: recursive})
|
||||
}
|
||||
|
||||
func (fs Vfs) DeleteRecursive(ctx context.Context, id uuid.UUID, hardDelete bool) error {
|
||||
query := fs.queries.DeleteRecursive
|
||||
if hardDelete {
|
||||
query = fs.queries.HardDeleteRecursive
|
||||
}
|
||||
deleted, err := query(ctx, id)
|
||||
if err == nil && hardDelete {
|
||||
for _, res := range deleted {
|
||||
if !res.Dir {
|
||||
fs.cs.Delete(res.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (fs Vfs) CreateResource(ctx context.Context, id uuid.UUID, parent uuid.UUID, name string, dir bool) error {
|
||||
return fs.queries.CreateResource(ctx, sql.CreateResourceParams{ID: id, Parent: &parent, Name: name, Dir: dir})
|
||||
}
|
||||
|
||||
func (fs Vfs) Move(ctx context.Context, id uuid.UUID, parent uuid.UUID, name string) error {
|
||||
return fs.queries.Rename(ctx, sql.RenameParams{ID: id, Parent: parent, Name: name})
|
||||
}
|
||||
|
||||
func (fs Vfs) UpdateContents(ctx context.Context, id uuid.UUID) (io.WriteCloser, error) {
|
||||
return fs.cs.Open(id, os.O_CREATE|os.O_RDWR|os.O_TRUNC, func(h hash.Hash, len int, err error) {
|
||||
if err == nil {
|
||||
etag := base64.StdEncoding.EncodeToString(h.Sum(nil))
|
||||
err = fs.queries.UpdateResourceContents(ctx, sql.UpdateResourceContentsParams{
|
||||
ID: id,
|
||||
Size: pgtype.Int4{Int32: int32(len), Valid: true},
|
||||
Etag: pgtype.Text{String: string(etag), Valid: true},
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Warn("Unable to update contents: " + err.Error())
|
||||
fs.cs.Delete(id)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (fs Vfs) ResourceByPath(ctx context.Context, path string) (sql.ResourceByPathRow, error) {
|
||||
path = strings.Trim(path, "/")
|
||||
segments := strings.Split(path, "/")
|
||||
if path == "" {
|
||||
// Calling strings.Split on an empty string returns a slice of length 1. That breaks the query
|
||||
segments = []string{}
|
||||
}
|
||||
|
||||
res, err := fs.queries.ResourceByPath(ctx, sql.ResourceByPathParams{Search: segments, Root: fs.root})
|
||||
if err != nil {
|
||||
return sql.ResourceByPathRow{}, iofs.ErrNotExist
|
||||
}
|
||||
|
||||
//TODO: Permissions checks
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -4,37 +4,37 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
webdav "github.com/emersion/go-webdav"
|
||||
"github.com/google/uuid"
|
||||
"github.com/shroff/phylum/server/internal/library"
|
||||
"github.com/shroff/phylum/server/internal/sql"
|
||||
"github.com/shroff/phylum/server/internal/vfs"
|
||||
)
|
||||
|
||||
type adapter struct {
|
||||
fs *vfs.Vfs
|
||||
lib *library.Library
|
||||
prefix string
|
||||
}
|
||||
|
||||
func (p adapter) Open(ctx context.Context, name string) (io.ReadCloser, error) {
|
||||
resource, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) Open(ctx context.Context, name string) (io.ReadCloser, error) {
|
||||
resource, err := a.resourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return p.fs.Open(resource.ID, os.O_RDONLY)
|
||||
return a.lib.Open(resource.ID, os.O_RDONLY)
|
||||
}
|
||||
|
||||
func (p adapter) Stat(ctx context.Context, name string) (*webdav.FileInfo, error) {
|
||||
resource, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) Stat(ctx context.Context, name string) (*webdav.FileInfo, error) {
|
||||
resource, err := a.resourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
val := &webdav.FileInfo{
|
||||
Path: string(p.prefix + resource.Path),
|
||||
Path: string(a.prefix + resource.Path),
|
||||
Size: int64(resource.Size.Int32),
|
||||
ModTime: resource.Modified.Time,
|
||||
IsDir: resource.Dir,
|
||||
@@ -43,21 +43,21 @@ func (p adapter) Stat(ctx context.Context, name string) (*webdav.FileInfo, error
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
func (p adapter) ReadDir(ctx context.Context, name string, recursive bool) ([]webdav.FileInfo, error) {
|
||||
dir, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) ReadDir(ctx context.Context, name string, recursive bool) ([]webdav.FileInfo, error) {
|
||||
dir, err := a.resourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !dir.Dir {
|
||||
return nil, iofs.ErrInvalid
|
||||
return nil, fs.ErrInvalid
|
||||
}
|
||||
children, err := p.fs.ReadDir(ctx, dir.ID, false, recursive)
|
||||
children, err := a.lib.ReadDir(ctx, dir.ID, false, recursive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make([]webdav.FileInfo, len(children))
|
||||
prefix := p.prefix + dir.Path
|
||||
prefix := a.prefix + dir.Path
|
||||
for i, c := range children {
|
||||
result[i] = webdav.FileInfo{
|
||||
Path: string(prefix + c.Path),
|
||||
@@ -70,86 +70,86 @@ func (p adapter) ReadDir(ctx context.Context, name string, recursive bool) ([]we
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
func (p adapter) Create(ctx context.Context, name string) (io.WriteCloser, error) {
|
||||
func (a adapter) Create(ctx context.Context, name string) (io.WriteCloser, error) {
|
||||
var id uuid.UUID
|
||||
if resource, err := p.resourceByPath(ctx, name); err == nil {
|
||||
if resource, err := a.resourceByPath(ctx, name); err == nil {
|
||||
id = resource.ID
|
||||
} else {
|
||||
name = strings.TrimRight(name, "/")
|
||||
index := strings.LastIndex(name, "/")
|
||||
parentPath := name[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.resourceByPath(ctx, parentPath)
|
||||
if err != nil {
|
||||
return nil, iofs.ErrNotExist
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
fileName := name[index+1:]
|
||||
id = uuid.New()
|
||||
if err = p.fs.CreateResource(ctx, id, parent.ID, fileName, false); err != nil {
|
||||
if err = a.lib.CreateResource(ctx, id, parent.ID, fileName, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return p.fs.UpdateContents(ctx, id)
|
||||
return a.lib.UpdateContents(ctx, id)
|
||||
}
|
||||
|
||||
func (p adapter) RemoveAll(ctx context.Context, name string) error {
|
||||
resource, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) RemoveAll(ctx context.Context, name string) error {
|
||||
resource, err := a.resourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return iofs.ErrNotExist
|
||||
return fs.ErrNotExist
|
||||
}
|
||||
return p.fs.DeleteRecursive(ctx, resource.ID, false)
|
||||
return a.lib.DeleteRecursive(ctx, resource.ID, false)
|
||||
}
|
||||
|
||||
func (p adapter) Mkdir(ctx context.Context, name string) error {
|
||||
if _, err := p.resourceByPath(ctx, name); err == nil {
|
||||
return iofs.ErrExist
|
||||
func (a adapter) Mkdir(ctx context.Context, name string) error {
|
||||
if _, err := a.resourceByPath(ctx, name); err == nil {
|
||||
return fs.ErrExist
|
||||
}
|
||||
name = strings.TrimRight(name, "/")
|
||||
index := strings.LastIndex(name, "/")
|
||||
parentPath := name[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.resourceByPath(ctx, parentPath)
|
||||
if err != nil {
|
||||
return iofs.ErrNotExist
|
||||
return fs.ErrNotExist
|
||||
}
|
||||
dirName := name[index+1:]
|
||||
err = p.fs.CreateResource(ctx, uuid.New(), parent.ID, dirName, true)
|
||||
err = a.lib.CreateResource(ctx, uuid.New(), parent.ID, dirName, true)
|
||||
return err
|
||||
}
|
||||
func (p adapter) Copy(ctx context.Context, name, dest string, options *webdav.CopyOptions) (created bool, err error) {
|
||||
func (a adapter) Copy(ctx context.Context, name, dest string, options *webdav.CopyOptions) (created bool, err error) {
|
||||
// TODO: Implement
|
||||
return false, webdav.NewHTTPError(http.StatusMethodNotAllowed, fmt.Errorf("not implemented"))
|
||||
}
|
||||
func (p adapter) Move(ctx context.Context, name, destName string, options *webdav.MoveOptions) (created bool, err error) {
|
||||
src, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) Move(ctx context.Context, name, destName string, options *webdav.MoveOptions) (created bool, err error) {
|
||||
src, err := a.resourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return false, iofs.ErrNotExist
|
||||
return false, fs.ErrNotExist
|
||||
}
|
||||
|
||||
existing, err := p.resourceByPath(ctx, destName)
|
||||
existing, err := a.resourceByPath(ctx, destName)
|
||||
if err == nil {
|
||||
if options.NoOverwrite {
|
||||
return false, iofs.ErrExist
|
||||
return false, fs.ErrExist
|
||||
} else {
|
||||
p.fs.DeleteRecursive(ctx, existing.ID, false)
|
||||
a.lib.DeleteRecursive(ctx, existing.ID, false)
|
||||
}
|
||||
}
|
||||
|
||||
destName = strings.TrimRight(destName, "/")
|
||||
index := strings.LastIndex(destName, "/")
|
||||
parentPath := destName[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.resourceByPath(ctx, parentPath)
|
||||
destName = destName[index+1:]
|
||||
|
||||
if err != nil {
|
||||
return false, webdav.NewHTTPError(http.StatusConflict, nil)
|
||||
}
|
||||
|
||||
if err = p.fs.Move(ctx, src.ID, parent.ID, destName); err != nil {
|
||||
if err = a.lib.Move(ctx, src.ID, parent.ID, destName); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (p adapter) resourceByPath(ctx context.Context, name string) (res sql.ResourceByPathRow, err error) {
|
||||
return p.fs.ResourceByPath(ctx, strings.TrimPrefix(name, p.prefix))
|
||||
func (a adapter) resourceByPath(ctx context.Context, name string) (res sql.ResourceByPathRow, err error) {
|
||||
return a.lib.ResourceByPath(ctx, strings.TrimPrefix(name, a.prefix))
|
||||
}
|
||||
|
||||
@@ -6,36 +6,36 @@ import (
|
||||
|
||||
webdav "github.com/emersion/go-webdav"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shroff/phylum/server/internal/vfs"
|
||||
"github.com/shroff/phylum/server/internal/library"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
vfsManager vfs.VfsManager
|
||||
prefix string
|
||||
libraryManager library.Manager
|
||||
prefix string
|
||||
}
|
||||
|
||||
func NewHandler(vfsManager vfs.VfsManager, prefix string) *handler {
|
||||
func NewHandler(libraryManager library.Manager, prefix string) *handler {
|
||||
logrus.Info("Setting up WebDAV access at " + prefix)
|
||||
return &handler{
|
||||
vfsManager: vfsManager,
|
||||
prefix: prefix,
|
||||
libraryManager: libraryManager,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) HandleRequest(c *gin.Context) {
|
||||
path := c.Params.ByName("path")
|
||||
vfsName := strings.TrimLeft(path, "/")
|
||||
if vfsName == "" {
|
||||
libraryName := strings.TrimLeft(path, "/")
|
||||
if libraryName == "" {
|
||||
// No path specified
|
||||
c.Writer.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
index := strings.Index(vfsName, "/")
|
||||
index := strings.Index(libraryName, "/")
|
||||
if index != -1 {
|
||||
vfsName = vfsName[0:index]
|
||||
libraryName = libraryName[0:index]
|
||||
}
|
||||
vfs, err := h.vfsManager.GetVfs(context.Background(), vfsName)
|
||||
library, err := h.libraryManager.Get(context.Background(), libraryName)
|
||||
if err != nil {
|
||||
c.Writer.WriteHeader(404)
|
||||
return
|
||||
@@ -43,8 +43,8 @@ func (h *handler) HandleRequest(c *gin.Context) {
|
||||
|
||||
webdavHandler := webdav.Handler{
|
||||
FileSystem: adapter{
|
||||
fs: vfs,
|
||||
prefix: h.prefix + "/" + vfsName,
|
||||
lib: library,
|
||||
prefix: h.prefix + "/" + libraryName,
|
||||
},
|
||||
}
|
||||
webdavHandler.ServeHTTP(c.Writer, c.Request)
|
||||
|
||||
@@ -3,23 +3,22 @@ package webdav
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
iofs "io/fs"
|
||||
"io/fs"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/shroff/phylum/server/internal/sql"
|
||||
"github.com/shroff/phylum/server/internal/vfs"
|
||||
"github.com/shroff/phylum/server/internal/library"
|
||||
"golang.org/x/net/webdav"
|
||||
)
|
||||
|
||||
type adapter struct {
|
||||
fs *vfs.Vfs
|
||||
lib *library.Library
|
||||
}
|
||||
|
||||
func (p adapter) Stat(ctx context.Context, name string) (os.FileInfo, error) {
|
||||
resource, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) Stat(ctx context.Context, name string) (os.FileInfo, error) {
|
||||
resource, err := a.lib.ResourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -33,8 +32,8 @@ func (p adapter) Stat(ctx context.Context, name string) (os.FileInfo, error) {
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (p adapter) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
||||
resource, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
||||
resource, err := a.lib.ResourceByPath(ctx, name)
|
||||
resourceId := resource.ID
|
||||
resourceName := resource.Name
|
||||
size := resource.Size.Int32
|
||||
@@ -47,22 +46,22 @@ func (p adapter) OpenFile(ctx context.Context, name string, flag int, perm os.Fi
|
||||
name = strings.TrimRight(name, "/")
|
||||
index := strings.LastIndex(name, "/")
|
||||
parentPath := name[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.lib.ResourceByPath(ctx, parentPath)
|
||||
if err != nil {
|
||||
return nil, iofs.ErrNotExist
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
resourceName := name[index+1:]
|
||||
resourceId = uuid.New()
|
||||
dir = false
|
||||
modTime = time.Time{}
|
||||
err = p.fs.CreateResource(ctx, resourceId, parent.ID, resourceName, dir)
|
||||
err = a.lib.CreateResource(ctx, resourceId, parent.ID, resourceName, dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
var src io.ReadWriteCloser
|
||||
if !dir {
|
||||
src, err = p.fs.Open(resourceId, flag)
|
||||
src, err = a.lib.Open(resourceId, flag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -76,83 +75,79 @@ func (p adapter) OpenFile(ctx context.Context, name string, flag int, perm os.Fi
|
||||
resourceID: resourceId,
|
||||
},
|
||||
src: src,
|
||||
fs: p.fs,
|
||||
lib: a.lib,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p adapter) Create(ctx context.Context, name string) (io.WriteCloser, error) {
|
||||
func (a adapter) Create(ctx context.Context, name string) (io.WriteCloser, error) {
|
||||
var id uuid.UUID
|
||||
if resource, err := p.resourceByPath(ctx, name); err == nil {
|
||||
if resource, err := a.lib.ResourceByPath(ctx, name); err == nil {
|
||||
id = resource.ID
|
||||
} else {
|
||||
name = strings.TrimRight(name, "/")
|
||||
index := strings.LastIndex(name, "/")
|
||||
parentPath := name[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.lib.ResourceByPath(ctx, parentPath)
|
||||
if err != nil {
|
||||
return nil, iofs.ErrNotExist
|
||||
return nil, fs.ErrNotExist
|
||||
}
|
||||
fileName := name[index+1:]
|
||||
id = uuid.New()
|
||||
if err = p.fs.CreateResource(ctx, id, parent.ID, fileName, false); err != nil {
|
||||
if err = a.lib.CreateResource(ctx, id, parent.ID, fileName, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return p.fs.UpdateContents(ctx, id)
|
||||
return a.lib.UpdateContents(ctx, id)
|
||||
}
|
||||
|
||||
func (p adapter) RemoveAll(ctx context.Context, name string) error {
|
||||
resource, err := p.resourceByPath(ctx, name)
|
||||
func (a adapter) RemoveAll(ctx context.Context, name string) error {
|
||||
resource, err := a.lib.ResourceByPath(ctx, name)
|
||||
if err != nil {
|
||||
return iofs.ErrNotExist
|
||||
return fs.ErrNotExist
|
||||
}
|
||||
return p.fs.DeleteRecursive(ctx, resource.ID, false)
|
||||
return a.lib.DeleteRecursive(ctx, resource.ID, false)
|
||||
}
|
||||
|
||||
func (p adapter) Mkdir(ctx context.Context, name string, mode iofs.FileMode) error {
|
||||
if _, err := p.resourceByPath(ctx, name); err == nil {
|
||||
return iofs.ErrExist
|
||||
func (a adapter) Mkdir(ctx context.Context, name string, mode fs.FileMode) error {
|
||||
if _, err := a.lib.ResourceByPath(ctx, name); err == nil {
|
||||
return fs.ErrExist
|
||||
}
|
||||
name = strings.TrimRight(name, "/")
|
||||
index := strings.LastIndex(name, "/")
|
||||
parentPath := name[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.lib.ResourceByPath(ctx, parentPath)
|
||||
if err != nil {
|
||||
return iofs.ErrNotExist
|
||||
return fs.ErrNotExist
|
||||
}
|
||||
dirName := name[index+1:]
|
||||
err = p.fs.CreateResource(ctx, uuid.New(), parent.ID, dirName, true)
|
||||
err = a.lib.CreateResource(ctx, uuid.New(), parent.ID, dirName, true)
|
||||
return err
|
||||
}
|
||||
|
||||
func (p adapter) Rename(ctx context.Context, oldName, newName string) error {
|
||||
src, err := p.resourceByPath(ctx, oldName)
|
||||
func (a adapter) Rename(ctx context.Context, oldName, newName string) error {
|
||||
src, err := a.lib.ResourceByPath(ctx, oldName)
|
||||
if err != nil {
|
||||
return iofs.ErrNotExist
|
||||
return fs.ErrNotExist
|
||||
}
|
||||
|
||||
_, err = p.resourceByPath(ctx, newName)
|
||||
_, err = a.lib.ResourceByPath(ctx, newName)
|
||||
if err == nil {
|
||||
return iofs.ErrExist
|
||||
return fs.ErrExist
|
||||
}
|
||||
|
||||
newName = strings.TrimRight(newName, "/")
|
||||
index := strings.LastIndex(newName, "/")
|
||||
parentPath := newName[0:index]
|
||||
parent, err := p.resourceByPath(ctx, parentPath)
|
||||
parent, err := a.lib.ResourceByPath(ctx, parentPath)
|
||||
newName = newName[index+1:]
|
||||
|
||||
if err != nil {
|
||||
return iofs.ErrNotExist
|
||||
return fs.ErrNotExist
|
||||
}
|
||||
|
||||
if err = p.fs.Move(ctx, src.ID, parent.ID, newName); err != nil {
|
||||
if err = a.lib.Move(ctx, src.ID, parent.ID, newName); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p adapter) resourceByPath(ctx context.Context, name string) (res sql.ResourceByPathRow, err error) {
|
||||
return p.fs.ResourceByPath(ctx, name)
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import (
|
||||
"io"
|
||||
"io/fs"
|
||||
|
||||
"github.com/shroff/phylum/server/internal/vfs"
|
||||
"github.com/shroff/phylum/server/internal/library"
|
||||
)
|
||||
|
||||
type file struct {
|
||||
fileInfo
|
||||
src io.ReadWriteCloser
|
||||
fs *vfs.Vfs
|
||||
lib *library.Library
|
||||
}
|
||||
|
||||
func (f file) Seek(offset int64, whence int) (int64, error) {
|
||||
@@ -36,7 +36,7 @@ func (f file) Readdir(count int) ([]fs.FileInfo, error) {
|
||||
return nil, fmt.Errorf("readdir not supported for non-collection resources")
|
||||
}
|
||||
|
||||
children, err := f.fs.ReadDir(context.Background(), f.resourceID, false, false)
|
||||
children, err := f.lib.ReadDir(context.Background(), f.resourceID, false, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -5,45 +5,45 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/shroff/phylum/server/internal/vfs"
|
||||
"github.com/shroff/phylum/server/internal/library"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/webdav"
|
||||
)
|
||||
|
||||
type handler struct {
|
||||
vfsManager vfs.VfsManager
|
||||
prefix string
|
||||
libraryManager library.Manager
|
||||
prefix string
|
||||
}
|
||||
|
||||
func NewHandler(vfsManager vfs.VfsManager, prefix string) *handler {
|
||||
func NewHandler(libraryManager library.Manager, prefix string) *handler {
|
||||
logrus.Info("Setting up WebDAV access at " + prefix)
|
||||
return &handler{
|
||||
vfsManager: vfsManager,
|
||||
prefix: prefix,
|
||||
libraryManager: libraryManager,
|
||||
prefix: prefix,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) HandleRequest(c *gin.Context) {
|
||||
path := c.Params.ByName("path")
|
||||
vfsName := strings.TrimLeft(path, "/")
|
||||
if vfsName == "" {
|
||||
libraryName := strings.TrimLeft(path, "/")
|
||||
if libraryName == "" {
|
||||
// No path specified
|
||||
c.Writer.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
index := strings.Index(vfsName, "/")
|
||||
index := strings.Index(libraryName, "/")
|
||||
if index != -1 {
|
||||
vfsName = vfsName[0:index]
|
||||
libraryName = libraryName[0:index]
|
||||
}
|
||||
vfs, err := h.vfsManager.GetVfs(context.Background(), vfsName)
|
||||
library, err := h.libraryManager.Get(context.Background(), libraryName)
|
||||
if err != nil {
|
||||
c.Writer.WriteHeader(404)
|
||||
return
|
||||
}
|
||||
|
||||
webdavHandler := webdav.Handler{
|
||||
Prefix: h.prefix + "/" + vfsName,
|
||||
FileSystem: adapter{fs: vfs},
|
||||
Prefix: h.prefix + "/" + libraryName,
|
||||
FileSystem: adapter{lib: library},
|
||||
LockSystem: webdav.NewMemLS(),
|
||||
}
|
||||
webdavHandler.ServeHTTP(c.Writer, c.Request)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
-- name: FindResource :one
|
||||
-- name: ResourceById :one
|
||||
SELECT * from resources WHERE id = $1;
|
||||
|
||||
-- name: FindRoot :one
|
||||
-- name: LibraryByName :one
|
||||
SELECT * from resources WHERE deleted IS NULL AND parent IS NULL AND name = $1;
|
||||
|
||||
-- name: CreateResource :exec
|
||||
|
||||
Reference in New Issue
Block a user