fix: delete cached previews when deleting file
This commit is contained in:
parent
c9340af8d0
commit
f5d02cdde9
|
@ -68,7 +68,7 @@ func (f *FileCache) Delete(ctx context.Context, key string) error {
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
|
|
||||||
fileName := f.getFileName(key)
|
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 err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -46,7 +46,7 @@ func NewHandler(imgSvc ImgService, fileCache FileCache, store *storage.Storage,
|
||||||
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
|
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
|
||||||
|
|
||||||
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
|
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("POST")
|
||||||
api.PathPrefix("/resources").Handler(monkey(resourcePostPutHandler, "/api/resources")).Methods("PUT")
|
api.PathPrefix("/resources").Handler(monkey(resourcePostPutHandler, "/api/resources")).Methods("PUT")
|
||||||
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler, "/api/resources")).Methods("PATCH")
|
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler, "/api/resources")).Methods("PATCH")
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:generate go-enum --sql --marshal --names --file $GOFILE
|
||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -13,10 +14,13 @@ import (
|
||||||
"github.com/filebrowser/filebrowser/v2/img"
|
"github.com/filebrowser/filebrowser/v2/img"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
/*
|
||||||
sizeThumb = "thumb"
|
ENUM(
|
||||||
sizeBig = "big"
|
thumb
|
||||||
|
big
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
|
type PreviewSize int
|
||||||
|
|
||||||
type ImgService interface {
|
type ImgService interface {
|
||||||
FormatFromExtension(ext string) (img.Format, error)
|
FormatFromExtension(ext string) (img.Format, error)
|
||||||
|
@ -26,6 +30,7 @@ type ImgService interface {
|
||||||
type FileCache interface {
|
type FileCache interface {
|
||||||
Store(ctx context.Context, key string, value []byte) error
|
Store(ctx context.Context, key string, value []byte) error
|
||||||
Load(ctx context.Context, key string) ([]byte, bool, 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 {
|
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
|
return http.StatusAccepted, nil
|
||||||
}
|
}
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
size := vars["size"]
|
|
||||||
if size != sizeBig && size != sizeThumb {
|
previewSize, err := ParsePreviewSize(vars["size"])
|
||||||
return http.StatusNotImplemented, nil
|
if err != nil {
|
||||||
|
return http.StatusBadRequest, err
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := files.NewFileInfo(files.FileOptions{
|
file, err := files.NewFileInfo(files.FileOptions{
|
||||||
|
@ -54,7 +60,7 @@ func previewHandler(imgSvc ImgService, fileCache FileCache, enableThumbnails, re
|
||||||
|
|
||||||
switch file.Type {
|
switch file.Type {
|
||||||
case "image":
|
case "image":
|
||||||
return handleImagePreview(w, r, imgSvc, fileCache, file, size, enableThumbnails, resizePreview)
|
return handleImagePreview(w, r, imgSvc, fileCache, file, previewSize, enableThumbnails, resizePreview)
|
||||||
default:
|
default:
|
||||||
return http.StatusNotImplemented, fmt.Errorf("can't create preview for %s type", file.Type)
|
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,
|
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)
|
format, err := imgSvc.FormatFromExtension(file.Extension)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Unsupported extensions directly return the raw data
|
// 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
|
return errToStatus(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
cacheKey := file.Path + size
|
cacheKey := previewCacheKey(file.Path, previewSize)
|
||||||
cachedFile, ok, err := fileCache.Load(r.Context(), cacheKey)
|
cachedFile, ok, err := fileCache.Load(r.Context(), cacheKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errToStatus(err), err
|
return errToStatus(err), err
|
||||||
|
@ -95,11 +101,11 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case size == sizeBig && resizePreview && format != img.FormatGif:
|
case previewSize == PreviewSizeBig && resizePreview && format != img.FormatGif:
|
||||||
width = 1080
|
width = 1080
|
||||||
height = 1080
|
height = 1080
|
||||||
options = append(options, img.WithMode(img.ResizeModeFit), img.WithQuality(img.QualityMedium))
|
options = append(options, img.WithMode(img.ResizeModeFit), img.WithQuality(img.QualityMedium))
|
||||||
case size == sizeThumb && enableThumbnails:
|
case previewSize == PreviewSizeThumb && enableThumbnails:
|
||||||
width = 128
|
width = 128
|
||||||
height = 128
|
height = 128
|
||||||
options = append(options, img.WithMode(img.ResizeModeFill), img.WithQuality(img.QualityLow), img.WithFormat(img.FormatJpeg))
|
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
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func previewCacheKey(fPath string, previewSize PreviewSize) string {
|
||||||
|
return fPath + previewSize.String()
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -50,21 +50,42 @@ var resourceGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d
|
||||||
return renderJSON(w, r, file)
|
return renderJSON(w, r, file)
|
||||||
})
|
})
|
||||||
|
|
||||||
var resourceDeleteHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
func resourceDeleteHandler(fileCache FileCache) handleFunc {
|
||||||
if r.URL.Path == "/" || !d.user.Perm.Delete {
|
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
return http.StatusForbidden, nil
|
if r.URL.Path == "/" || !d.user.Perm.Delete {
|
||||||
}
|
return http.StatusForbidden, nil
|
||||||
|
}
|
||||||
|
|
||||||
err := d.RunHook(func() error {
|
file, err := files.NewFileInfo(files.FileOptions{
|
||||||
return d.user.Fs.RemoveAll(r.URL.Path)
|
Fs: d.user.Fs,
|
||||||
}, "delete", r.URL.Path, "", d.user)
|
Path: r.URL.Path,
|
||||||
|
Modify: d.user.Perm.Modify,
|
||||||
|
Expand: true,
|
||||||
|
Checker: d,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return errToStatus(err), err
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
// delete thumbnails
|
||||||
return errToStatus(err), err
|
for _, previewSizeName := range PreviewSizeNames() {
|
||||||
}
|
size, _ := ParsePreviewSize(previewSizeName)
|
||||||
|
if err := fileCache.Delete(r.Context(), previewCacheKey(file.Path, size)); err != nil {
|
||||||
|
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) {
|
var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
if !d.user.Perm.Create && r.Method == http.MethodPost {
|
if !d.user.Perm.Create && r.Method == http.MethodPost {
|
||||||
|
|
Loading…
Reference in New Issue