mirror of https://github.com/jlelse/GoBlog
Some performance and memory improvements
This commit is contained in:
parent
48f2ac888b
commit
763188169f
26
cache.go
26
cache.go
|
@ -22,16 +22,14 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type cache struct {
|
type cache struct {
|
||||||
g singleflight.Group
|
g singleflight.Group
|
||||||
c *ristretto.Cache
|
c *ristretto.Cache
|
||||||
cfg *configCache
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) initCache() (err error) {
|
func (a *goBlog) initCache() (err error) {
|
||||||
a.cache = &cache{
|
a.cache = &cache{}
|
||||||
cfg: a.cfg.Cache,
|
if a.cfg.Cache != nil && !a.cfg.Cache.Enable {
|
||||||
}
|
// Cache disabled
|
||||||
if a.cache.cfg != nil && !a.cache.cfg.Enable {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
a.cache.c, err = ristretto.NewCache(&ristretto.Config{
|
a.cache.c, err = ristretto.NewCache(&ristretto.Config{
|
||||||
|
@ -50,13 +48,8 @@ func cacheLoggedIn(next http.Handler) http.Handler {
|
||||||
|
|
||||||
func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
|
func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if a.cache.c == nil {
|
|
||||||
// No cache configured
|
|
||||||
next.ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Do checks
|
// Do checks
|
||||||
if !cacheable(r) {
|
if a.cache.c == nil || !cacheable(r) {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -79,7 +72,7 @@ func (a *goBlog) cacheMiddleware(next http.Handler) http.Handler {
|
||||||
})
|
})
|
||||||
ci := cacheInterface.(*cacheItem)
|
ci := cacheInterface.(*cacheItem)
|
||||||
// copy and set headers
|
// copy and set headers
|
||||||
a.cache.setHeaders(w, ci)
|
a.setCacheHeaders(w, ci)
|
||||||
// check conditional request
|
// check conditional request
|
||||||
if ifNoneMatchHeader := r.Header.Get("If-None-Match"); ifNoneMatchHeader != "" && ifNoneMatchHeader == ci.eTag {
|
if ifNoneMatchHeader := r.Header.Get("If-None-Match"); ifNoneMatchHeader != "" && ifNoneMatchHeader == ci.eTag {
|
||||||
// send 304
|
// send 304
|
||||||
|
@ -129,7 +122,7 @@ func cacheKey(r *http.Request) string {
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cache) setHeaders(w http.ResponseWriter, cache *cacheItem) {
|
func (a *goBlog) setCacheHeaders(w http.ResponseWriter, cache *cacheItem) {
|
||||||
// Copy headers
|
// Copy headers
|
||||||
for k, v := range cache.header.Clone() {
|
for k, v := range cache.header.Clone() {
|
||||||
w.Header()[k] = v
|
w.Header()[k] = v
|
||||||
|
@ -141,7 +134,8 @@ func (c *cache) setHeaders(w http.ResponseWriter, cache *cacheItem) {
|
||||||
if cache.expiration != 0 {
|
if cache.expiration != 0 {
|
||||||
w.Header().Set("Cache-Control", fmt.Sprintf("public,max-age=%d,stale-while-revalidate=%d", cache.expiration, cache.expiration))
|
w.Header().Set("Cache-Control", fmt.Sprintf("public,max-age=%d,stale-while-revalidate=%d", cache.expiration, cache.expiration))
|
||||||
} else {
|
} else {
|
||||||
w.Header().Set("Cache-Control", fmt.Sprintf("public,max-age=%d,s-max-age=%d,stale-while-revalidate=%d", c.cfg.Expiration, c.cfg.Expiration/3, c.cfg.Expiration))
|
exp := a.cfg.Cache.Expiration
|
||||||
|
w.Header().Set("Cache-Control", fmt.Sprintf("public,max-age=%d,s-max-age=%d,stale-while-revalidate=%d", exp, exp/3, exp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.goblog.app/app/pkgs/bufferpool"
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
ws "nhooyr.io/websocket"
|
ws "nhooyr.io/websocket"
|
||||||
|
@ -71,9 +72,11 @@ func (a *goBlog) createMarkdownPreview(blog string, markdown []byte) (rendered [
|
||||||
p.RenderedTitle = a.renderMdTitle(t)
|
p.RenderedTitle = a.renderMdTitle(t)
|
||||||
}
|
}
|
||||||
// Render post
|
// Render post
|
||||||
var hb htmlBuilder
|
buf := bufferpool.Get()
|
||||||
a.renderEditorPreview(&hb, a.cfg.Blogs[blog], p)
|
hb := newHtmlBuilder(buf)
|
||||||
rendered = hb.Bytes()
|
a.renderEditorPreview(hb, a.cfg.Blogs[blog], p)
|
||||||
|
rendered = buf.Bytes()
|
||||||
|
bufferpool.Put(buf)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -61,7 +61,7 @@ require (
|
||||||
golang.org/x/text v0.3.7
|
golang.org/x/text v0.3.7
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
nhooyr.io/websocket v1.8.7
|
nhooyr.io/websocket v1.8.7
|
||||||
tailscale.com v1.20.2
|
tailscale.com v1.20.3
|
||||||
// main
|
// main
|
||||||
willnorris.com/go/microformats v1.1.2-0.20210827044458-ff2a6ae41971
|
willnorris.com/go/microformats v1.1.2-0.20210827044458-ff2a6ae41971
|
||||||
)
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -823,8 +823,8 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
|
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
|
||||||
tailscale.com v1.20.2 h1:Vk6SVGmczDFSx+PYjbKMa8gIC9Y9Rq+vT1XoZni+fkA=
|
tailscale.com v1.20.3 h1:C3g2AgmQaOi0YT5dAal9mslugPXMxwj0EXY7YfL2QrA=
|
||||||
tailscale.com v1.20.2/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4=
|
tailscale.com v1.20.3/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4=
|
||||||
willnorris.com/go/microformats v1.1.2-0.20210827044458-ff2a6ae41971 h1:b4juh5znIpBA1KnzHMP0UB4Cs+3/0b0XfchkWE81FXw=
|
willnorris.com/go/microformats v1.1.2-0.20210827044458-ff2a6ae41971 h1:b4juh5znIpBA1KnzHMP0UB4Cs+3/0b0XfchkWE81FXw=
|
||||||
willnorris.com/go/microformats v1.1.2-0.20210827044458-ff2a6ae41971/go.mod h1:kvVnWrkkEscVAIITCEoiTX66Hcyg59C7q0E49mb9TJ0=
|
willnorris.com/go/microformats v1.1.2-0.20210827044458-ff2a6ae41971/go.mod h1:kvVnWrkkEscVAIITCEoiTX66Hcyg59C7q0E49mb9TJ0=
|
||||||
willnorris.com/go/webmention v0.0.0-20211028201829-b0044f1a24d0 h1:3/ozQ2qGZat82ON3AYMTot3gCg/vU7tgn/LYSJbkVPM=
|
willnorris.com/go/webmention v0.0.0-20211028201829-b0044f1a24d0 h1:3/ozQ2qGZat82ON3AYMTot3gCg/vU7tgn/LYSJbkVPM=
|
||||||
|
|
22
markdown.go
22
markdown.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"html/template"
|
"html/template"
|
||||||
|
"io"
|
||||||
|
|
||||||
marktag "git.jlel.se/jlelse/goldmark-mark"
|
marktag "git.jlel.se/jlelse/goldmark-mark"
|
||||||
chromahtml "github.com/alecthomas/chroma/formatters/html"
|
chromahtml "github.com/alecthomas/chroma/formatters/html"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"github.com/yuin/goldmark/renderer"
|
"github.com/yuin/goldmark/renderer"
|
||||||
"github.com/yuin/goldmark/renderer/html"
|
"github.com/yuin/goldmark/renderer/html"
|
||||||
"github.com/yuin/goldmark/util"
|
"github.com/yuin/goldmark/util"
|
||||||
|
"go.goblog.app/app/pkgs/bufferpool"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *goBlog) initMarkdown() {
|
func (a *goBlog) initMarkdown() {
|
||||||
|
@ -76,13 +78,20 @@ func (a *goBlog) initMarkdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) renderMarkdown(source string, absoluteLinks bool) (rendered []byte, err error) {
|
func (a *goBlog) renderMarkdown(source string, absoluteLinks bool) (rendered []byte, err error) {
|
||||||
var buffer bytes.Buffer
|
buffer := bufferpool.Get()
|
||||||
|
a.renderMarkdownToWriter(buffer, source, absoluteLinks)
|
||||||
|
rendered = buffer.Bytes()
|
||||||
|
bufferpool.Put(buffer)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) renderMarkdownToWriter(w io.Writer, source string, absoluteLinks bool) (err error) {
|
||||||
if absoluteLinks {
|
if absoluteLinks {
|
||||||
err = a.absoluteMd.Convert([]byte(source), &buffer)
|
err = a.absoluteMd.Convert([]byte(source), w)
|
||||||
} else {
|
} else {
|
||||||
err = a.md.Convert([]byte(source), &buffer)
|
err = a.md.Convert([]byte(source), w)
|
||||||
}
|
}
|
||||||
return buffer.Bytes(), err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) renderMarkdownAsHTML(source string, absoluteLinks bool) (rendered template.HTML, err error) {
|
func (a *goBlog) renderMarkdownAsHTML(source string, absoluteLinks bool) (rendered template.HTML, err error) {
|
||||||
|
@ -113,8 +122,9 @@ func (a *goBlog) renderMdTitle(s string) string {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
var buffer bytes.Buffer
|
buffer := bufferpool.Get()
|
||||||
err := a.titleMd.Convert([]byte(s), &buffer)
|
defer bufferpool.Put(buffer)
|
||||||
|
err := a.titleMd.Convert([]byte(s), buffer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package bufferpool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var bufferPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var poolMutex sync.Mutex
|
||||||
|
|
||||||
|
func Get() *bytes.Buffer {
|
||||||
|
poolMutex.Lock()
|
||||||
|
defer poolMutex.Unlock()
|
||||||
|
return bufferPool.Get().(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Put(bufs ...*bytes.Buffer) {
|
||||||
|
poolMutex.Lock()
|
||||||
|
defer poolMutex.Unlock()
|
||||||
|
for _, buf := range bufs {
|
||||||
|
buf.Reset()
|
||||||
|
bufferPool.Put(buf)
|
||||||
|
}
|
||||||
|
}
|
21
render.go
21
render.go
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
@ -9,6 +8,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"go.goblog.app/app/pkgs/bufferpool"
|
||||||
"go.goblog.app/app/pkgs/contenttype"
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,9 +33,12 @@ func (a *goBlog) initRendering() error {
|
||||||
"html": wrapStringAsHTML,
|
"html": wrapStringAsHTML,
|
||||||
// Code based rendering
|
// Code based rendering
|
||||||
"tor": func(rd *renderData) template.HTML {
|
"tor": func(rd *renderData) template.HTML {
|
||||||
var hb htmlBuilder
|
buf := bufferpool.Get()
|
||||||
a.renderTorNotice(&hb, rd)
|
hb := newHtmlBuilder(buf)
|
||||||
return hb.html()
|
a.renderTorNotice(hb, rd)
|
||||||
|
res := template.HTML(buf.String())
|
||||||
|
bufferpool.Put(buf)
|
||||||
|
return res
|
||||||
},
|
},
|
||||||
// Others
|
// Others
|
||||||
"dateformat": dateFormat,
|
"dateformat": dateFormat,
|
||||||
|
@ -103,16 +106,14 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
||||||
// Set content type
|
// Set content type
|
||||||
w.Header().Set(contentType, contenttype.HTMLUTF8)
|
w.Header().Set(contentType, contenttype.HTMLUTF8)
|
||||||
// Render template and write minified HTML
|
// Render template and write minified HTML
|
||||||
var templateBuffer bytes.Buffer
|
buf := bufferpool.Get()
|
||||||
if err := a.templates[template].ExecuteTemplate(&templateBuffer, template, data); err != nil {
|
defer bufferpool.Put(buf)
|
||||||
|
if err := a.templates[template].ExecuteTemplate(buf, template, data); err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
if err := a.min.Minify(contenttype.HTML, w, &templateBuffer); err != nil {
|
_ = a.min.Minify(contenttype.HTML, w, buf)
|
||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) renderNew(w http.ResponseWriter, r *http.Request, f func(*htmlBuilder, *renderData), data *renderData) {
|
func (a *goBlog) renderNew(w http.ResponseWriter, r *http.Request, f func(*htmlBuilder, *renderData), data *renderData) {
|
||||||
|
|
16
ui.go
16
ui.go
|
@ -71,7 +71,7 @@ func (a *goBlog) renderBase(hb *htmlBuilder, rd *renderData, title, main func(hb
|
||||||
// Announcement
|
// Announcement
|
||||||
if ann := rd.Blog.Announcement; ann != nil && ann.Text != "" {
|
if ann := rd.Blog.Announcement; ann != nil && ann.Text != "" {
|
||||||
hb.writeElementOpen("div", "id", "announcement", "data-nosnippet", "")
|
hb.writeElementOpen("div", "id", "announcement", "data-nosnippet", "")
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(ann.Text))
|
_ = a.renderMarkdownToWriter(hb, ann.Text, false)
|
||||||
hb.writeElementClose("div")
|
hb.writeElementClose("div")
|
||||||
}
|
}
|
||||||
// Header
|
// Header
|
||||||
|
@ -270,7 +270,7 @@ func (a *goBlog) renderSearch(hb *htmlBuilder, rd *renderData) {
|
||||||
// Description
|
// Description
|
||||||
if sc.Description != "" {
|
if sc.Description != "" {
|
||||||
titleOrDesc = true
|
titleOrDesc = true
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(sc.Description))
|
_ = a.renderMarkdownToWriter(hb, sc.Description, false)
|
||||||
}
|
}
|
||||||
if titleOrDesc {
|
if titleOrDesc {
|
||||||
hb.writeElementOpen("hr")
|
hb.writeElementOpen("hr")
|
||||||
|
@ -381,7 +381,7 @@ func (a *goBlog) renderIndex(hb *htmlBuilder, rd *renderData) {
|
||||||
// Description
|
// Description
|
||||||
if id.description != "" {
|
if id.description != "" {
|
||||||
titleOrDesc = true
|
titleOrDesc = true
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(id.description))
|
_ = a.renderMarkdownToWriter(hb, id.description, false)
|
||||||
}
|
}
|
||||||
if titleOrDesc {
|
if titleOrDesc {
|
||||||
hb.writeElementOpen("hr")
|
hb.writeElementOpen("hr")
|
||||||
|
@ -445,7 +445,7 @@ func (a *goBlog) renderBlogStats(hb *htmlBuilder, rd *renderData) {
|
||||||
}
|
}
|
||||||
// Description
|
// Description
|
||||||
if bs.Description != "" {
|
if bs.Description != "" {
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(bs.Description))
|
_ = a.renderMarkdownToWriter(hb, bs.Description, false)
|
||||||
}
|
}
|
||||||
// Table
|
// Table
|
||||||
hb.writeElementOpen("p", "id", "loading", "data-table", bsd.tableUrl)
|
hb.writeElementOpen("p", "id", "loading", "data-table", bsd.tableUrl)
|
||||||
|
@ -663,7 +663,7 @@ func (a *goBlog) renderBlogroll(hb *htmlBuilder, rd *renderData) {
|
||||||
// Description
|
// Description
|
||||||
if bd.description != "" {
|
if bd.description != "" {
|
||||||
hb.writeElementOpen("p")
|
hb.writeElementOpen("p")
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(bd.description))
|
_ = a.renderMarkdownToWriter(hb, bd.description, false)
|
||||||
hb.writeElementClose("p")
|
hb.writeElementClose("p")
|
||||||
}
|
}
|
||||||
// Download button
|
// Download button
|
||||||
|
@ -745,7 +745,7 @@ func (a *goBlog) renderContact(hb *htmlBuilder, rd *renderData) {
|
||||||
}
|
}
|
||||||
// Description
|
// Description
|
||||||
if cd.description != "" {
|
if cd.description != "" {
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(cd.description))
|
_ = a.renderMarkdownToWriter(hb, cd.description, false)
|
||||||
}
|
}
|
||||||
// Form
|
// Form
|
||||||
hb.writeElementOpen("form", "class", "fw p", "method", "post")
|
hb.writeElementOpen("form", "class", "fw p", "method", "post")
|
||||||
|
@ -760,7 +760,7 @@ func (a *goBlog) renderContact(hb *htmlBuilder, rd *renderData) {
|
||||||
hb.writeElementClose("textarea")
|
hb.writeElementClose("textarea")
|
||||||
// Send
|
// Send
|
||||||
if cd.privacy != "" {
|
if cd.privacy != "" {
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(cd.privacy))
|
_ = a.renderMarkdownToWriter(hb, cd.privacy, false)
|
||||||
hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "contactagreesend"))
|
hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "contactagreesend"))
|
||||||
} else {
|
} else {
|
||||||
hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "contactsend"))
|
hb.writeElementOpen("input", "type", "submit", "value", a.ts.GetTemplateStringVariant(rd.Blog.Lang, "contactsend"))
|
||||||
|
@ -837,7 +837,7 @@ func (a *goBlog) renderTaxonomy(hb *htmlBuilder, rd *renderData) {
|
||||||
}
|
}
|
||||||
// Description
|
// Description
|
||||||
if trd.taxonomy.Description != "" {
|
if trd.taxonomy.Description != "" {
|
||||||
hb.writeHtml(a.safeRenderMarkdownAsHTML(trd.taxonomy.Description))
|
_ = a.renderMarkdownToWriter(hb, trd.taxonomy.Description, false)
|
||||||
}
|
}
|
||||||
// List
|
// List
|
||||||
for _, valGroup := range trd.valueGroups {
|
for _, valGroup := range trd.valueGroups {
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (a *goBlog) renderSummary(hb *htmlBuilder, bc *configBlog, p *post, typ sum
|
||||||
photos := a.photoLinks(p)
|
photos := a.photoLinks(p)
|
||||||
if typ == photoSummary && len(photos) > 0 {
|
if typ == photoSummary && len(photos) > 0 {
|
||||||
for _, photo := range photos {
|
for _, photo := range photos {
|
||||||
hb.write(string(a.safeRenderMarkdownAsHTML(fmt.Sprintf("![](%s)", photo))))
|
_ = a.renderMarkdownToWriter(hb, fmt.Sprintf("![](%s)", photo), false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Post meta
|
// Post meta
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
|
@ -9,8 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type htmlBuilder struct {
|
type htmlBuilder struct {
|
||||||
w io.Writer
|
w io.Writer
|
||||||
buf bytes.Buffer
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHtmlBuilder(w io.Writer) *htmlBuilder {
|
func newHtmlBuilder(w io.Writer) *htmlBuilder {
|
||||||
|
@ -20,10 +18,7 @@ func newHtmlBuilder(w io.Writer) *htmlBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *htmlBuilder) getWriter() io.Writer {
|
func (h *htmlBuilder) getWriter() io.Writer {
|
||||||
if h.w != nil {
|
return h.w
|
||||||
return h.w
|
|
||||||
}
|
|
||||||
return &h.buf
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *htmlBuilder) Write(p []byte) (int, error) {
|
func (h *htmlBuilder) Write(p []byte) (int, error) {
|
||||||
|
@ -34,22 +29,6 @@ func (h *htmlBuilder) WriteString(s string) (int, error) {
|
||||||
return io.WriteString(h.getWriter(), s)
|
return io.WriteString(h.getWriter(), s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *htmlBuilder) Read(p []byte) (int, error) {
|
|
||||||
return h.buf.Read(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *htmlBuilder) String() string {
|
|
||||||
return h.buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *htmlBuilder) Bytes() []byte {
|
|
||||||
return h.buf.Bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *htmlBuilder) html() template.HTML {
|
|
||||||
return template.HTML(h.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *htmlBuilder) write(s string) {
|
func (h *htmlBuilder) write(s string) {
|
||||||
_, _ = h.WriteString(s)
|
_, _ = h.WriteString(s)
|
||||||
}
|
}
|
||||||
|
|
55
ui_test.go
55
ui_test.go
|
@ -1,7 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"html/template"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -14,7 +14,6 @@ import (
|
||||||
|
|
||||||
var _ io.Writer = &htmlBuilder{}
|
var _ io.Writer = &htmlBuilder{}
|
||||||
var _ io.StringWriter = &htmlBuilder{}
|
var _ io.StringWriter = &htmlBuilder{}
|
||||||
var _ io.Reader = &htmlBuilder{}
|
|
||||||
|
|
||||||
func Test_renderPostTax(t *testing.T) {
|
func Test_renderPostTax(t *testing.T) {
|
||||||
app := &goBlog{
|
app := &goBlog{
|
||||||
|
@ -30,13 +29,16 @@ func Test_renderPostTax(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
var hb htmlBuilder
|
buf := &bytes.Buffer{}
|
||||||
app.renderPostTax(&hb, p, app.cfg.Blogs["default"])
|
hb := newHtmlBuilder(buf)
|
||||||
res := hb.html()
|
|
||||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
app.renderPostTax(hb, p, app.cfg.Blogs["default"])
|
||||||
|
res := buf.String()
|
||||||
|
|
||||||
|
_, err := goquery.NewDocumentFromReader(strings.NewReader(res))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, template.HTML("<p><strong>Tags</strong>: <a class=\"p-category\" rel=\"tag\" href=\"/tags/bar\">Bar</a>, <a class=\"p-category\" rel=\"tag\" href=\"/tags/foo\">Foo</a></p>"), res)
|
assert.Equal(t, "<p><strong>Tags</strong>: <a class=\"p-category\" rel=\"tag\" href=\"/tags/bar\">Bar</a>, <a class=\"p-category\" rel=\"tag\" href=\"/tags/foo\">Foo</a></p>", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_renderOldContentWarning(t *testing.T) {
|
func Test_renderOldContentWarning(t *testing.T) {
|
||||||
|
@ -51,13 +53,16 @@ func Test_renderOldContentWarning(t *testing.T) {
|
||||||
Published: "2018-01-01",
|
Published: "2018-01-01",
|
||||||
}
|
}
|
||||||
|
|
||||||
var hb htmlBuilder
|
buf := &bytes.Buffer{}
|
||||||
app.renderOldContentWarning(&hb, p, app.cfg.Blogs["default"])
|
hb := newHtmlBuilder(buf)
|
||||||
res := hb.html()
|
|
||||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
app.renderOldContentWarning(hb, p, app.cfg.Blogs["default"])
|
||||||
|
res := buf.String()
|
||||||
|
|
||||||
|
_, err := goquery.NewDocumentFromReader(strings.NewReader(res))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, template.HTML("<strong class=\"p border-top border-bottom\">⚠️ This entry is already over one year old. It may no longer be up to date. Opinions may have changed.</strong>"), res)
|
assert.Equal(t, "<strong class=\"p border-top border-bottom\">⚠️ This entry is already over one year old. It may no longer be up to date. Opinions may have changed.</strong>", res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_renderInteractions(t *testing.T) {
|
func Test_renderInteractions(t *testing.T) {
|
||||||
|
@ -112,16 +117,19 @@ func Test_renderInteractions(t *testing.T) {
|
||||||
err = app.db.approveWebmentionId(2)
|
err = app.db.approveWebmentionId(2)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var hb htmlBuilder
|
buf := &bytes.Buffer{}
|
||||||
app.renderInteractions(&hb, app.cfg.Blogs["default"], "https://example.com/testpost1")
|
hb := newHtmlBuilder(buf)
|
||||||
res := hb.html()
|
|
||||||
_, err = goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
app.renderInteractions(hb, app.cfg.Blogs["default"], "https://example.com/testpost1")
|
||||||
|
res := buf.Bytes()
|
||||||
|
|
||||||
|
_, err = goquery.NewDocumentFromReader(bytes.NewReader(res))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
expected, err := os.ReadFile("testdata/interactionstest.html")
|
expected, err := os.ReadFile("testdata/interactionstest.html")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, template.HTML(expected), res)
|
assert.Equal(t, expected, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_renderAuthor(t *testing.T) {
|
func Test_renderAuthor(t *testing.T) {
|
||||||
|
@ -134,11 +142,14 @@ func Test_renderAuthor(t *testing.T) {
|
||||||
_ = app.initDatabase(false)
|
_ = app.initDatabase(false)
|
||||||
app.initComponents(false)
|
app.initComponents(false)
|
||||||
|
|
||||||
var hb htmlBuilder
|
buf := &bytes.Buffer{}
|
||||||
app.renderAuthor(&hb)
|
hb := newHtmlBuilder(buf)
|
||||||
res := hb.html()
|
|
||||||
_, err := goquery.NewDocumentFromReader(strings.NewReader(string(res)))
|
app.renderAuthor(hb)
|
||||||
|
res := buf.String()
|
||||||
|
|
||||||
|
_, err := goquery.NewDocumentFromReader(strings.NewReader(res))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, template.HTML("<div class=\"p-author h-card hide\"><data class=\"u-photo\" value=\"https://example.com/picture.jpg\"></data><a class=\"p-name u-url\" rel=\"me\" href=\"/\">John Doe</a></div>"), res)
|
assert.Equal(t, "<div class=\"p-author h-card hide\"><data class=\"u-photo\" value=\"https://example.com/picture.jpg\"></data><a class=\"p-name u-url\" rel=\"me\" href=\"/\">John Doe</a></div>", res)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue