allow to delete/create file and directory
This commit is contained in:
parent
997df7cf7c
commit
8305c01dee
3 changed files with 265 additions and 39 deletions
|
|
@ -19,6 +19,12 @@ type File struct {
|
||||||
Extension string `json:"extension"`
|
Extension string `json:"extension"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Directory struct {
|
||||||
|
Id string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
type Tree struct {
|
type Tree struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -39,7 +45,7 @@ func getFileId(path string) string {
|
||||||
|
|
||||||
func GetTree(name, path string) Tree {
|
func GetTree(name, path string) Tree {
|
||||||
tree := Tree{
|
tree := Tree{
|
||||||
Id: getFileId(name),
|
Id: getFileId(path),
|
||||||
Name: name,
|
Name: name,
|
||||||
Directories: []Tree{},
|
Directories: []Tree{},
|
||||||
Path: path,
|
Path: path,
|
||||||
|
|
@ -103,3 +109,27 @@ func GetFiles(files *[]File, path string) *[]File {
|
||||||
|
|
||||||
return files
|
return files
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetDirectories(files *[]Directory, path string) *[]Directory {
|
||||||
|
entries, err := os.ReadDir(path)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range entries {
|
||||||
|
p := path + "/" + e.Name()
|
||||||
|
|
||||||
|
if e.IsDir() {
|
||||||
|
*files = append(*files, Directory{
|
||||||
|
Id: getFileId(p),
|
||||||
|
Name: e.Name(),
|
||||||
|
Path: p,
|
||||||
|
})
|
||||||
|
|
||||||
|
GetDirectories(files, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,10 @@
|
||||||
<div v-if="tree !== null && fileId === null" class="w-100">
|
<div v-if="tree !== null && fileId === null" class="w-100">
|
||||||
<div class="d-block d-md-flex justify-content-between p-3">
|
<div class="d-block d-md-flex justify-content-between p-3">
|
||||||
<h3 class="mb-0">Fichiers</h3>
|
<h3 class="mb-0">Fichiers</h3>
|
||||||
<BButton variant="primary" @click="doAdd">Ajouter</BButton>
|
<div class="d-flex justify-content-between gap-2">
|
||||||
|
<BButton variant="primary" @click="doAddFile">+ fichier</BButton>
|
||||||
|
<BButton variant="primary" @click="doAddDirectory">+ Dossier</BButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!tree.is_root" class="p-3 border-bottom cursor" @click="cd(tree.parent)">
|
<div v-if="!tree.is_root" class="p-3 border-bottom cursor" @click="cd(tree.parent)">
|
||||||
|
|
@ -11,14 +14,26 @@
|
||||||
</span>
|
</span>
|
||||||
..
|
..
|
||||||
</div>
|
</div>
|
||||||
<div v-for="item in tree.directories" class="p-3 border-bottom cursor" @click="cd(item)">
|
<div v-for="item in tree.directories" class="p-3 border-bottom flex justify-content-center">
|
||||||
<span class="text-warning">
|
<div class="float-end">
|
||||||
<i class="fa-solid fa-folder me-2"></i>
|
<BDropdown text="" size="sm" variant="outline-primary">
|
||||||
</span>
|
<BDropdownItem @click="doDelete(item)">Supprimer</BDropdownItem>
|
||||||
|
</BDropdown>
|
||||||
{{ item.name }}
|
</div>
|
||||||
|
<div class="cursor" @click="cd(item)">
|
||||||
|
<span class="text-warning cursor" >
|
||||||
|
<i class="fa-solid fa-folder me-2"></i>
|
||||||
|
</span>
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="item in tree.files">
|
<div v-for="item in tree.files">
|
||||||
|
<div class="float-end pt-3 me-3">
|
||||||
|
<BDropdown text="" size="sm" variant="outline-primary">
|
||||||
|
<BDropdownItem :href="`/api/filemanager/file/${item.id}/${item.name}`" target="_blank">Télécharger</BDropdownItem>
|
||||||
|
<BDropdownItem @click="doDelete(item)">Supprimer</BDropdownItem>
|
||||||
|
</BDropdown>
|
||||||
|
</div>
|
||||||
<div v-if="isEditable(item)" class="p-3 border-bottom cursor" @click="doEdit(item)">
|
<div v-if="isEditable(item)" class="p-3 border-bottom cursor" @click="doEdit(item)">
|
||||||
<span class="text-success">
|
<span class="text-success">
|
||||||
<i class="fa-solid fa-file-lines"></i>
|
<i class="fa-solid fa-file-lines"></i>
|
||||||
|
|
@ -95,8 +110,11 @@ import {
|
||||||
BAlert,
|
BAlert,
|
||||||
BFormSelect,
|
BFormSelect,
|
||||||
BFormFile,
|
BFormFile,
|
||||||
|
BDropdown,
|
||||||
|
BDropdownItem,
|
||||||
} from 'bootstrap-vue-next'
|
} from 'bootstrap-vue-next'
|
||||||
|
|
||||||
|
let formFile = false
|
||||||
const tree = ref(null)
|
const tree = ref(null)
|
||||||
const parent = ref(null)
|
const parent = ref(null)
|
||||||
const fileId = ref(null)
|
const fileId = ref(null)
|
||||||
|
|
@ -127,14 +145,15 @@ const isEditable = (item) => {
|
||||||
].includes(item.type)
|
].includes(item.type)
|
||||||
}
|
}
|
||||||
|
|
||||||
const doAdd = () => {
|
const doAddFile = () => {
|
||||||
const data = {file: null}
|
const data = {file: null}
|
||||||
|
formFile = true
|
||||||
|
|
||||||
form.value = {
|
form.value = {
|
||||||
action: `/api/file`,
|
action: `/api/filemanager/file`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: data,
|
data: data,
|
||||||
label: 'Importer',
|
label: 'Nouveau fichier',
|
||||||
error: null,
|
error: null,
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
|
@ -150,41 +169,103 @@ const doAdd = () => {
|
||||||
formShow.value = true
|
formShow.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const doAddDirectory = () => {
|
||||||
|
const data = {directory: null, path: tree.value.id}
|
||||||
|
formFile = false
|
||||||
|
|
||||||
|
form.value = {
|
||||||
|
action: `/api/filemanager/directory`,
|
||||||
|
method: 'POST',
|
||||||
|
data: data,
|
||||||
|
label: 'Nouveau dossier',
|
||||||
|
error: null,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: 'Nom',
|
||||||
|
widget: 'text',
|
||||||
|
required: true,
|
||||||
|
key: 'directory',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
formShow.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const doDelete = (item) => {
|
||||||
|
if (!confirm('Je confirme la suppression')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`/api/filemanager/file/${item.id}`, {
|
||||||
|
method: 'DELETE',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
refresh({current: tree.value.id})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const doSave = (e) => {
|
const doSave = (e) => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const payload = new FormData();
|
if (formFile) {
|
||||||
payload.append('file', form.value.data.file)
|
const payload = new FormData();
|
||||||
payload.append('path', tree.value.id)
|
payload.append('file', form.value.data.file)
|
||||||
|
payload.append('path', tree.value.id)
|
||||||
|
|
||||||
fetch(`/api/file`, {
|
fetch(`/api/filemanager/file`, {
|
||||||
method: form.value.method,
|
method: form.value.method,
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
},
|
},
|
||||||
body: payload,
|
body: payload,
|
||||||
})
|
|
||||||
.then((response) => {
|
|
||||||
return response.json()
|
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((response) => {
|
||||||
if (data.code === 400) {
|
return response.json()
|
||||||
form.value.error = data.message
|
})
|
||||||
} else {
|
.then((data) => {
|
||||||
form.value = null
|
if (data && data.code === 400) {
|
||||||
formShow.value = false
|
form.value.error = data.message
|
||||||
refresh({current: tree.value.id})
|
} else {
|
||||||
}
|
form.value = null
|
||||||
})
|
formShow.value = false
|
||||||
.catch((err) => {
|
refresh({current: tree.value.id})
|
||||||
form.value.error = `Une erreur s'est produite : ${err}`
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
form.value.error = `Une erreur s'est produite : ${err}`
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
fetch(`/api/filemanager/directory`, {
|
||||||
|
method: form.value.method,
|
||||||
|
headers: {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(form.value.data),
|
||||||
})
|
})
|
||||||
|
.then((response) => {
|
||||||
|
return response.json()
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (data && data.code === 400) {
|
||||||
|
form.value.error = data.message
|
||||||
|
} else {
|
||||||
|
form.value = null
|
||||||
|
formShow.value = false
|
||||||
|
refresh({current: tree.value.id})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
form.value.error = `Une erreur s'est produite : ${err}`
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const refresh = (options) => {
|
const refresh = (options) => {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
|
|
||||||
fetch('/api/files')
|
fetch('/api/filemanager/files')
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
tree.value = withParents(data)
|
tree.value = withParents(data)
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ package file
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"gitnet.fr/deblan/budget/config"
|
"gitnet.fr/deblan/budget/config"
|
||||||
|
|
@ -18,13 +20,16 @@ type Controller struct {
|
||||||
func New(e *echo.Echo) *Controller {
|
func New(e *echo.Echo) *Controller {
|
||||||
c := Controller{}
|
c := Controller{}
|
||||||
|
|
||||||
e.GET("/api/files", c.Files)
|
e.GET("/api/filemanager/files", c.List)
|
||||||
e.POST("/api/file", c.File)
|
e.POST("/api/filemanager/file", c.CreateFile)
|
||||||
|
e.POST("/api/filemanager/directory", c.CreateDirectory)
|
||||||
|
e.GET("/api/filemanager/file/:id/:name", c.Download)
|
||||||
|
e.DELETE("/api/filemanager/file/:id", c.Delete)
|
||||||
|
|
||||||
return &c
|
return &c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctrl *Controller) Files(c echo.Context) error {
|
func (ctrl *Controller) List(c echo.Context) error {
|
||||||
if nil == model.LoadSessionUser(c) {
|
if nil == model.LoadSessionUser(c) {
|
||||||
return c.Redirect(302, "/login")
|
return c.Redirect(302, "/login")
|
||||||
}
|
}
|
||||||
|
|
@ -34,7 +39,7 @@ func (ctrl *Controller) Files(c echo.Context) error {
|
||||||
return c.JSON(200, tree)
|
return c.JSON(200, tree)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctrl *Controller) File(c echo.Context) error {
|
func (ctrl *Controller) CreateFile(c echo.Context) error {
|
||||||
if nil == model.LoadSessionUser(c) {
|
if nil == model.LoadSessionUser(c) {
|
||||||
return c.Redirect(302, "/login")
|
return c.Redirect(302, "/login")
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +75,116 @@ func (ctrl *Controller) File(c echo.Context) error {
|
||||||
return c.JSON(400, false)
|
return c.JSON(400, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctrl *Controller) CreateDirectory(c echo.Context) error {
|
||||||
|
if nil == model.LoadSessionUser(c) {
|
||||||
|
return c.Redirect(302, "/login")
|
||||||
|
}
|
||||||
|
|
||||||
|
type body struct {
|
||||||
|
Directory string `json:"directory" validate:"required"`
|
||||||
|
Path string `json:"path" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
value := new(body)
|
||||||
|
|
||||||
|
if err := c.Bind(value); err != nil {
|
||||||
|
return c.JSON(400, crud.Error{
|
||||||
|
Code: 400,
|
||||||
|
Message: "Bad request",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v\n", value)
|
||||||
|
|
||||||
|
value.Directory = strings.ReplaceAll(value.Directory, "..", "")
|
||||||
|
value.Directory = strings.ReplaceAll(value.Directory, "/", "")
|
||||||
|
value.Directory = strings.ReplaceAll(value.Directory, "\\", "")
|
||||||
|
value.Directory = strings.TrimSpace(value.Directory)
|
||||||
|
|
||||||
|
fmt.Printf("%+v\n", value)
|
||||||
|
|
||||||
|
if err := c.Validate(value); err != nil {
|
||||||
|
return c.JSON(400, crud.Error{
|
||||||
|
Code: 400,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
output := searchDirectory(value.Path, f.GetTree("", config.Get().File.Path))
|
||||||
|
|
||||||
|
if output != nil {
|
||||||
|
dir := strings.ReplaceAll(output.Path+"/"+value.Directory, "//", "/")
|
||||||
|
os.MkdirAll(dir, 0750)
|
||||||
|
return c.JSON(200, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(400, crud.Error{
|
||||||
|
Code: 400,
|
||||||
|
Message: "Bad request",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *Controller) Download(c echo.Context) error {
|
||||||
|
if nil == model.LoadSessionUser(c) {
|
||||||
|
return c.Redirect(302, "/login")
|
||||||
|
}
|
||||||
|
|
||||||
|
var file string
|
||||||
|
var contentType string
|
||||||
|
|
||||||
|
files := []f.File{}
|
||||||
|
f.GetFiles(&files, config.Get().File.Path)
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if f.Id == c.Param("id") {
|
||||||
|
file = f.Path
|
||||||
|
contentType = f.Type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if file == "" {
|
||||||
|
return c.JSON(404, "File not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
content, _ := os.ReadFile(file)
|
||||||
|
|
||||||
|
return c.Blob(200, contentType, content)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctrl *Controller) Delete(c echo.Context) error {
|
||||||
|
if nil == model.LoadSessionUser(c) {
|
||||||
|
return c.Redirect(302, "/login")
|
||||||
|
}
|
||||||
|
|
||||||
|
var file string
|
||||||
|
|
||||||
|
files := []f.File{}
|
||||||
|
directories := []f.Directory{}
|
||||||
|
|
||||||
|
f.GetFiles(&files, config.Get().File.Path)
|
||||||
|
f.GetDirectories(&directories, config.Get().File.Path)
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if f.Id == c.Param("id") {
|
||||||
|
file = f.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range directories {
|
||||||
|
if f.Id == c.Param("id") {
|
||||||
|
file = f.Path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if file == "" {
|
||||||
|
return c.JSON(404, "File not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
os.RemoveAll(file)
|
||||||
|
|
||||||
|
return c.JSON(200, nil)
|
||||||
|
}
|
||||||
|
|
||||||
func searchDirectory(id string, tree f.Tree) *f.Tree {
|
func searchDirectory(id string, tree f.Tree) *f.Tree {
|
||||||
if tree.Id == id {
|
if tree.Id == id {
|
||||||
return &tree
|
return &tree
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue