From ccc26c3a0bb65ae2613e222c3ead7f6db377b483 Mon Sep 17 00:00:00 2001 From: Mitchell Riedstra Date: Sat, 17 Sep 2022 11:41:20 -0400 Subject: More work on the editor and update the example site to utilize it. --- .gitignore | 4 ++ cmd/server/dashboard.go | 5 -- cmd/server/edit.go | 14 ++++- cmd/server/handlers.go | 23 +++++++- cmd/server/middleware.go | 114 +++++++++++++++++++++++++++++---------- example-site/conf.yml | 3 +- example-site/inc/base.html | 51 ------------------ example-site/login.md | 21 ++++++++ example-site/reIndex.md | 4 +- example-site/tpl/4xx.md | 7 +++ example-site/tpl/5xx.md | 30 +++++++++++ example-site/tpl/base.md | 77 ++++++++++++++++++++++++++ example-site/tpl/dashboard.md | 53 ++++++++++++++++++ example-site/tpl/edit.md | 27 ++++++++++ example-site/tpl/new-template.md | 55 +++++++++++++++++++ 15 files changed, 397 insertions(+), 91 deletions(-) create mode 100644 .gitignore delete mode 100644 cmd/server/dashboard.go delete mode 100644 example-site/inc/base.html create mode 100644 example-site/login.md create mode 100644 example-site/tpl/4xx.md create mode 100644 example-site/tpl/5xx.md create mode 100644 example-site/tpl/base.md create mode 100644 example-site/tpl/dashboard.md create mode 100644 example-site/tpl/edit.md create mode 100644 example-site/tpl/new-template.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ca77d1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +example-site/auth.json +cmd/server/server +server + diff --git a/cmd/server/dashboard.go b/cmd/server/dashboard.go deleted file mode 100644 index a07e722..0000000 --- a/cmd/server/dashboard.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -// func (a *App) DashboardHandler(w http.ResponseWriter, r *http.Request) { -// page.Render(" -// } diff --git a/cmd/server/edit.go b/cmd/server/edit.go index 730e6e9..eb0c4cc 100644 --- a/cmd/server/edit.go +++ b/cmd/server/edit.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "riedstra.dev/mitch/go-website/page" ) @@ -17,6 +18,7 @@ func (a *App) EditPage(w http.ResponseWriter, r *http.Request) { page.Render(w, r, page.TemplateDirectory+"/4xx", map[string]interface{}{ "Title": "Method Not allowed", "Description": "Method not allowed", + "LoggedIn": a.IsLoggedIn(r), }, http.StatusMethodNotAllowed) return @@ -27,12 +29,18 @@ func (a *App) EditPage(w http.ResponseWriter, r *http.Request) { p = filepath.Clean(p) fh, err := os.Open("./" + p + page.Suffix) + if err != nil && strings.Contains(err.Error(), "no such file or directory") { + fh, err = os.Open("./" + page.TemplateDirectory + + "/new-template" + page.Suffix) + } + if err != nil { log.Printf("opening page: %s", err) a.Err500Default(w, r) return } + defer fh.Close() b, err := io.ReadAll(fh) if err != nil { @@ -43,8 +51,9 @@ func (a *App) EditPage(w http.ResponseWriter, r *http.Request) { } page.Render(w, r, page.TemplateDirectory+"/edit", map[string]interface{}{ - "Page": p, - "Content": html.EscapeString(string(b)), + "Page": p, + "Content": html.EscapeString(string(b)), + "LoggedIn": a.IsLoggedIn(r), }, http.StatusOK) } @@ -70,6 +79,7 @@ func (a *App) SaveEditPage(w http.ResponseWriter, r *http.Request) { return } + defer fh.Close() c := bytes.ReplaceAll([]byte(content), []byte{'\r'}, []byte{}) diff --git a/cmd/server/handlers.go b/cmd/server/handlers.go index 1711ae3..c44c19c 100644 --- a/cmd/server/handlers.go +++ b/cmd/server/handlers.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "path/filepath" + "strings" "github.com/gorilla/mux" "riedstra.dev/mitch/go-website/page" @@ -22,7 +23,7 @@ func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) { rtr.PathPrefix("/edit/").Handler( a.RequiresLogin(http.StripPrefix("/edit/", http.HandlerFunc(a.SaveEditPage)))).Methods("POST") - if a.redisPool != nil { + if a.redisPool != nil && !a.IsLoggedIn(r) { rtr.PathPrefix(fmt.Sprintf("/%s/{tag}", a.FeedPrefix)).Handler( rediscache.HandleWithParams(a.redisPool, a.RedisKey, http.HandlerFunc(a.FeedHandler))) @@ -45,9 +46,27 @@ func (a *App) PageHandler(w http.ResponseWriter, r *http.Request) { u = "/index" } + loggedIn := a.IsLoggedIn(r) + + if u == "/dashboard" && loggedIn { + u = page.TemplateDirectory + "/dashboard" + } + + // Render nothing inside of the template directory if we're not logged in + if strings.HasPrefix(u[1:], filepath.Clean(page.TemplateDirectory)) && + !loggedIn { + + page.Render4xx(w, r, map[string]interface{}{ + "LoggedIn": loggedIn, + }, 404) + return + } + u = filepath.Join(".", u) - page.RenderForPath(w, r, u) + page.RenderWithVars(w, r, u, map[string]interface{}{ + "LoggedIn": loggedIn, + }) } func (a *App) RebuildIndexHandler(w http.ResponseWriter, r *http.Request) { diff --git a/cmd/server/middleware.go b/cmd/server/middleware.go index 5e4bf26..0d332cd 100644 --- a/cmd/server/middleware.go +++ b/cmd/server/middleware.go @@ -1,8 +1,10 @@ package main import ( + "errors" "log" "net/http" + "net/url" "time" jwt "github.com/dgrijalva/jwt-go" @@ -30,27 +32,41 @@ func (a *App) LogoutHandler(w http.ResponseWriter, r *http.Request) { SameSite: a.auth.SameSiteStrict, Secure: a.auth.Secure, Value: "logout", - Expires: time.Now().Add(time.Second * 15), //nolint + Expires: time.Now().Add(time.Second), //nolint }) http.Redirect(w, r, "/", http.StatusFound) } func (a *App) LoginHandler(w http.ResponseWriter, r *http.Request) { //nolint - if r.Method == "GET" { + loggedIn := a.IsLoggedIn(r) + + next, _ := url.Parse(r.URL.Query().Get("next")) + + if r.Method == "GET" && !loggedIn { page.RenderForPath(w, r, "login") return } + if r.Method == "GET" && loggedIn { + if next.Path != "" { + http.Redirect(w, r, next.Path, http.StatusFound) + + return + } + + http.Redirect(w, r, "/dashboard", http.StatusFound) + + return + } + if r.Method != "POST" { a.Err500Default(w, r) return } - log.Printf("made it") - username := r.FormValue("username") password := r.FormValue("password") @@ -90,8 +106,6 @@ func (a *App) LoginHandler(w http.ResponseWriter, r *http.Request) { //nolint return } - log.Println(ss) - http.SetCookie(w, &http.Cookie{ Name: "Auth", HttpOnly: a.auth.HTTPOnly, @@ -103,40 +117,82 @@ func (a *App) LoginHandler(w http.ResponseWriter, r *http.Request) { //nolint http.Redirect(w, r, "/login", http.StatusFound) } +func (a *App) IsLoggedIn(r *http.Request) bool { + _, err := a.GetAuthToken(r) + if err != nil { + log.Printf("%s IsLoggedIn: false", r.URL.Path) + return false + } + log.Printf("%s IsLoggedIn: true", r.URL.Path) + return true +} + +func (a *App) GetAuthToken(r *http.Request) (*jwt.Token, error) { + c, err := r.Cookie("Auth") + if err != nil { + return nil, err + } + + token, err := jwt.Parse(c.Value, + func(token *jwt.Token) (interface{}, error) { + return []byte(a.auth.TokenKey), nil + }, + ) + + if err != nil { + return nil, err + } + + if !token.Valid { + return token, errors.New("IsLoggedIn: token not valid") + } + + return token, nil +} + func (a *App) RequiresLogin(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - mustLoginResp := func() { + + if !a.IsLoggedIn(r) { + log.Printf("Unauthorized request %s %s", r.Method, r.URL.Path) page.Render(w, r, "login", map[string]interface{}{ "Error": "You must login to view this page", }, http.StatusUnauthorized) - } - - c, err := r.Cookie("Auth") - if err != nil { - mustLoginResp() return } - token, err := jwt.Parse(c.Value, - func(token *jwt.Token) (interface{}, error) { - return []byte(a.auth.TokenKey), nil - }, - ) - - if err != nil { - log.Printf("Unauthorized request %s %s", r.Method, r.URL.Path) - mustLoginResp() + next.ServeHTTP(w, r) - return - } + }) +} - if !token.Valid { - mustLoginResp() +/* +// ConditionalMiddleware is used to select one handler or another based on +// a test function. If the test function returns true, use handler A, otherwise B. +// This allows the test condition to only be run when the handler is selected +// rather than trying to do this as a top level and ending up with a condition +// that is tested on every single request, regardless of whether or not +// the specific handler is selected +type ConditionalMiddleware struct { + A, B http.Handler + Test func(r *http.Request) bool +} - return - } +func NewConditionalMiddleware(test func(r *http.Request) bool, + A, B http.Handler) http.Handler { + return &ConditionalMiddleware{ + Test: test, + A: A, + B: B, + } +} - next.ServeHTTP(w, r) - }) +func (cm *ConditionalMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if cm.Test(r) { + cm.A.ServeHTTP(w, r) + } else { + cm.B.ServeHTTP(w, r) + } } +*/ diff --git a/example-site/conf.yml b/example-site/conf.yml index b6f0202..3cdb44d 100644 --- a/example-site/conf.yml +++ b/example-site/conf.yml @@ -1,7 +1,8 @@ # All these can be unset reindexpath: /reIndex staticdirectory: static -basetemplate: inc/base.html +templatedirectory: tpl +basetemplate: tpl/base.md documentsplit: "|---\n" suffix: ".md" diff --git a/example-site/inc/base.html b/example-site/inc/base.html deleted file mode 100644 index 2f93ec7..0000000 --- a/example-site/inc/base.html +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - {{if .AuthorName }} - - {{else if .Global.App.Author.Name }} - - {{else}} - {{end}} - -{{if .Title}} - {{.Title}} -{{else}} - Please change me! -{{end}} - -{{if .Description}} - -{{else}} - -{{end}} - - - - - -{{if .Description}}{{else}} -

