aboutsummaryrefslogtreecommitdiff
path: root/page
diff options
context:
space:
mode:
authorMitchell Riedstra <mitch@riedstra.dev>2022-12-03 13:38:05 -0500
committerMitchell Riedstra <mitch@riedstra.dev>2022-12-03 13:39:47 -0500
commit10c60b3c9ba2c17419534cf4089328a66568e4f1 (patch)
tree33e6ecedfc3dec7d96488878291179c8a593e779 /page
parentd6f60ce24e123ee83b73f6c9dbe8c4b9af5c629e (diff)
downloadgo-website-10c60b3c9ba2c17419534cf4089328a66568e4f1.tar.gz
go-website-10c60b3c9ba2c17419534cf4089328a66568e4f1.tar.xz
Add a couple handlers to serve up JSON and markdown
Diffstat (limited to 'page')
-rw-r--r--page/page.go14
-rw-r--r--page/renderJson.go118
-rw-r--r--page/renderMarkdown.go69
3 files changed, 199 insertions, 2 deletions
diff --git a/page/page.go b/page/page.go
index d34a1f7..29b35bc 100644
--- a/page/page.go
+++ b/page/page.go
@@ -55,7 +55,10 @@ type Page struct {
Date *PageTime
Published bool
Vars map[string]interface{}
- markdown []byte
+ // Keys of vars to be included when RenderJson is called, all vars are
+ // omitted if empty.
+ JsonVars []string
+ markdown []byte
}
// Global is meant to be supplied by external users of this package to populate
@@ -232,6 +235,13 @@ func (p *Page) Read() error {
// markdown file, then runs it through the markdown parser. Typically
// this is called in the base template.
func (p *Page) RenderBody() (string, error) {
+ s, err := p.GetMarkdown()
+ return string(blackfriday.Run([]byte(s))), err
+}
+
+// GetMarkdown renders and executes a template from the body of the
+// markdown file, then simply returns the unrendered markdown.
+func (p *Page) GetMarkdown() (string, error) {
buf := &bytes.Buffer{}
t, err := template.New("Body").Funcs(Funcs).Parse(string(p.markdown))
@@ -245,7 +255,7 @@ func (p *Page) RenderBody() (string, error) {
return "", fmt.Errorf("template execute; %w", err)
}
- return string(blackfriday.Run(buf.Bytes())), nil
+ return buf.String(), nil
}
func (p Page) String() string {
diff --git a/page/renderJson.go b/page/renderJson.go
new file mode 100644
index 0000000..52e707d
--- /dev/null
+++ b/page/renderJson.go
@@ -0,0 +1,118 @@
+package page
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "io/fs"
+ "net/http"
+ "path/filepath"
+)
+
+// RenderJson is analogous to Render, though it renders the page,
+// as a JSON key "content", optionally from the YAML it'll include
+// additional keys in the json if specified under the 'pageJson' key.
+// E.g.
+// ---
+// title: Some page
+// description: Some page desc
+// jsonvars:
+// - includeMeInJSON
+// vars:
+// notIncluded: some value
+// includeMeInJSON:
+// some: arbitrary
+// data: to
+// send: down
+// |--
+// My page content, wee!
+func RenderJson(w http.ResponseWriter, r *http.Request,
+ path string, vars map[string]interface{}, statusCode int) {
+ u := getURLPath(r)
+
+ u = filepath.Join(".", u)
+
+ // Sepcifically use the specified path for the page
+ p := NewPage(path)
+
+ if vars != nil {
+ p.Vars = vars
+ }
+
+ out := map[string]interface{}{}
+
+ buf := &bytes.Buffer{}
+ err := p.Render(buf)
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ renderJsonErr(w, r, "Not found", http.StatusNotFound)
+
+ return
+ }
+
+ Logger.Printf("%s %s path: %s rendering encountered: %s",
+ r.RemoteAddr,
+ r.Method,
+ u,
+ err)
+ renderJsonErr(w, r, "Internal server error",
+ http.StatusInternalServerError)
+
+ return
+ }
+
+ if r.URL.Query().Get("content") == "1" {
+ out["Content"] = buf.String()
+ }
+
+ if r.URL.Query().Get("markdown") == "1" {
+ // Tossing the error, since it would've been revealed above
+ md, _ := p.GetMarkdown()
+ out["Markdown"] = md
+ }
+
+ // Make a "set" of keys
+ keys := map[string]struct{}{}
+ for _, k := range p.JsonVars {
+ keys[k] = struct{}{}
+ }
+
+ // And chuck the keys specified into the output
+ for k, v := range p.Vars {
+ _, ok := keys[k]
+ if ok {
+ out[k] = v
+ }
+ }
+
+ w.WriteHeader(statusCode)
+
+ enc := json.NewEncoder(w)
+ enc.SetIndent("", " ")
+ enc.SetEscapeHTML(false)
+
+ err = enc.Encode(out)
+ if err != nil {
+ Logger.Printf("%s %s %d %s: while writing buf: %s",
+ r.RemoteAddr,
+ r.Method,
+ statusCode,
+ u,
+ err)
+
+ return
+ }
+
+ Logger.Printf("%s %s %d %s", r.RemoteAddr, r.Method, statusCode, u)
+}
+
+func renderJsonErr(w http.ResponseWriter, r *http.Request, msg string,
+ statusCode int) {
+ enc := json.NewEncoder(w)
+ w.WriteHeader(statusCode)
+ _ = enc.Encode(&struct {
+ Status string
+ }{
+ Status: msg,
+ })
+}
diff --git a/page/renderMarkdown.go b/page/renderMarkdown.go
new file mode 100644
index 0000000..c876169
--- /dev/null
+++ b/page/renderMarkdown.go
@@ -0,0 +1,69 @@
+package page
+
+import (
+ "bytes"
+ "errors"
+ "io/fs"
+ "net/http"
+ "path/filepath"
+)
+
+// RenderMarkdown is analogous to Render, except it spits out rendered markdown
+// as text/plain
+func RenderMarkdown(w http.ResponseWriter, r *http.Request,
+ path string, vars map[string]interface{}, statusCode int) {
+ u := getURLPath(r)
+
+ u = filepath.Join(".", u)
+
+ // Sepcifically use the specified path for the page
+ p := NewPage(path)
+
+ if vars != nil {
+ p.Vars = vars
+ }
+
+ buf := &bytes.Buffer{}
+
+ err := p.Render(buf)
+ if err != nil {
+ if errors.Is(err, fs.ErrNotExist) {
+ w.WriteHeader(http.StatusNotFound)
+ w.Header().Set("Content-type", "text/plain")
+ w.Write([]byte("Not found"))
+
+ return
+ }
+
+ Logger.Printf("%s %s path: %s rendering encountered: %s",
+ r.RemoteAddr,
+ r.Method,
+ u,
+ err)
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Header().Set("Content-type", "text/plain")
+ w.Write([]byte("Internal server error"))
+
+ return
+ }
+
+ // Error was handled above
+ md, _ := p.GetMarkdown()
+
+ w.WriteHeader(statusCode)
+ w.Header().Set("Content-type", "text/plain")
+
+ _, err = w.Write([]byte(md))
+ if err != nil {
+ Logger.Printf("%s %s %d %s: while writing buf: %s",
+ r.RemoteAddr,
+ r.Method,
+ statusCode,
+ u,
+ err)
+
+ return
+ }
+
+ Logger.Printf("%s %s %d %s", r.RemoteAddr, r.Method, statusCode, u)
+}