mirror of https://github.com/jlelse/GoBlog
More strings.Builder and io.Pipe
This commit is contained in:
parent
ffe7065621
commit
e8a5e9eb0e
13
micropub.go
13
micropub.go
|
@ -13,7 +13,6 @@ import (
|
|||
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/cast"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
@ -76,14 +75,12 @@ func (a *goBlog) serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
|
|||
a.serve404(w, r)
|
||||
return
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
if err := json.NewEncoder(buf).Encode(result); err != nil {
|
||||
a.serveError(w, r, "Failed to encode json", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(result))
|
||||
}()
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
_ = a.min.Get().Minify(contenttype.JSON, w, buf)
|
||||
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
|
||||
}
|
||||
|
||||
func (a *goBlog) getMicropubChannelsMap() []map[string]any {
|
||||
|
|
30
nodeinfo.go
30
nodeinfo.go
|
@ -5,29 +5,24 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
err := json.NewEncoder(buf).Encode(map[string]any{
|
||||
result := map[string]any{
|
||||
"links": []map[string]any{
|
||||
{
|
||||
"href": a.getFullAddress("/nodeinfo"),
|
||||
"rel": "http://nodeinfo.diaspora.software/ns/schema/2.1",
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(result))
|
||||
}()
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
mw := a.min.Get().Writer(contenttype.JSON, w)
|
||||
_, _ = io.Copy(mw, buf)
|
||||
_ = mw.Close()
|
||||
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
|
||||
}
|
||||
|
||||
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -35,9 +30,7 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
|||
status: []postStatus{statusPublished},
|
||||
visibility: []postVisibility{visibilityPublic},
|
||||
})
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
if err := json.NewEncoder(buf).Encode(map[string]any{
|
||||
result := map[string]any{
|
||||
"version": "2.1",
|
||||
"software": map[string]any{
|
||||
"name": "goblog",
|
||||
|
@ -55,10 +48,11 @@ func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
|||
"webmention",
|
||||
},
|
||||
"metadata": map[string]any{},
|
||||
}); err != nil {
|
||||
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(result))
|
||||
}()
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
_ = a.min.Get().Minify(contenttype.JSON, w, buf)
|
||||
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
|
@ -49,15 +49,13 @@ func (a *goBlog) serveOpenSearch(w http.ResponseWriter, r *http.Request) {
|
|||
},
|
||||
SearchForm: sURL,
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
_, _ = buf.WriteString(xml.Header)
|
||||
if err := xml.NewEncoder(buf).Encode(openSearch); err != nil {
|
||||
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_, _ = io.WriteString(pw, xml.Header)
|
||||
_ = pw.CloseWithError(xml.NewEncoder(pw).Encode(openSearch))
|
||||
}()
|
||||
w.Header().Set(contentType, "application/opensearchdescription+xml"+contenttype.CharsetUtf8Suffix)
|
||||
_ = a.min.Get().Minify(contenttype.XML, w, buf)
|
||||
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.XML, w, pr))
|
||||
}
|
||||
|
||||
func openSearchUrl(b *configBlog) string {
|
||||
|
|
|
@ -2,13 +2,13 @@ package highlighting
|
|||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/yuin/goldmark"
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/renderer"
|
||||
"github.com/yuin/goldmark/renderer/html"
|
||||
"github.com/yuin/goldmark/util"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
|
||||
"github.com/alecthomas/chroma/v2"
|
||||
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
|
||||
|
@ -60,8 +60,7 @@ func (r *htmlRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, no
|
|||
n := node.(*ast.FencedCodeBlock)
|
||||
|
||||
// Read code block content.
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
var buf strings.Builder
|
||||
for _, line := range n.Lines().Sliced(0, n.Lines().Len()) {
|
||||
buf.Write(line.Value(source))
|
||||
}
|
||||
|
@ -70,7 +69,7 @@ func (r *htmlRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, no
|
|||
if highlight(w, buf.String(), string(n.Language(source)), r.formatter) != nil {
|
||||
// Highlight failed, fallback to plain text.
|
||||
_, _ = w.WriteString("<pre><code>")
|
||||
r.Writer.RawWrite(w, buf.Bytes())
|
||||
r.Writer.RawWrite(w, []byte(buf.String()))
|
||||
_, _ = w.WriteString("</code></pre>\n")
|
||||
}
|
||||
|
||||
|
|
39
postsDb.go
39
postsDb.go
|
@ -206,8 +206,7 @@ func (db *database) savePost(p *post, o *postCreationOptions) error {
|
|||
db.pcm.Lock()
|
||||
defer db.pcm.Unlock()
|
||||
// Build SQL
|
||||
sqlBuilder := bufferpool.Get()
|
||||
defer bufferpool.Put(sqlBuilder)
|
||||
var sqlBuilder strings.Builder
|
||||
var sqlArgs = []any{dbNoCache}
|
||||
// Start transaction
|
||||
sqlBuilder.WriteString("begin;")
|
||||
|
@ -336,7 +335,7 @@ func (db *database) replacePostParam(path, param string, values []string) error
|
|||
db.pcm.Lock()
|
||||
defer db.pcm.Unlock()
|
||||
// Build SQL
|
||||
sqlBuilder := bufferpool.Get()
|
||||
var sqlBuilder strings.Builder
|
||||
var sqlArgs = []any{dbNoCache}
|
||||
// Start transaction
|
||||
sqlBuilder.WriteString("begin;")
|
||||
|
@ -352,7 +351,6 @@ func (db *database) replacePostParam(path, param string, values []string) error
|
|||
sqlBuilder.WriteString("commit;")
|
||||
// Execute
|
||||
_, err := db.Exec(sqlBuilder.String(), sqlArgs...)
|
||||
bufferpool.Put(sqlBuilder)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -387,8 +385,7 @@ type postsRequestConfig struct {
|
|||
}
|
||||
|
||||
func buildPostsQuery(c *postsRequestConfig, selection string) (query string, args []any) {
|
||||
queryBuilder := bufferpool.Get()
|
||||
defer bufferpool.Put(queryBuilder)
|
||||
var queryBuilder strings.Builder
|
||||
// Selection
|
||||
queryBuilder.WriteString("select ")
|
||||
queryBuilder.WriteString(selection)
|
||||
|
@ -413,11 +410,11 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
|||
queryBuilder.WriteString(", ")
|
||||
}
|
||||
named := "status" + strconv.Itoa(i)
|
||||
queryBuilder.WriteByte('@')
|
||||
queryBuilder.WriteString("@")
|
||||
queryBuilder.WriteString(named)
|
||||
args = append(args, sql.Named(named, status))
|
||||
}
|
||||
queryBuilder.WriteByte(')')
|
||||
queryBuilder.WriteString(")")
|
||||
}
|
||||
if c.visibility != nil && len(c.visibility) > 0 {
|
||||
queryBuilder.WriteString(" and visibility in (")
|
||||
|
@ -426,11 +423,11 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
|||
queryBuilder.WriteString(", ")
|
||||
}
|
||||
named := "visibility" + strconv.Itoa(i)
|
||||
queryBuilder.WriteByte('@')
|
||||
queryBuilder.WriteString("@")
|
||||
queryBuilder.WriteString(named)
|
||||
args = append(args, sql.Named(named, visibility))
|
||||
}
|
||||
queryBuilder.WriteByte(')')
|
||||
queryBuilder.WriteString(")")
|
||||
}
|
||||
if c.blog != "" {
|
||||
queryBuilder.WriteString(" and blog = @blog")
|
||||
|
@ -451,7 +448,7 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
|||
queryBuilder.WriteString(", ")
|
||||
}
|
||||
named := "param" + strconv.Itoa(i)
|
||||
queryBuilder.WriteByte('@')
|
||||
queryBuilder.WriteString("@")
|
||||
queryBuilder.WriteString(named)
|
||||
args = append(args, param)
|
||||
}
|
||||
|
@ -477,11 +474,11 @@ func buildPostsQuery(c *postsRequestConfig, selection string) (query string, arg
|
|||
queryBuilder.WriteString(", ")
|
||||
}
|
||||
named := "section" + strconv.Itoa(i)
|
||||
queryBuilder.WriteByte('@')
|
||||
queryBuilder.WriteString("@")
|
||||
queryBuilder.WriteString(named)
|
||||
args = append(args, sql.Named(named, section))
|
||||
}
|
||||
queryBuilder.WriteByte(')')
|
||||
queryBuilder.WriteString(")")
|
||||
}
|
||||
if c.publishedYear != 0 {
|
||||
queryBuilder.WriteString(" and substr(tolocal(published), 1, 4) = @publishedyear")
|
||||
|
@ -522,8 +519,7 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
|
|||
}
|
||||
// Build query
|
||||
sqlArgs := make([]any, 0)
|
||||
queryBuilder := bufferpool.Get()
|
||||
defer bufferpool.Put(queryBuilder)
|
||||
var queryBuilder strings.Builder
|
||||
queryBuilder.WriteString("select path, parameter, value from post_parameters where")
|
||||
// Paths
|
||||
queryBuilder.WriteString(" path in (")
|
||||
|
@ -532,11 +528,11 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
|
|||
queryBuilder.WriteString(", ")
|
||||
}
|
||||
named := "path" + strconv.Itoa(i)
|
||||
queryBuilder.WriteByte('@')
|
||||
queryBuilder.WriteString("@")
|
||||
queryBuilder.WriteString(named)
|
||||
sqlArgs = append(sqlArgs, sql.Named(named, p.Path))
|
||||
}
|
||||
queryBuilder.WriteByte(')')
|
||||
queryBuilder.WriteString(")")
|
||||
// Parameters
|
||||
if len(parameters) > 0 {
|
||||
queryBuilder.WriteString(" and parameter in (")
|
||||
|
@ -545,11 +541,11 @@ func (d *database) loadPostParameters(posts []*post, parameters ...string) (err
|
|||
queryBuilder.WriteString(", ")
|
||||
}
|
||||
named := "param" + strconv.Itoa(i)
|
||||
queryBuilder.WriteByte('@')
|
||||
queryBuilder.WriteString("@")
|
||||
queryBuilder.WriteString(named)
|
||||
sqlArgs = append(sqlArgs, sql.Named(named, p))
|
||||
}
|
||||
queryBuilder.WriteByte(')')
|
||||
queryBuilder.WriteString(")")
|
||||
}
|
||||
// Order
|
||||
queryBuilder.WriteString(" order by id")
|
||||
|
@ -695,7 +691,7 @@ group by name;
|
|||
|
||||
func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) {
|
||||
sqlArgs := []any{dbNoCache}
|
||||
nameValues := bufferpool.Get()
|
||||
var nameValues strings.Builder
|
||||
for i, n := range names {
|
||||
if i > 0 {
|
||||
nameValues.WriteString(", ")
|
||||
|
@ -703,11 +699,10 @@ func (db *database) usesOfMediaFile(names ...string) (counts []int, err error) {
|
|||
named := "name" + strconv.Itoa(i)
|
||||
nameValues.WriteString("(@")
|
||||
nameValues.WriteString(named)
|
||||
nameValues.WriteByte(')')
|
||||
nameValues.WriteString(")")
|
||||
sqlArgs = append(sqlArgs, sql.Named(named, n))
|
||||
}
|
||||
rows, err := db.Query(fmt.Sprintf(mediaUseSql, nameValues.String()), sqlArgs...)
|
||||
bufferpool.Put(nameValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
18
reactions.go
18
reactions.go
|
@ -5,10 +5,10 @@ import (
|
|||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/dgraph-io/ristretto"
|
||||
"github.com/samber/lo"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
|
@ -88,15 +88,12 @@ func (a *goBlog) getReactions(w http.ResponseWriter, r *http.Request) {
|
|||
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
err = json.NewEncoder(buf).Encode(reactions)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(reactions))
|
||||
}()
|
||||
w.Header().Set(contentType, contenttype.JSONUTF8)
|
||||
_, _ = io.Copy(w, buf)
|
||||
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
|
||||
}
|
||||
|
||||
func (a *goBlog) getReactionsFromDatabase(path string) (map[string]int, error) {
|
||||
|
@ -110,8 +107,7 @@ func (a *goBlog) getReactionsFromDatabase(path string) (map[string]int, error) {
|
|||
// Get reactions
|
||||
res, err, _ := a.reactionsSfg.Do(path, func() (any, error) {
|
||||
// Build query
|
||||
sqlBuf := bufferpool.Get()
|
||||
defer bufferpool.Put(sqlBuf)
|
||||
var sqlBuf strings.Builder
|
||||
sqlArgs := []any{}
|
||||
sqlBuf.WriteString("select reaction, count from reactions where path=? and reaction in (")
|
||||
sqlArgs = append(sqlArgs, path)
|
||||
|
|
|
@ -136,13 +136,13 @@ func Test_reactionsHighLevel(t *testing.T) {
|
|||
rec = httptest.NewRecorder()
|
||||
app.getReactions(rec, req)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, "{\"❤️\":1}\n", rec.Body.String())
|
||||
assert.Equal(t, "{\"❤️\":1}", rec.Body.String())
|
||||
|
||||
// Get reactions for a non-existing post
|
||||
req = httptest.NewRequest(http.MethodGet, "/?path=/non-existing-post", nil)
|
||||
rec = httptest.NewRecorder()
|
||||
app.getReactions(rec, req)
|
||||
assert.Equal(t, http.StatusOK, rec.Code)
|
||||
assert.Equal(t, "{}\n", rec.Body.String())
|
||||
assert.Equal(t, "{}", rec.Body.String())
|
||||
|
||||
}
|
||||
|
|
22
sitemap.go
22
sitemap.go
|
@ -3,12 +3,12 @@ package main
|
|||
import (
|
||||
"database/sql"
|
||||
"encoding/xml"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/araddon/dateparse"
|
||||
"github.com/snabb/sitemap"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
|
@ -174,18 +174,16 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func (a *goBlog) writeSitemapXML(w http.ResponseWriter, r *http.Request, sm any) {
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
_, _ = buf.WriteString(xml.Header)
|
||||
_, _ = buf.WriteString(`<?xml-stylesheet type="text/xsl" href="`)
|
||||
_, _ = buf.WriteString(a.assetFileName("sitemap.xsl"))
|
||||
_, _ = buf.WriteString(`" ?>`)
|
||||
if err := xml.NewEncoder(buf).Encode(sm); err != nil {
|
||||
a.serveError(w, r, "Failed to encode sitemap", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_, _ = io.WriteString(pw, xml.Header)
|
||||
_, _ = io.WriteString(pw, `<?xml-stylesheet type="text/xsl" href="`)
|
||||
_, _ = io.WriteString(pw, a.assetFileName("sitemap.xsl"))
|
||||
_, _ = io.WriteString(pw, `" ?>`)
|
||||
_ = pw.CloseWithError(xml.NewEncoder(pw).Encode(sm))
|
||||
}()
|
||||
w.Header().Set(contentType, contenttype.XMLUTF8)
|
||||
_ = a.min.Get().Minify(contenttype.XML, w, buf)
|
||||
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.XML, w, pr))
|
||||
}
|
||||
|
||||
const sitemapDatePathsSql = `
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
)
|
||||
|
||||
func (a *goBlog) initTelegram() {
|
||||
|
@ -135,8 +135,7 @@ func (tg *configTelegram) generateHTML(title, fullURL, shortURL string) (html st
|
|||
if !tg.enabled() {
|
||||
return ""
|
||||
}
|
||||
message := bufferpool.Get()
|
||||
defer bufferpool.Put(message)
|
||||
var message strings.Builder
|
||||
if title != "" {
|
||||
message.WriteString(tgbotapi.EscapeText(tgbotapi.ModeHTML, title))
|
||||
message.WriteString("\n\n")
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/htmlbuilder"
|
||||
)
|
||||
|
||||
|
@ -65,7 +64,7 @@ func (a *goBlog) renderSummary(hb *htmlbuilder.HtmlBuilder, bc *configBlog, p *p
|
|||
}
|
||||
// Show link to full post
|
||||
hb.WriteElementOpen("p")
|
||||
prefix := bufferpool.Get()
|
||||
var prefix strings.Builder
|
||||
if len(photos) > 0 {
|
||||
// Contains photos
|
||||
prefix.WriteString("🖼️")
|
||||
|
@ -75,10 +74,9 @@ func (a *goBlog) renderSummary(hb *htmlbuilder.HtmlBuilder, bc *configBlog, p *p
|
|||
prefix.WriteString("🗺️")
|
||||
}
|
||||
if prefix.Len() > 0 {
|
||||
prefix.WriteRune(' ')
|
||||
prefix.WriteString(" ")
|
||||
hb.WriteEscaped(prefix.String())
|
||||
}
|
||||
bufferpool.Put(prefix)
|
||||
hb.WriteElementOpen("a", "class", "u-url", "href", p.Path)
|
||||
hb.WriteEscaped(a.ts.GetTemplateStringVariant(bc.Lang, "view"))
|
||||
hb.WriteElementClose("a")
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
|
@ -231,8 +230,7 @@ type webmentionsRequestConfig struct {
|
|||
}
|
||||
|
||||
func buildWebmentionsQuery(config *webmentionsRequestConfig) (query string, args []any) {
|
||||
queryBuilder := bufferpool.Get()
|
||||
defer bufferpool.Put(queryBuilder)
|
||||
var queryBuilder strings.Builder
|
||||
queryBuilder.WriteString("select id, source, target, url, created, title, content, author, status from webmentions ")
|
||||
if config != nil {
|
||||
queryBuilder.WriteString("where 1")
|
||||
|
|
Loading…
Reference in New Issue