diff --git a/docs/files.md b/docs/files.md index dfd33524ff4..5ae2d55a93e 100644 --- a/docs/files.md +++ b/docs/files.md @@ -935,18 +935,6 @@ By default the `content-disposition` will be `inline`, but it will be GET /files/download?Path=/Documents/hello.txt&Dl=1 HTTP/1.1 ``` -### GET /files/:file-id/icon/:secret - -Get an image that shows the first page of a PDF in a small resolution (96x96). - -**Note:** this route is deprecated, you should use thumbnails instead. - -### GET /files/:file-id/preview/:secret - -Get an image that shows the first page of a PDF (at most 1080x1920). - -**Note:** this route is deprecated, you should use thumbnails instead. - ### GET /files/:file-id/thumbnails/:secret/:format Get a thumbnail of a file (for an image & pdf only). `:format` can be `tiny` (96x96) diff --git a/docs/important-changes.md b/docs/important-changes.md index efa00d6b31c..cd4a0f369bb 100644 --- a/docs/important-changes.md +++ b/docs/important-changes.md @@ -4,6 +4,12 @@ This section will list important changes to the stack or its usage, and migration procedures if any is needed. +## April 2024: Routes for PDF + +The deprecated routes for getting the icon or preview of a PDF file has been removed. + +You should use the thumbnails instead. + ## December 2023: Iterations for PBKDF2 increased We have increased the number of PBKDF2 iterations for new users to 650_000, and removed the exception for Edge as it now supports PBKDF2 via the subtle crypto API. diff --git a/model/vfs/pdf.go b/model/vfs/pdf.go deleted file mode 100644 index fc6ce3bd69f..00000000000 --- a/model/vfs/pdf.go +++ /dev/null @@ -1,178 +0,0 @@ -package vfs - -import ( - "bytes" - "fmt" - "net/http" - "os" - "os/exec" - - "github.com/cozy/cozy-stack/pkg/config/config" - "github.com/cozy/cozy-stack/pkg/logger" - "github.com/cozy/cozy-stack/pkg/previewfs" -) - -// ServePDFIcon will send the icon image for a PDF. -func ServePDFIcon(w http.ResponseWriter, req *http.Request, fs VFS, doc *FileDoc) error { - name := fmt.Sprintf("%s-icon.jpg", doc.ID()) - modtime := doc.UpdatedAt - if doc.CozyMetadata != nil && doc.CozyMetadata.UploadedAt != nil { - modtime = *doc.CozyMetadata.UploadedAt - } - buf, err := icon(fs, doc) - if err != nil { - return err - } - http.ServeContent(w, req, name, modtime, bytes.NewReader(buf.Bytes())) - return nil -} - -func icon(fs VFS, doc *FileDoc) (*bytes.Buffer, error) { - cache := previewfs.SystemCache() - if buf, err := cache.GetIcon(doc.MD5Sum); err == nil { - return buf, nil - } - - buf, err := generateIcon(fs, doc) - if err != nil { - return nil, err - } - _ = cache.SetIcon(doc.MD5Sum, buf) - return buf, nil -} - -func generateIcon(fs VFS, doc *FileDoc) (*bytes.Buffer, error) { - f, err := fs.OpenFile(doc) - if err != nil { - return nil, err - } - defer f.Close() - - tempDir, err := os.MkdirTemp("", "magick") - if err != nil { - return nil, err - } - defer os.RemoveAll(tempDir) - envTempDir := fmt.Sprintf("MAGICK_TEMPORARY_PATH=%s", tempDir) - env := []string{envTempDir} - - convertCmd := config.GetConfig().Jobs.ImageMagickConvertCmd - if convertCmd == "" { - convertCmd = "convert" - } - args := []string{ - "-limit", "Memory", "1GB", - "-limit", "Map", "1GB", - "-[0]", // Takes the input from stdin - "-quality", "99", // At small resolution, we want a very good quality - "-interlace", "none", // Don't use progressive JPEGs, they are heavier - "-thumbnail", "96x96", // Makes a thumbnail that fits inside the given format - "-background", "white", // Use white for the background - "-alpha", "remove", // JPEGs don't have an alpha channel - "-colorspace", "sRGB", // Use the colorspace recommended for web, sRGB - "jpg:-", // Send the output on stdout, in JPEG format - } - - var stdout, stderr bytes.Buffer - cmd := exec.Command(convertCmd, args...) - cmd.Env = env - cmd.Stdin = f - cmd.Stdout = &stdout - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - // Truncate very long messages - msg := stderr.String() - if len(msg) > 4000 { - msg = msg[:4000] - } - logger.WithNamespace("pdf_icon"). - WithField("stderr", msg). - WithField("file_id", doc.ID()). - Errorf("imagemagick failed: %s", err) - return nil, err - } - return &stdout, nil -} - -// ServePDFPreview will send the preview image for a PDF. -func ServePDFPreview(w http.ResponseWriter, req *http.Request, fs VFS, doc *FileDoc) error { - name := fmt.Sprintf("%s-preview.jpg", doc.ID()) - modtime := doc.UpdatedAt - if doc.CozyMetadata != nil && doc.CozyMetadata.UploadedAt != nil { - modtime = *doc.CozyMetadata.UploadedAt - } - buf, err := preview(fs, doc) - if err != nil { - return err - } - http.ServeContent(w, req, name, modtime, bytes.NewReader(buf.Bytes())) - return nil -} - -func preview(fs VFS, doc *FileDoc) (*bytes.Buffer, error) { - cache := previewfs.SystemCache() - if buf, err := cache.GetPreview(doc.MD5Sum); err == nil { - return buf, nil - } - - buf, err := generatePreview(fs, doc) - if err != nil { - return nil, err - } - _ = cache.SetPreview(doc.MD5Sum, buf) - return buf, nil -} - -func generatePreview(fs VFS, doc *FileDoc) (*bytes.Buffer, error) { - f, err := fs.OpenFile(doc) - if err != nil { - return nil, err - } - defer f.Close() - - tempDir, err := os.MkdirTemp("", "magick") - if err != nil { - return nil, err - } - defer os.RemoveAll(tempDir) - envTempDir := fmt.Sprintf("MAGICK_TEMPORARY_PATH=%s", tempDir) - env := []string{envTempDir} - - convertCmd := config.GetConfig().Jobs.ImageMagickConvertCmd - if convertCmd == "" { - convertCmd = "convert" - } - args := []string{ - "-limit", "Memory", "2GB", - "-limit", "Map", "3GB", - "-density", "300", // We want a high resolution for PDFs - "-[0]", // Takes the input from stdin - "-quality", "82", // A good compromise between file size and quality - "-interlace", "none", // Don't use progressive JPEGs, they are heavier - "-thumbnail", "1080x1920>", // Makes a thumbnail that fits inside the given format - "-background", "white", // Use white for the background - "-alpha", "remove", // JPEGs don't have an alpha channel - "-colorspace", "sRGB", // Use the colorspace recommended for web, sRGB - "jpg:-", // Send the output on stdout, in JPEG format - } - - var stdout, stderr bytes.Buffer - cmd := exec.Command(convertCmd, args...) - cmd.Env = env - cmd.Stdin = f - cmd.Stdout = &stdout - cmd.Stderr = &stderr - if err := cmd.Run(); err != nil { - // Truncate very long messages - msg := stderr.String() - if len(msg) > 4000 { - msg = msg[:4000] - } - logger.WithNamespace("pdf_preview"). - WithField("stderr", msg). - WithField("file_id", doc.ID()). - Errorf("imagemagick failed: %s", err) - return nil, err - } - return &stdout, nil -} diff --git a/pkg/jsonapi/data.go b/pkg/jsonapi/data.go index 5ecae7df2a9..c82ddae43ef 100644 --- a/pkg/jsonapi/data.go +++ b/pkg/jsonapi/data.go @@ -39,8 +39,6 @@ type LinksList struct { Small string `json:"small,omitempty"` Medium string `json:"medium,omitempty"` Large string `json:"large,omitempty"` - // Preview for PDF - Preview string `json:"preview,omitempty"` } // Relationship is a resource linkage, as described in JSON-API diff --git a/web/files/files.go b/web/files/files.go index 26b663d44d0..645c9446fd7 100644 --- a/web/files/files.go +++ b/web/files/files.go @@ -992,48 +992,6 @@ func HeadDirOrFile(c echo.Context) error { return nil } -// IconHandler serves icon for the PDFs. -func IconHandler(c echo.Context) error { - instance := middlewares.GetInstance(c) - - secret := c.Param("secret") - fileID, err := vfs.GetStore().GetThumb(instance, secret) - if err != nil { - return WrapVfsError(err) - } - if c.Param("file-id") != fileID { - return jsonapi.NewError(http.StatusBadRequest, "Wrong download token") - } - - doc, err := instance.VFS().FileByID(fileID) - if err != nil { - return WrapVfsError(err) - } - - return vfs.ServePDFIcon(c.Response(), c.Request(), instance.VFS(), doc) -} - -// PreviewHandler serves preview images for the PDFs. -func PreviewHandler(c echo.Context) error { - instance := middlewares.GetInstance(c) - - secret := c.Param("secret") - fileID, err := vfs.GetStore().GetThumb(instance, secret) - if err != nil { - return WrapVfsError(err) - } - if c.Param("file-id") != fileID { - return jsonapi.NewError(http.StatusBadRequest, "Wrong download token") - } - - doc, err := instance.VFS().FileByID(fileID) - if err != nil { - return WrapVfsError(err) - } - - return vfs.ServePDFPreview(c.Response(), c.Request(), instance.VFS(), doc) -} - // ThumbnailHandler serves thumbnails of the images/photos func ThumbnailHandler(c echo.Context) error { instance := middlewares.GetInstance(c) @@ -1922,8 +1880,6 @@ func Routes(router *echo.Group) { router.POST("/upload/metadata", UploadMetadataHandler) router.POST("/:file-id/copy", FileCopyHandler) - router.GET("/:file-id/icon/:secret", IconHandler) - router.GET("/:file-id/preview/:secret", PreviewHandler) router.GET("/:file-id/thumbnails/:secret/:format", ThumbnailHandler) router.POST("/archive", ArchiveDownloadCreateHandler) diff --git a/web/files/files_test.go b/web/files/files_test.go index a741b73097c..38e443ea0bf 100644 --- a/web/files/files_test.go +++ b/web/files/files_test.go @@ -3614,10 +3614,6 @@ func TestFiles(t *testing.T) { data.ValueEqual("id", parentID) data.Value("attributes").Object().ValueEqual("size", "90") }) - - t.Run("DeprecatePreviewAndIcon", func(t *testing.T) { - testutils.TODO(t, "2024-05-01", "Remove the deprecated preview and icon for PDF files") - }) } func readFile(fs vfs.VFS, name string) ([]byte, error) { diff --git a/web/files/paginated.go b/web/files/paginated.go index 3fd5a9c19bc..659c04cb32b 100644 --- a/web/files/paginated.go +++ b/web/files/paginated.go @@ -389,10 +389,6 @@ func (f *file) Links() *jsonapi.LinksList { links.Small = "/files/" + f.doc.DocID + "/thumbnails/" + f.thumbSecret + "/small" links.Medium = "/files/" + f.doc.DocID + "/thumbnails/" + f.thumbSecret + "/medium" links.Large = "/files/" + f.doc.DocID + "/thumbnails/" + f.thumbSecret + "/large" - if f.doc.Class == "pdf" { - links.Icon = "/files/" + f.doc.DocID + "/icon/" + f.thumbSecret - links.Preview = "/files/" + f.doc.DocID + "/preview/" + f.thumbSecret - } } } return &links