diff --git a/file/manager.go b/file/manager.go index d632d09..a707d2a 100644 --- a/file/manager.go +++ b/file/manager.go @@ -19,6 +19,12 @@ type File struct { Extension string `json:"extension"` } +type Directory struct { + Id string `json:"id"` + Name string `json:"name"` + Path string `json:"path"` +} + type Tree struct { Id string `json:"id"` Name string `json:"name"` @@ -39,7 +45,7 @@ func getFileId(path string) string { func GetTree(name, path string) Tree { tree := Tree{ - Id: getFileId(name), + Id: getFileId(path), Name: name, Directories: []Tree{}, Path: path, @@ -103,3 +109,27 @@ func GetFiles(files *[]File, path string) *[]File { 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 +} diff --git a/frontend/js/views/FilesView.vue b/frontend/js/views/FilesView.vue index 019ee9f..d64cb5f 100644 --- a/frontend/js/views/FilesView.vue +++ b/frontend/js/views/FilesView.vue @@ -2,7 +2,10 @@

Fichiers

- Ajouter +
+ + fichier + + Dossier +
@@ -11,14 +14,26 @@ ..
-
- - - - - {{ item.name }} +
+
+ + Supprimer + +
+
+ + + + {{ item.name }} +
+
+ + Télécharger + Supprimer + +
@@ -95,8 +110,11 @@ import { BAlert, BFormSelect, BFormFile, + BDropdown, + BDropdownItem, } from 'bootstrap-vue-next' +let formFile = false const tree = ref(null) const parent = ref(null) const fileId = ref(null) @@ -127,14 +145,15 @@ const isEditable = (item) => { ].includes(item.type) } -const doAdd = () => { +const doAddFile = () => { const data = {file: null} + formFile = true form.value = { - action: `/api/file`, + action: `/api/filemanager/file`, method: 'POST', data: data, - label: 'Importer', + label: 'Nouveau fichier', error: null, fields: [ { @@ -150,41 +169,103 @@ const doAdd = () => { 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) => { e.preventDefault() - const payload = new FormData(); - payload.append('file', form.value.data.file) - payload.append('path', tree.value.id) + if (formFile) { + const payload = new FormData(); + payload.append('file', form.value.data.file) + payload.append('path', tree.value.id) - fetch(`/api/file`, { - method: form.value.method, - headers: { - 'Accept': 'application/json', - }, - body: payload, - }) - .then((response) => { - return response.json() + fetch(`/api/filemanager/file`, { + method: form.value.method, + headers: { + 'Accept': 'application/json', + }, + body: payload, }) - .then((data) => { - if (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}` + .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}` + }) + } 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) => { options = options || {} - fetch('/api/files') + fetch('/api/filemanager/files') .then((response) => response.json()) .then((data) => { tree.value = withParents(data) diff --git a/web/controller/file/controller.go b/web/controller/file/controller.go index afa6685..f48f50b 100644 --- a/web/controller/file/controller.go +++ b/web/controller/file/controller.go @@ -2,8 +2,10 @@ package file import ( "bytes" + "fmt" "io" "os" + "strings" "github.com/labstack/echo/v4" "gitnet.fr/deblan/budget/config" @@ -18,13 +20,16 @@ type Controller struct { func New(e *echo.Echo) *Controller { c := Controller{} - e.GET("/api/files", c.Files) - e.POST("/api/file", c.File) + e.GET("/api/filemanager/files", c.List) + 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 } -func (ctrl *Controller) Files(c echo.Context) error { +func (ctrl *Controller) List(c echo.Context) error { if nil == model.LoadSessionUser(c) { return c.Redirect(302, "/login") } @@ -34,7 +39,7 @@ func (ctrl *Controller) Files(c echo.Context) error { 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) { return c.Redirect(302, "/login") } @@ -70,6 +75,116 @@ func (ctrl *Controller) File(c echo.Context) error { 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 { if tree.Id == id { return &tree