diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2022-09-17 11:41:20 -0400 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2022-09-17 11:41:20 -0400 |
| commit | ccc26c3a0bb65ae2613e222c3ead7f6db377b483 (patch) | |
| tree | 6124ae75f557fffe863134b967ee7c6bfcc87d3d | |
| parent | bf7d9c79cae53f64fcd04527248987bd4e7ca3c4 (diff) | |
| download | go-website-0.0.18.tar.gz go-website-0.0.18.tar.xz | |
More work on the editor and update the example site to utilize it.v0.0.18
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | cmd/server/dashboard.go | 5 | ||||
| -rw-r--r-- | cmd/server/edit.go | 14 | ||||
| -rw-r--r-- | cmd/server/handlers.go | 23 | ||||
| -rw-r--r-- | cmd/server/middleware.go | 114 | ||||
| -rw-r--r-- | example-site/conf.yml | 3 | ||||
| -rw-r--r-- | example-site/login.md | 21 | ||||
| -rw-r--r-- | example-site/reIndex.md | 4 | ||||
| -rw-r--r-- | example-site/tpl/4xx.md | 7 | ||||
| -rw-r--r-- | example-site/tpl/5xx.md | 30 | ||||
| -rw-r--r-- | example-site/tpl/base.md (renamed from example-site/inc/base.html) | 36 | ||||
| -rw-r--r-- | example-site/tpl/dashboard.md | 53 | ||||
| -rw-r--r-- | example-site/tpl/edit.md | 27 | ||||
| -rw-r--r-- | example-site/tpl/new-template.md | 55 |
14 files changed, 351 insertions, 45 deletions
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/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 +|--- + +<form action="/login" method="POST"> +<label for="username">Username:</label> +<input type="text" id="username" name="username" /> + +<label for="password">Password:</label> +<input type="password" id="password" name="password" /> + +<input type="submit" value="submit"> +</form> + + +{{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 <!--no detailed message available --> +{{end}} + +{{else}} +# Internal server error +{{end}} + +{{if .Vars}} + +{{if .Vars.Description}} +{{.Vars.Description}} +{{else}} +That's all we know. +<!-- No detailed description available --> +{{end}} + +{{else}} +That's all we know. +{{end}} + +Perhaps you'd like to go back to the [home page](/)? diff --git a/example-site/inc/base.html b/example-site/tpl/base.md index 2f93ec7..97842c1 100644 --- a/example-site/inc/base.html +++ b/example-site/tpl/base.md @@ -38,14 +38,40 @@ </h1> {{end}} +{{block "nav" .}} <nav> - <a href="/">Home</a> - - <div style="display: block; float: right;"> - <a href="#">Git</a> - </div> + | <a href="/">Home</a> | + <div style="display: block; float: right;"> + | <a href="#">Git</a> + {{if .Vars.LoggedIn}} +<span id="edit">| <a href="#" onClick="editThisPage()">Edit</a></span> + | <a href="/dashboard">Dashboard</a> + | <a href="/logout">Logout</a> | + {{else}} + | <a href="/login">Login</a> + {{end}} + </div> </nav> +{{end}} {{.RenderBody}} + +{{if .Vars.LoggedIn}} +<script> +function editThisPage() +{ + if(window.location.pathname == "/") { + window.location = "/edit/index"; + return; + } + if(window.location.pathname == "/dashboard") { + window.location = "/edit/tpl/dashboard"; + return; + } + window.location = "/edit/" + window.location.pathname; +} +</script> +{{end}} </body> + 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}} + + +<script> +/* +window.addEventListener('load', (event) => { + document.querySelector("#edit").remove(); +}); +*/ +</script> 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 +|--- + +<form action="/edit/{{.Vars.Page}}" method="POST"> +<br /> +<label for="content">Page content:</label><br /> +<br /> + + +<textarea id="content" name="content" rows="24" cols="80"> +{{.Vars.Content}} +</textarea> + +<br /> +<input type="submit" value="save"> +</form> + + +<!-- test --> + +<script> +window.addEventListener('load', (event) => { + document.querySelector("#edit").remove(); +}); +</script> 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 <span style="color: red;">html tags</span> can be used in here as well. + |
