Update Go, some more improvements and fixes

This commit is contained in:
Jan-Lukas Else 2023-02-02 22:59:12 +01:00
parent fd681ffaf3
commit 8f2b7c7101
18 changed files with 131 additions and 137 deletions

View File

@ -126,41 +126,36 @@ func (a *goBlog) apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
} }
apIri := a.apIri(blog) apIri := a.apIri(blog)
// Encode // Encode
buf := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(buf) go func() {
if err := json.NewEncoder(buf).Encode(map[string]any{ _ = pw.CloseWithError(json.NewEncoder(pw).Encode(map[string]any{
"subject": a.webfingerAccts[apIri], "subject": a.webfingerAccts[apIri],
"aliases": []string{ "aliases": []string{a.webfingerAccts[apIri], apIri},
a.webfingerAccts[apIri],
apIri,
},
"links": []map[string]string{ "links": []map[string]string{
{ {
"rel": "self", "rel": "self", "type": contenttype.AS, "href": apIri,
"type": contenttype.AS,
"href": apIri,
}, },
{ {
"rel": "http://webfinger.net/rel/profile-page", "rel": "http://webfinger.net/rel/profile-page",
"type": "text/html", "type": "text/html", "href": apIri,
"href": apIri,
}, },
}, },
}); err != nil { }))
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError) }()
return
}
w.Header().Set(contentType, "application/jrd+json"+contenttype.CharsetUtf8Suffix) w.Header().Set(contentType, "application/jrd+json"+contenttype.CharsetUtf8Suffix)
_ = a.min.Get().Minify(contenttype.JSON, w, buf) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }
const activityPubMentionsParameter = "activitypubmentions" const activityPubMentionsParameter = "activitypubmentions"
func (a *goBlog) apCheckMentions(p *post) { func (a *goBlog) apCheckMentions(p *post) {
contentBuf := bufferpool.Get() pr, pw := io.Pipe()
a.postHtmlToWriter(contentBuf, &postHtmlOptions{p: p}) go func() {
links, err := allLinksFromHTML(contentBuf, a.fullPostURL(p)) a.postHtmlToWriter(pw, &postHtmlOptions{p: p})
bufferpool.Put(contentBuf) _ = pw.Close()
}()
links, err := allLinksFromHTML(pr, a.fullPostURL(p))
_ = pr.CloseWithError(err)
if err != nil { if err != nil {
log.Println("Failed to extract links from post: " + err.Error()) log.Println("Failed to extract links from post: " + err.Error())
return return

View File

@ -171,6 +171,7 @@ func (a *goBlog) serveAPItem(w http.ResponseWriter, r *http.Request, status int,
return return
} }
// Send response // Send response
w.WriteHeader(status)
w.Header().Set(contentType, contenttype.ASUTF8) w.Header().Set(contentType, contenttype.ASUTF8)
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary)) _ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
} }

View File

@ -125,6 +125,7 @@ func (a *goBlog) allLinksToCheck(posts ...*post) ([]*stringPair, error) {
_ = pw.Close() _ = pw.Close()
}() }()
links, err := allLinksFromHTML(pr, a.fullPostURL(post)) links, err := allLinksFromHTML(pr, a.fullPostURL(post))
_ = pr.CloseWithError(err)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -30,7 +30,7 @@ Requirements:
- Linux - Linux
- git - git
- go >= 1.19 - go >= 1.20
- libsqlite3 with FTS5 enabled >= 3.31 (the newer the better) - libsqlite3 with FTS5 enabled >= 3.31 (the newer the better)
Build command: Build command:

View File

