diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go index d9b31431ac..a7bd8531d2 100644 --- a/services/graph/pkg/config/config.go +++ b/services/graph/pkg/config/config.go @@ -45,7 +45,7 @@ type Spaces struct { ExtendedSpacePropertiesCacheTTL int `yaml:"extended_space_properties_cache_ttl" env:"GRAPH_SPACES_EXTENDED_SPACE_PROPERTIES_CACHE_TTL" desc:"Max TTL in seconds for the spaces property cache." introductionVersion:"pre5.0"` UsersCacheTTL int `yaml:"users_cache_ttl" env:"GRAPH_SPACES_USERS_CACHE_TTL" desc:"Max TTL in seconds for the spaces users cache." introductionVersion:"pre5.0"` GroupsCacheTTL int `yaml:"groups_cache_ttl" env:"GRAPH_SPACES_GROUPS_CACHE_TTL" desc:"Max TTL in seconds for the spaces groups cache." introductionVersion:"pre5.0"` - TemplatePath string `yaml:"template_path" env:"GRAPH_SPACES_TEMPLATE_PATH" desc:"The path to a folder containg files and folder that should be created on any space." introductionVersion:"5.0"` + StorageUsersAddress string `yaml:"storage_users_address" env:"GRAPH_SPACES_STORAGE_USERS_ADDRESS" desc:"The address of the storage-users service." introductionVersion:"5.0"` } type LDAP struct { diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go index cb0ce6d0f1..df08d4ad7b 100644 --- a/services/graph/pkg/config/defaults/defaultconfig.go +++ b/services/graph/pkg/config/defaults/defaultconfig.go @@ -51,9 +51,10 @@ func DefaultConfig() *config.Config { }, Reva: shared.DefaultRevaConfig(), Spaces: config.Spaces{ - WebDavBase: "https://localhost:9200", - WebDavPath: "/dav/spaces/", - DefaultQuota: "1000000000", + StorageUsersAddress: "com.owncloud.api.storage-users", + WebDavBase: "https://localhost:9200", + WebDavPath: "/dav/spaces/", + DefaultQuota: "1000000000", // 1 minute ExtendedSpacePropertiesCacheTTL: 60, // 1 minute diff --git a/services/graph/pkg/service/v0/drives.go b/services/graph/pkg/service/v0/drives.go index 645e99417b..fcf28430c6 100644 --- a/services/graph/pkg/service/v0/drives.go +++ b/services/graph/pkg/service/v0/drives.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "io/fs" "math" "net/http" "net/url" @@ -483,31 +482,24 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) { return } - newDrive, err := g.cs3StorageSpaceToDrive(r.Context(), webDavBaseURL, resp.StorageSpace) + if driveType == _spaceTypeProject { + opaque, err := g.applySpaceTemplate(gatewayClient, resp.GetStorageSpace().GetRoot(), r.URL.Query().Get("template")) + if err != nil { + logger.Error().Err(err).Msg("could not apply template to space") + errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) + return + } + + resp.StorageSpace.Opaque = utils.MergeOpaques(resp.GetStorageSpace().GetOpaque(), opaque) + } + + newDrive, err := g.cs3StorageSpaceToDrive(r.Context(), webDavBaseURL, resp.GetStorageSpace()) if err != nil { logger.Debug().Err(err).Msg("could not create drive: error parsing drive") errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) return } - - ctx, cs3, err := g.getCS3Client(gatewayClient, resp.GetStorageSpace().GetRoot()) - if err != nil { - logger.Error().Err(err).Msg("could not get cs3 client") - errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not get cs3 client, aborting") - return - } - - fsys, err := fs.Sub(_spaceTemplateFS, "spacetemplate") - if err != nil { - logger.Error().Err(err).Msg("could not create drive: error parsing fs") - errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error()) - return - } - - if err := applyTemplate(ctx, cs3, gatewayClient, resp.GetStorageSpace().GetRoot(), fsys.(fs.ReadDirFS)); err != nil { - logger.Error().Err(err).Msg("could not apply template to space") - return - } + newDrive.Special = g.getSpecialDriveItems(r.Context(), webDavBaseURL, resp.GetStorageSpace()) render.Status(r, http.StatusCreated) render.JSON(w, r, newDrive) diff --git a/services/graph/pkg/service/v0/spacetemplate/.space/image.jpg b/services/graph/pkg/service/v0/spacetemplate/.space/image.jpg deleted file mode 100644 index 11e7f78b4a..0000000000 Binary files a/services/graph/pkg/service/v0/spacetemplate/.space/image.jpg and /dev/null differ diff --git a/services/graph/pkg/service/v0/spacetemplate/.space/image.png b/services/graph/pkg/service/v0/spacetemplate/.space/image.png new file mode 100644 index 0000000000..3b03d6554d Binary files /dev/null and b/services/graph/pkg/service/v0/spacetemplate/.space/image.png differ diff --git a/services/graph/pkg/service/v0/spacetemplate/.space/readme.md b/services/graph/pkg/service/v0/spacetemplate/.space/readme.md index bcd32e9de0..b96145d09b 100644 --- a/services/graph/pkg/service/v0/spacetemplate/.space/readme.md +++ b/services/graph/pkg/service/v0/spacetemplate/.space/readme.md @@ -1 +1 @@ -Space description goes here +Here you can add a description for this Space. diff --git a/services/graph/pkg/service/v0/utils.go b/services/graph/pkg/service/v0/utils.go index 09e695652c..270db47e34 100644 --- a/services/graph/pkg/service/v0/utils.go +++ b/services/graph/pkg/service/v0/utils.go @@ -3,7 +3,7 @@ package svc import ( "context" "encoding/json" - "errors" + "fmt" "io" "io/fs" "net/http" @@ -434,91 +434,103 @@ func cs3ReceivedShareToLibreGraphPermissions(ctx context.Context, logger *log.Lo return permission, nil } -func (g Graph) getCS3Client(gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId) (context.Context, *metadata.CS3, error) { - mdc := metadata.NewCS3(g.config.Reva.Address, "com.owncloud.api.storage-users") - mdc.SpaceRoot = root - ctx, err := utils.GetServiceUserContext(g.config.ServiceAccount.ServiceAccountID, gwc, g.config.ServiceAccount.ServiceAccountSecret) - return ctx, mdc, err -} +func (g Graph) applySpaceTemplate(gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, template string) (*v1beta1.Opaque, error) { + var fsys fs.ReadDirFS -func applyTemplate(ctx context.Context, mdc *metadata.CS3, gwc gateway.GatewayAPIClient, root *storageprovider.ResourceId, fsys fs.ReadDirFS) error { - entries, err := fsys.ReadDir(".") - if err != nil { - return err + switch template { + default: + fallthrough + case "none": + return &v1beta1.Opaque{}, nil + case "default": + f, err := fs.Sub(_spaceTemplateFS, "spacetemplate") + if err != nil { + return nil, err + } + fsys = f.(fs.ReadDirFS) } - updateSpaceRequest := &storageprovider.UpdateStorageSpaceRequest{ - // Prepare the object to apply the diff from. The properties on StorageSpace will overwrite - // the original storage space. + mdc := metadata.NewCS3(g.config.Reva.Address, g.config.Spaces.StorageUsersAddress) + mdc.SpaceRoot = root + + ctx, err := utils.GetServiceUserContext(g.config.ServiceAccount.ServiceAccountID, gwc, g.config.ServiceAccount.ServiceAccountSecret) + if err != nil { + return nil, err + } + + opaque, err := uploadFolder(ctx, mdc, ".", "", nil, fsys) + if err != nil { + return nil, err + } + + resp, err := gwc.UpdateStorageSpace(ctx, &storageprovider.UpdateStorageSpaceRequest{ StorageSpace: &storageprovider.StorageSpace{ Id: &storageprovider.StorageSpaceId{ OpaqueId: storagespace.FormatResourceID(*root), }, - Root: root, + Root: root, + Opaque: opaque, }, - } - - updateSpaceRequest.StorageSpace.Opaque, err = uploadFolder(ctx, mdc, "", "", updateSpaceRequest.StorageSpace.Opaque, fsys, entries) - if err != nil { - return err - } - - if len(updateSpaceRequest.StorageSpace.Opaque.Map) == 0 { - return nil - } - - resp, err := gwc.UpdateStorageSpace(ctx, updateSpaceRequest) + }) switch { case err != nil: - return err - case resp.Status.Code == rpc.Code_CODE_OK: - return nil + return nil, err + case resp.GetStatus().GetCode() != rpc.Code_CODE_OK: + return nil, fmt.Errorf("could not update storage space: %s", resp.GetStatus().GetMessage()) default: - return errors.New(resp.Status.Message) + return opaque, nil } } -func uploadFolder(ctx context.Context, mdc *metadata.CS3, pathOnDisc, pathOnSpace string, opaque *v1beta1.Opaque, fsys fs.ReadDirFS, entries []os.DirEntry) (*v1beta1.Opaque, error) { +func uploadFolder(ctx context.Context, mdc *metadata.CS3, pathOnDisc, pathOnSpace string, opaque *v1beta1.Opaque, fsys fs.ReadDirFS) (*v1beta1.Opaque, error) { + entries, err := fsys.ReadDir(pathOnDisc) + if err != nil { + return nil, err + } + for _, entry := range entries { - spacePath := filepath.Join(pathOnSpace, entry.Name()) - discPath := filepath.Join(pathOnDisc, entry.Name()) - - if entry.IsDir() { - err := mdc.MakeDirIfNotExist(ctx, spacePath) - if err != nil { - return opaque, err - } - - entries, err := fsys.ReadDir(discPath) - if err != nil { - return opaque, err - } - - opaque, err = uploadFolder(ctx, mdc, discPath, spacePath, opaque, fsys, entries) - if err != nil { - return opaque, err - } - continue - } - - b, err := fs.ReadFile(fsys, discPath) + opaque, err = uploadEntry(ctx, mdc, pathOnDisc, pathOnSpace, opaque, fsys, entry) if err != nil { - return opaque, err + return nil, err } - - if err := mdc.SimpleUpload(ctx, spacePath, b); err != nil { - return opaque, err - } - - // TODO: use upload to avoid second stat - i, err := mdc.Stat(ctx, spacePath) - if err != nil { - return opaque, err - } - - identifier := strings.TrimSuffix(entry.Name(), filepath.Ext(entry.Name())) - opaque = utils.AppendPlainToOpaque(opaque, identifier, storagespace.FormatResourceID(*i.Id)) } return opaque, nil } + +func uploadEntry(ctx context.Context, mdc *metadata.CS3, pathOnDisc, pathOnSpace string, opaque *v1beta1.Opaque, fsys fs.ReadDirFS, entry os.DirEntry) (*v1beta1.Opaque, error) { + spacePath := filepath.Join(pathOnSpace, entry.Name()) + discPath := filepath.Join(pathOnDisc, entry.Name()) + + switch { + case entry.IsDir(): + err := mdc.MakeDirIfNotExist(ctx, spacePath) + if err != nil { + return nil, err + } + + return uploadFolder(ctx, mdc, discPath, spacePath, opaque, fsys) + default: + b, err := fs.ReadFile(fsys, discPath) + if err != nil { + return nil, err + } + + res, err := mdc.Upload(ctx, metadata.UploadRequest{ + Path: spacePath, + Content: b, + }) + if err != nil { + return nil, err + } + + identifier := strings.TrimSuffix(entry.Name(), filepath.Ext(entry.Name())) + for _, special := range []string{ReadmeSpecialFolderName, SpaceImageSpecialFolderName} { + if special == identifier { + opaque = utils.AppendPlainToOpaque(opaque, identifier, res.FileID) + break + } + } + return opaque, nil + } +}