diff --git a/srv/default.nix b/srv/default.nix index 7aa11b8..6f93f2f 100644 --- a/srv/default.nix +++ b/srv/default.nix @@ -25,12 +25,10 @@ # pow export MEDIOCRE_BLOG_POW_SECRET="${config.powSecret}" - # listening + # http export MEDIOCRE_BLOG_LISTEN_PROTO="${config.listenProto}" export MEDIOCRE_BLOG_LISTEN_ADDR="${config.listenAddr}" - - # api - export MEDIOCRE_BLOG_API_AUTH_USERS='${builtins.toJSON config.httpAuthUsers}' + export MEDIOCRE_BLOG_HTTP_AUTH_USERS='${builtins.toJSON config.httpAuthUsers}' ''; build = buildGoModule { diff --git a/srv/src/cmd/mediocre-blog/main.go b/srv/src/cmd/mediocre-blog/main.go index f76188b..ce43c61 100644 --- a/srv/src/cmd/mediocre-blog/main.go +++ b/srv/src/cmd/mediocre-blog/main.go @@ -2,7 +2,6 @@ package main import ( "context" - "encoding/json" "os" "os/signal" "syscall" @@ -56,8 +55,6 @@ func main() { pathPrefix := cfg.String("path-prefix", "", "Prefix which is optionally applied to all URL paths rendered by the blog") - httpAuthUsersStr := cfg.String("http-auth-users", "{}", "JSON object with usernames as values and password hashes (produced by the hash-password binary) as values. Denotes users which are able to edit server-side data") - // initialization err := cfg.Init(ctx) @@ -131,11 +128,6 @@ func main() { postStore := post.NewStore(postSQLDB) postAssetStore := post.NewAssetStore(postSQLDB) - var httpAuthUsers map[string]string - if err := json.Unmarshal([]byte(*httpAuthUsersStr), &httpAuthUsers); err != nil { - logger.Fatal(ctx, "unmarshaling -http-auth-users", err) - } - httpParams.Logger = logger.WithNamespace("http") httpParams.PowManager = powMgr httpParams.PathPrefix = *pathPrefix @@ -144,7 +136,6 @@ func main() { httpParams.MailingList = ml httpParams.GlobalRoom = chatGlobalRoom httpParams.UserIDCalculator = chatUserIDCalc - httpParams.AuthUsers = httpAuthUsers logger.Info(ctx, "listening") httpAPI, err := http.New(httpParams) diff --git a/srv/src/http/api.go b/srv/src/http/api.go index abf08e7..8e89c4e 100644 --- a/srv/src/http/api.go +++ b/srv/src/http/api.go @@ -4,6 +4,7 @@ package http import ( "context" "embed" + "encoding/json" "errors" "fmt" "html/template" @@ -57,6 +58,15 @@ type Params struct { func (p *Params) SetupCfg(cfg *cfg.Cfg) { cfg.StringVar(&p.ListenProto, "listen-proto", "tcp", "Protocol to listen for HTTP requests with") cfg.StringVar(&p.ListenAddr, "listen-addr", ":4000", "Address/path to listen for HTTP requests on") + + httpAuthUsersStr := cfg.String("http-auth-users", "{}", "JSON object with usernames as values and password hashes (produced by the hash-password binary) as values. Denotes users which are able to edit server-side data") + + cfg.OnInit(func(context.Context) error { + if err := json.Unmarshal([]byte(*httpAuthUsersStr), &p.AuthUsers); err != nil { + return fmt.Errorf("unmarshaling -http-auth-users: %w", err) + } + return nil + }) } // Annotate implements mctx.Annotator interface. diff --git a/srv/src/http/assets.go b/srv/src/http/assets.go index f782c69..aacef96 100644 --- a/srv/src/http/assets.go +++ b/srv/src/http/assets.go @@ -17,6 +17,15 @@ import ( "golang.org/x/image/draw" ) +func isImgResizable(id string) bool { + switch strings.ToLower(filepath.Ext(id)) { + case ".jpg", ".jpeg", ".png": + return true + default: + return false + } +} + func resizeImage(out io.Writer, in io.Reader, maxWidth float64) error { img, format, err := image.Decode(in) @@ -123,24 +132,21 @@ func (a *api) getPostAssetHandler() http.Handler { return } - switch ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(id), ".")); ext { - case "jpg", "jpeg", "png": - - if err := resizeImage(rw, buf, float64(maxWidth)); err != nil { - apiutil.InternalServerError( - rw, r, - fmt.Errorf( - "resizing image with id %q to size %d: %w", - id, maxWidth, err, - ), - ) - } - - default: - apiutil.BadRequest(rw, r, fmt.Errorf("cannot resize file with extension %q", ext)) + if !isImgResizable(id) { + apiutil.BadRequest(rw, r, fmt.Errorf("cannot resize file %q", id)) return } + if err := resizeImage(rw, buf, float64(maxWidth)); err != nil { + apiutil.InternalServerError( + rw, r, + fmt.Errorf( + "resizing image with id %q to size %d: %w", + id, maxWidth, err, + ), + ) + } + }) } diff --git a/srv/src/http/posts.go b/srv/src/http/posts.go index fd583ea..0aea3e3 100644 --- a/srv/src/http/posts.go +++ b/srv/src/http/posts.go @@ -1,6 +1,7 @@ package http import ( + "bytes" "errors" "fmt" "html/template" @@ -23,13 +24,24 @@ type postTplPayload struct { } func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload, error) { + + bodyTpl, err := a.parseTpl(storedPost.Body) + if err != nil { + return postTplPayload{}, fmt.Errorf("parsing post body as template: %w", err) + } + + bodyBuf := new(bytes.Buffer) + if err := bodyTpl.Execute(bodyBuf, nil); err != nil { + return postTplPayload{}, fmt.Errorf("executing post body as template: %w", err) + } + parserExt := parser.CommonExtensions | parser.AutoHeadingIDs parser := parser.NewWithExtensions(parserExt) htmlFlags := html.CommonFlags | html.HrefTargetBlank htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags}) - renderedBody := markdown.ToHTML([]byte(storedPost.Body), parser, htmlRenderer) + renderedBody := markdown.ToHTML(bodyBuf.Bytes(), parser, htmlRenderer) tplPayload := postTplPayload{ StoredPost: storedPost, diff --git a/srv/src/http/tpl.go b/srv/src/http/tpl.go index 65f23a7..62a4d06 100644 --- a/srv/src/http/tpl.go +++ b/srv/src/http/tpl.go @@ -1,6 +1,7 @@ package http import ( + "bytes" "embed" "fmt" "html/template" @@ -27,7 +28,7 @@ func mustReadTplFile(fileName string) string { return string(b) } -func (a *api) mustParseTpl(name string) *template.Template { +func (a *api) parseTpl(tplBody string) (*template.Template, error) { blogURL := func(path string) string { @@ -43,7 +44,9 @@ func (a *api) mustParseTpl(name string) *template.Template { return path } - tpl := template.New("").Funcs(template.FuncMap{ + tpl := template.New("root") + + tpl = tpl.Funcs(template.FuncMap{ "BlogURL": blogURL, "StaticURL": func(path string) string { path = filepath.Join("static", path) @@ -62,9 +65,39 @@ func (a *api) mustParseTpl(name string) *template.Template { }, }) - tpl = template.Must(tpl.Parse(mustReadTplFile(name))) + tpl = template.Must(tpl.New("image.html").Parse(mustReadTplFile("image.html"))) - return tpl + tpl = tpl.Funcs(template.FuncMap{ + "Image": func(id string) (template.HTML, error) { + + tplPayload := struct { + ID string + Resizable bool + }{ + ID: id, + Resizable: isImgResizable(id), + } + + buf := new(bytes.Buffer) + if err := tpl.ExecuteTemplate(buf, "image.html", tplPayload); err != nil { + return "", err + } + + return template.HTML(buf.Bytes()), nil + }, + }) + + var err error + + if tpl, err = tpl.New("").Parse(tplBody); err != nil { + return nil, err + } + + return tpl, nil +} + +func (a *api) mustParseTpl(name string) *template.Template { + return template.Must(a.parseTpl(mustReadTplFile(name))) } func (a *api) mustParseBasedTpl(name string) *template.Template { diff --git a/srv/src/http/tpl/image.html b/srv/src/http/tpl/image.html new file mode 100644 index 0000000..ba9b75d --- /dev/null +++ b/srv/src/http/tpl/image.html @@ -0,0 +1,5 @@ +
+ + + +