mirror of
https://github.com/filebrowser/filebrowser.git
synced 2024-06-07 23:00:43 +00:00
Merge pull request #1321 from ramiresviana/fixes-6
This commit is contained in:
commit
381f09087a
@ -1,23 +1,47 @@
|
||||
import { fetchJSON, removePrefix } from './utils'
|
||||
import { fetchURL, removePrefix } from './utils'
|
||||
import { baseURL } from '@/utils/constants'
|
||||
|
||||
export async function fetch(hash, password = "") {
|
||||
return fetchJSON(`/api/public/share/${hash}`, {
|
||||
export async function fetch (url, password = "") {
|
||||
url = removePrefix(url)
|
||||
|
||||
const res = await fetchURL(`/api/public/share${url}`, {
|
||||
headers: {'X-SHARE-PASSWORD': password},
|
||||
})
|
||||
|
||||
if (res.status === 200) {
|
||||
let data = await res.json()
|
||||
data.url = `/share${url}`
|
||||
|
||||
if (data.isDir) {
|
||||
if (!data.url.endsWith('/')) data.url += '/'
|
||||
data.items = data.items.map((item, index) => {
|
||||
item.index = index
|
||||
item.url = `${data.url}${encodeURIComponent(item.name)}`
|
||||
|
||||
if (item.isDir) {
|
||||
item.url += '/'
|
||||
}
|
||||
|
||||
return item
|
||||
})
|
||||
}
|
||||
|
||||
return data
|
||||
} else {
|
||||
throw new Error(res.status)
|
||||
}
|
||||
}
|
||||
|
||||
export function download(format, hash, token, ...files) {
|
||||
let url = `${baseURL}/api/public/dl/${hash}`
|
||||
|
||||
const prefix = `/share/${hash}`
|
||||
if (files.length === 1) {
|
||||
url += removePrefix(files[0], prefix) + '?'
|
||||
url += encodeURIComponent(files[0]) + '?'
|
||||
} else {
|
||||
let arg = ''
|
||||
|
||||
for (let file of files) {
|
||||
arg += removePrefix(file, prefix) + ','
|
||||
arg += encodeURIComponent(file) + ','
|
||||
}
|
||||
|
||||
arg = arg.substring(0, arg.length - 1)
|
||||
|
@ -33,12 +33,8 @@ export async function fetchJSON (url, opts) {
|
||||
}
|
||||
}
|
||||
|
||||
export function removePrefix (url, prefix) {
|
||||
if (url.startsWith('/files')) {
|
||||
url = url.slice(6)
|
||||
} else if (prefix) {
|
||||
url = url.replace(prefix, '')
|
||||
}
|
||||
export function removePrefix (url) {
|
||||
url = url.split('/').splice(2).join('/')
|
||||
|
||||
if (url === '') url = '/'
|
||||
if (url[0] !== '/') url = '/' + url
|
||||
|
@ -66,7 +66,7 @@ export default {
|
||||
return this.readOnly == undefined && this.user.perm.rename
|
||||
},
|
||||
canDrop () {
|
||||
if (!this.isDir || this.readOnly == undefined) return false
|
||||
if (!this.isDir || this.readOnly !== undefined) return false
|
||||
|
||||
for (let i of this.selected) {
|
||||
if (this.req.items[i].url === this.url) {
|
||||
@ -78,7 +78,11 @@ export default {
|
||||
},
|
||||
thumbnailUrl () {
|
||||
const path = this.url.replace(/^\/files\//, '')
|
||||
return `${baseURL}/api/preview/thumb/${path}?auth=${this.jwt}&inline=true`
|
||||
|
||||
// reload the image when the file is replaced
|
||||
const key = Date.parse(this.modified)
|
||||
|
||||
return `${baseURL}/api/preview/thumb/${path}?auth=${this.jwt}&inline=true&k=${key}`
|
||||
},
|
||||
isThumbsEnabled () {
|
||||
return enableThumbs
|
||||
|
@ -63,7 +63,7 @@ export default {
|
||||
return moment(this.req.modified).fromNow()
|
||||
}
|
||||
|
||||
return moment(this.req.items[this.selected[0]]).fromNow()
|
||||
return moment(this.req.items[this.selected[0]].modified).fromNow()
|
||||
},
|
||||
name: function () {
|
||||
return this.selectedCount === 0 ? this.req.name : this.req.items[this.selected[0]].name
|
||||
|
@ -123,15 +123,21 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#previewer .action i {
|
||||
#previewer header > .action i {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#previewer .action:hover {
|
||||
@media (min-width: 738px) {
|
||||
#previewer header #dropdown .action i {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
#previewer header .action:hover {
|
||||
background-color: rgba(255, 255, 255, 0.3)
|
||||
}
|
||||
|
||||
#previewer .action span {
|
||||
#previewer header .action span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,10 @@ function loading (button) {
|
||||
return
|
||||
}
|
||||
|
||||
if (el.innerHTML == 'autorenew' || el.innerHTML == 'done') {
|
||||
return
|
||||
}
|
||||
|
||||
el.dataset.icon = el.innerHTML
|
||||
el.style.opacity = 0
|
||||
|
||||
|
@ -31,7 +31,7 @@
|
||||
<a target="_blank" :href="link" class="button button--flat">{{ $t('buttons.download') }}</a>
|
||||
</div>
|
||||
<div class="share__box__element share__box__center">
|
||||
<qrcode-vue :value="fullLink" size="200" level="M"></qrcode-vue>
|
||||
<qrcode-vue :value="link" size="200" level="M"></qrcode-vue>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="req.isDir && req.items.length > 0" class="share__box share__box__items">
|
||||
@ -122,7 +122,6 @@ export default {
|
||||
},
|
||||
data: () => ({
|
||||
error: null,
|
||||
path: '',
|
||||
showLimit: 500,
|
||||
password: '',
|
||||
attemptedPasswordLogin: false,
|
||||
@ -158,10 +157,9 @@ export default {
|
||||
if (this.token !== ''){
|
||||
queryArg = `?token=${this.token}`
|
||||
}
|
||||
return `${baseURL}/api/public/dl/${this.hash}${this.path}${queryArg}`
|
||||
},
|
||||
fullLink: function () {
|
||||
return window.location.origin + this.link
|
||||
|
||||
const path = this.$route.path.split('/').splice(2).join('/')
|
||||
return `${baseURL}/api/public/dl/${path}${queryArg}`
|
||||
},
|
||||
humanSize: function () {
|
||||
if (this.req.isDir) {
|
||||
@ -193,20 +191,19 @@ export default {
|
||||
this.setLoading(true)
|
||||
this.error = null
|
||||
|
||||
if (this.password !== ''){
|
||||
this.attemptedPasswordLogin = true
|
||||
}
|
||||
|
||||
let url = this.$route.path
|
||||
if (url === '') url = '/'
|
||||
if (url[0] !== '/') url = '/' + url
|
||||
|
||||
try {
|
||||
if (this.password !== ''){
|
||||
this.attemptedPasswordLogin = true
|
||||
}
|
||||
let file = await api.fetch(encodeURIComponent(this.$route.params.pathMatch), this.password)
|
||||
this.path = file.path
|
||||
if (this.path.endsWith('/')) this.path = this.path.slice(0, -1)
|
||||
let file = await api.fetch(url, this.password)
|
||||
|
||||
this.token = file.token || ''
|
||||
if (file.isDir) file.items = file.items.map((item, index) => {
|
||||
item.index = index
|
||||
item.url = `/share/${this.hash}${this.path}/${encodeURIComponent(item.name)}`
|
||||
return item
|
||||
})
|
||||
|
||||
this.updateRequest(file)
|
||||
this.setLoading(false)
|
||||
} catch (e) {
|
||||
@ -228,7 +225,7 @@ export default {
|
||||
},
|
||||
download () {
|
||||
if (this.selectedCount === 1 && !this.req.items[this.selected[0]].isDir) {
|
||||
api.download(null, this.hash, this.token, this.req.items[this.selected[0]].url)
|
||||
api.download(null, this.hash, this.token, this.req.items[this.selected[0]].path)
|
||||
return
|
||||
}
|
||||
|
||||
@ -240,7 +237,7 @@ export default {
|
||||
let files = []
|
||||
|
||||
for (let i of this.selected) {
|
||||
files.push(this.req.items[i].url)
|
||||
files.push(this.req.items[i].path)
|
||||
}
|
||||
|
||||
api.download(format, this.hash, this.token, ...files)
|
||||
|
@ -588,8 +588,12 @@ export default {
|
||||
|
||||
let files = []
|
||||
|
||||
for (let i of this.selected) {
|
||||
files.push(this.req.items[i].url)
|
||||
if (this.selectedCount > 0) {
|
||||
for (let i of this.selected) {
|
||||
files.push(this.req.items[i].url)
|
||||
}
|
||||
} else {
|
||||
files.push(this.$route.path)
|
||||
}
|
||||
|
||||
api.download(format, ...files)
|
||||
|
@ -102,10 +102,13 @@ export default {
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${this.jwt}`
|
||||
},
|
||||
previewUrl () {
|
||||
// reload the image when the file is replaced
|
||||
const key = Date.parse(this.req.modified)
|
||||
|
||||
if (this.req.type === 'image' && !this.fullSize) {
|
||||
return `${baseURL}/api/preview/big${url.encodePath(this.req.path)}?auth=${this.jwt}`
|
||||
return `${baseURL}/api/preview/big${url.encodePath(this.req.path)}?auth=${this.jwt}&k=${key}`
|
||||
}
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${this.jwt}`
|
||||
return `${baseURL}/api/raw${url.encodePath(this.req.path)}?auth=${this.jwt}&k=${key}`
|
||||
},
|
||||
raw () {
|
||||
return `${this.previewUrl}&inline=true`
|
||||
|
@ -54,8 +54,8 @@ func NewHandler(
|
||||
|
||||
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
|
||||
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(resourcePostHandler(fileCache), "/api/resources")).Methods("POST")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePutHandler, "/api/resources")).Methods("PUT")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePatchHandler, "/api/resources")).Methods("PATCH")
|
||||
|
||||
api.Path("/shares").Handler(monkey(shareListHandler, "/api/shares")).Methods("GET")
|
||||
|
@ -79,7 +79,7 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
cacheKey := previewCacheKey(file.Path, previewSize)
|
||||
cacheKey := previewCacheKey(file.Path, file.ModTime.Unix(), previewSize)
|
||||
cachedFile, ok, err := fileCache.Load(r.Context(), cacheKey)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
@ -133,6 +133,6 @@ func handleImagePreview(w http.ResponseWriter, r *http.Request, imgSvc ImgServic
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func previewCacheKey(fPath string, previewSize PreviewSize) string {
|
||||
return fPath + previewSize.String()
|
||||
func previewCacheKey(fPath string, fTime int64, previewSize PreviewSize) string {
|
||||
return fmt.Sprintf("%x%x%x", fPath, fTime, previewSize)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
|
||||
var withHashFile = func(fn handleFunc) handleFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
id, path := ifPathWithName(r)
|
||||
id, ifPath := ifPathWithName(r)
|
||||
link, err := d.store.Share.GetByHash(id)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
@ -47,21 +47,30 @@ var withHashFile = func(fn handleFunc) handleFunc {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if file.IsDir {
|
||||
// set fs root to the shared folder
|
||||
d.user.Fs = afero.NewBasePathFs(d.user.Fs, filepath.Dir(link.Path))
|
||||
// share base path
|
||||
basePath := link.Path
|
||||
|
||||
file, err = files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Checker: d,
|
||||
Token: link.Token,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
// file relative path
|
||||
filePath := ""
|
||||
|
||||
if file.IsDir {
|
||||
basePath = filepath.Dir(basePath)
|
||||
filePath = ifPath
|
||||
}
|
||||
|
||||
// set fs root to the shared file/folder
|
||||
d.user.Fs = afero.NewBasePathFs(d.user.Fs, basePath)
|
||||
|
||||
file, err = files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: filePath,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
Checker: d,
|
||||
Token: link.Token,
|
||||
})
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
d.raw = file
|
||||
|
25
http/raw.go
25
http/raw.go
@ -108,8 +108,6 @@ var rawHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data)
|
||||
})
|
||||
|
||||
func addFile(ar archiver.Writer, d *data, path, commonPath string) error {
|
||||
// Checks are always done with paths with "/" as path separator.
|
||||
path = strings.Replace(path, "\\", "/", -1)
|
||||
if !d.Check(path) {
|
||||
return nil
|
||||
}
|
||||
@ -134,7 +132,7 @@ func addFile(ar archiver.Writer, d *data, path, commonPath string) error {
|
||||
|
||||
if path != commonPath {
|
||||
filename := strings.TrimPrefix(path, commonPath)
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
filename = strings.TrimPrefix(filename, string(filepath.Separator))
|
||||
err = ar.Write(archiver.File{
|
||||
FileInfo: archiver.FileInfo{
|
||||
FileInfo: info,
|
||||
@ -175,20 +173,25 @@ func rawDirHandler(w http.ResponseWriter, r *http.Request, d *data, file *files.
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
name := file.Name
|
||||
if name == "." || name == "" {
|
||||
name = "archive"
|
||||
}
|
||||
name += extension
|
||||
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
|
||||
|
||||
err = ar.Create(w)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer ar.Close()
|
||||
|
||||
commonDir := fileutils.CommonPrefix('/', filenames...)
|
||||
commonDir := fileutils.CommonPrefix(filepath.Separator, filenames...)
|
||||
|
||||
var name string
|
||||
if len(filenames) > 1 {
|
||||
name = "_" + filepath.Base(commonDir)
|
||||
} else {
|
||||
name = file.Name
|
||||
}
|
||||
if name == "." || name == "" {
|
||||
name = "archive"
|
||||
}
|
||||
name += extension
|
||||
w.Header().Set("Content-Disposition", "attachment; filename*=utf-8''"+url.PathEscape(name))
|
||||
|
||||
for _, fname := range filenames {
|
||||
err = addFile(ar, d, fname, commonDir)
|
||||
|
152
http/resource.go
152
http/resource.go
@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -71,11 +72,9 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc {
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
err = delThumbs(r.Context(), fileCache, file)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
err = d.RunHook(func() error {
|
||||
@ -90,12 +89,59 @@ func resourceDeleteHandler(fileCache FileCache) handleFunc {
|
||||
})
|
||||
}
|
||||
|
||||
var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.user.Perm.Create && r.Method == http.MethodPost {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
func resourcePostHandler(fileCache FileCache) handleFunc {
|
||||
return withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.user.Perm.Create || !d.Check(r.URL.Path) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
if !d.user.Perm.Modify && r.Method == http.MethodPut {
|
||||
defer func() {
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
}()
|
||||
|
||||
// Directories creation on POST.
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
err := d.user.Fs.MkdirAll(r.URL.Path, 0775)
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: true,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
})
|
||||
if err == nil {
|
||||
if r.URL.Query().Get("override") != "true" {
|
||||
return http.StatusConflict, nil
|
||||
}
|
||||
|
||||
err = delThumbs(r.Context(), fileCache, file)
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
}
|
||||
|
||||
err = d.RunHook(func() error {
|
||||
info, _ := writeFile(d.user.Fs, r.URL.Path, r.Body)
|
||||
|
||||
etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
|
||||
w.Header().Set("ETag", etag)
|
||||
return nil
|
||||
}, "upload", r.URL.Path, "", d.user)
|
||||
|
||||
if err != nil {
|
||||
_ = d.user.Fs.RemoveAll(r.URL.Path)
|
||||
}
|
||||
|
||||
return errToStatus(err), err
|
||||
})
|
||||
}
|
||||
|
||||
var resourcePutHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if !d.user.Perm.Modify || !d.Check(r.URL.Path) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
@ -103,55 +149,18 @@ var resourcePostPutHandler = withUser(func(w http.ResponseWriter, r *http.Reques
|
||||
_, _ = io.Copy(ioutil.Discard, r.Body)
|
||||
}()
|
||||
|
||||
// For directories, only allow POST for creation.
|
||||
// Only allow PUT for files.
|
||||
if strings.HasSuffix(r.URL.Path, "/") {
|
||||
if r.Method == http.MethodPut {
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
|
||||
err := d.user.Fs.MkdirAll(r.URL.Path, 0775)
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
if r.Method == http.MethodPost && r.URL.Query().Get("override") != "true" {
|
||||
if _, err := d.user.Fs.Stat(r.URL.Path); err == nil {
|
||||
return http.StatusConflict, nil
|
||||
}
|
||||
}
|
||||
|
||||
action := "upload"
|
||||
if r.Method == http.MethodPut {
|
||||
action = "save"
|
||||
return http.StatusMethodNotAllowed, nil
|
||||
}
|
||||
|
||||
err := d.RunHook(func() error {
|
||||
dir, _ := path.Split(r.URL.Path)
|
||||
err := d.user.Fs.MkdirAll(dir, 0775)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := d.user.Fs.OpenFile(r.URL.Path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(file, r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Gets the info about the file.
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, _ := writeFile(d.user.Fs, r.URL.Path, r.Body)
|
||||
|
||||
etag := fmt.Sprintf(`"%x%x"`, info.ModTime().UnixNano(), info.Size())
|
||||
w.Header().Set("ETag", etag)
|
||||
return nil
|
||||
}, action, r.URL.Path, "", d.user)
|
||||
}, "save", r.URL.Path, "", d.user)
|
||||
|
||||
if err != nil {
|
||||
_ = d.user.Fs.RemoveAll(r.URL.Path)
|
||||
@ -165,6 +174,9 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
|
||||
dst := r.URL.Query().Get("destination")
|
||||
action := r.URL.Query().Get("action")
|
||||
dst, err := url.QueryUnescape(dst)
|
||||
if !d.Check(src) || !d.Check(dst) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
@ -242,3 +254,41 @@ func addVersionSuffix(source string, fs afero.Fs) string {
|
||||
|
||||
return source
|
||||
}
|
||||
|
||||
func writeFile(fs afero.Fs, dst string, in io.Reader) (os.FileInfo, error) {
|
||||
dir, _ := path.Split(dst)
|
||||
err := fs.MkdirAll(dir, 0775)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := fs.OpenFile(dst, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(file, in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Gets the info about the file.
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func delThumbs(ctx context.Context, fileCache FileCache, file *files.FileInfo) error {
|
||||
for _, previewSizeName := range PreviewSizeNames() {
|
||||
size, _ := ParsePreviewSize(previewSizeName)
|
||||
if err := fileCache.Delete(ctx, previewCacheKey(file.Path, file.ModTime.Unix(), size)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user