@ -15,11 +15,12 @@ func (a *goBlog) proxyTiles() http.HandlerFunc {
} }
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
// Create a new request to proxy to the tile server // Create a new request to proxy to the tile server
targetUrl := tileSource targetUrl := strings.NewReplacer(
targetUrl = strings.ReplaceAll(targetUrl, "{s}", chi.URLParam(r, "s")) "{s}", chi.URLParam(r, "s"),
targetUrl = strings.ReplaceAll(targetUrl, "{z}", chi.URLParam(r, "z")) "{z}", chi.URLParam(r, "z"),
targetUrl = strings.ReplaceAll(targetUrl, "{x}", chi.URLParam(r, "x")) "{x}", chi.URLParam(r, "x"),
targetUrl = strings.ReplaceAll(targetUrl, "{y}", chi.URLParam(r, "y")) "{y}", chi.URLParam(r, "y"),
).Replace(tileSource)
proxyRequest, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, targetUrl, nil) proxyRequest, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, targetUrl, nil)
// Copy request headers // Copy request headers
for _, k := range []string{ for _, k := range []string{
@ -29,7 +30,6 @@ func (a *goBlog) proxyTiles() http.HandlerFunc {
cacheControl, cacheControl,
"If-Modified-Since", "If-Modified-Since",
"If-None-Match", "If-None-Match",
"User-Agent",
} { } {
proxyRequest.Header.Set(k, r.Header.Get(k)) proxyRequest.Header.Set(k, r.Header.Get(k))
} }

9
go.mod
View File

@ -1,6 +1,6 @@
module go.goblog.app/app module go.goblog.app/app
go 1.19 go 1.20
require ( require (
git.jlel.se/jlelse/go-geouri v0.0.0-20210525190615-a9c1d50f42d6 git.jlel.se/jlelse/go-geouri v0.0.0-20210525190615-a9c1d50f42d6
@ -11,7 +11,7 @@ require (
github.com/alecthomas/chroma/v2 v2.4.0 github.com/alecthomas/chroma/v2 v2.4.0
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b
github.com/carlmjohnson/requests v0.23.1 github.com/carlmjohnson/requests v0.23.2
// master // master
github.com/cretz/bine v0.2.1-0.20221201125941-b9d31d9c7866 github.com/cretz/bine v0.2.1-0.20221201125941-b9d31d9c7866
github.com/dchest/captcha v1.0.0 github.com/dchest/captcha v1.0.0
@ -61,7 +61,7 @@ require (
github.com/traefik/yaegi v0.14.4-0.20230117132604-1679870ea3c8 github.com/traefik/yaegi v0.14.4-0.20230117132604-1679870ea3c8
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2 github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
github.com/xhit/go-simple-mail/v2 v2.13.0 github.com/xhit/go-simple-mail/v2 v2.13.0
github.com/yuin/goldmark v1.5.3 github.com/yuin/goldmark v1.5.4
// master // master
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
golang.org/x/crypto v0.5.0 golang.org/x/crypto v0.5.0
@ -71,8 +71,7 @@ require (
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mautrix v0.13.0 maunium.net/go/mautrix v0.13.0
nhooyr.io/websocket v1.8.7 nhooyr.io/websocket v1.8.7
// main willnorris.com/go/microformats v1.2.0
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e
) )
require ( require (

12
go.sum
View File

@ -77,8 +77,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY=
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
github.com/carlmjohnson/requests v0.23.1 h1:d8FpOZC6We3raxa3cW1oEyK+cNz4XSW8A6wjMkMsbOM= github.com/carlmjohnson/requests v0.23.2 h1:SzaY+/5v8QOvt++7HTXe1xgmIb3wc/bYf2QJmrO73sM=
github.com/carlmjohnson/requests v0.23.1/go.mod h1:i/0+cvgndkM7SPtw911bcbYR4CP+/9vP/+TbcFqPz3A= github.com/carlmjohnson/requests v0.23.2/go.mod h1:09VwhOaRQYCraJcByjEuvuOGO1jxUjIx6vnAEkt2ges=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -589,8 +589,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M= github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w= github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w=
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -993,7 +993,7 @@ nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 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=
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e h1:9dbX305ANi38nJlQP9ve2v6U1NQp9YFGP3JgrT7Rp7g= willnorris.com/go/microformats v1.2.0 h1:73pzJCLJM69kYE5qsLI9OOC/7sImNVOzya9EQ0+1wmM=
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4= willnorris.com/go/microformats v1.2.0/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4=
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 h1:V5+O+YZHchEwu6ZmPcqT1dQ+mHgE356Q+w9SVOQ+QZg= willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0 h1:V5+O+YZHchEwu6ZmPcqT1dQ+mHgE356Q+w9SVOQ+QZg=
willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0/go.mod h1:DgeruqKIsZtcDXVXNbBHa0YYEm88oAnK7PahkDtuCvw= willnorris.com/go/webmention v0.0.0-20220108183051-4a23794272f0/go.mod h1:DgeruqKIsZtcDXVXNbBHa0YYEm88oAnK7PahkDtuCvw=

View File

@ -20,7 +20,7 @@ func (a *goBlog) healthcheck() bool {
fmt.Println("healthcheck:", err.Error()) fmt.Println("healthcheck:", err.Error())
return false return false
} }
defer resp.Body.Close() _ = resp.Body.Close()
return resp.StatusCode == 200 return resp.StatusCode == 200
} }

View File

@ -33,7 +33,7 @@ var (
// Server Metadata // Server Metadata
// https://indieauth.spec.indieweb.org/#x4-1-1-indieauth-server-metadata // https://indieauth.spec.indieweb.org/#x4-1-1-indieauth-server-metadata
func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, r *http.Request) { func (a *goBlog) indieAuthMetadata(w http.ResponseWriter, _ *http.Request) {
resp := map[string]any{ resp := map[string]any{
"issuer": a.getInstanceRootURL(), "issuer": a.getInstanceRootURL(),
"authorization_endpoint": a.getFullAddress(indieAuthPath), "authorization_endpoint": a.getFullAddress(indieAuthPath),

View File

@ -87,9 +87,9 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
return "", tinifyErr return "", tinifyErr
} }
// Resize and download image // Resize and download image
imgBuffer := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(imgBuffer) go func() {
err = requests. _ = pw.CloseWithError(requests.
URL(compressedLocation). URL(compressedLocation).
Client(hc). Client(hc).
Method(http.MethodPost). Method(http.MethodPost).
@ -101,14 +101,13 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
"height": defaultCompressionHeight, "height": defaultCompressionHeight,
}, },
}). }).
ToBytesBuffer(imgBuffer). ToWriter(pw).
Fetch(context.Background()) Fetch(context.Background()))
if err != nil { }()
log.Println("Tinify error:", err.Error())
return "", tinifyErr
}
// Upload compressed file // Upload compressed file
return uploadCompressedFile(fileExtension, imgBuffer, upload) res, err := uploadCompressedFile(fileExtension, pr, upload)
_ = pr.CloseWithError(err)
return res, err
} }
type cloudflare struct{} type cloudflare struct{}
@ -121,19 +120,18 @@ func (*cloudflare) compress(url string, upload mediaStorageSaveFunc, hc *http.Cl
// Force jpeg // Force jpeg
fileExtension := "jpeg" fileExtension := "jpeg"
// Compress // Compress
imgBuffer := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(imgBuffer) go func() {
err := requests. _ = pw.CloseWithError(requests.
URL(fmt.Sprintf("https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=%d,h=%d/%s", defaultCompressionWidth, defaultCompressionHeight, url)). URL(fmt.Sprintf("https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=%d,h=%d/%s", defaultCompressionWidth, defaultCompressionHeight, url)).
Client(hc). Client(hc).
ToBytesBuffer(imgBuffer). ToWriter(pw).
Fetch(context.Background()) Fetch(context.Background()))
if err != nil { }()
log.Println("Cloudflare error:", err.Error())
return "", errors.New("failed to compress image using cloudflare")
}
// Upload compressed file // Upload compressed file
return uploadCompressedFile(fileExtension, imgBuffer, upload) res, err := uploadCompressedFile(fileExtension, pr, upload)
_ = pr.CloseWithError(err)
return res, err
} }
type localMediaCompressor struct{} type localMediaCompressor struct{}
@ -144,20 +142,13 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
if !allowed { if !allowed {
return "", nil return "", nil
} }
// Download image // Download and decode image
imgBuffer := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(imgBuffer) go func() {
err := requests. _ = pw.CloseWithError(requests.URL(url).Client(hc).ToWriter(pw).Fetch(context.Background()))
URL(url). }()
Client(hc). img, err := imaging.Decode(pr, imaging.AutoOrientation(true))
ToBytesBuffer(imgBuffer). _ = pr.CloseWithError(err)
Fetch(context.Background())
if err != nil {
log.Println("Local compressor error:", err.Error())
return "", errors.New("failed to download image using local compressor")
}
// Decode image
img, err := imaging.Decode(imgBuffer, imaging.AutoOrientation(true))
if err != nil { if err != nil {
log.Println("Local compressor error:", err.Error()) log.Println("Local compressor error:", err.Error())
return "", errors.New("failed to compress image using local compressor") return "", errors.New("failed to compress image using local compressor")
@ -165,20 +156,19 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
// Resize image // Resize image
resizedImage := imaging.Fit(img, defaultCompressionWidth, defaultCompressionHeight, imaging.Lanczos) resizedImage := imaging.Fit(img, defaultCompressionWidth, defaultCompressionHeight, imaging.Lanczos)
// Encode image // Encode image
resizedBuffer := bufferpool.Get() pr, pw = io.Pipe()
defer bufferpool.Put(resizedBuffer) go func() {
switch fileExtension { switch fileExtension {
case "jpg", "jpeg":
err = imaging.Encode(resizedBuffer, resizedImage, imaging.JPEG, imaging.JPEGQuality(75))
case "png": case "png":
err = imaging.Encode(resizedBuffer, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression)) _ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression)))
} default:
if err != nil { _ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.JPEG, imaging.JPEGQuality(75)))
log.Println("Local compressor error:", err.Error())
return "", errors.New("failed to compress image using local compressor")
} }
}()
// Upload compressed file // Upload compressed file
return uploadCompressedFile(fileExtension, resizedBuffer, upload) res, err := uploadCompressedFile(fileExtension, pr, upload)
_ = pr.CloseWithError(err)
return res, err
} }
func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorageSaveFunc) (string, error) { func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorageSaveFunc) (string, error) {
@ -186,7 +176,10 @@ func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorage
hash := sha256.New() hash := sha256.New()
tempBuffer := bufferpool.Get() tempBuffer := bufferpool.Get()
defer bufferpool.Put(tempBuffer) defer bufferpool.Put(tempBuffer)
_, _ = io.Copy(io.MultiWriter(tempBuffer, hash), r) _, err := io.Copy(io.MultiWriter(tempBuffer, hash), r)
if err != nil {
return "", err
}
// Upload buffer // Upload buffer
return upload(fmt.Sprintf("%x.%s", hash.Sum(nil), fileExtension), tempBuffer) return upload(fmt.Sprintf("%x.%s", hash.Sum(nil), fileExtension), tempBuffer)
} }

