From b530a492ba83df0d63390991494bd8eb09a5e00c Mon Sep 17 00:00:00 2001 From: Derek Stevens Date: Sun, 12 Jun 2022 00:00:38 -0600 Subject: [PATCH] begin working out static file manager actions --- archetype/fileManager.go | 36 ++++++++++++++++++++++++++++++- lfo/middleware.go | 12 +++++++++++ nirvash.go | 38 +++++++++++++++++++++++++++++++-- static/actions.png | Bin 0 -> 612 bytes static/delete.svg | 4 ++-- static/move.svg | 2 +- static/style.css | 19 +++++++++++------ templates/error.html | 19 +++++------------ templates/file_actions.html | 41 ++++++++++++++++++++++++++++++++++++ templates/file_list.html | 13 +++++++++--- 10 files changed, 155 insertions(+), 29 deletions(-) create mode 100644 static/actions.png create mode 100644 templates/file_actions.html diff --git a/archetype/fileManager.go b/archetype/fileManager.go index 69471ce..428bd7e 100644 --- a/archetype/fileManager.go +++ b/archetype/fileManager.go @@ -2,6 +2,7 @@ package archetype import ( "io/ioutil" + "os" "path/filepath" "strings" ) @@ -12,6 +13,13 @@ type SimpleFileManager struct { ShowHidden bool } +type FileData struct { + Error string + Path string + Name string + IsDir bool +} + type FileListing struct { Error string Root string @@ -24,6 +32,7 @@ type FileManager interface { Init(cfg *Config) error // ListTree() FileListing ListSubTree(root string) FileListing + GetFileData(slug string) FileData // AddFile(path string, file multipart.FileHeader) error // MkDir(path string) error // Remove(path string) error @@ -31,7 +40,7 @@ type FileManager interface { } func (self *SimpleFileManager) Init(cfg *Config) error { - self.Root = cfg.StaticRoot + self.Root = filepath.Clean(cfg.StaticRoot) self.ShowHtml = cfg.StaticShowHtml self.ShowHidden = cfg.StaticShowHidden return nil @@ -86,3 +95,28 @@ func (self *SimpleFileManager) ListSubTree(root string) FileListing { return list } + +func (self *SimpleFileManager) GetFileData(slug string) FileData { + fullPath := filepath.Join(self.Root, slug) + fileInfo, err := os.Stat(fullPath) + + if err != nil { + return FileData{ + Error: err.Error(), + } + } + if !strings.HasPrefix(fullPath, self.Root) { + return FileData{ + Error: "You cannot escape!", + } + } + + cleanedSlug := filepath.Clean(slug) + fileBase := filepath.Base(cleanedSlug) + + return FileData{ + Path: filepath.Clean(slug), + Name: fileBase, + IsDir: fileInfo.IsDir(), + } +} diff --git a/lfo/middleware.go b/lfo/middleware.go index accbed5..847a114 100644 --- a/lfo/middleware.go +++ b/lfo/middleware.go @@ -74,3 +74,15 @@ func FormMapToAdapterConfig(next http.Handler, adapter core.Adapter) http.Handle return http.HandlerFunc(handlerFunc) } + +func WithFileData(next http.Handler, fileManager core.FileManager) http.Handler { + handlerFunc := func(w http.ResponseWriter, req *http.Request) { + ctx := req.Context() + fileSlug := ctx.Value("params").(map[string]string)["Slug"] + fileData := fileManager.GetFileData(fileSlug) + *req = *req.WithContext(context.WithValue(req.Context(), "file-data", fileData)) + next.ServeHTTP(w, req) + } + + return http.HandlerFunc(handlerFunc) +} diff --git a/nirvash.go b/nirvash.go index 1a5077b..c8fa483 100644 --- a/nirvash.go +++ b/nirvash.go @@ -1,6 +1,7 @@ package main import ( + "html/template" "net/http" core "nilfm.cc/git/nirvash/archetype" . "nilfm.cc/git/nirvash/lfo" @@ -31,16 +32,19 @@ func main() { fileManager.Init(cfg) pathConcat := filepath.Join + templateRoot := pathConcat(cfg.AssetRoot, "templates") rtr := &router.Router{ StaticPaths: map[string]string{ "/static/": filepath.Join(cfg.AssetRoot, "static"), "/files/": cfg.StaticRoot, }, + Fallback: *template.Must(template.ParseFiles( + pathConcat(templateRoot, "error.html"), + pathConcat(templateRoot, "header.html"), + pathConcat(templateRoot, "footer.html"))), } - templateRoot := pathConcat(cfg.AssetRoot, "templates") - rtr.Get("/login", renderer.Template( pathConcat(templateRoot, "login.html"))) @@ -205,6 +209,12 @@ func main() { udb, "/")) + rtr.Get( + `/static-mgr`, + http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + http.Redirect(w, req, "/static-mgr/", http.StatusSeeOther) + })) + rtr.Get( `/static-mgr/(?P.*)`, Fortify( @@ -219,5 +229,29 @@ func main() { udb, "/login"))) + rtr.Get( + `/file-actions/(?P.*)`, + Fortify( + Protected( + WithFileManager( + WithFileData( + renderer.Template( + pathConcat(templateRoot, "file_actions.html"), + pathConcat(templateRoot, "header.html"), + pathConcat(templateRoot, "footer.html")), + fileManager), + fileManager), + http.MethodGet, + udb, + "/login"))) + // file upload GET contains form for file upload + // file upload POST performs the action of creating/overwriting + // add directory GET contains the form for directory creation + // add directory POST performs the action of creating directory + // delete GET contains the form for confirming deletion + // delete POST performs the action of deleting + // move GET (not required?) + // move-choose POST uses a form to navigate through the file tree + // move-do POST moves the file when finalized in move-choose http.ListenAndServe(":8080", rtr) } diff --git a/static/actions.png b/static/actions.png new file mode 100644 index 0000000000000000000000000000000000000000..16da63e3477b08b4a0157c77aa6b4f83cf89ab29 GIT binary patch literal 612 zcmV-q0-ODbP)EX>4Tx04R}tkv&MmKpe$iQ)@*lf_4yb$WWauh+jBr6^c+H)C#RSm|XfHG-*gu zTpR`0f`cE6RRgW_ z+u*!U9A*VsB|aw}G3kQDk6c$ge&d{XS>TyrGnJep4ik&{CRUo56-U7%KRobO}Dsht4+XW&Y2_)Aq_`jhlZ zLyH^%J=?&=bwgA3fXf}A|H+U|*_He>g_CX>@2HM@dakSAh-}0000xNkl diff --git a/static/move.svg b/static/move.svg index 6c19612..37e55f5 100644 --- a/static/move.svg +++ b/static/move.svg @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/static/style.css b/static/style.css index 860f8ee..374b08e 100644 --- a/static/style.css +++ b/static/style.css @@ -160,6 +160,8 @@ a.new-page-button { padding: 0.2em; text-transform: uppercase; transition: background 1s, color 1s; + display: inline-block; + margin-bottom: 0.2em; } a.new-page-button:hover { @@ -178,7 +180,7 @@ h2 { position: relative; } -.page-list, form.editor, form.build, form.configurator, span.adapter-error, span.adapter-success, .danger-zone { +.page-list, form.editor, form.build, form.configurator, span.adapter-error, span.adapter-success, .file-move, .danger-zone { display: block; overflow-x: hidden; width: 80%; @@ -194,13 +196,13 @@ span.adapter-error { border-bottom: 2px solid crimson; } -form.editor label, form.build label, .danger-zone label, form.configurator label { +form.editor label, form.build label, .danger-zone label, form.configurator label, form.file-move label { font-size: 80%; color: lightgray; text-transform: uppercase; } -form.editor input, form.build input, form.editor textarea, form.configurator input, form.configurator textarea, .danger-zone input[type="submit"] { +form.editor input, form.build input, form.editor textarea, form.configurator input, form.configurator textarea, .danger-zone input[type="submit"], .file-move input[type="submit"] { display: block; margin: 0; margin-top: 0.2em; @@ -234,7 +236,7 @@ form input:focus, form textarea:focus { border: 2px solid cyan; } -form.editor, .danger-zone { +form.editor, form.editor.danger-zone { max-width: 80em; } @@ -255,7 +257,7 @@ form.configurator input, form.configurator textarea { font-size: 125%; } -form.editor input[type="submit"], form.build input[type="submit"], .danger-zone input[type="submit"], form.configurator input[type="submit"] { +form.editor input[type="submit"], form.build input[type="submit"], .danger-zone input[type="submit"], form.configurator input[type="submit"], .file-move input[type="submit"] { margin-left: auto; margin-right: 0; font-size: 150%; @@ -263,7 +265,7 @@ form.editor input[type="submit"], form.build input[type="submit"], .danger-zone transition: background 1s, color 1s; } -form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .danger-zone input[type="submit"]:hover, form.configurator input[type="submit"]:hover { +form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .danger-zone input[type="submit"]:hover, form.configurator input[type="submit"]:hover, .file-move input[type="submit"]:hover { background: lightgray; color: black; } @@ -296,4 +298,9 @@ form input[readonly] { .edit-error { display: block; border-bottom: solid 2px crimson; +} + +.file-actions-icon { + display: inline-block; + max-height: 16px; } \ No newline at end of file diff --git a/templates/error.html b/templates/error.html index eed8715..7981238 100644 --- a/templates/error.html +++ b/templates/error.html @@ -1,17 +1,8 @@ {{ $params := (.Context).Value "params" }} - - - - +{{ template "header" . }} - - - test — error - - -

{{ $params.ErrorCode }}

-
- {{ $params.ErrorMessage }} -
-{{ template "footer" .}} +

Error

+ {{$params.ErrorCode}}: {{$params.ErrorMessage}} + +{{ template "footer" . }} diff --git a/templates/file_actions.html b/templates/file_actions.html new file mode 100644 index 0000000..ccd7d4b --- /dev/null +++ b/templates/file_actions.html @@ -0,0 +1,41 @@ +{{ $slug := ((.Context).Value "params").Slug }} +{{ $file := (.Context).Value "file-data" }} +{{ $csrfToken := (.Context).Value "csrfToken" }} + +{{ template "header" . }} + +{{ if ($file).Error }} +

File Error

+ +{{($file).Error}} + +{{ else }} +{{ if ($file).IsDir }} +

Directory: {{($file).Name}}

+{{ else }} +

File: {{($file).Name}}

+{{end}} + +
+
+ /{{($file).Path}} + + +
+
Danger Zone +
+ + + + +
+
+
+ +{{ end }} + +{{ template "footer" . }} \ No newline at end of file diff --git a/templates/file_list.html b/templates/file_list.html index f8089ab..936f3e6 100644 --- a/templates/file_list.html +++ b/templates/file_list.html @@ -12,7 +12,8 @@

Files: {{($fileList).Root}}

@@ -21,10 +22,16 @@
  • ..
  • {{ end }} {{ range $dir := ($fileList).SubDirs }} -
  • {{$dir}}/
  • +
  • + actions + {{$dir}}/ +
  • {{ end }} {{ range $file := ($fileList).Files }} -
  • {{$file}}
  • +
  • + actions + {{$file}} +
  • {{ end }}