Implement image macro for rendering images
This commit is contained in:
parent
16cfbd1915
commit
af434077ef
@ -25,12 +25,10 @@
|
|||||||
# pow
|
# pow
|
||||||
export MEDIOCRE_BLOG_POW_SECRET="${config.powSecret}"
|
export MEDIOCRE_BLOG_POW_SECRET="${config.powSecret}"
|
||||||
|
|
||||||
# listening
|
# http
|
||||||
export MEDIOCRE_BLOG_LISTEN_PROTO="${config.listenProto}"
|
export MEDIOCRE_BLOG_LISTEN_PROTO="${config.listenProto}"
|
||||||
export MEDIOCRE_BLOG_LISTEN_ADDR="${config.listenAddr}"
|
export MEDIOCRE_BLOG_LISTEN_ADDR="${config.listenAddr}"
|
||||||
|
export MEDIOCRE_BLOG_HTTP_AUTH_USERS='${builtins.toJSON config.httpAuthUsers}'
|
||||||
# api
|
|
||||||
export MEDIOCRE_BLOG_API_AUTH_USERS='${builtins.toJSON config.httpAuthUsers}'
|
|
||||||
'';
|
'';
|
||||||
|
|
||||||
build = buildGoModule {
|
build = buildGoModule {
|
||||||
|
@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"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")
|
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
|
// initialization
|
||||||
err := cfg.Init(ctx)
|
err := cfg.Init(ctx)
|
||||||
|
|
||||||
@ -131,11 +128,6 @@ func main() {
|
|||||||
postStore := post.NewStore(postSQLDB)
|
postStore := post.NewStore(postSQLDB)
|
||||||
postAssetStore := post.NewAssetStore(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.Logger = logger.WithNamespace("http")
|
||||||
httpParams.PowManager = powMgr
|
httpParams.PowManager = powMgr
|
||||||
httpParams.PathPrefix = *pathPrefix
|
httpParams.PathPrefix = *pathPrefix
|
||||||
@ -144,7 +136,6 @@ func main() {
|
|||||||
httpParams.MailingList = ml
|
httpParams.MailingList = ml
|
||||||
httpParams.GlobalRoom = chatGlobalRoom
|
httpParams.GlobalRoom = chatGlobalRoom
|
||||||
httpParams.UserIDCalculator = chatUserIDCalc
|
httpParams.UserIDCalculator = chatUserIDCalc
|
||||||
httpParams.AuthUsers = httpAuthUsers
|
|
||||||
|
|
||||||
logger.Info(ctx, "listening")
|
logger.Info(ctx, "listening")
|
||||||
httpAPI, err := http.New(httpParams)
|
httpAPI, err := http.New(httpParams)
|
||||||
|
@ -4,6 +4,7 @@ package http
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"embed"
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -57,6 +58,15 @@ type Params struct {
|
|||||||
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
func (p *Params) SetupCfg(cfg *cfg.Cfg) {
|
||||||
cfg.StringVar(&p.ListenProto, "listen-proto", "tcp", "Protocol to listen for HTTP requests with")
|
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")
|
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.
|
// Annotate implements mctx.Annotator interface.
|
||||||
|
@ -17,6 +17,15 @@ import (
|
|||||||
"golang.org/x/image/draw"
|
"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 {
|
func resizeImage(out io.Writer, in io.Reader, maxWidth float64) error {
|
||||||
|
|
||||||
img, format, err := image.Decode(in)
|
img, format, err := image.Decode(in)
|
||||||
@ -123,24 +132,21 @@ func (a *api) getPostAssetHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(id), ".")); ext {
|
if !isImgResizable(id) {
|
||||||
case "jpg", "jpeg", "png":
|
apiutil.BadRequest(rw, r, fmt.Errorf("cannot resize file %q", id))
|
||||||
|
|
||||||
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))
|
|
||||||
return
|
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
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -23,13 +24,24 @@ type postTplPayload struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload, error) {
|
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
|
parserExt := parser.CommonExtensions | parser.AutoHeadingIDs
|
||||||
parser := parser.NewWithExtensions(parserExt)
|
parser := parser.NewWithExtensions(parserExt)
|
||||||
|
|
||||||
htmlFlags := html.CommonFlags | html.HrefTargetBlank
|
htmlFlags := html.CommonFlags | html.HrefTargetBlank
|
||||||
htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags})
|
htmlRenderer := html.NewRenderer(html.RendererOptions{Flags: htmlFlags})
|
||||||
|
|
||||||
renderedBody := markdown.ToHTML([]byte(storedPost.Body), parser, htmlRenderer)
|
renderedBody := markdown.ToHTML(bodyBuf.Bytes(), parser, htmlRenderer)
|
||||||
|
|
||||||
tplPayload := postTplPayload{
|
tplPayload := postTplPayload{
|
||||||
StoredPost: storedPost,
|
StoredPost: storedPost,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
@ -27,7 +28,7 @@ func mustReadTplFile(fileName string) string {
|
|||||||
return string(b)
|
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 {
|
blogURL := func(path string) string {
|
||||||
|
|
||||||
@ -43,7 +44,9 @@ func (a *api) mustParseTpl(name string) *template.Template {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
tpl := template.New("").Funcs(template.FuncMap{
|
tpl := template.New("root")
|
||||||
|
|
||||||
|
tpl = tpl.Funcs(template.FuncMap{
|
||||||
"BlogURL": blogURL,
|
"BlogURL": blogURL,
|
||||||
"StaticURL": func(path string) string {
|
"StaticURL": func(path string) string {
|
||||||
path = filepath.Join("static", path)
|
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 {
|
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