- - This is a draft, preview or otherwise unfinished page. - -

-{{end}} - - - -{{.RenderBody}} - - diff --git a/example-site/login.md b/example-site/login.md new file mode 100644 index 0000000..1b29ddd --- /dev/null +++ b/example-site/login.md @@ -0,0 +1,21 @@ +--- +title: Login +description: Login page +|--- + +
+ + + + + + + +
+ + +{{if .Vars}} +``` +{{.Vars}} +``` +{{else}}{{end}} diff --git a/example-site/reIndex.md b/example-site/reIndex.md index 12c608a..88cb278 100644 --- a/example-site/reIndex.md +++ b/example-site/reIndex.md @@ -6,7 +6,7 @@ description: > # {{.Title}} -{{.ClearRedis}} +{{.Global.App.ClearRedis}} this is the reindex page @@ -22,3 +22,5 @@ This handler adds some information to the page vars: {{.Vars}} ``` + + diff --git a/example-site/tpl/4xx.md b/example-site/tpl/4xx.md new file mode 100644 index 0000000..3b0103b --- /dev/null +++ b/example-site/tpl/4xx.md @@ -0,0 +1,7 @@ +--- +title: Page not found +description: Page not found +|--- +# {{.Title}} + +That's all we know. Perhaps you'd like to go back to the [home page](/)? diff --git a/example-site/tpl/5xx.md b/example-site/tpl/5xx.md new file mode 100644 index 0000000..43b28b6 --- /dev/null +++ b/example-site/tpl/5xx.md @@ -0,0 +1,30 @@ +--- +title: Server Error +description: Server Error +|--- +{{if .Vars}} + +{{if .Vars.Error}} +{{.Vars.Error}} +{{else}} +# Internal server error +{{end}} + +{{else}} +# Internal server error +{{end}} + +{{if .Vars}} + +{{if .Vars.Description}} +{{.Vars.Description}} +{{else}} +That's all we know. + +{{end}} + +{{else}} +That's all we know. +{{end}} + +Perhaps you'd like to go back to the [home page](/)? diff --git a/example-site/tpl/base.md b/example-site/tpl/base.md new file mode 100644 index 0000000..97842c1 --- /dev/null +++ b/example-site/tpl/base.md @@ -0,0 +1,77 @@ + + + + + + + + + + {{if .AuthorName }} + + {{else if .Global.App.Author.Name }} + + {{else}} + {{end}} + +{{if .Title}} + {{.Title}} +{{else}} + Please change me! +{{end}} + +{{if .Description}} + +{{else}} + +{{end}} + + + + + +{{if .Description}}{{else}} +

