diff options
| -rw-r--r-- | LICENSE | 2 | ||||
| -rw-r--r-- | cmd/server/app.go | 42 | ||||
| -rw-r--r-- | cmd/server/edit.go | 130 | ||||
| -rw-r--r-- | cmd/server/handlers.go | 130 | ||||
| -rw-r--r-- | cmd/server/main.go | 17 | ||||
| -rw-r--r-- | cmd/server/middleware.go | 140 | ||||
| -rw-r--r-- | example-site/index.md | 2 | ||||
| -rw-r--r-- | example-site/login.md | 21 | ||||
| -rw-r--r-- | example-site/tpl/dashboard.md | 62 | ||||
| -rw-r--r-- | example-site/tpl/edit.md | 27 | ||||
| -rw-r--r-- | example-site/tpl/new-template.md | 55 | ||||
| -rw-r--r-- | page/page.go | 12 | ||||
| -rw-r--r-- | page/renderJson.go | 108 | ||||
| -rw-r--r-- | page/renderMarkdown.go | 61 |
14 files changed, 70 insertions, 739 deletions
@@ -1,4 +1,4 @@ -Copyright 2021 Mitchell Riedstra +Copyright 2026 Mitchell Riedstra Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/cmd/server/app.go b/cmd/server/app.go index c694753..a8d1ce8 100644 --- a/cmd/server/app.go +++ b/cmd/server/app.go @@ -39,25 +39,7 @@ type App struct { FeedPrefix string } -func newApp() *App { - return &App{} -} - -func loadConf(fn string) (*App, error) { - fh, err := os.Open(fn) - if err != nil { - return nil, fmt.Errorf("loading config: %w", err) - } - - dec := yaml.NewDecoder(fh) - - app := newApp() - - err = dec.Decode(app) - if err != nil { - return nil, fmt.Errorf("decoding yaml: %w", err) - } - +func (app *App) setDefaults() { if app.StaticDirectory == "" { app.StaticDirectory = "static" } @@ -85,8 +67,28 @@ func loadConf(fn string) (*App, error) { page.Global = map[string]interface{}{ "App": app, } +} + +func loadConf(fn string) (*App, error) { + var dec *yaml.Decoder + app := &App{} + + fh, err := os.Open(fn) + if err != nil { + err = fmt.Errorf("loading config: %w", err) + goto loadConfRet + } + + dec = yaml.NewDecoder(fh) + + err = dec.Decode(app) + if err != nil { + err = fmt.Errorf("decoding yaml: %w", err) + } - return app, nil +loadConfRet: + app.setDefaults() + return app, err } func (a *App) ClearCache() error { diff --git a/cmd/server/edit.go b/cmd/server/edit.go deleted file mode 100644 index 5b40f51..0000000 --- a/cmd/server/edit.go +++ /dev/null @@ -1,130 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "html" - "io" - "io/fs" - "log" - "net/http" - "os" - "path/filepath" - - "riedstra.dev/mitch/go-website/page" -) - -const editFileMode = 0644 - -func (a *App) EditPage() http.Handler { - getFunc := a.GetEditPage().ServeHTTP - postFunc := a.SaveEditPage().ServeHTTP - - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - switch r.Method { - case "GET": //nolint:goconst - getFunc(w, r) - - return - case "POST": //nolint:goconst - postFunc(w, r) - - return - default: - a.Err500Default(w, r) - - return - } - }) -} - -func (a *App) GetEditPage() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != "GET" { - 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 - } - - p := r.URL.Path - - p = filepath.Clean(p) - - fh, err := os.Open("./" + p + page.Suffix) - if err != nil && errors.Is(err, fs.ErrNotExist) { - 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 { - log.Printf("opening page: %s", err) - a.Err500Default(w, r) - - return - } - - page.Render(w, r, page.TemplateDirectory+"/edit", map[string]interface{}{ - "Page": p, - "Content": html.EscapeString(string(b)), - "LoggedIn": a.IsLoggedIn(r), - }, http.StatusOK) - }) -} - -func (a *App) SaveEditPage() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Method != "POST" { - a.Err500Default(w, r) - - return - } - - p := r.URL.Path - content := r.FormValue("content") - - content = html.UnescapeString(content) - p = filepath.Clean(p) - - fn := "./" + p + page.Suffix - - fh, err := os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, - editFileMode) - if err != nil { - log.Printf("opening file %s for writing: %s", fn, err) - a.Err500Default(w, r) - - return - } - defer fh.Close() - - c := bytes.ReplaceAll([]byte(content), []byte{'\r'}, []byte{}) - - _, err = fh.Write(c) - if err != nil { - log.Printf("opening file %s for writing: %s", fn, err) - a.Err500Default(w, r) - - return - } - - http.Redirect(w, r, "/"+r.URL.Path, http.StatusFound) - - err = a.ClearCache() // Clear out the cache if any - if err != nil { - log.Printf("after editing %s: %s", fn, err) - } - }) -} diff --git a/cmd/server/handlers.go b/cmd/server/handlers.go index 010705e..49f2039 100644 --- a/cmd/server/handlers.go +++ b/cmd/server/handlers.go @@ -1,127 +1,81 @@ package main import ( - "fmt" "net/http" "path/filepath" "strings" + "fmt" "riedstra.dev/mitch/go-website/mapcache" "riedstra.dev/mitch/go-website/page" "riedstra.dev/mitch/go-website/rediscache" ) -func (a *App) baseRouter() *http.ServeMux { - r := http.NewServeMux() - r.Handle(a.ReIndexPath, a.RebuildIndexHandler()) - r.Handle("/static/", a.StaticHandler()) - r.Handle("/login", a.LoginHandler()) - r.Handle("/logout", a.RequiresLogin(a.LogoutHandler())) - r.Handle("/edit/", a.RequiresLogin( - http.StripPrefix("/edit/", a.EditPage()))) - - return r -} - -func (a *App) addCacheableRoutes(r *http.ServeMux) *http.ServeMux { - // make sure it always has a trailing slash and starts with a slash - a.FeedPrefix = fmt.Sprintf("/%s/", - strings.TrimSuffix(strings.TrimPrefix(a.FeedPrefix, "/"), "/")) +func (a *App) Handler() http.Handler { + mux := http.NewServeMux() - routes := map[string]http.Handler{ - "/_json/": http.StripPrefix("/_json/", a.PageJSONHandler()), - "/_md/": http.StripPrefix("/_md/", a.PageMarkdownHandler()), - a.FeedPrefix: http.StripPrefix(a.FeedPrefix, a.FeedHandler()), - "/": a.PageHandler(), + type route struct { + path string + handler http.Handler } - if a.cache == nil { - a.cache = mapcache.New() + routes := []route{ + {a.ReIndexPath, a.RebuildIndexHandler()}, + {"/static/", a.StaticHandler()}, } - - for route, handler := range routes { - switch { - case a.mapCache: - r.Handle(route, conditionalMiddleware( - a.IsLoggedIn, - handler, - a.cache.Handle(handler))) - case a.redisPool != nil: - r.Handle(route, conditionalMiddleware( - a.IsLoggedIn, - handler, - rediscache.Handle(a.redisPool, a.RedisKey, handler))) - default: - r.Handle(route, handler) - } + cacheableRoutes := []route{ + {a.FeedPrefix, http.StripPrefix(a.FeedPrefix, a.FeedHandler())}, + {"/", a.PageHandler()}, } - return r -} - -func (a *App) Handler() http.Handler { - return a.addCacheableRoutes(a.baseRouter()) -} - -func (a *App) PageHandler() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - u := page.GetURLPath(r) + for _, route := range routes { + mux.Handle(route.path, route.handler) + } - loggedIn := a.IsLoggedIn(r) + if a.cache == nil && a.RedisKey != "" { + a.cache = mapcache.New() + } - if u == "/dashboard" && loggedIn { - u = page.TemplateDirectory + "/dashboard" + var cacheHandler func(http.Handler) http.Handler = nil + switch { + case a.mapCache: + cacheHandler = a.cache.Handle + case a.redisPool != nil: + cacheHandler = func(h http.Handler) http.Handler { + return rediscache.Handle(a.redisPool, a.RedisKey, h) } - - // 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, - }, http.StatusNotFound) - - return + default: + // No caching middleware + cacheHandler = func(h http.Handler) http.Handler { + return h } + } - u = filepath.Join(".", u) - - page.RenderWithVars(w, r, u, map[string]interface{}{ - "LoggedIn": loggedIn, - }) - }) -} - -func (a *App) PageJSONHandler() http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - u := page.GetURLPath(r) - - // Skip template directory - if strings.HasPrefix(u[1:], filepath.Clean(page.TemplateDirectory)) { - page.Render4xx(w, r, map[string]interface{}{}, http.StatusNotFound) - - return - } + if cacheHandler == nil { + panic("cacheHandler cannot be nil") + } - u = filepath.Join(".", u) + for _, r := range cacheableRoutes { + fmt.Printf("Calling mux.Handle(r.path(%s), cacheHandler(r.handler(%v)))\n", + r.path, r.handler) + mux.Handle(r.path, cacheHandler(r.handler)) + } - page.RenderJSON(w, r, u, map[string]interface{}{}, http.StatusOK) - }) + return mux } -func (a *App) PageMarkdownHandler() http.Handler { +func (a *App) PageHandler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { u := page.GetURLPath(r) - // Skip template directory + // Render nothing inside of the template directory if strings.HasPrefix(u[1:], filepath.Clean(page.TemplateDirectory)) { - page.Render4xx(w, r, map[string]interface{}{}, http.StatusNotFound) - return } u = filepath.Join(".", u) - page.RenderMarkdown(w, r, u, map[string]interface{}{}, http.StatusOK) + page.RenderWithVars(w, r, u, map[string]interface{}{}) }) } diff --git a/cmd/server/main.go b/cmd/server/main.go index 10bbbf0..1b59bbe 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -36,15 +36,14 @@ func main() { //nolint:funlen directory = "." version = false confFn = "conf.yml" - authConfFn = "auth.json" verbose = false defaultIndexPath = "/reIndex" indexPath = "/reIndex" - redisAddr = "127.0.0.1:6379" + redisAddr = "" redisKey = "go-website" pageTimeout = 15 genhash = false - mapcache = false + mapcache = true ) fl := flag.NewFlagSet("Website", flag.ExitOnError) @@ -56,8 +55,6 @@ func main() { //nolint:funlen "print version and exit")) envflag.String(fl, &confFn, "c", "CONFIG_FILE", "Location for configuration file") - envflag.String(fl, &authConfFn, "ac", "AUTH_CONFIG", - "location for the authorization config") logIfErr(envflag.Bool(fl, &verbose, "V", "VERBOSE", "Be more verbose, dump config and such")) envflag.String(fl, &page.TimeFormat, "T", "TIME_FORMAT", @@ -94,20 +91,14 @@ func main() { //nolint:funlen app, err := loadConf(confFn) if err != nil { - logger.Println(err) - - app = &App{} + logger.Print(err) + logger.Fatal("A configuration file must be supplied. No valid config read") } if mapcache { app.mapCache = true } - err = app.ReadAuth(authConfFn) - if err != nil { - logger.Println(err) - } - if app.ReIndexPath == "" || indexPath != defaultIndexPath { app.ReIndexPath = indexPath } diff --git a/cmd/server/middleware.go b/cmd/server/middleware.go index 17717c2..991aa8c 100644 --- a/cmd/server/middleware.go +++ b/cmd/server/middleware.go @@ -1,20 +1,12 @@ package main import ( - "errors" - "fmt" - "log" "net/http" - "net/url" "time" - jwt "github.com/golang-jwt/jwt/v4" "riedstra.dev/mitch/go-website/page" - "riedstra.dev/mitch/go-website/users" ) -var ErrInvalidJWTToken = errors.New("invalid JWT token") - func (a *App) Err5xx(w http.ResponseWriter, r *http.Request, statusCode int, title, desc string) { page.Render5xx(w, r, map[string]interface{}{ @@ -42,135 +34,3 @@ func (a *App) LogoutHandler() http.Handler { http.Redirect(w, r, "/", http.StatusFound) }) } - -func (a *App) LoginHandler() http.Handler { //nolint - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - 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 - } - - username := r.FormValue("username") - password := r.FormValue("password") - - var ( - err error - u *users.SiteUser - found = false - ) - - for _, u = range a.auth.Users { - if u.Username == username { - err = u.CheckPassword(password) - found = true - } - } - - if err != nil || !found { - page.Render(w, r, "login", map[string]interface{}{ - "Error": "Invalid username or password", - "Username": username, - }, http.StatusUnauthorized) - - return - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS512, &jwt.StandardClaims{ - ExpiresAt: time.Now().Add( - time.Hour * time.Duration(a.auth.LoginHours)).Unix(), - Id: u.Username, - }) - - ss, err := token.SignedString([]byte(a.auth.TokenKey)) - if err != nil { - log.Println("login: encountered while setting up JWT: ", err) - a.Err500Default(w, r) - - return - } - - http.SetCookie(w, &http.Cookie{ - Name: "Auth", - HttpOnly: a.auth.HTTPOnly, - SameSite: a.auth.SameSiteStrict, - Secure: a.auth.Secure, - Value: ss, - }) - - 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, fmt.Errorf("getting auth token: %w", err) - } - - token, err := jwt.Parse(c.Value, - func(token *jwt.Token) (interface{}, error) { - return []byte(a.auth.TokenKey), nil - }, - ) - - if err != nil { - return nil, fmt.Errorf("while parsing jwt %w", err) - } - - if !token.Valid { - return token, ErrInvalidJWTToken - } - - return token, nil -} - -func (a *App) RequiresLogin(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - 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) - - return - } - - next.ServeHTTP(w, r) - }) -} diff --git a/example-site/index.md b/example-site/index.md index 7d51067..451f36e 100644 --- a/example-site/index.md +++ b/example-site/index.md @@ -24,7 +24,7 @@ it. {{range $val := .Index.Blog.SortDate}} * [{{$val.Date.Time.Format "2006-01-02"}} {{$val.Title}}]({{$val.Path}}){{end}} -## Published Blog Entries: +## Only Published Blog Entries: {{range $val := .Index.Blog.SortDate}}{{if $val.Published}} * [{{$val.Date.Time.Format "2006-01-02"}} {{$val.Title}}]({{$val.Path}}){{end}}{{end}} diff --git a/example-site/login.md b/example-site/login.md deleted file mode 100644 index 1b29ddd..0000000 --- a/example-site/login.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -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/tpl/dashboard.md b/example-site/tpl/dashboard.md deleted file mode 100644 index 4b18b98..0000000 --- a/example-site/tpl/dashboard.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -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}} - - -It can also be a good idea to clear the cache on the dashboard page: - -``` -{{.Global.App.ClearCache}} -``` - -The element can be hidden, if you'd like. - -<script> -/* -window.addEventListener('load', (event) => { - document.querySelector("#edit").remove(); -}); -*/ -</script> - diff --git a/example-site/tpl/edit.md b/example-site/tpl/edit.md deleted file mode 100644 index 4e55f82..0000000 --- a/example-site/tpl/edit.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -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 deleted file mode 100644 index c4fdd95..0000000 --- a/example-site/tpl/new-template.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -# 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. - diff --git a/page/page.go b/page/page.go index 03d1f1e..b0fcfc7 100644 --- a/page/page.go +++ b/page/page.go @@ -284,15 +284,3 @@ func (p *Page) GetMarkdown() (string, error) { func (p Page) String() string { return fmt.Sprintf("Page: %s", p.path) } - -// setRenderingMarkdownOnly simply sets the special vars field, -// "RenderingMarkdownOnly" to be true. -func (p *Page) setRenderingMarkdownOnly() { - if p.Vars != nil { - p.Vars["RenderingMarkdownOnly"] = true - } else { - p.Vars = map[string]interface{}{ - "RenderingMarkdownOnly": true, - } - } -} diff --git a/page/renderJson.go b/page/renderJson.go deleted file mode 100644 index 4c03e12..0000000 --- a/page/renderJson.go +++ /dev/null @@ -1,108 +0,0 @@ -package page - -import ( - "bytes" - "encoding/json" - "errors" - "io/fs" - "net/http" -) - -// 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, //nolint:funlen - path string, vars map[string]interface{}, statusCode int) { - // 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, "Not found", http.StatusNotFound) - - return - } - - logErr(r, "rendering JSON encountered", err) - renderJSONErr(w, "Internal server error", - http.StatusInternalServerError) - - return - } - - if r.URL.Query().Get("content") == "1" { - out["Content"] = buf.String() - } - - if r.URL.Query().Get("markdown") == "1" { - p.setRenderingMarkdownOnly() - // 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 { - logErr(r, "while writing to buf", err) - - return - } - - logReq(r, statusCode) -} - -func renderJSONErr(w http.ResponseWriter, msg string, statusCode int) { - enc := json.NewEncoder(w) - w.WriteHeader(statusCode) - - _ = enc.Encode(&struct { - StatusCode int - Msg string - }{ - StatusCode: statusCode, - Msg: msg, - }) -} diff --git a/page/renderMarkdown.go b/page/renderMarkdown.go deleted file mode 100644 index 8356e55..0000000 --- a/page/renderMarkdown.go +++ /dev/null @@ -1,61 +0,0 @@ -package page - -import ( - "bytes" - "errors" - "io/fs" - "net/http" -) - -// RenderMarkdown is analogous to Render, except it spits out rendered markdown -// as text/plain. It also sets .Vars.RenderingMarkdownOnly so templates can -// vary on whether or not they're plain markdown. For instance, not including -// some HTML tags. -func RenderMarkdown(w http.ResponseWriter, r *http.Request, - path string, vars map[string]interface{}, statusCode int) { - // Sepcifically use the specified path for the page - p := NewPage(path) - - if vars != nil { - p.Vars = vars - } - - if p.Vars != nil { - p.Vars["RenderingMarkdownOnly"] = true - } else { - p.Vars = map[string]interface{}{ - "RenderingMarkdownOnly": true, - } - } - - buf := &bytes.Buffer{} - - err := p.Render(buf) - if err != nil { - if errors.Is(err, fs.ErrNotExist) { - plainResp(w, http.StatusNotFound, "Not found") - - return - } - - logErr(r, "while rendering", err) - plainResp(w, http.StatusInternalServerError, "Internal server error") - - return - } - - // Error was handled above - md, _ := p.GetMarkdown() - - w.Header().Set("Content-type", "text/plain") - w.WriteHeader(statusCode) - - _, err = w.Write([]byte(md)) - if err != nil { - logErr(r, "while writing buf", err) - - return - } - - logReq(r, statusCode) -} |
