Compare commits

..

4 Commits

Author SHA1 Message Date
Oleg Lobanov
a2fb499a20 chore(release): 2.6.1 2020-07-28 13:40:19 +02:00
Oleg Lobanov
411a928fea chore: fix lint errors 2020-07-28 13:40:06 +02:00
Oleg Lobanov
f5d02cdde9 fix: delete cached previews when deleting file 2020-07-28 11:59:55 +02:00
Oleg Lobanov
c9340af8d0 fix: escape special characters in preview url (closes #1002) 2020-07-28 11:59:32 +02:00
7 changed files with 167 additions and 28 deletions

View File

@@ -2,6 +2,14 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [2.6.1](https://github.com/filebrowser/filebrowser/compare/v2.6.0...v2.6.1) (2020-07-28)
### Bug Fixes
* delete cached previews when deleting file ([f5d02cd](https://github.com/filebrowser/filebrowser/commit/f5d02cdde97923b963878abf5a300393b9feb348))
* escape special characters in preview url (closes [#1002](https://github.com/filebrowser/filebrowser/issues/1002)) ([c9340af](https://github.com/filebrowser/filebrowser/commit/c9340af8d045671ad3338c5d2d887c335ab92de4))
## [2.6.0](https://github.com/filebrowser/filebrowser/compare/v2.5.0...v2.6.0) (2020-07-27)

View File

@@ -68,7 +68,7 @@ func (f *FileCache) Delete(ctx context.Context, key string) error {
defer mu.Unlock()
fileName := f.getFileName(key)
if err := f.fs.Remove(fileName); err != nil && err != os.ErrNotExist {
if err := f.fs.Remove(fileName); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
return nil

View File

@@ -99,13 +99,13 @@ export default {
return (this.nextLink !== '')
},
download () {
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
return `${baseURL}/api/raw${escape(this.req.path)}?auth=${this.jwt}`
},
previewUrl () {
if (this.req.type === 'image') {
return `${baseURL}/api/preview/big${this.req.path}?auth=${this.jwt}`
return `${baseURL}/api/preview/big${escape(this.req.path)}?auth=${this.jwt}`
}
return `${baseURL}/api/raw${this.req.path}?auth=${this.jwt}`
return `${baseURL}/api/raw${escape(this.req.path)}?auth=${this.jwt}`
},
raw () {
return `${this.previewUrl}&inline=true`

View File

@@ -46,7 +46,7 @@ func NewHandler(imgSvc ImgService, fileCache FileCache, store *storage.Storage,
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler, "/api/resources")).Methods("DELETE")
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE")
api.PathPrefix("/resources").Handler(monkey(resourcePostPutHandler, "/api/resources")).Methods("POST")
api.PathPrefix("/resources").Handler(monkey(resourcePostPutHandler, "/api/resources")).Methods("PUT")
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler, "/api/resources")).Methods("PATCH")

View File

@@ -1,3 +1,4 @@
//go:generate go-enum --sql --marshal --names --file $GOFILE
package http
import (
@@ -13,10 +14,13 @@ import (
"github.com/filebrowser/filebrowser/v2/img"
)
const (
sizeThumb = "thumb"
sizeBig = "big"
/*
ENUM(
thumb
big
)
*/
type PreviewSize int
type ImgService interface {
FormatFromExtension(ext string) (img.Format, error)
@@ -26,6 +30,7 @@ type ImgService interface {
type FileCache interface {
Store(ctx context.Context, key string, value []byte) error
Load(ctx context.Context, key string) ([]byte, bool, error)
Delete(ctx context.Context, key string) error
}
func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, resizePreview bool) handleFunc {
@@ -34,9 +39,10 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
return http.StatusAccepted, nil
}
vars := mux.Vars(r)
size := vars["size"]
if size != sizeBig && size != sizeThumb {
return http.StatusNotImplemented, nil
previewSize, err := ParsePreviewSize(vars["size"])
if err != nil {
return http.StatusBadRequest, err
}
file, err := files.NewFileInfo(files.FileOptions{
@@ -54,7 +60,7 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
switch file.Type {
case "image":
return handleImagePreview(w, r, imgSvc, fileCache, file, size, enableThumbnails, resizePreview)
return handleImagePreview(w, r, imgSvc, fileCache, file, previewSize, enableThumbnails, resizePreview)
default:
return http.StatusNotImplemented, fmt.Errorf("can't create preview for %s type", file.Type)
}
@@ -62,7 +68,7 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
}
func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgService, fileCache FileCache,
file *files.FileInfo, size string, enableThumbnails, resizePreview bool) (int, error) {
file *files.FileInfo, previewSize PreviewSize, enableThumbnails, resizePreview bool) (int, error) {
format, err := imgSvc.FormatFromExtension(file.Extension)
if err != nil {
// Unsupported extensions directly return the raw data
@@ -72,7 +78,7 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
return errToStatus(err), err
}
cacheKey := file.Path + size
cacheKey := previewCacheKey(file.Path, previewSize)
cachedFile, ok, err := fileCache.Load(r.Context(), cacheKey)
if err != nil {
return errToStatus(err), err
@@ -95,11 +101,11 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
)
switch {
case size == sizeBig && resizePreview && format != img.FormatGif:
case previewSize == PreviewSizeBig && resizePreview && format != img.FormatGif:
width = 1080
height = 1080
options = append(options, img.WithMode(img.ResizeModeFit), img.WithQuality(img.QualityMedium))
case size == sizeThumb && enableThumbnails:
case previewSize == PreviewSizeThumb && enableThumbnails:
width = 128
height = 128
options = append(options, img.WithMode(img.ResizeModeFill), img.WithQuality(img.QualityLow), img.WithFormat(img.FormatJpeg))
@@ -125,3 +131,7 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
return 0, nil
}
func previewCacheKey(fPath string, previewSize PreviewSize) string {
return fPath + previewSize.String()
}

100
http/preview_enum.go Normal file
View File

@@ -0,0 +1,100 @@
// Code generated by go-enum
// DO NOT EDIT!
package http
import (
"database/sql/driver"
"fmt"
"strings"
)
const (
// PreviewSizeThumb is a PreviewSize of type Thumb
PreviewSizeThumb PreviewSize = iota
// PreviewSizeBig is a PreviewSize of type Big
PreviewSizeBig
)
const _PreviewSizeName = "thumbbig"
var _PreviewSizeNames = []string{
_PreviewSizeName[0:5],
_PreviewSizeName[5:8],
}
// PreviewSizeNames returns a list of possible string values of PreviewSize.
func PreviewSizeNames() []string {
tmp := make([]string, len(_PreviewSizeNames))
copy(tmp, _PreviewSizeNames)
return tmp
}
var _PreviewSizeMap = map[PreviewSize]string{
0: _PreviewSizeName[0:5],
1: _PreviewSizeName[5:8],
}
// String implements the Stringer interface.
func (x PreviewSize) String() string {
if str, ok := _PreviewSizeMap[x]; ok {
return str
}
return fmt.Sprintf("PreviewSize(%d)", x)
}
var _PreviewSizeValue = map[string]PreviewSize{
_PreviewSizeName[0:5]: 0,
_PreviewSizeName[5:8]: 1,
}
// ParsePreviewSize attempts to convert a string to a PreviewSize
func ParsePreviewSize(name string) (PreviewSize, error) {
if x, ok := _PreviewSizeValue[name]; ok {
return x, nil
}
return PreviewSize(0), fmt.Errorf("%s is not a valid PreviewSize, try [%s]", name, strings.Join(_PreviewSizeNames, ", "))
}
// MarshalText implements the text marshaller method
func (x PreviewSize) MarshalText() ([]byte, error) {
return []byte(x.String()), nil
}
// UnmarshalText implements the text unmarshaller method
func (x *PreviewSize) UnmarshalText(text []byte) error {
name := string(text)
tmp, err := ParsePreviewSize(name)
if err != nil {
return err
}
*x = tmp
return nil
}
// Scan implements the Scanner interface.
func (x *PreviewSize) Scan(value interface{}) error {
var name string
switch v := value.(type) {
case string:
name = v
case []byte:
name = string(v)
case nil:
*x = PreviewSize(0)
return nil
}
tmp, err := ParsePreviewSize(name)
if err != nil {
return err
}
*x = tmp
return nil
}
// Value implements the driver Valuer interface.
func (x PreviewSize) Value() (driver.Value, error) {
return x.String(), nil
}

View File

@@ -50,21 +50,42 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
return renderJSON(w, r, file)
})
var resourceDeleteHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
if r.URL.Path == "/" || !d.user.Perm.Delete {
return http.StatusForbidden, nil
}
func resourceDeleteHandler(fileCache FileCache) handleFunc {
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
if r.URL.Path == "/" || !d.user.Perm.Delete {
return http.StatusForbidden, nil
}
err := d.RunHook(func() error {
return d.user.Fs.RemoveAll(r.URL.Path)
}, "delete", r.URL.Path, "", d.user)
file, err := files.NewFileInfo(files.FileOptions{
Fs: d.user.Fs,
Path: r.URL.Path,
Modify: d.user.Perm.Modify,
Expand: true,
Checker: d,
})
if err != nil {
return errToStatus(err), err
}
if err != nil {
return errToStatus(err), err
}
// delete thumbnails
for _, previewSizeName := range PreviewSizeNames() {
size, _ := ParsePreviewSize(previewSizeName)
if err := fileCache.Delete(r.Context(), previewCacheKey(file.Path, size)); err != nil { //nolint:govet
return errToStatus(err), err
}
}
return http.StatusOK, nil
})
err = d.RunHook(func() error {
return d.user.Fs.RemoveAll(r.URL.Path)
}, "delete", r.URL.Path, "", d.user)
if err != nil {
return errToStatus(err), err
}
return http.StatusOK, nil
})
}
var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
if !d.user.Perm.Create && r.Method == http.MethodPost {