diff --git a/changelog/unreleased/spaces-thumbnails.md b/changelog/unreleased/spaces-thumbnails.md new file mode 100644 index 00000000000..7b6817eff9f --- /dev/null +++ b/changelog/unreleased/spaces-thumbnails.md @@ -0,0 +1,5 @@ +Enhancement: Thumbnails in spaces + +Added support for thumbnails in spaces. + +https://github.com/owncloud/ocis/pull/3219 diff --git a/thumbnails/pkg/service/v0/service.go b/thumbnails/pkg/service/v0/service.go index eb80ef9738c..b4978e3fb10 100644 --- a/thumbnails/pkg/service/v0/service.go +++ b/thumbnails/pkg/service/v0/service.go @@ -217,11 +217,24 @@ func (g Thumbnail) handleWebdavSource(ctx context.Context, req *thumbnailssvc.Ge func (g Thumbnail) stat(path, auth string) (*provider.StatResponse, error) { ctx := metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, auth) - req := &provider.StatRequest{ - Ref: &provider.Reference{ + var ref *provider.Reference + if strings.Contains(path, "!") { + parts := strings.Split(path, "!") + spaceID, path := parts[0], parts[1] + ref = &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: spaceID, + OpaqueId: spaceID, + }, Path: path, - }, + } + } else { + ref = &provider.Reference{ + Path: path, + } } + + req := &provider.StatRequest{Ref: ref} rsp, err := g.cs3Client.Stat(ctx, req) if err != nil { g.logger.Error().Err(err).Str("path", path).Msg("could not stat file") diff --git a/thumbnails/pkg/thumbnail/imgsource/cs3.go b/thumbnails/pkg/thumbnail/imgsource/cs3.go index 9cf3db58abd..27b37aa6e73 100644 --- a/thumbnails/pkg/thumbnail/imgsource/cs3.go +++ b/thumbnails/pkg/thumbnail/imgsource/cs3.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strings" gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" @@ -42,12 +43,24 @@ func (s CS3) Get(ctx context.Context, path string) (io.ReadCloser, error) { if !ok { return nil, errors.New("cs3source: authorization missing") } - ctx = metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, auth) - rsp, err := s.client.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{ - Ref: &provider.Reference{ + var ref *provider.Reference + if strings.Contains(path, "!") { + parts := strings.Split(path, "!") + spaceID, path := parts[0], parts[1] + ref = &provider.Reference{ + ResourceId: &provider.ResourceId{ + StorageId: spaceID, + OpaqueId: spaceID, + }, + Path: path, + } + } else { + ref = &provider.Reference{ Path: path, - }, - }) + } + } + ctx = metadata.AppendToOutgoingContext(context.Background(), revactx.TokenHeader, auth) + rsp, err := s.client.InitiateFileDownload(ctx, &provider.InitiateFileDownloadRequest{Ref: ref}) if err != nil { return nil, err diff --git a/webdav/pkg/dav/requests/thumbnail.go b/webdav/pkg/dav/requests/thumbnail.go index be1e7d43c4e..48b80ad4882 100644 --- a/webdav/pkg/dav/requests/thumbnail.go +++ b/webdav/pkg/dav/requests/thumbnail.go @@ -33,13 +33,13 @@ type ThumbnailRequest struct { Height int32 // In case of a public share the public link token. PublicLinkToken string - // The username from the requested URL - Username string + // The Identifier from the requested URL + Identifier string } // ParseThumbnailRequest extracts all required parameters from a http request. func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { - fp, username, err := extractFilePath(r) + fp, id, err := extractFilePath(r) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { Width: int32(width), Height: int32(height), PublicLinkToken: chi.URLParam(r, "token"), - Username: username, + Identifier: id, }, nil } @@ -68,16 +68,17 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) { // User and filepath are dynamic and filepath can contain slashes // So using the URLParam function is not possible. func extractFilePath(r *http.Request) (string, string, error) { - user := chi.URLParam(r, "user") - user, err := url.QueryUnescape(user) + id := chi.URLParam(r, "id") + id, err := url.QueryUnescape(id) if err != nil { return "", "", errors.New("could not unescape user") } - if user != "" { - parts := strings.SplitN(r.URL.Path, user, 2) - return parts[1], user, nil + if id != "" { + parts := strings.SplitN(r.URL.Path, id, 2) + return parts[1], id, nil } + // This is for public links token := chi.URLParam(r, "token") if token != "" { parts := strings.SplitN(r.URL.Path, token, 2) diff --git a/webdav/pkg/service/v0/service.go b/webdav/pkg/service/v0/service.go index 2ab53bd486f..a5562171f19 100644 --- a/webdav/pkg/service/v0/service.go +++ b/webdav/pkg/service/v0/service.go @@ -66,7 +66,8 @@ func NewService(opts ...Option) (Service, error) { } m.Route(options.Config.HTTP.Root, func(r chi.Router) { - r.Get("/remote.php/dav/files/{user}/*", svc.Thumbnail) + r.Get("/remote.php/dav/spaces/{id}/*", svc.SpacesThumbnail) + r.Get("/remote.php/dav/files/{id}/*", svc.Thumbnail) r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail) r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead) }) @@ -88,6 +89,53 @@ func (g Webdav) ServeHTTP(w http.ResponseWriter, r *http.Request) { g.mux.ServeHTTP(w, r) } +// SpacesThumbnail is the endpoint for retrieving thumbnails inside of spaces. +func (g Webdav) SpacesThumbnail(w http.ResponseWriter, r *http.Request) { + tr, err := requests.ParseThumbnailRequest(r) + if err != nil { + g.log.Error().Err(err).Msg("could not create Request") + renderError(w, r, errBadRequest(err.Error())) + return + } + t := r.Header.Get(TokenHeader) + + fullPath := tr.Identifier + "!" + tr.Filepath + rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnailssvc.GetThumbnailRequest{ + Filepath: strings.TrimLeft(tr.Filepath, "/"), + ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")), + Width: tr.Width, + Height: tr.Height, + Source: &thumbnailssvc.GetThumbnailRequest_Cs3Source{ + Cs3Source: &thumbnailsmsg.CS3Source{ + Path: fullPath, + Authorization: t, + }, + }, + }) + if err != nil { + e := merrors.Parse(err.Error()) + switch e.Code { + case http.StatusNotFound: + // StatusNotFound is expected for unsupported files + renderError(w, r, errNotFound(notFoundMsg(tr.Filename))) + return + case http.StatusBadRequest: + renderError(w, r, errBadRequest(err.Error())) + default: + renderError(w, r, errInternalError(err.Error())) + } + g.log.Error().Err(err).Msg("could not get thumbnail") + return + } + + if len(rsp.Thumbnail) == 0 { + renderError(w, r, errNotFound("")) + return + } + + g.mustRender(w, r, newThumbnailResponse(rsp)) +} + // Thumbnail implements the Service interface. func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { tr, err := requests.ParseThumbnailRequest(r) @@ -101,7 +149,7 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) { ctx := metadata.AppendToOutgoingContext(r.Context(), TokenHeader, t) userRes, err := g.revaClient.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{ Claim: "username", - Value: tr.Username, + Value: tr.Identifier, }) if err != nil || userRes.Status.Code != rpcv1beta1.Code_CODE_OK { g.log.Error().Err(err).Msg("could not get user")