mirror of https://github.com/jlelse/GoBlog
More optimizations
This commit is contained in:
parent
6e767e3612
commit
d8caf1e6f5
|
@ -6,7 +6,6 @@ import (
|
|||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -120,8 +119,7 @@ func (a *goBlog) apSendSigned(blogIri, to string, activity []byte) error {
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
if !apRequestIsSuccess(resp.StatusCode) {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return fmt.Errorf("signed request failed with status %d: %s", resp.StatusCode, string(body))
|
||||
return fmt.Errorf("signed request failed with status %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ func (a *goBlog) captchaMiddleware(next http.Handler) http.Handler {
|
|||
}
|
||||
// Encode original request
|
||||
h, _ := json.Marshal(r.Header)
|
||||
b, _ := io.ReadAll(io.LimitReader(r.Body, 20*1000*1000)) // Only allow 20 MB
|
||||
b, _ := io.ReadAll(io.LimitReader(r.Body, 20000000)) // Only allow 20 MB
|
||||
_ = r.Body.Close()
|
||||
if len(b) == 0 {
|
||||
// Maybe it's a form
|
||||
|
|
19
editor.go
19
editor.go
|
@ -36,7 +36,7 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
|
|||
defer cancel()
|
||||
for {
|
||||
// Retrieve content
|
||||
mt, message, err := c.Read(ctx)
|
||||
mt, message, err := c.Reader(ctx)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
@ -48,21 +48,24 @@ func (a *goBlog) serveEditorPreview(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
break
|
||||
}
|
||||
a.createMarkdownPreview(w, blog, string(message))
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
a.createMarkdownPreview(w, blog, message)
|
||||
if err = w.Close(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *goBlog) createMarkdownPreview(w io.Writer, blog string, markdown string) {
|
||||
func (a *goBlog) createMarkdownPreview(w io.Writer, blog string, markdown io.Reader) {
|
||||
md, err := io.ReadAll(markdown)
|
||||
if err != nil {
|
||||
_, _ = io.WriteString(w, err.Error())
|
||||
return
|
||||
}
|
||||
p := &post{
|
||||
Blog: blog,
|
||||
Content: markdown,
|
||||
Content: string(md),
|
||||
}
|
||||
err := a.computeExtraPostParameters(p)
|
||||
if err != nil {
|
||||
if err = a.computeExtraPostParameters(p); err != nil {
|
||||
_, _ = io.WriteString(w, err.Error())
|
||||
return
|
||||
}
|
||||
|
|
68
markdown.go
68
markdown.go
|
@ -15,7 +15,6 @@ import (
|
|||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/util"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
)
|
||||
|
||||
func (a *goBlog) initMarkdown() {
|
||||
|
@ -76,14 +75,6 @@ func (a *goBlog) initMarkdown() {
|
|||
)
|
||||
}
|
||||
|
||||
func (a *goBlog) renderMarkdown(source string, absoluteLinks bool) (rendered []byte, err error) {
|
||||
buffer := bufferpool.Get()
|
||||
err = 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 {
|
||||
err = a.absoluteMd.Convert([]byte(source), w)
|
||||
|
@ -97,24 +88,34 @@ func (a *goBlog) renderText(s string) string {
|
|||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
h, err := a.renderMarkdown(s, false)
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
var err error
|
||||
go func() {
|
||||
err = a.renderMarkdownToWriter(pipeWriter, s, false)
|
||||
_ = pipeWriter.Close()
|
||||
}()
|
||||
text := htmlTextFromReader(pipeReader)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return htmlText(string(h))
|
||||
return text
|
||||
}
|
||||
|
||||
func (a *goBlog) renderMdTitle(s string) string {
|
||||
if s == "" {
|
||||
return ""
|
||||
}
|
||||
buffer := bufferpool.Get()
|
||||
defer bufferpool.Put(buffer)
|
||||
err := a.titleMd.Convert([]byte(s), buffer)
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
var err error
|
||||
go func() {
|
||||
err = a.titleMd.Convert([]byte(s), pipeWriter)
|
||||
_ = pipeWriter.Close()
|
||||
}()
|
||||
text := htmlTextFromReader(pipeReader)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return htmlText(buffer.String())
|
||||
return text
|
||||
}
|
||||
|
||||
// Extensions etc...
|
||||
|
@ -177,27 +178,24 @@ func (c *customRenderer) renderImage(w util.BufWriter, source []byte, node ast.N
|
|||
return ast.WalkContinue, nil
|
||||
}
|
||||
n := node.(*ast.Image)
|
||||
// Make URL absolute if it's relative
|
||||
destination := util.URLEscape(n.Destination, true)
|
||||
if c.absoluteLinks && c.publicAddress != "" && bytes.HasPrefix(destination, []byte("/")) {
|
||||
destination = util.EscapeHTML(append([]byte(c.publicAddress), destination...))
|
||||
} else {
|
||||
destination = util.EscapeHTML(destination)
|
||||
dest := string(n.Destination)
|
||||
// Make destination absolute if it's relative
|
||||
if c.absoluteLinks && c.publicAddress != "" {
|
||||
resolved, err := resolveURLReferences(c.publicAddress, dest)
|
||||
if err != nil {
|
||||
return ast.WalkStop, err
|
||||
}
|
||||
if len(resolved) > 0 {
|
||||
dest = resolved[0]
|
||||
}
|
||||
}
|
||||
_, _ = w.WriteString("<a href=\"")
|
||||
_, _ = w.Write(destination)
|
||||
_, _ = w.WriteString("\">")
|
||||
_, _ = w.WriteString("<img src=\"")
|
||||
_, _ = w.Write(destination)
|
||||
_, _ = w.WriteString("\" alt=\"")
|
||||
_, _ = w.Write(util.EscapeHTML(n.Text(source)))
|
||||
_ = w.WriteByte('"')
|
||||
_, _ = w.WriteString(" loading=\"lazy\"")
|
||||
if n.Title != nil {
|
||||
_, _ = w.WriteString(" title=\"")
|
||||
_, _ = w.Write(n.Title)
|
||||
_ = w.WriteByte('"')
|
||||
hb := newHtmlBuilder(w)
|
||||
hb.writeElementOpen("a", "href", dest)
|
||||
imgEls := []interface{}{"src", dest, "alt", string(n.Text(source)), "loading", "lazy"}
|
||||
if len(n.Title) > 0 {
|
||||
imgEls = append(imgEls, "title", string(n.Title))
|
||||
}
|
||||
_, _ = w.WriteString("></a>")
|
||||
hb.writeElementOpen("img", imgEls...)
|
||||
hb.writeElementClose("a")
|
||||
return ast.WalkSkipChildren, nil
|
||||
}
|
||||
|
|
|
@ -6,8 +6,17 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
)
|
||||
|
||||
func (a *goBlog) renderMarkdown(source string, absoluteLinks bool) (rendered []byte, err error) {
|
||||
buffer := bufferpool.Get()
|
||||
err = a.renderMarkdownToWriter(buffer, source, absoluteLinks)
|
||||
rendered = buffer.Bytes()
|
||||
bufferpool.Put(buffer)
|
||||
return
|
||||
}
|
||||
|
||||
func Test_markdown(t *testing.T) {
|
||||
t.Run("Basic Markdown tests", func(t *testing.T) {
|
||||
app := &goBlog{
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
func Test_compress(t *testing.T) {
|
||||
fakeFileContent := "Test"
|
||||
hash := sha256.New()
|
||||
io.WriteString(hash, fakeFileContent)
|
||||
_, _ = io.WriteString(hash, fakeFileContent)
|
||||
fakeSha256 := fmt.Sprintf("%x", hash.Sum(nil))
|
||||
|
||||
var uf mediaStorageSaveFunc = func(filename string, f io.Reader) (location string, err error) {
|
||||
|
|
|
@ -113,8 +113,8 @@ func (a *goBlog) serveMicropubPost(w http.ResponseWriter, r *http.Request) {
|
|||
a.micropubCreatePostFromForm(w, r)
|
||||
case contenttype.JSON:
|
||||
parsedMfItem := µformatItem{}
|
||||
b, _ := io.ReadAll(io.LimitReader(r.Body, 10000000)) // 10 MB
|
||||
if err := json.Unmarshal(b, parsedMfItem); err != nil {
|
||||
err := json.NewDecoder(io.LimitReader(r.Body, 10000000)).Decode(parsedMfItem)
|
||||
if err != nil {
|
||||
a.serveError(w, r, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -93,13 +93,14 @@ func (a *goBlog) postSummary(p *post) (summary string) {
|
|||
if summary != "" {
|
||||
return
|
||||
}
|
||||
if splitted := strings.Split(p.Content, summaryDivider); len(splitted) > 1 {
|
||||
rendered, _ := a.renderMarkdown(splitted[0], false)
|
||||
summary = htmlText(string(rendered))
|
||||
} else {
|
||||
rendered, _ := a.renderMarkdown(splitted[0], false)
|
||||
summary = strings.Split(htmlText(string(rendered)), "\n\n")[0]
|
||||
splitted := strings.Split(p.Content, summaryDivider)
|
||||
hasDivider := len(splitted) > 1
|
||||
markdown := splitted[0]
|
||||
summary = a.renderText(markdown)
|
||||
if !hasDivider {
|
||||
summary = strings.Split(summary, "\n\n")[0]
|
||||
}
|
||||
summary = strings.TrimSpace(strings.ReplaceAll(summary, "\n\n", " "))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
17
render.go
17
render.go
|
@ -1,9 +1,9 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
|
@ -39,13 +39,14 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
|||
// Write status code
|
||||
w.WriteHeader(statusCode)
|
||||
// Render
|
||||
buf := bufferpool.Get()
|
||||
mw := a.min.Writer(contenttype.HTML, buf)
|
||||
hb := newHtmlBuilder(mw)
|
||||
f(hb, data)
|
||||
_ = mw.Close()
|
||||
_, _ = buf.WriteTo(w)
|
||||
bufferpool.Put(buf)
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
go func() {
|
||||
mw := a.min.Writer(contenttype.HTML, pipeWriter)
|
||||
f(newHtmlBuilder(mw), data)
|
||||
_ = mw.Close()
|
||||
_ = pipeWriter.Close()
|
||||
}()
|
||||
_, _ = io.Copy(w, pipeReader)
|
||||
}
|
||||
|
||||
func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -34,15 +33,11 @@ func (a *goBlog) initTemplateAssets() error {
|
|||
return err
|
||||
}
|
||||
// Compile asset and close file
|
||||
compiled, err := a.compileAsset(path, file)
|
||||
err = a.compileAsset(strings.TrimPrefix(path, assetsFolder+"/"), file)
|
||||
_ = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Add to map
|
||||
if compiled != "" {
|
||||
a.assetFileNames[strings.TrimPrefix(path, assetsFolder+"/")] = compiled
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
|
@ -55,7 +50,7 @@ func (a *goBlog) initTemplateAssets() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *goBlog) compileAsset(name string, read io.Reader) (string, error) {
|
||||
func (a *goBlog) compileAsset(name string, read io.Reader) error {
|
||||
ext := path.Ext(name)
|
||||
switch ext {
|
||||
case ".js":
|
||||
|
@ -69,16 +64,18 @@ func (a *goBlog) compileAsset(name string, read io.Reader) (string, error) {
|
|||
hash := sha256.New()
|
||||
body, err := io.ReadAll(io.TeeReader(read, hash))
|
||||
if err != nil {
|
||||
return "", err
|
||||
return err
|
||||
}
|
||||
// File name
|
||||
compiledFileName := fmt.Sprintf("%x%s", hash.Sum(nil), ext)
|
||||
// Create struct
|
||||
// Save file
|
||||
a.assetFiles[compiledFileName] = &assetFile{
|
||||
contentType: mime.TypeByExtension(ext),
|
||||
body: body,
|
||||
}
|
||||
return compiledFileName, err
|
||||
// Save mapping of original file name to compiled file name
|
||||
a.assetFileNames[name] = compiledFileName
|
||||
return err
|
||||
}
|
||||
|
||||
// Function for templates
|
||||
|
@ -113,22 +110,15 @@ func (a *goBlog) initChromaCSS() error {
|
|||
return nil
|
||||
}
|
||||
// Initialize the style
|
||||
chromaStyleBuilder := chromaGoBlogStyle.Builder()
|
||||
chromaStyle, err := chromaStyleBuilder.Build()
|
||||
chromaStyle, err := chromaGoBlogStyle.Builder().Build()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Write the CSS to a buffer
|
||||
var cssBuffer bytes.Buffer
|
||||
if err = chromahtml.New(chromahtml.ClassPrefix("c-")).WriteCSS(&cssBuffer, chromaStyle); err != nil {
|
||||
return err
|
||||
}
|
||||
// Compile asset
|
||||
compiled, err := a.compileAsset(chromaPath, &cssBuffer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Add to map
|
||||
a.assetFileNames[chromaPath] = compiled
|
||||
return nil
|
||||
// Generate and minify CSS
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
go func() {
|
||||
_ = chromahtml.New(chromahtml.ClassPrefix("c-")).WriteCSS(pipeWriter, chromaStyle)
|
||||
_ = pipeWriter.Close()
|
||||
}()
|
||||
return a.compileAsset(chromaPath, pipeReader)
|
||||
}
|
||||
|
|
10
utils.go
10
utils.go
|
@ -221,16 +221,18 @@ func mBytesString(size int64) string {
|
|||
}
|
||||
|
||||
func htmlText(s string) string {
|
||||
return htmlTextFromReader(strings.NewReader(s))
|
||||
}
|
||||
|
||||
func htmlTextFromReader(r io.Reader) string {
|
||||
// Build policy to only allow a subset of HTML tags
|
||||
textPolicy := bluemonday.StrictPolicy()
|
||||
textPolicy.AllowElements("h1", "h2", "h3", "h4", "h5", "h6") // Headers
|
||||
textPolicy.AllowElements("p") // Paragraphs
|
||||
textPolicy.AllowElements("ol", "ul", "li") // Lists
|
||||
textPolicy.AllowElements("blockquote") // Blockquotes
|
||||
// Filter HTML tags
|
||||
htmlBuf := textPolicy.SanitizeReader(strings.NewReader(s))
|
||||
// Read HTML into document
|
||||
doc, _ := goquery.NewDocumentFromReader(htmlBuf)
|
||||
// Read filtered HTML into document
|
||||
doc, _ := goquery.NewDocumentFromReader(textPolicy.SanitizeReader(r))
|
||||
var text strings.Builder
|
||||
if bodyChild := doc.Find("body").Children(); bodyChild.Length() > 0 {
|
||||
// Input was real HTML, so build the text from the body
|
||||
|
|
Loading…
Reference in New Issue