+ + This is a draft, preview or otherwise unfinished page. + +

+{{end}} + +{{block "nav" .}} + +{{end}} + +{{.RenderBody}} + + +{{if .Vars.LoggedIn}} + +{{end}} + + diff --git a/example-site/tpl/dashboard.md b/example-site/tpl/dashboard.md new file mode 100644 index 0000000..9b90445 --- /dev/null +++ b/example-site/tpl/dashboard.md @@ -0,0 +1,53 @@ +--- +title: Website Dashboard +description: Website dashboard +|--- + +# Dashboard page ( File: `tpl/dashboard.md` ) + +Almost every page on the website can be edited by simply adding `edit/` before +the path, for instance if we want to edit our `/reIndex` page, we simply go to +`/edit/reIndex` + +There are a few pages where this is not possible, such as with the dashboard and +other templated pages like 404's and 5xx's. That being said, you can still edit +them by referencing their path on disk. + +You can create new pages that way too, the new page template is +`tpl/new-template.md` if you wish to change what the defaults are. + +You'll also notice there's an `edit` button in the navbar, that's a little bit +of Javascript trickery that you'll have to edit if you wish to change the +template directory from `tpl` to anything else. + +Some useful edit links: + + * 4xx page [visit](/some/path/that/does/not/exist) [edit](/edit/tpl/4xx) + * 5xx page [visit](/tpl/5xx) [edit](/edit/tpl/5xx) + * New page template [visit](/tpl/new-template) [edit](/edit/tpl/new-template) + * Edit page [visit](/tpl/edit) [edit](/edit/tpl/edit) + * Login page [visit](/login) [edit](/edit/login) + * Base template [edit](/edit/tpl/base) + +A note about the base template page, editing works, but viewing does not. + +Pages by tags: + +{{range $key, $val := .Index}} +### {{$key}}: + +{{range $v2 := $val}} + * {{$v2.Path}} [visit](/{{$v2.Path}}) [edit](/edit/{{$v2.Path}}){{end}} + + + +{{end}} + + + diff --git a/example-site/tpl/edit.md b/example-site/tpl/edit.md new file mode 100644 index 0000000..4e55f82 --- /dev/null +++ b/example-site/tpl/edit.md @@ -0,0 +1,27 @@ +--- +title: Editing Page +description: Editor +|--- + +
+
+
+
+ + + + +
+ +
+ + + + + diff --git a/example-site/tpl/new-template.md b/example-site/tpl/new-template.md new file mode 100644 index 0000000..c4fdd95 --- /dev/null +++ b/example-site/tpl/new-template.md @@ -0,0 +1,55 @@ +--- +# This is the contents of the `tpl/new-template.md` file. You're likely seeing +# This because you've created a new page, or are trying to edit a page that +# does not exist. To save yourself some confusion in the future, if you save +# This page it may be helpful to delete this first paragraph, as it will no +# longer be true. + +# This top section is YAML, this sets up a few of the variables for rendering +# a web page. Below is also some markdown that's executed as a go template +# for the page's content + +# You can edit this page by editing `tpl/new-template` If you changed the +# template directory, take that into account. + +# Controls whether or not the page is published, this includes the +# Atom/RSS feed +published: false +# What tags should be applied to this page? +# The default index/home page shows a list of published blog pages +tags: + Blog: +# This is the meta description, i.e. what you're going to seeing the little blurb +# if you post the link to in say a discord chat. It's also what shows up in +# Google and other search engines +description: >- + This is an example description. Note that the line starts with spaces, and + not tabs. +title: New page from `new-template` +# Used for the RSS/Atom feed, you can also use it in the template below +date: 09.11.2022 09:59:00 EDT + +# This line tells our server to stop parsing yaml and start processing +# the rest of the text file as a markdown template +|--- + +{{/* This is a comment inside of the template */}} + +{{/* + Print out the date, format information can be found here: + https://pkg.go.dev/time#pkg-constants +*/}} +`{{.Date.Time.Format "Monday January 2 2006"}}` + +# {{.Title}} + +Some documentation on the templating language can be found [here]( +https://pkg.go.dev/text/template) + + +A nice markdown reference can be found +[here](https://www.markdownguide.org/basic-syntax/) + + +Additionally html tags can be used in here as well. + -- cgit v1.2.3