View File

@ -29,12 +29,14 @@ func (a *goBlog) mediaStorageEnabled() bool {
return a.mediaStorage != nil return a.mediaStorage != nil
} }
var errNoMediaStorageConfigured = errors.New("no media storage configured")
type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error) type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error)
func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) { func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) {
a.initMediaStorage() a.initMediaStorage()
if a.mediaStorage == nil { if a.mediaStorage == nil {
return "", errors.New("no media storage configured") return "", errNoMediaStorageConfigured
} }
loc, err := a.mediaStorage.save(filename, f) loc, err := a.mediaStorage.save(filename, f)
if err != nil { if err != nil {
@ -46,7 +48,7 @@ func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) {
func (a *goBlog) deleteMediaFile(filename string) error { func (a *goBlog) deleteMediaFile(filename string) error {
a.initMediaStorage() a.initMediaStorage()
if a.mediaStorage == nil { if a.mediaStorage == nil {
return errors.New("no media storage configured") return errNoMediaStorageConfigured
} }
return a.mediaStorage.delete(filepath.Base(filename)) return a.mediaStorage.delete(filepath.Base(filename))
} }
@ -61,7 +63,7 @@ type mediaFile struct {
func (a *goBlog) mediaFiles() ([]*mediaFile, error) { func (a *goBlog) mediaFiles() ([]*mediaFile, error) {
a.initMediaStorage() a.initMediaStorage()
if a.mediaStorage == nil { if a.mediaStorage == nil {
return nil, errors.New("no media storage configured") return nil, errNoMediaStorageConfigured
} }
return a.mediaStorage.files() return a.mediaStorage.files()
} }

View File

@ -8,7 +8,7 @@ import (
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
) )
func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, _ *http.Request) {
result := map[string]any{ result := map[string]any{
"links": []map[string]any{ "links": []map[string]any{
{ {
@ -25,7 +25,7 @@ func (a *goBlog) serveNodeInfoDiscover(w http.ResponseWriter, r *http.Request) {
_ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr)) _ = pr.CloseWithError(a.min.Get().Minify(contenttype.JSON, w, pr))
} }
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) { func (a *goBlog) serveNodeInfo(w http.ResponseWriter, _ *http.Request) {
localPosts, _ := a.db.countPosts(&postsRequestConfig{ localPosts, _ := a.db.countPosts(&postsRequestConfig{
status: []postStatus{statusPublished}, status: []postStatus{statusPublished},
visibility: []postVisibility{visibilityPublic}, visibility: []postVisibility{visibilityPublic},

View File

@ -19,7 +19,6 @@ import (
_ "embed" _ "embed"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"go.goblog.app/app/pkgs/bufferpool"
"go.goblog.app/app/pkgs/contenttype" "go.goblog.app/app/pkgs/contenttype"
) )
@ -91,19 +90,20 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
quality = 75 quality = 75
} }
// Read from database // Read from database
var imageBytes []byte var imageReader io.ReadCloser
if a.hasProfileImage() { if a.hasProfileImage() {
var err error var err error
imageBytes, err = os.ReadFile(profileImageFile) imageReader, err = os.Open(profileImageFile)
if err != nil || imageBytes == nil { if err != nil {
a.serveError(w, r, "Failed to read image file", http.StatusInternalServerError) a.serveError(w, r, "Failed to open image file", http.StatusInternalServerError)
return return
} }
} else { } else {
imageBytes = defaultLogo imageReader = io.NopCloser(bytes.NewReader(defaultLogo))
} }
// Decode image // Decode image
img, err := imaging.Decode(bytes.NewReader(imageBytes), imaging.AutoOrientation(true)) img, err := imaging.Decode(imageReader, imaging.AutoOrientation(true))
_ = imageReader.Close()
if err != nil { if err != nil {
a.serveError(w, r, "Failed to decode image", http.StatusInternalServerError) a.serveError(w, r, "Failed to decode image", http.StatusInternalServerError)
return return
@ -111,16 +111,14 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
// Resize image // Resize image
resizedImage := imaging.Fit(img, width, height, imaging.Lanczos) resizedImage := imaging.Fit(img, width, height, imaging.Lanczos)
// Encode // Encode
resizedBuffer := bufferpool.Get() pr, pw := io.Pipe()
defer bufferpool.Put(resizedBuffer) go func() {
err = encode(resizedBuffer, resizedImage, quality) _ = pw.CloseWithError(encode(pw, resizedImage, quality))
if err != nil { }()
a.serveError(w, r, "Failed to encode image", http.StatusInternalServerError)
return
}
// Return // Return
w.Header().Set(contentType, mediaType) w.Header().Set(contentType, mediaType)
_, _ = io.Copy(w, resizedBuffer) _, err = io.Copy(w, pr)
_ = pr.CloseWithError(err)
} }
} }

View File

@ -43,7 +43,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
renderPipeReader, renderPipeWriter := io.Pipe() renderPipeReader, renderPipeWriter := io.Pipe()
go func() { go func() {
f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data) f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data)
renderPipeWriter.Close() _ = renderPipeWriter.Close()
}() }()
// Run UI plugins // Run UI plugins
pluginPipeReader, pluginPipeWriter := io.Pipe() pluginPipeReader, pluginPipeWriter := io.Pipe()
@ -52,7 +52,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
blog: data.BlogString, blog: data.BlogString,
path: r.URL.Path, path: r.URL.Path,
}, renderPipeReader, pluginPipeWriter) }, renderPipeReader, pluginPipeWriter)
pluginPipeWriter.Close() _ = pluginPipeWriter.Close()
}() }()
// Return minified HTML // Return minified HTML
_ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader)) _ = pluginPipeReader.CloseWithError(a.min.Get().Minify(contenttype.HTML, w, pluginPipeReader))
@ -69,6 +69,7 @@ func (a *goBlog) chainUiPlugins(plugins []any, rc *pluginRenderContext, rendered
_ = writer.Close() _ = writer.Close()
}() }()
a.chainUiPlugins(plugins[1:], rc, reader, modified) a.chainUiPlugins(plugins[1:], rc, reader, modified)
_ = reader.Close()
} }
func (a *goBlog) checkRenderData(r *http.Request, data *renderData) { func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {

View File

@ -173,7 +173,7 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
a.writeSitemapXML(w, r, sm) a.writeSitemapXML(w, r, sm)
} }
func (a *goBlog) writeSitemapXML(w http.ResponseWriter, r *http.Request, sm any) { func (a *goBlog) writeSitemapXML(w http.ResponseWriter, _ *http.Request, sm any) {
pr, pw := io.Pipe() pr, pw := io.Pipe()
go func() { go func() {
_, _ = io.WriteString(pw, xml.Header) _, _ = io.WriteString(pw, xml.Header)

9
tts.go
View File

@ -75,6 +75,7 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
_ = phw.Close() _ = phw.Close()
}() }()
postHtmlText, err := htmlTextFromReader(phr) postHtmlText, err := htmlTextFromReader(phr)
_ = phr.CloseWithError(err)
if err != nil { if err != nil {
return err return err
} }
@ -82,14 +83,13 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
// Create TTS audio for each part // Create TTS audio for each part
partReaders := []io.Reader{} partReaders := []io.Reader{}
partWriters := []*io.PipeWriter{}
var g errgroup.Group var g errgroup.Group
for _, part := range parts { for _, part := range parts {
part := part part := part
pr, pw := io.Pipe() pr, pw := io.Pipe()
defer func() {
pw.Close()
}()
partReaders = append(partReaders, pr) partReaders = append(partReaders, pr)
partWriters = append(partWriters, pw)
g.Go(func() error { g.Go(func() error {
// Build SSML // Build SSML
ssml := "<speak>" + html.EscapeString(part) + "<break time=\"500ms\"/></speak>" ssml := "<speak>" + html.EscapeString(part) + "<break time=\"500ms\"/></speak>"
@ -105,6 +105,9 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
defer bufferpool.Put(buf) defer bufferpool.Put(buf)
hash := sha256.New() hash := sha256.New()
err = mp3merge.MergeMP3(io.MultiWriter(buf, hash), partReaders...) err = mp3merge.MergeMP3(io.MultiWriter(buf, hash), partReaders...)
for _, pw := range partWriters {
_ = pw.CloseWithError(err)
}
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,7 +6,6 @@ EXTRA="
github.com/cretz/bine@master github.com/cretz/bine@master
github.com/tkrajina/gpxgo@master github.com/tkrajina/gpxgo@master
github.com/yuin/goldmark-emoji@master github.com/yuin/goldmark-emoji@master
willnorris.com/go/microformats@main
github.com/traefik/yaegi@master github.com/traefik/yaegi@master
" "

View File

@ -289,7 +289,9 @@ func cleanHTMLText(s string) string {
// Clean HTML with UGC policy and return text // Clean HTML with UGC policy and return text
pr, pw := io.Pipe() pr, pw := io.Pipe()
go func() { _ = pw.CloseWithError(bluemonday.UGCPolicy().SanitizeReaderToWriter(strings.NewReader(s), pw)) }() go func() { _ = pw.CloseWithError(bluemonday.UGCPolicy().SanitizeReaderToWriter(strings.NewReader(s), pw)) }()
s, _ = htmlTextFromReader(pr) var err error
s, err = htmlTextFromReader(pr)
_ = pr.CloseWithError(err)
return s return s
} }