Merge pull request #1042 from ramiresviana/fixes-2
commit
14e2f84ceb
|
@ -16,4 +16,5 @@ var (
|
||||||
ErrInvalidAuthMethod = errors.New("invalid auth method")
|
ErrInvalidAuthMethod = errors.New("invalid auth method")
|
||||||
ErrPermissionDenied = errors.New("permission denied")
|
ErrPermissionDenied = errors.New("permission denied")
|
||||||
ErrInvalidRequestParams = errors.New("invalid request params")
|
ErrInvalidRequestParams = errors.New("invalid request params")
|
||||||
|
ErrSourceIsParent = errors.New("source is parent")
|
||||||
)
|
)
|
||||||
|
|
|
@ -43,7 +43,7 @@ async function resourceAction (url, method, content) {
|
||||||
const res = await fetchURL(`/api/resources${url}`, opts)
|
const res = await fetchURL(`/api/resources${url}`, opts)
|
||||||
|
|
||||||
if (res.status !== 200) {
|
if (res.status !== 200) {
|
||||||
throw new Error(res.responseText)
|
throw new Error(await res.text())
|
||||||
} else {
|
} else {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
<input style="display:none" type="file" id="upload-folder-input" @change="uploadInput($event)" webkitdirectory multiple>
|
<input style="display:none" type="file" id="upload-folder-input" @change="uploadInput($event)" webkitdirectory multiple>
|
||||||
</div>
|
</div>
|
||||||
<div v-else id="listing"
|
<div v-else id="listing"
|
||||||
:class="user.viewMode"
|
:class="user.viewMode">
|
||||||
@dragenter="dragEnter"
|
|
||||||
@dragend="dragEnd">
|
|
||||||
<div>
|
<div>
|
||||||
<div class="item header">
|
<div class="item header">
|
||||||
<div></div>
|
<div></div>
|
||||||
|
@ -99,7 +97,8 @@ export default {
|
||||||
components: { Item },
|
components: { Item },
|
||||||
data: function () {
|
data: function () {
|
||||||
return {
|
return {
|
||||||
showLimit: 50
|
showLimit: 50,
|
||||||
|
dragCounter: 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -171,6 +170,8 @@ export default {
|
||||||
window.addEventListener('resize', this.resizeEvent)
|
window.addEventListener('resize', this.resizeEvent)
|
||||||
window.addEventListener('scroll', this.scrollEvent)
|
window.addEventListener('scroll', this.scrollEvent)
|
||||||
document.addEventListener('dragover', this.preventDefault)
|
document.addEventListener('dragover', this.preventDefault)
|
||||||
|
document.addEventListener('dragenter', this.dragEnter)
|
||||||
|
document.addEventListener('dragleave', this.dragLeave)
|
||||||
document.addEventListener('drop', this.drop)
|
document.addEventListener('drop', this.drop)
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
|
@ -179,6 +180,8 @@ export default {
|
||||||
window.removeEventListener('resize', this.resizeEvent)
|
window.removeEventListener('resize', this.resizeEvent)
|
||||||
window.removeEventListener('scroll', this.scrollEvent)
|
window.removeEventListener('scroll', this.scrollEvent)
|
||||||
document.removeEventListener('dragover', this.preventDefault)
|
document.removeEventListener('dragover', this.preventDefault)
|
||||||
|
document.removeEventListener('dragenter', this.dragEnter)
|
||||||
|
document.removeEventListener('dragleave', this.dragLeave)
|
||||||
document.removeEventListener('drop', this.drop)
|
document.removeEventListener('drop', this.drop)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -326,6 +329,8 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dragEnter () {
|
dragEnter () {
|
||||||
|
this.dragCounter++
|
||||||
|
|
||||||
// When the user starts dragging an item, put every
|
// When the user starts dragging an item, put every
|
||||||
// file on the listing with 50% opacity.
|
// file on the listing with 50% opacity.
|
||||||
let items = document.getElementsByClassName('item')
|
let items = document.getElementsByClassName('item')
|
||||||
|
@ -334,11 +339,16 @@ export default {
|
||||||
file.style.opacity = 0.5
|
file.style.opacity = 0.5
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
dragEnd () {
|
dragLeave () {
|
||||||
this.resetOpacity()
|
this.dragCounter--
|
||||||
|
|
||||||
|
if (this.dragCounter == 0) {
|
||||||
|
this.resetOpacity()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
drop: async function (event) {
|
drop: async function (event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
this.dragCounter = 0
|
||||||
this.resetOpacity()
|
this.resetOpacity()
|
||||||
|
|
||||||
let dt = event.dataTransfer
|
let dt = event.dataTransfer
|
||||||
|
@ -408,12 +418,13 @@ export default {
|
||||||
confirm: (event) => {
|
confirm: (event) => {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
this.$store.commit('closeHovers')
|
this.$store.commit('closeHovers')
|
||||||
this.handleFiles(files, path, true)
|
upload.handleFiles(files, path, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
upload.handleFiles(files, path)
|
upload.handleFiles(files, path)
|
||||||
},
|
},
|
||||||
resetOpacity () {
|
resetOpacity () {
|
||||||
|
|
|
@ -27,9 +27,11 @@ export default {
|
||||||
name: 'upload',
|
name: 'upload',
|
||||||
methods: {
|
methods: {
|
||||||
uploadFile: function () {
|
uploadFile: function () {
|
||||||
|
document.getElementById('upload-input').value = ''
|
||||||
document.getElementById('upload-input').click()
|
document.getElementById('upload-input').click()
|
||||||
},
|
},
|
||||||
uploadFolder: function () {
|
uploadFolder: function () {
|
||||||
|
document.getElementById('upload-folder-input').value = ''
|
||||||
document.getElementById('upload-folder-input').click()
|
document.getElementById('upload-folder-input').click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/afero"
|
||||||
|
|
||||||
"github.com/filebrowser/filebrowser/v2/errors"
|
"github.com/filebrowser/filebrowser/v2/errors"
|
||||||
"github.com/filebrowser/filebrowser/v2/files"
|
"github.com/filebrowser/filebrowser/v2/files"
|
||||||
"github.com/filebrowser/filebrowser/v2/fileutils"
|
"github.com/filebrowser/filebrowser/v2/fileutils"
|
||||||
|
@ -139,38 +141,25 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
|
||||||
dst := r.URL.Query().Get("destination")
|
dst := r.URL.Query().Get("destination")
|
||||||
action := r.URL.Query().Get("action")
|
action := r.URL.Query().Get("action")
|
||||||
dst, err := url.QueryUnescape(dst)
|
dst, err := url.QueryUnescape(dst)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errToStatus(err), err
|
return errToStatus(err), err
|
||||||
}
|
}
|
||||||
|
|
||||||
if dst == "/" || src == "/" {
|
if dst == "/" || src == "/" {
|
||||||
return http.StatusForbidden, nil
|
return http.StatusForbidden, nil
|
||||||
}
|
}
|
||||||
|
if err = checkParent(src, dst); err != nil {
|
||||||
|
return http.StatusBadRequest, err
|
||||||
|
}
|
||||||
|
|
||||||
override := r.URL.Query().Get("override") == "true"
|
override := r.URL.Query().Get("override") == "true"
|
||||||
rename := r.URL.Query().Get("rename") == "true"
|
rename := r.URL.Query().Get("rename") == "true"
|
||||||
|
|
||||||
if !override && !rename {
|
if !override && !rename {
|
||||||
if _, err = d.user.Fs.Stat(dst); err == nil {
|
if _, err = d.user.Fs.Stat(dst); err == nil {
|
||||||
return http.StatusConflict, nil
|
return http.StatusConflict, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rename {
|
if rename {
|
||||||
counter := 1
|
dst = addVersionSuffix(dst, d.user.Fs)
|
||||||
dir, name := filepath.Split(dst)
|
|
||||||
ext := filepath.Ext(name)
|
|
||||||
base := strings.TrimSuffix(name, ext)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if _, err = d.user.Fs.Stat(dst); err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
new := fmt.Sprintf("%s(%d)%s", base, counter, ext)
|
|
||||||
dst = filepath.Join(dir, new)
|
|
||||||
counter++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = d.RunHook(func() error {
|
err = d.RunHook(func() error {
|
||||||
|
@ -180,11 +169,14 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
|
||||||
if !d.user.Perm.Create {
|
if !d.user.Perm.Create {
|
||||||
return errors.ErrPermissionDenied
|
return errors.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
|
||||||
return fileutils.Copy(d.user.Fs, src, dst)
|
return fileutils.Copy(d.user.Fs, src, dst)
|
||||||
case "rename":
|
case "rename":
|
||||||
if !d.user.Perm.Rename {
|
if !d.user.Perm.Rename {
|
||||||
return errors.ErrPermissionDenied
|
return errors.ErrPermissionDenied
|
||||||
}
|
}
|
||||||
|
dst = filepath.Clean("/" + dst)
|
||||||
|
|
||||||
return d.user.Fs.Rename(src, dst)
|
return d.user.Fs.Rename(src, dst)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
|
return fmt.Errorf("unsupported action %s: %w", action, errors.ErrInvalidRequestParams)
|
||||||
|
@ -193,3 +185,35 @@ var resourcePatchHandler = withUser(func(w http.ResponseWriter, r *http.Request,
|
||||||
|
|
||||||
return errToStatus(err), err
|
return errToStatus(err), err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func checkParent(src, dst string) error {
|
||||||
|
rel, err := filepath.Rel(src, dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rel = filepath.ToSlash(rel)
|
||||||
|
if !strings.HasPrefix(rel, "../") && rel != ".." && rel != "." {
|
||||||
|
return errors.ErrSourceIsParent
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func addVersionSuffix(path string, fs afero.Fs) string {
|
||||||
|
counter := 1
|
||||||
|
dir, name := filepath.Split(path)
|
||||||
|
ext := filepath.Ext(name)
|
||||||
|
base := strings.TrimSuffix(name, ext)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if _, err := fs.Stat(path); err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
renamed := fmt.Sprintf("%s(%d)%s", base, counter, ext)
|
||||||
|
path = filepath.ToSlash(dir) + renamed
|
||||||
|
counter++
|
||||||
|
}
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue