Implement image macro for rendering images

main
Brian Picciano 2 years ago
parent 16cfbd1915
commit af434077ef
  1. 6
      srv/default.nix
  2. 9
      srv/src/cmd/mediocre-blog/main.go
  3. 10
      srv/src/http/api.go
  4. 36
      srv/src/http/assets.go
  5. 14
      srv/src/http/posts.go
  6. 41
      srv/src/http/tpl.go
  7. 5
      srv/src/http/tpl/image.html

@ -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 {

@ -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)

@ -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.

@ -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,
),
)
}
})
}

@ -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,

@ -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 {

@ -0,0 +1,5 @@
<div style="text-align: center;">
<a href="{{ AssetURL .ID }}" target="_blank">
<img src="{{ AssetURL .ID }}{{ if .Resizable }}?w=800{{ end }}" />
</a>
</div>
Loading…
Cancel
Save