Implement image macro for rendering images
This commit is contained in:
parent
16cfbd1915
commit
af434077ef
@ -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 {
|
||||
|
5
srv/src/http/tpl/image.html
Normal file
5
srv/src/http/tpl/image.html
Normal file
@ -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…
Reference in New Issue
Block a user