diff --git a/server/internal/api/api.go b/server/internal/api/api.go index 309eee66..413dd206 100644 --- a/server/internal/api/api.go +++ b/server/internal/api/api.go @@ -16,7 +16,6 @@ func Setup(r *gin.RouterGroup, a *app.App) { // Authenticated routes r.Use(auth.CreateBearerAuthHandler(a)) - routes.SetupSiloRoutes(r, a) routes.SetupResourceRoutes(r, a) } diff --git a/server/internal/api/routes/silos.go b/server/internal/api/routes/silos.go deleted file mode 100644 index 4fcbd4f0..00000000 --- a/server/internal/api/routes/silos.go +++ /dev/null @@ -1,41 +0,0 @@ -package routes - -import ( - "context" - - "github.com/gin-gonic/gin" - "github.com/google/uuid" - "github.com/shroff/phylum/server/internal/api/auth" - "github.com/shroff/phylum/server/internal/app" -) - -type siloResponse struct { - ID uuid.UUID `json:"id"` - Name string `json:"name"` - Owner int32 `json:"owner"` - Storage string `json:"storage"` -} - -func SetupSiloRoutes(r *gin.RouterGroup, a *app.App) { - group := r.Group("/silos") - group.GET("/list", createSiloListRouteHandler(a)) -} - -func createSiloListRouteHandler(a *app.App) func(c *gin.Context) { - return func(c *gin.Context) { - silos, err := a.SilosForUser(context.Background(), auth.GetUserID(c)) - if err != nil { - panic(err) - } - results := make([]siloResponse, len(silos)) - for i, s := range silos { - results[i] = siloResponse{ - ID: s.ID(), - Name: s.Name(), - Owner: s.Owner(), - Storage: s.StorageName(), - } - } - c.JSON(200, results) - } -} diff --git a/server/internal/app/app.go b/server/internal/app/app.go index ac4360bc..63384194 100644 --- a/server/internal/app/app.go +++ b/server/internal/app/app.go @@ -6,41 +6,47 @@ import ( "github.com/google/uuid" "github.com/shroff/phylum/server/internal/app/core" "github.com/shroff/phylum/server/internal/db" - "github.com/shroff/phylum/server/internal/sql" + "github.com/shroff/phylum/server/internal/storage" ) type App struct { - Debug bool - Db *db.DbHandler - backends map[string]core.Storage + Debug bool + Db *db.DbHandler + Cs storage.Storage + adminUserID int32 } var Default *App -func Initialize(db *db.DbHandler, debug bool) error { - backends, err := restoreStorageBackends(db) - if err != nil { +func Initialize(db *db.DbHandler, contentDir string, debug bool) error { + var adminUserID int32 + if u, err := db.Queries().UserByUsername(context.Background(), "root"); err != nil { + return err + } else { + adminUserID = u.ID + } + + if _, err := db.Queries().ResourceById(context.Background(), uuid.UUID{}); err != nil { return err } - if _, err := db.Queries().CreateResource(context.Background(), sql.CreateResourceParams{ - ID: uuid.UUID{}, - Parent: uuid.UUID{}, - Name: "root", - Dir: true, - }); err != nil { + if cs, err := storage.Open(db, contentDir); err != nil { return err - } - - Default = &App{ - Debug: debug, - Db: db, - backends: backends, + } else { + Default = &App{ + Debug: debug, + Db: db, + Cs: cs, + adminUserID: adminUserID, + } } return nil } func (a App) OpenFileSystem(ctx context.Context, user int32) (core.FileSystem, error) { - return core.OpenFileSystem(a.Db, ctx, nil, user) + if user <= 0 { + user = a.adminUserID + } + return core.OpenFileSystem(a.Db, ctx, a.Cs, nil, user) } diff --git a/server/internal/app/core/filesystem.go b/server/internal/app/core/filesystem.go index 293919c5..4d706512 100644 --- a/server/internal/app/core/filesystem.go +++ b/server/internal/app/core/filesystem.go @@ -12,6 +12,7 @@ import ( "github.com/jackc/pgx/v5/pgtype" "github.com/shroff/phylum/server/internal/db" "github.com/shroff/phylum/server/internal/sql" + "github.com/shroff/phylum/server/internal/storage" "github.com/sirupsen/logrus" ) @@ -32,11 +33,12 @@ type FileSystem interface { type filesystem struct { db *db.DbHandler ctx context.Context + cs storage.Storage root Resource user int32 } -func OpenFileSystem(db *db.DbHandler, ctx context.Context, root Resource, user int32) (FileSystem, error) { +func OpenFileSystem(db *db.DbHandler, ctx context.Context, cs storage.Storage, root Resource, user int32) (FileSystem, error) { if root == nil { root = resource{ id: uuid.UUID{}, @@ -52,6 +54,7 @@ func OpenFileSystem(db *db.DbHandler, ctx context.Context, root Resource, user i return filesystem{ db: db, ctx: ctx, + cs: cs, root: root, user: user, }, nil @@ -61,6 +64,7 @@ func (f filesystem) OpenWithRoot(root Resource) FileSystem { return filesystem{ db: f.db, ctx: f.ctx, + cs: f.cs, root: root, user: f.user, } @@ -119,16 +123,16 @@ func (f filesystem) OpenRead(r Resource, start, length int64) (io.ReadCloser, er if r.Permission() < PermissionReadOnly { return nil, ErrInsufficientPermissions } - return r.storage.OpenRead(r.ID(), start, length) + return f.cs.OpenRead(r.ID(), start, length) } func (f filesystem) OpenWrite(r Resource) (io.WriteCloser, error) { if r.Permission() < PermissionReadWrite { return nil, ErrInsufficientPermissions } - return r.storage.OpenWrite(r.id, func(len int, etag string) error { - return r.db.Queries().UpdateResourceContents(f.ctx, sql.UpdateResourceContentsParams{ - ID: r.id, + return f.cs.OpenWrite(r.ID(), func(len int, etag string) error { + return f.db.Queries().UpdateResourceContents(f.ctx, sql.UpdateResourceContentsParams{ + ID: r.ID(), Size: pgtype.Int8{Int64: int64(len), Valid: true}, Etag: pgtype.Text{String: etag, Valid: true}, }) @@ -206,7 +210,7 @@ func (f filesystem) DeleteRecursive(r Resource, hardDelete bool) error { if err != nil { return err } - errors := r.storage.Delete(deleted) + errors := f.cs.Delete(deleted) for err := range errors { logrus.Warn(err) } diff --git a/server/internal/app/core/silo.go b/server/internal/app/core/silo.go deleted file mode 100644 index 9c9c0e02..00000000 --- a/server/internal/app/core/silo.go +++ /dev/null @@ -1,47 +0,0 @@ -package core - -import ( - "github.com/google/uuid" - "github.com/shroff/phylum/server/internal/db" -) - -type Silo interface { - ID() uuid.UUID - Name() string - Owner() int32 - StorageName() string -} - -type silo struct { - db *db.DbHandler - name string - owner int32 - root uuid.UUID - storage Storage -} - -func NewSilo(db *db.DbHandler, name string, owner int32, root uuid.UUID, storage Storage) Silo { - return &silo{ - db: db, - name: name, - owner: owner, - root: root, - storage: storage, - } -} - -func (s *silo) ID() uuid.UUID { - return s.root -} - -func (s *silo) Name() string { - return s.name -} - -func (s *silo) Owner() int32 { - return s.owner -} - -func (s *silo) StorageName() string { - return s.storage.Name() -} diff --git a/server/internal/app/silos.go b/server/internal/app/silos.go deleted file mode 100644 index 655219b6..00000000 --- a/server/internal/app/silos.go +++ /dev/null @@ -1,111 +0,0 @@ -package app - -import ( - "context" - "errors" - - "github.com/google/uuid" - "github.com/shroff/phylum/server/internal/app/core" - "github.com/shroff/phylum/server/internal/sql" -) - -func (a App) CreateSilo(ctx context.Context, id uuid.UUID, owner int32, storage, name string) error { - return a.Db.RunInTx(ctx, func(q *sql.Queries) error { - if err := q.CreateSilo(ctx, sql.CreateSiloParams{ - ID: id, - Owner: owner, - Storage: storage, - Name: name, - }); err != nil { - return err - } - if _, err := q.CreateResource(ctx, sql.CreateResourceParams{ - ID: id, - Parent: uuid.UUID{}, - Name: id.String(), - Dir: true, - }); err != nil { - return err - } - - err := q.UpdatePermissionsForResource(ctx, sql.UpdatePermissionsForResourceParams{ - ResourceID: id, - UserID: owner, - Permission: core.PermissionOwner, - }) - - return err - }) -} - -func (a App) AllSilos(ctx context.Context) ([]core.Silo, error) { - silos, err := a.Db.Queries().ListAllSilos(ctx) - if err != nil { - return nil, err - } - results := make([]core.Silo, len(silos)) - for i, s := range silos { - results[i] = core.NewSilo(a.Db, s.Name, s.Owner, s.ID, a.FindStorageBackend(s.Storage)) - } - return results, nil -} - -func (a App) SilosForUser(ctx context.Context, userID int32) ([]core.Silo, error) { - silos, err := a.Db.Queries().ListSilosForUser(ctx, userID) - if err != nil { - return nil, err - } - results := make([]core.Silo, len(silos)) - for i, s := range silos { - results[i] = core.NewSilo(a.Db, s.Name, s.Owner, s.ID, a.FindStorageBackend(s.Storage)) - } - return results, nil -} - -func (a App) FindSilo(ctx context.Context, idOrName string) (core.Silo, error) { - var silo sql.Silo - - if id, err := uuid.Parse(idOrName); err == nil { - silo, err = a.Db.Queries().SiloById(ctx, id) - if err != nil { - return nil, err - } - } else { - silo, err = a.Db.Queries().SiloByName(ctx, idOrName) - if err != nil { - return nil, err - } - } - - storage := a.FindStorageBackend(silo.Storage) - if storage == nil { - return nil, errors.New("storage backend not found for " + silo.Name + "(" + silo.ID.String() + ")") - } - - return core.NewSilo(a.Db, silo.Name, silo.Owner, silo.ID, storage), nil -} - -func (a App) DeleteSilo(ctx context.Context, id uuid.UUID) error { - return nil - // return a.Db.RunInTx(ctx, func(q *sql.Queries) error { - // result, err := q.SiloById(ctx, id) - // if err != nil { - // return err - // } - // storage := a.FindStorageBackend(result.Storage) - // if storage == nil { - // return errors.New("storage backend not found for " + id.String()) - // } - - // silo := core.NewSilo(a.Db, result.Name, result.Owner, result.ID, storage) - // resource, err := silo.ResourceByPath(ctx, "/", -1) - // if err != nil { - // return err - // } - // if err := resource.DeleteRecursive(ctx, true); err != nil { - // return err - // } - - // return q.DeleteSilo(ctx, id) - // }) -} diff --git a/server/internal/app/storage_backends.go b/server/internal/app/storage_backends.go deleted file mode 100644 index 25c25539..00000000 --- a/server/internal/app/storage_backends.go +++ /dev/null @@ -1,51 +0,0 @@ -package app - -import ( - "context" - - "github.com/shroff/phylum/server/internal/app/core" - "github.com/shroff/phylum/server/internal/db" - "github.com/shroff/phylum/server/internal/sql" - "github.com/shroff/phylum/server/internal/storage" -) - -func restoreStorageBackends(db *db.DbHandler) (map[string]core.Storage, error) { - backends, err := db.Queries().AllStorageBackends(context.Background()) - if err != nil { - return nil, err - } - results := map[string]core.Storage{} - for _, b := range backends { - store, err := storage.Open(b.Name, b.Driver, b.Params) - if err != nil { - return nil, err - } - results[b.Name] = store - } - return results, nil -} - -func (a App) CreateStorageBackend(name string, driver string, params map[string]string) error { - storage, err := storage.Open(name, driver, params) - if err != nil { - return nil - } - err = a.Db.Queries().CreateStorageBackend(context.Background(), sql.CreateStorageBackendParams{ - Name: name, - Driver: driver, - Params: params, - }) - if err != nil { - return err - } - a.backends[name] = storage - return nil -} - -func (a App) FindStorageBackend(name string) core.Storage { - return a.backends[name] -} - -func (a App) ListStorageBackends() map[string]core.Storage { - return a.backends -} diff --git a/server/internal/command/appcmd/admincmd/admin.go b/server/internal/command/appcmd/admincmd/admin.go index 078580ab..c33da1af 100644 --- a/server/internal/command/appcmd/admincmd/admin.go +++ b/server/internal/command/appcmd/admincmd/admin.go @@ -12,7 +12,7 @@ func SetupCommand() *cobra.Command { cmd.AddCommand([]*cobra.Command{ setupStorageCommand(), setupUserCommand(), - setupSiloCommand(), + setupResourceCommand(), }...) return cmd diff --git a/server/internal/command/appcmd/admincmd/resource.go b/server/internal/command/appcmd/admincmd/resource.go new file mode 100644 index 00000000..6bd7b16a --- /dev/null +++ b/server/internal/command/appcmd/admincmd/resource.go @@ -0,0 +1,79 @@ +package admincmd + +import ( + "context" + "strings" + + "github.com/google/uuid" + "github.com/shroff/phylum/server/internal/app" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +func setupResourceCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "resource", + Short: "Resource Management", + } + cmd.AddCommand([]*cobra.Command{ + setupResourceMkdirCommand(), + setupSiloListCommand(), + setupSiloDeleteCommand(), + }...) + return cmd +} + +func setupResourceMkdirCommand() *cobra.Command { + return &cobra.Command{ + Use: "mkdir path", + Short: "Create Directory", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + path := args[0] + fs, err := app.Default.OpenFileSystem(context.Background(), 0) + if err != nil { + logrus.Fatal(err) + } + if _, err := fs.ResourceByPath(path); err == nil { + logrus.Fatal("Resource already exists: " + path) + } + // Streamline with WebDAV mkcol request handling + path = strings.TrimRight(path, "/") + index := strings.LastIndex(path, "/") + parentPath := path[0:index] + parent, err := fs.ResourceByPath(parentPath) + + if err != nil { + logrus.Fatal("Parent resource does not exist: " + parentPath) + } + + id := uuid.New() + name := path[index+1:] + if _, err = fs.CreateMemberResource(parent, id, name, true); err != nil { + logrus.Fatal(err) + } else { + logrus.Info("Created directory " + path + " (" + id.String() + ")") + } + }, + } +} + +func setupSiloListCommand() *cobra.Command { + return &cobra.Command{ + Use: "list", + Short: "List Silos", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + }, + } +} + +func setupSiloDeleteCommand() *cobra.Command { + return &cobra.Command{ + Use: "delete id", + Short: "Delete Silo", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + }, + } +} diff --git a/server/internal/command/appcmd/admincmd/silo.go b/server/internal/command/appcmd/admincmd/silo.go deleted file mode 100644 index c75b9fb7..00000000 --- a/server/internal/command/appcmd/admincmd/silo.go +++ /dev/null @@ -1,90 +0,0 @@ -package admincmd - -import ( - "context" - - "github.com/google/uuid" - "github.com/shroff/phylum/server/internal/app" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -func setupSiloCommand() *cobra.Command { - cmd := &cobra.Command{ - Use: "silo", - Short: "Silo Management", - } - cmd.AddCommand([]*cobra.Command{ - setupSiloCreateCommand(), - setupSiloListCommand(), - setupSiloDeleteCommand(), - }...) - return cmd -} - -func setupSiloCreateCommand() *cobra.Command { - return &cobra.Command{ - Use: "create owner storage name", - Short: "Create Silo", - Args: cobra.ExactArgs(3), - Run: func(cmd *cobra.Command, args []string) { - id := uuid.New() - - username := args[0] - user, err := app.Default.FindUser(context.Background(), username) - if err != nil { - logrus.Fatal("User not found: " + username) - } - - storageName := args[1] - storage := app.Default.FindStorageBackend(storageName) - if storage == nil { - logrus.Fatal("Storage not found: " + storageName) - } - - name := args[2] - if err := app.Default.CreateSilo(context.Background(), id, user.ID(), storageName, name); err != nil { - logrus.Fatal(err) - } - logrus.Info("Created " + id.String()) - }, - } -} - -func setupSiloListCommand() *cobra.Command { - return &cobra.Command{ - Use: "list", - Short: "List Silos", - Args: cobra.ExactArgs(0), - Run: func(cmd *cobra.Command, args []string) { - ctx := context.Background() - silos, err := app.Default.AllSilos(ctx) - if err != nil { - logrus.Fatal(err) - } - for _, silo := range silos { - logrus.Infof("%-16s: %s\n", silo.Name(), silo.ID().String()) - // logrus.Infof(" storage: %s\n", silo.StorageName()) - logrus.Infof(" owner: %d\n", silo.Owner()) - logrus.Info() - } - }, - } -} - -func setupSiloDeleteCommand() *cobra.Command { - return &cobra.Command{ - Use: "delete id", - Short: "Delete Silo", - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - id, err := uuid.Parse(args[0]) - if err != nil { - logrus.Fatal("Not an ID: " + args[0]) - } - if err := app.Default.DeleteSilo(context.Background(), id); err != nil { - logrus.Fatal(err) - } - }, - } -} diff --git a/server/internal/command/appcmd/admincmd/storage.go b/server/internal/command/appcmd/admincmd/storage.go index cfbe72aa..07a88f24 100644 --- a/server/internal/command/appcmd/admincmd/storage.go +++ b/server/internal/command/appcmd/admincmd/storage.go @@ -31,10 +31,6 @@ func setupStorageCreateCommand() *cobra.Command { Args: cobra.ExactArgs(2), Run: func(cmd *cobra.Command, args []string) { name := args[0] - if app.Default.FindStorageBackend(name) != nil { - logrus.Fatal("Storage backand already exists: " + name) - } - driver := args[1] paramNames, err := storage.ParamNames(driver) if err != nil { @@ -53,7 +49,7 @@ func setupStorageCreateCommand() *cobra.Command { params[paramName] = val } - if err := app.Default.CreateStorageBackend(name, driver, params); err != nil { + if err := app.Default.Cs.CreateBackend(name, driver, params); err != nil { logrus.Fatal(err) } @@ -68,7 +64,7 @@ func setupStorageListCommand() *cobra.Command { Short: "List all storage backends", Run: func(cmd *cobra.Command, args []string) { logrus.Info("Available storage backends:") - for k, v := range app.Default.ListStorageBackends() { + for k, v := range app.Default.Cs.ListBackends() { logrus.Info(fmt.Sprintf("%s: %s", k, v)) } }, diff --git a/server/internal/command/appcmd/appcmd.go b/server/internal/command/appcmd/appcmd.go index 83c81bd0..78336470 100644 --- a/server/internal/command/appcmd/appcmd.go +++ b/server/internal/command/appcmd/appcmd.go @@ -14,10 +14,14 @@ func SetupCommand(db **db.DbHandler, debug bool) *cobra.Command { Use: "app", Short: "Server Administration", PersistentPreRun: func(cmd *cobra.Command, args []string) { + c := cmd.Parent() + for ; c.Parent() != nil; c = c.Parent() { + } + c.PersistentPreRun(cmd, args) if err := (*db).CheckVersion(!viper.GetBool("no_auto_migrate")); err != nil { logrus.Fatal(err) } - if err := app.Initialize(*db, debug); err != nil { + if err := app.Initialize(*db, viper.GetString("content-dir"), debug); err != nil { logrus.Fatal(err) } }, @@ -26,6 +30,9 @@ func SetupCommand(db **db.DbHandler, debug bool) *cobra.Command { flags.Bool("no-auto-migrate", false, "Do not automatically migrate database schema") viper.BindPFlag("no_auto_migrate", flags.Lookup("auto-migrate")) + flags.StringP("content-dir", "C", "content", "Content Directory") + viper.BindPFlag("content_dir", flags.Lookup("content-dir")) + cmd.AddCommand([]*cobra.Command{ admincmd.SetupCommand(), setupServeCommand(), diff --git a/server/internal/migrations/data/001_resources.sql b/server/internal/migrations/data/001_resources.sql index 8ebe147a..87e70cae 100644 --- a/server/internal/migrations/data/001_resources.sql +++ b/server/internal/migrations/data/001_resources.sql @@ -11,6 +11,19 @@ CREATE TABLE resources ( -- CONSTRAINT resource_parent FOREIGN KEY REFERENCES resources(id) ON UPDATE CASCADE ON DELETE CASCADE; ); +INSERT INTO resources +VALUES( + '00000000-0000-0000-0000-000000000000', + '00000000-0000-0000-0000-000000000000', + 'root', + true, + NOW(), + NOW(), + NULL, + 0, + '' +); + CREATE UNIQUE INDEX unique_member_resource_name ON resources(parent, name) WHERE deleted IS NULL; ---- create above / drop below ---- diff --git a/server/internal/migrations/data/002_users.sql b/server/internal/migrations/data/002_users.sql index ccff06e2..1328157b 100644 --- a/server/internal/migrations/data/002_users.sql +++ b/server/internal/migrations/data/002_users.sql @@ -6,6 +6,12 @@ CREATE TABLE users( deleted TIMESTAMP ); +INSERT INTO users(username, display_name, password_hash) VALUES( + 'root', + 'Admin', + 'CANNOT_LOG_IN' +); + ---- create above / drop below ---- DROP TABLE users; \ No newline at end of file diff --git a/server/internal/migrations/data/005_access_tokens.sql b/server/internal/migrations/data/004_access_tokens.sql similarity index 100% rename from server/internal/migrations/data/005_access_tokens.sql rename to server/internal/migrations/data/004_access_tokens.sql diff --git a/server/internal/migrations/data/004_silos.sql b/server/internal/migrations/data/004_silos.sql deleted file mode 100644 index 2d5bc674..00000000 --- a/server/internal/migrations/data/004_silos.sql +++ /dev/null @@ -1,17 +0,0 @@ -CREATE TABLE silos( - id UUID PRIMARY KEY, - created TIMESTAMP NOT NULL, - modified TIMESTAMP NOT NULL, - deleted TIMESTAMP, - owner INTEGER NOT NULL REFERENCES users(id) ON UPDATE CASCADE ON DELETE CASCADE, - name TEXT NOT NULL, - storage TEXT NOT NULL REFERENCES storage_backends(name) ON UPDATE CASCADE ON DELETE CASCADE -); - -CREATE UNIQUE INDEX silo_by_name ON silos(name); - ----- create above / drop below ---- - -DROP INDEX silo_by_name; - -DROP TABLE silos; diff --git a/server/internal/migrations/data/006_permissions.sql b/server/internal/migrations/data/005_permissions.sql similarity index 69% rename from server/internal/migrations/data/006_permissions.sql rename to server/internal/migrations/data/005_permissions.sql index d72965b1..a7e71df1 100644 --- a/server/internal/migrations/data/006_permissions.sql +++ b/server/internal/migrations/data/005_permissions.sql @@ -5,6 +5,10 @@ CREATE TABLE permissions( PRIMARY KEY(resource_id, user_id) ); +INSERT INTO permissions ( + SELECT '00000000-0000-0000-0000-000000000000', id, 511 FROM users WHERE username = 'root' +) + ---- create above / drop below ---- DROP TABLE permissions; \ No newline at end of file diff --git a/server/internal/sql/models.go b/server/internal/sql/models.go index 13433c01..17a23917 100644 --- a/server/internal/sql/models.go +++ b/server/internal/sql/models.go @@ -34,16 +34,6 @@ type Resource struct { Etag pgtype.Text } -type Silo struct { - ID uuid.UUID - Created pgtype.Timestamp - Modified pgtype.Timestamp - Deleted pgtype.Timestamp - Owner int32 - Name string - Storage string -} - type StorageBackend struct { Name string Driver string diff --git a/server/internal/sql/silos.sql.go b/server/internal/sql/silos.sql.go deleted file mode 100644 index 45ea9344..00000000 --- a/server/internal/sql/silos.sql.go +++ /dev/null @@ -1,148 +0,0 @@ -// Code generated by sqlc. DO NOT EDIT. -// versions: -// sqlc v1.26.0 -// source: silos.sql - -package sql - -import ( - "context" - - "github.com/google/uuid" -) - -const createSilo = `-- name: CreateSilo :exec -INSERT INTO silos( - id, created, modified, owner, name, storage -) VALUES( - $1, NOW(), NOW(), $2, $3, $4 -) -` - -type CreateSiloParams struct { - ID uuid.UUID - Owner int32 - Name string - Storage string -} - -func (q *Queries) CreateSilo(ctx context.Context, arg CreateSiloParams) error { - _, err := q.db.Exec(ctx, createSilo, - arg.ID, - arg.Owner, - arg.Name, - arg.Storage, - ) - return err -} - -const deleteSilo = `-- name: DeleteSilo :exec -DELETE from silos where id = $1 -` - -func (q *Queries) DeleteSilo(ctx context.Context, id uuid.UUID) error { - _, err := q.db.Exec(ctx, deleteSilo, id) - return err -} - -const listAllSilos = `-- name: ListAllSilos :many -SELECT id, created, modified, deleted, owner, name, storage from silos -` - -func (q *Queries) ListAllSilos(ctx context.Context) ([]Silo, error) { - rows, err := q.db.Query(ctx, listAllSilos) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Silo - for rows.Next() { - var i Silo - if err := rows.Scan( - &i.ID, - &i.Created, - &i.Modified, - &i.Deleted, - &i.Owner, - &i.Name, - &i.Storage, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const listSilosForUser = `-- name: ListSilosForUser :many -SELECT id, created, modified, deleted, owner, name, storage from silos where owner = $1::int -` - -func (q *Queries) ListSilosForUser(ctx context.Context, userID int32) ([]Silo, error) { - rows, err := q.db.Query(ctx, listSilosForUser, userID) - if err != nil { - return nil, err - } - defer rows.Close() - var items []Silo - for rows.Next() { - var i Silo - if err := rows.Scan( - &i.ID, - &i.Created, - &i.Modified, - &i.Deleted, - &i.Owner, - &i.Name, - &i.Storage, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const siloById = `-- name: SiloById :one -SELECT id, created, modified, deleted, owner, name, storage from silos where id = $1 -` - -func (q *Queries) SiloById(ctx context.Context, id uuid.UUID) (Silo, error) { - row := q.db.QueryRow(ctx, siloById, id) - var i Silo - err := row.Scan( - &i.ID, - &i.Created, - &i.Modified, - &i.Deleted, - &i.Owner, - &i.Name, - &i.Storage, - ) - return i, err -} - -const siloByName = `-- name: SiloByName :one -SELECT id, created, modified, deleted, owner, name, storage from silos where name = $1 -` - -func (q *Queries) SiloByName(ctx context.Context, name string) (Silo, error) { - row := q.db.QueryRow(ctx, siloByName, name) - var i Silo - err := row.Scan( - &i.ID, - &i.Created, - &i.Modified, - &i.Deleted, - &i.Owner, - &i.Name, - &i.Storage, - ) - return i, err -} diff --git a/server/internal/storage/local_storage.go b/server/internal/storage/local_storage.go index 381481c0..67154966 100644 --- a/server/internal/storage/local_storage.go +++ b/server/internal/storage/local_storage.go @@ -10,7 +10,6 @@ import ( "path/filepath" "github.com/google/uuid" - "github.com/shroff/phylum/server/internal/app/core" "github.com/shroff/phylum/server/internal/sql" ) @@ -19,7 +18,7 @@ type localStorage struct { root string } -func newLocalStorage(name, root string) (core.Storage, error) { +func newLocalStorage(name, root string) (Backend, error) { if root == "" { return nil, errors.New("local storage root not provided") } diff --git a/server/internal/storage/minio_storage.go b/server/internal/storage/minio_storage.go index c9e768a7..45ec6c68 100644 --- a/server/internal/storage/minio_storage.go +++ b/server/internal/storage/minio_storage.go @@ -14,7 +14,6 @@ import ( "github.com/google/uuid" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/shroff/phylum/server/internal/app/core" "github.com/shroff/phylum/server/internal/sql" ) @@ -33,7 +32,7 @@ func newMinioStorage( accessKey, tmp, bucketName, - prefix string) (core.Storage, error) { + prefix string) (Backend, error) { if endpoint == "" { return nil, errors.New("minio endpoint not provided") } diff --git a/server/internal/storage/storage.go b/server/internal/storage/storage.go index f1ca157e..d4d1fc49 100644 --- a/server/internal/storage/storage.go +++ b/server/internal/storage/storage.go @@ -1,12 +1,104 @@ package storage import ( + "context" "errors" + "io" - "github.com/shroff/phylum/server/internal/app/core" + "github.com/google/uuid" + "github.com/shroff/phylum/server/internal/db" + "github.com/shroff/phylum/server/internal/sql" ) -func Open(name string, driver string, params map[string]string) (core.Storage, error) { +type Storage interface { + CreateBackend(name string, driver string, params map[string]string) error + ListBackends() map[string]Backend + OpenRead(id uuid.UUID, start, length int64) (io.ReadCloser, error) + OpenWrite(id uuid.UUID, callback func(int, string) error) (io.WriteCloser, error) + Delete(rows []sql.HardDeleteRecursiveRow) []error +} + +type storage struct { + db *db.DbHandler + backends map[string]Backend + defaultBackend Backend +} + +func Open(db *db.DbHandler, contentDir string) (Storage, error) { + if backends, err := restoreBackends(db); err == nil { + return nil, err + } else { + return storage{ + db: db, + backends: backends, + defaultBackend: localStorage{name: "", root: contentDir}, + }, nil + } +} + +func (s storage) OpenRead(id uuid.UUID, start, length int64) (io.ReadCloser, error) { + if backend, err := s.findStorageBackend(id); err != nil { + return nil, err + } else { + return backend.OpenRead(id, start, length) + } +} +func (s storage) OpenWrite(id uuid.UUID, callback func(int, string) error) (io.WriteCloser, error) { + if backend, err := s.findStorageBackend(id); err != nil { + return nil, err + } else { + return backend.OpenWrite(id, callback) + } +} +func (s storage) Delete(rows []sql.HardDeleteRecursiveRow) []error { + // TODO: implement + return nil + +} + +func (s *storage) findStorageBackend(id uuid.UUID) (Backend, error) { + // TODO: implement + return s.defaultBackend, nil +} + +func (s storage) CreateBackend(name string, driver string, params map[string]string) error { + backend, err := openBackend(name, driver, params) + if err != nil { + return nil + } + err = s.db.Queries().CreateStorageBackend(context.Background(), sql.CreateStorageBackendParams{ + Name: name, + Driver: driver, + Params: params, + }) + if err != nil { + return err + } + s.backends[name] = backend + return nil +} + +func (s storage) ListBackends() map[string]Backend { + return s.backends +} + +func restoreBackends(db *db.DbHandler) (map[string]Backend, error) { + backends, err := db.Queries().AllStorageBackends(context.Background()) + if err != nil { + return nil, err + } + results := map[string]Backend{} + for _, b := range backends { + store, err := openBackend(b.Name, b.Driver, b.Params) + if err != nil { + return nil, err + } + results[b.Name] = store + } + return results, nil +} + +func openBackend(name string, driver string, params map[string]string) (Backend, error) { switch driver { case "local": return newLocalStorage(name, params["root"]) diff --git a/server/internal/app/core/storage.go b/server/internal/storage/storage_backend.go similarity index 88% rename from server/internal/app/core/storage.go rename to server/internal/storage/storage_backend.go index e7cea353..f3b18b59 100644 --- a/server/internal/app/core/storage.go +++ b/server/internal/storage/storage_backend.go @@ -1,4 +1,4 @@ -package core +package storage import ( "io" @@ -7,7 +7,7 @@ import ( "github.com/shroff/phylum/server/internal/sql" ) -type Storage interface { +type Backend interface { Name() string OpenRead(id uuid.UUID, start, length int64) (io.ReadCloser, error) OpenWrite(id uuid.UUID, callback func(int, string) error) (io.WriteCloser, error) diff --git a/server/sql/queries/silos.sql b/server/sql/queries/silos.sql deleted file mode 100644 index 199abe32..00000000 --- a/server/sql/queries/silos.sql +++ /dev/null @@ -1,21 +0,0 @@ --- name: SiloById :one -SELECT * from silos where id = $1; - --- name: SiloByName :one -SELECT * from silos where name = $1; - --- name: DeleteSilo :exec -DELETE from silos where id = $1; - --- name: CreateSilo :exec -INSERT INTO silos( - id, created, modified, owner, name, storage -) VALUES( - $1, NOW(), NOW(), $2, $3, $4 -); - --- name: ListAllSilos :many -SELECT * from silos; - --- name: ListSilosForUser :many -SELECT * from silos where owner = @user_id::int; \ No newline at end of file