diff --git a/archetype/adapter.go b/archetype/adapter.go index 467d1c0..4fecc96 100644 --- a/archetype/adapter.go +++ b/archetype/adapter.go @@ -1,10 +1,21 @@ package archetype +import ( + "time" +) + type BuildStatus struct { Success bool Message string } +type Page struct { + Title string + Content string + Edited time.Time + Error string +} + type ConfigOption struct { Name string Type string @@ -18,7 +29,7 @@ type Adapter interface { GetConfig() map[ConfigOption]string SetConfig(map[ConfigOption]string) error ListPages() map[string]string - GetPage(string) (Page, error) + GetPage(string) Page FormatPage(string) string FormattingHelp() string CreatePage(slug, title, content string) error diff --git a/archetype/eureka.go b/archetype/eureka.go index d0d481b..cdcc9dd 100644 --- a/archetype/eureka.go +++ b/archetype/eureka.go @@ -72,16 +72,25 @@ func (self *EurekaAdapter) ListPages() map[string]string { return pages } -func (self *EurekaAdapter) GetPage(filename string) (Page, error) { +func (self *EurekaAdapter) GetPage(filename string) Page { + if strings.Contains(filename, "../") || strings.Contains(filename, "..\\") { + return Page{ + Error: "You cannot escape!", + } + } fullPath := filepath.Join(self.Root, "inc", filename) f, err := os.ReadFile(fullPath) if err != nil { - return Page{}, err + return Page{ + Error: err.Error(), + } } if !strings.HasSuffix(filename, ".htm") { - return Page{}, errors.New("Page file extension is not '.htm'") + return Page{ + Error: "Page file extension is not '.htm'", + } } title := strings.Replace( @@ -93,7 +102,7 @@ func (self *EurekaAdapter) GetPage(filename string) (Page, error) { Title: title, Content: content, Edited: fileInfo.ModTime(), - }, nil + } } func (self *EurekaAdapter) FormatPage(raw string) string { @@ -111,6 +120,10 @@ func (self *EurekaAdapter) CreatePage(slug, title, content string) error { slug = strings.ReplaceAll(title, " ", "_") + ".htm" path := filepath.Join(self.Root, "inc", slug) + if strings.Contains(slug, "../") || strings.Contains(slug, "..\\") { + return errors.New("You cannot escape!") + } + _, err := os.Stat(path) if err == nil || !os.IsNotExist(err) { return errors.New("File already exists") @@ -127,6 +140,12 @@ func (self *EurekaAdapter) CreatePage(slug, title, content string) error { func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) error { // eureka creates titles from slugs, so we transform the title into the slug newSlug = strings.ReplaceAll(title, " ", "_") + ".htm" + + if strings.Contains(newSlug, "../") || strings.Contains(newSlug, "..\\") || + strings.Contains(oldSlug, "../") || strings.Contains(oldSlug, "..\\") { + return errors.New("You cannot escape!") + } + f, err := os.Create(filepath.Join(self.Root, "inc", newSlug)) if err != nil { return err @@ -151,6 +170,10 @@ func (self *EurekaAdapter) SavePage(oldSlug, newSlug, title, content string) err } func (self *EurekaAdapter) DeletePage(slug string) error { + if strings.Contains(slug, "../") || strings.Contains(slug, "..\\") { + return errors.New("You cannot escape!") + } + siteRoot := self.Config[ConfigOption{ Name: "SITEROOT", Type: "string", @@ -164,14 +187,14 @@ func (self *EurekaAdapter) DeletePage(slug string) error { } func (self *EurekaAdapter) Build(buildOptions map[string][]string) BuildStatus { - - twtxt := buildOptions["twtxt"][0] - cmdArgs := "" + twtxt := strings.Join(buildOptions["twtxt"], " ") + cmdArgs := []string{} if twtxt != "" { - cmdArgs += "-t " + twtxt + cmdArgs = append(cmdArgs, "-t") + cmdArgs = append(cmdArgs, twtxt) } - cmd := exec.Command("./build.sh", cmdArgs) + cmd := exec.Command("./build.sh", cmdArgs...) cmd.Dir = self.Root out, err := cmd.CombinedOutput() diff --git a/archetype/page.go b/archetype/page.go deleted file mode 100644 index 6054646..0000000 --- a/archetype/page.go +++ /dev/null @@ -1,11 +0,0 @@ -package archetype - -import ( - "time" -) - -type Page struct { - Title string - Content string - Edited time.Time -} diff --git a/archetype/staticFileManager.go b/archetype/staticFileManager.go new file mode 100644 index 0000000..de6d1e4 --- /dev/null +++ b/archetype/staticFileManager.go @@ -0,0 +1,16 @@ +package archetype + +type StaticFileManager struct { + Root string + ShowHtml bool + ShowHidden bool +} + +type FileManager interface { + Init(cfg Config) error + ListTree() []string + ListSubTree(root string) []string + AddFile(path string, file interface{}) error + MkDir(path string) error + Remove(path string) error +} diff --git a/nirvash.go b/nirvash.go index 2b83ca5..aa81a3d 100644 --- a/nirvash.go +++ b/nirvash.go @@ -3,9 +3,9 @@ package main import ( "net/http" core "nilfm.cc/git/nirvash/archetype" - shell "nilfm.cc/git/nirvash/lfo" + . "nilfm.cc/git/nirvash/lfo" "nilfm.cc/git/quartzgun/indentalUserDB" - "nilfm.cc/git/quartzgun/middleware" + . "nilfm.cc/git/quartzgun/middleware" "nilfm.cc/git/quartzgun/renderer" "nilfm.cc/git/quartzgun/router" "os" @@ -40,14 +40,14 @@ func main() { rtr.Get("/login", renderer.Template( pathConcat(templateRoot, "login.html"))) - rtr.Post("/login", middleware.Authorize("/", udb, "/login?tryagain=1")) + rtr.Post("/login", Authorize("/", udb, "/login?tryagain=1")) - rtr.Get("/logout", middleware.Bunt("/", udb, "/login?tryagain=1")) + rtr.Get("/logout", Bunt("/", udb, "/login?tryagain=1")) rtr.Get( "/", - middleware.Protected( - shell.WithAdapter( + Protected( + WithAdapter( renderer.Template( pathConcat(templateRoot, "cms_list.html"), pathConcat(templateRoot, "header.html"), @@ -59,9 +59,9 @@ func main() { rtr.Get( `/edit/(?P\S+)`, - middleware.Fortify( - middleware.Protected( - shell.WithAdapter( + Fortify( + Protected( + WithAdapter( renderer.Template( pathConcat(templateRoot, "cms_edit.html"), pathConcat(templateRoot, "header.html"), @@ -73,10 +73,10 @@ func main() { rtr.Post( `/save/(?P\S+)`, - middleware.Defend( - middleware.Protected( - shell.WithAdapter( - shell.EnsurePageData( + Defend( + Protected( + WithAdapter( + EnsurePageData( renderer.Template( pathConcat(templateRoot, "cms_save.html"), pathConcat(templateRoot, "header.html"), @@ -91,9 +91,9 @@ func main() { rtr.Get( `/new`, - middleware.Fortify( - middleware.Protected( - shell.WithAdapter( + Fortify( + Protected( + WithAdapter( renderer.Template( pathConcat(templateRoot, "cms_new.html"), pathConcat(templateRoot, "header.html"), @@ -105,10 +105,10 @@ func main() { rtr.Post( `/create`, - middleware.Defend( - middleware.Protected( - shell.WithAdapter( - shell.EnsurePageData( + Defend( + Protected( + WithAdapter( + EnsurePageData( renderer.Template( pathConcat(templateRoot, "cms_create.html"), pathConcat(templateRoot, "header.html"), @@ -123,9 +123,9 @@ func main() { rtr.Get( `/build`, - middleware.Fortify( - middleware.Protected( - shell.WithAdapter( + Fortify( + Protected( + WithAdapter( renderer.Template( pathConcat(templateRoot, "build.html"), pathConcat(templateRoot, "header.html"), @@ -137,10 +137,10 @@ func main() { rtr.Post( `/build-run`, - middleware.Defend( - middleware.Protected( - shell.SanitizeFormMap( - shell.WithAdapter( + Defend( + Protected( + SanitizeFormMap( + WithAdapter( renderer.Template( pathConcat(templateRoot, "build_run.html"), pathConcat(templateRoot, "header.html"), @@ -154,9 +154,9 @@ func main() { rtr.Post( `/delete/(?P\S+)`, - middleware.Defend( - middleware.Protected( - shell.WithAdapter( + Defend( + Protected( + WithAdapter( renderer.Template( pathConcat(templateRoot, "delete.html"), pathConcat(templateRoot, "header.html"), @@ -170,9 +170,9 @@ func main() { rtr.Get( `/config`, - middleware.Fortify( - middleware.Protected( - shell.WithAdapter( + Fortify( + Protected( + WithAdapter( renderer.Template( pathConcat(templateRoot, "config.html"), pathConcat(templateRoot, "header.html"), @@ -184,11 +184,11 @@ func main() { rtr.Post( `/config-set`, - middleware.Defend( - middleware.Protected( - shell.SanitizeFormMap( - shell.FormMapToAdapterConfig( - shell.WithAdapter( + Defend( + Protected( + SanitizeFormMap( + FormMapToAdapterConfig( + WithAdapter( renderer.Template( pathConcat(templateRoot, "config_set.html"), pathConcat(templateRoot, "header.html"), diff --git a/static/style.css b/static/style.css index 47b8b68..c12b84f 100644 --- a/static/style.css +++ b/static/style.css @@ -3,7 +3,6 @@ body { margin: 0; font-family: sans-serif; font-size: 16px; - height: 100vh; background: black; color: white; background: url('/static/bg2.png'); @@ -16,6 +15,7 @@ body { background: url('/static/bg.png'); background-size: cover; background-position: center center; + height: 100vh; } .login { @@ -48,7 +48,7 @@ body { } -.login form input, .login form input:-internal-autofill-selected { +.login form input, #user-input, #password-input { display: block; margin: 1em; margin-left: auto; @@ -60,7 +60,7 @@ body { padding: 0.2em; } -.login form input[type="text"], login form input[type="password"] { +.login form input[type="text"], .login form input[type="password"] { transition: border 1s; outline: none; margin-bottom: -17px; @@ -73,7 +73,6 @@ body { .login form input[type="submit"] { - margin-top: -17px; text-transform: uppercase; transition: background 1s, color 1s; } @@ -84,7 +83,7 @@ body { } .login .error { - positon: relative; + position: relative; text-align: center; margin-left: auto; margin-right: auto; @@ -109,11 +108,12 @@ h1 { nav { text-align: center; - background: rgba(0,0,0,0.8); - padding-top: 0.5em; + background: rgba(0,0,0,0.8); + padding-top: 0.5em; padding-bottom: 0.5em; position: sticky; top: 0px; + z-index: 3; } nav ul { @@ -136,9 +136,23 @@ a:hover { color: cyan; } -a.new-page-button { +.new-page-button-wrapper { + display: block; + width: 80%; + max-width: 500px; position: sticky; - top: 0; + top: 5em; + margin-left: auto; + margin-right: auto; + z-index: 2; + text-align: right; + height: 0; + overflow-y: visible; +} + +a.new-page-button { + position: relative; + top: 1em; text-decoration: none; background: transparent; border: solid 2px lightgray; @@ -147,8 +161,6 @@ a.new-page-button { padding: 0.2em; text-transform: uppercase; transition: background 1s, color 1s; - float: right; - z-index: 2; } a.new-page-button:hover { @@ -164,8 +176,7 @@ h2 { display: block; border-left: 8px solid cyan; padding-left: 8px; - position: sticky; - top: 3em; + position: relative; } .page-list, form.editor, form.build, form.configurator, span.adapter-error, span.adapter-success, .danger-zone { @@ -177,7 +188,6 @@ h2 { padding: 2em; margin-left: auto; margin-right: auto; - max-height: calc(100vh - 20em); overflow-y: auto; } @@ -210,6 +220,12 @@ form.editor input[type="text"], form.configurator input[type="text"], form.confi } + + +form.editor input.slug-input { + font-size: 100%; +} + form.editor input.title-input { font-size: 150%; } @@ -230,7 +246,7 @@ form.editor textarea { } form.configurator textarea { - marign: 0; + margin: 0; width: 100%; height: 5em; } @@ -272,3 +288,7 @@ form.editor input[type="submit"]:hover,form.build input[type="submit"]:hover, .d form input[hidden] { display: none; } + +form input[readonly] { + border: none; +} \ No newline at end of file diff --git a/templates/cms_edit.html b/templates/cms_edit.html index c265d7b..30ea57f 100644 --- a/templates/cms_edit.html +++ b/templates/cms_edit.html @@ -6,6 +6,12 @@ {{ template "header" . }} +{{ if ($page).Error }} +

Page Error

+ + {{($page).Error}} +{{ else }} +

Edit Page

@@ -14,12 +20,12 @@ {{ end }} - {{ if $editableSlugs }} +
+
+ {{ if $editableSlugs }}

{{ end }} -
-
last edited {{($page).Edited.Format "2006-01-02 15:04"}}


@@ -38,4 +44,7 @@
+ +{{ end }} + {{ template "footer" . }} diff --git a/templates/cms_list.html b/templates/cms_list.html index 69a80b3..e0cf286 100644 --- a/templates/cms_list.html +++ b/templates/cms_list.html @@ -4,8 +4,11 @@

Pages

-
+ + +
    {{ range $slug, $title := $pages }}
  • {{$title}}
  • @@ -13,6 +16,4 @@
-
-
-{{ template "footer" .}} \ No newline at end of file +{{ template "footer" .}} diff --git a/templates/cms_new.html b/templates/cms_new.html index 3b09e39..5ea2adf 100644 --- a/templates/cms_new.html +++ b/templates/cms_new.html @@ -11,12 +11,12 @@ Empty fields are not allowed - please try again
{{ end }} +
+
{{ if $editableSlugs }}

{{ end }} -
-