mirror of https://github.com/jlelse/GoBlog
Update Go, some more improvements and fixes
parent
fd681ffaf3
commit
8f2b7c7101
|
@ -126,41 +126,36 @@ func (a *goBlog) apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
apIri := a.apIri(blog)
|
||||
// Encode
|
||||
buf := bufferpool.Get()
|
||||
defer bufferpool.Put(buf)
|
||||
if err := json.NewEncoder(buf).Encode(map[string]any{
|
||||
"subject": a.webfingerAccts[apIri],
|
||||
"aliases": []string{
|
||||
a.webfingerAccts[apIri],
|
||||
apIri,
|
||||
},
|
||||
"links": []map[string]string{
|
||||
{
|
||||
"rel": "self",
|
||||
"type": contenttype.AS,
|
||||
"href": apIri,
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(json.NewEncoder(pw).Encode(map[string]any{
|
||||
"subject": a.webfingerAccts[apIri],
|
||||
"aliases": []string{a.webfingerAccts[apIri], apIri},
|
||||
"links": []map[string]string{
|
||||
{
|
||||
"rel": "self", "type": contenttype.AS, "href": apIri,
|
||||
},
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html", "href": apIri,
|
||||
},
|
||||
},
|
||||
{
|
||||
"rel": "http://webfinger.net/rel/profile-page",
|
||||
"type": "text/html",
|
||||
"href": apIri,
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
a.serveError(w, r, "Encoding failed", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}))
|
||||
}()
|
||||
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"
|
||||
|
||||
func (a *goBlog) apCheckMentions(p *post) {
|
||||
contentBuf := bufferpool.Get()
|
||||
a.postHtmlToWriter(contentBuf, &postHtmlOptions{p: p})
|
||||
links, err := allLinksFromHTML(contentBuf, a.fullPostURL(p))
|
||||
bufferpool.Put(contentBuf)
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
a.postHtmlToWriter(pw, &postHtmlOptions{p: p})
|
||||
_ = pw.Close()
|
||||
}()
|
||||
links, err := allLinksFromHTML(pr, a.fullPostURL(p))
|
||||
_ = pr.CloseWithError(err)
|
||||
if err != nil {
|
||||
log.Println("Failed to extract links from post: " + err.Error())
|
||||
return
|
||||
|
|
|
@ -171,6 +171,7 @@ func (a *goBlog) serveAPItem(w http.ResponseWriter, r *http.Request, status int,
|
|||
return
|
||||
}
|
||||
// Send response
|
||||
w.WriteHeader(status)
|
||||
w.Header().Set(contentType, contenttype.ASUTF8)
|
||||
_ = a.min.Get().Minify(contenttype.AS, w, bytes.NewReader(binary))
|
||||
}
|
||||
|
|
1
check.go
1
check.go
|
@ -125,6 +125,7 @@ func (a *goBlog) allLinksToCheck(posts ...*post) ([]*stringPair, error) {
|
|||
_ = pw.Close()
|
||||
}()
|
||||
links, err := allLinksFromHTML(pr, a.fullPostURL(post))
|
||||
_ = pr.CloseWithError(err)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ Requirements:
|
|||
|
||||
- Linux
|
||||
- git
|
||||
- go >= 1.19
|
||||
- go >= 1.20
|
||||
- libsqlite3 with FTS5 enabled >= 3.31 (the newer the better)
|
||||
|
||||
Build command:
|
||||
|
|
12
geoTiles.go
12
geoTiles.go
|
@ -15,11 +15,12 @@ func (a *goBlog) proxyTiles() http.HandlerFunc {
|
|||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// Create a new request to proxy to the tile server
|
||||
targetUrl := tileSource
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{s}", chi.URLParam(r, "s"))
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{z}", chi.URLParam(r, "z"))
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{x}", chi.URLParam(r, "x"))
|
||||
targetUrl = strings.ReplaceAll(targetUrl, "{y}", chi.URLParam(r, "y"))
|
||||
targetUrl := strings.NewReplacer(
|
||||
"{s}", chi.URLParam(r, "s"),
|
||||
"{z}", chi.URLParam(r, "z"),
|
||||
"{x}", chi.URLParam(r, "x"),
|
||||
"{y}", chi.URLParam(r, "y"),
|
||||
).Replace(tileSource)
|
||||
proxyRequest, _ := http.NewRequestWithContext(r.Context(), http.MethodGet, targetUrl, nil)
|
||||
// Copy request headers
|
||||
for _, k := range []string{
|
||||
|
@ -29,7 +30,6 @@ func (a *goBlog) proxyTiles() http.HandlerFunc {
|
|||
cacheControl,
|
||||
"If-Modified-Since",
|
||||
"If-None-Match",
|
||||
"User-Agent",
|
||||
} {
|
||||
proxyRequest.Header.Set(k, r.Header.Get(k))
|
||||
}
|
||||
|
|
9
go.mod
9
go.mod
|
@ -1,6 +1,6 @@
|
|||
module go.goblog.app/app
|
||||
|
||||
go 1.19
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
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/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
|
||||
github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b
|
||||
github.com/carlmjohnson/requests v0.23.1
|
||||
github.com/carlmjohnson/requests v0.23.2
|
||||
// master
|
||||
github.com/cretz/bine v0.2.1-0.20221201125941-b9d31d9c7866
|
||||
github.com/dchest/captcha v1.0.0
|
||||
|
@ -61,7 +61,7 @@ require (
|
|||
github.com/traefik/yaegi v0.14.4-0.20230117132604-1679870ea3c8
|
||||
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
|
||||
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
|
||||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
||||
golang.org/x/crypto v0.5.0
|
||||
|
@ -71,8 +71,7 @@ require (
|
|||
gopkg.in/yaml.v3 v3.0.1
|
||||
maunium.net/go/mautrix v0.13.0
|
||||
nhooyr.io/websocket v1.8.7
|
||||
// main
|
||||
willnorris.com/go/microformats v1.1.2-0.20221115043057-ffbbdaef989e
|
||||
willnorris.com/go/microformats v1.2.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
12
go.sum
12
go.sum
|
@ -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/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/carlmjohnson/requests v0.23.1 h1:d8FpOZC6We3raxa3cW1oEyK+cNz4XSW8A6wjMkMsbOM=
|
||||
github.com/carlmjohnson/requests v0.23.1/go.mod h1:i/0+cvgndkM7SPtw911bcbYR4CP+/9vP/+TbcFqPz3A=
|
||||
github.com/carlmjohnson/requests v0.23.2 h1:SzaY+/5v8QOvt++7HTXe1xgmIb3wc/bYf2QJmrO73sM=
|
||||
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/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
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.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.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
|
||||
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||
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/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
|
||||
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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
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.1.2-0.20221115043057-ffbbdaef989e/go.mod h1:RrlwCSvib4qz+JICKiN7rON4phzQ3HAT7j6s4O2cZj4=
|
||||
willnorris.com/go/microformats v1.2.0 h1:73pzJCLJM69kYE5qsLI9OOC/7sImNVOzya9EQ0+1wmM=
|
||||
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/go.mod h1:DgeruqKIsZtcDXVXNbBHa0YYEm88oAnK7PahkDtuCvw=
|
||||
|
|
|
@ -20,7 +20,7 @@ func (a *goBlog) healthcheck() bool {
|
|||
fmt.Println("healthcheck:", err.Error())
|
||||
return false
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
_ = resp.Body.Close()
|
||||
return resp.StatusCode == 200
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ var (
|
|||
|
||||
// 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{
|
||||
"issuer": a.getInstanceRootURL(),
|
||||
"authorization_endpoint": a.getFullAddress(indieAuthPath),
|
||||
|
|
|
@ -87,28 +87,27 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc *http.Cli
|
|||
return "", tinifyErr
|
||||
}
|
||||
// Resize and download image
|
||||
imgBuffer := bufferpool.Get()
|
||||
defer bufferpool.Put(imgBuffer)
|
||||
err = requests.
|
||||
URL(compressedLocation).
|
||||
Client(hc).
|
||||
Method(http.MethodPost).
|
||||
BasicAuth("api", tf.key).
|
||||
BodyJSON(map[string]any{
|
||||
"resize": map[string]any{
|
||||
"method": "fit",
|
||||
"width": defaultCompressionWidth,
|
||||
"height": defaultCompressionHeight,
|
||||
},
|
||||
}).
|
||||
ToBytesBuffer(imgBuffer).
|
||||
Fetch(context.Background())
|
||||
if err != nil {
|
||||
log.Println("Tinify error:", err.Error())
|
||||
return "", tinifyErr
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(requests.
|
||||
URL(compressedLocation).
|
||||
Client(hc).
|
||||
Method(http.MethodPost).
|
||||
BasicAuth("api", tf.key).
|
||||
BodyJSON(map[string]any{
|
||||
"resize": map[string]any{
|
||||
"method": "fit",
|
||||
"width": defaultCompressionWidth,
|
||||
"height": defaultCompressionHeight,
|
||||
},
|
||||
}).
|
||||
ToWriter(pw).
|
||||
Fetch(context.Background()))
|
||||
}()
|
||||
// Upload compressed file
|
||||
return uploadCompressedFile(fileExtension, imgBuffer, upload)
|
||||
res, err := uploadCompressedFile(fileExtension, pr, upload)
|
||||
_ = pr.CloseWithError(err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
type cloudflare struct{}
|
||||
|
@ -121,19 +120,18 @@ func (*cloudflare) compress(url string, upload mediaStorageSaveFunc, hc *http.Cl
|
|||
// Force jpeg
|
||||
fileExtension := "jpeg"
|
||||
// Compress
|
||||
imgBuffer := bufferpool.Get()
|
||||
defer bufferpool.Put(imgBuffer)
|
||||
err := 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)).
|
||||
Client(hc).
|
||||
ToBytesBuffer(imgBuffer).
|
||||
Fetch(context.Background())
|
||||
if err != nil {
|
||||
log.Println("Cloudflare error:", err.Error())
|
||||
return "", errors.New("failed to compress image using cloudflare")
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = 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)).
|
||||
Client(hc).
|
||||
ToWriter(pw).
|
||||
Fetch(context.Background()))
|
||||
}()
|
||||
// Upload compressed file
|
||||
return uploadCompressedFile(fileExtension, imgBuffer, upload)
|
||||
res, err := uploadCompressedFile(fileExtension, pr, upload)
|
||||
_ = pr.CloseWithError(err)
|
||||
return res, err
|
||||
}
|
||||
|
||||
type localMediaCompressor struct{}
|
||||
|
@ -144,20 +142,13 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
|
|||
if !allowed {
|
||||
return "", nil
|
||||
}
|
||||
// Download image
|
||||
imgBuffer := bufferpool.Get()
|
||||
defer bufferpool.Put(imgBuffer)
|
||||
err := requests.
|
||||
URL(url).
|
||||
Client(hc).
|
||||
ToBytesBuffer(imgBuffer).
|
||||
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))
|
||||
// Download and decode image
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(requests.URL(url).Client(hc).ToWriter(pw).Fetch(context.Background()))
|
||||
}()
|
||||
img, err := imaging.Decode(pr, imaging.AutoOrientation(true))
|
||||
_ = pr.CloseWithError(err)
|
||||
if err != nil {
|
||||
log.Println("Local compressor error:", err.Error())
|
||||
return "", errors.New("failed to compress image using local compressor")
|
||||
|
@ -165,20 +156,19 @@ func (*localMediaCompressor) compress(url string, upload mediaStorageSaveFunc, h
|
|||
// Resize image
|
||||
resizedImage := imaging.Fit(img, defaultCompressionWidth, defaultCompressionHeight, imaging.Lanczos)
|
||||
// Encode image
|
||||
resizedBuffer := bufferpool.Get()
|
||||
defer bufferpool.Put(resizedBuffer)
|
||||
switch fileExtension {
|
||||
case "jpg", "jpeg":
|
||||
err = imaging.Encode(resizedBuffer, resizedImage, imaging.JPEG, imaging.JPEGQuality(75))
|
||||
case "png":
|
||||
err = imaging.Encode(resizedBuffer, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression))
|
||||
}
|
||||
if err != nil {
|
||||
log.Println("Local compressor error:", err.Error())
|
||||
return "", errors.New("failed to compress image using local compressor")
|
||||
}
|
||||
pr, pw = io.Pipe()
|
||||
go func() {
|
||||
switch fileExtension {
|
||||
case "png":
|
||||
_ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression)))
|
||||
default:
|
||||
_ = pw.CloseWithError(imaging.Encode(pw, resizedImage, imaging.JPEG, imaging.JPEGQuality(75)))
|
||||
}
|
||||
}()
|
||||
// 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) {
|
||||
|
@ -186,7 +176,10 @@ func uploadCompressedFile(fileExtension string, r io.Reader, upload mediaStorage
|
|||
hash := sha256.New()
|
||||
tempBuffer := bufferpool.Get()
|
||||
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
|
||||
return upload(fmt.Sprintf("%x.%s", hash.Sum(nil), fileExtension), tempBuffer)
|
||||
}
|
||||
|
|
|
@ -29,12 +29,14 @@ func (a *goBlog) mediaStorageEnabled() bool {
|
|||
return a.mediaStorage != nil
|
||||
}
|
||||
|
||||
var errNoMediaStorageConfigured = errors.New("no media storage configured")
|
||||
|
||||
type mediaStorageSaveFunc func(filename string, file io.Reader) (location string, err error)
|
||||
|
||||
func (a *goBlog) saveMediaFile(filename string, f io.Reader) (string, error) {
|
||||
a.initMediaStorage()
|
||||
if a.mediaStorage == nil {
|
||||
return "", errors.New("no media storage configured")
|
||||
return "", errNoMediaStorageConfigured
|
||||
}
|
||||
loc, err := a.mediaStorage.save(filename, f)
|
||||
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 {
|
||||
a.initMediaStorage()
|
||||
if a.mediaStorage == nil {
|
||||
return errors.New("no media storage configured")
|
||||
return errNoMediaStorageConfigured
|
||||
}
|
||||
return a.mediaStorage.delete(filepath.Base(filename))
|
||||
}
|
||||
|
@ -61,7 +63,7 @@ type mediaFile struct {
|
|||
func (a *goBlog) mediaFiles() ([]*mediaFile, error) {
|
||||
a.initMediaStorage()
|
||||
if a.mediaStorage == nil {
|
||||
return nil, errors.New("no media storage configured")
|
||||
return nil, errNoMediaStorageConfigured
|
||||
}
|
||||
return a.mediaStorage.files()
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"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{
|
||||
"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))
|
||||
}
|
||||
|
||||
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, r *http.Request) {
|
||||
func (a *goBlog) serveNodeInfo(w http.ResponseWriter, _ *http.Request) {
|
||||
localPosts, _ := a.db.countPosts(&postsRequestConfig{
|
||||
status: []postStatus{statusPublished},
|
||||
visibility: []postVisibility{visibilityPublic},
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
_ "embed"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"go.goblog.app/app/pkgs/bufferpool"
|
||||
"go.goblog.app/app/pkgs/contenttype"
|
||||
)
|
||||
|
||||
|
@ -91,19 +90,20 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
|
|||
quality = 75
|
||||
}
|
||||
// Read from database
|
||||
var imageBytes []byte
|
||||
var imageReader io.ReadCloser
|
||||
if a.hasProfileImage() {
|
||||
var err error
|
||||
imageBytes, err = os.ReadFile(profileImageFile)
|
||||
if err != nil || imageBytes == nil {
|
||||
a.serveError(w, r, "Failed to read image file", http.StatusInternalServerError)
|
||||
imageReader, err = os.Open(profileImageFile)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to open image file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
imageBytes = defaultLogo
|
||||
imageReader = io.NopCloser(bytes.NewReader(defaultLogo))
|
||||
}
|
||||
// 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 {
|
||||
a.serveError(w, r, "Failed to decode image", http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -111,16 +111,14 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
|
|||
// Resize image
|
||||
resizedImage := imaging.Fit(img, width, height, imaging.Lanczos)
|
||||
// Encode
|
||||
resizedBuffer := bufferpool.Get()
|
||||
defer bufferpool.Put(resizedBuffer)
|
||||
err = encode(resizedBuffer, resizedImage, quality)
|
||||
if err != nil {
|
||||
a.serveError(w, r, "Failed to encode image", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
_ = pw.CloseWithError(encode(pw, resizedImage, quality))
|
||||
}()
|
||||
// Return
|
||||
w.Header().Set(contentType, mediaType)
|
||||
_, _ = io.Copy(w, resizedBuffer)
|
||||
_, err = io.Copy(w, pr)
|
||||
_ = pr.CloseWithError(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
|||
renderPipeReader, renderPipeWriter := io.Pipe()
|
||||
go func() {
|
||||
f(htmlbuilder.NewHtmlBuilder(renderPipeWriter), data)
|
||||
renderPipeWriter.Close()
|
||||
_ = renderPipeWriter.Close()
|
||||
}()
|
||||
// Run UI plugins
|
||||
pluginPipeReader, pluginPipeWriter := io.Pipe()
|
||||
|
@ -52,7 +52,7 @@ func (a *goBlog) renderWithStatusCode(w http.ResponseWriter, r *http.Request, st
|
|||
blog: data.BlogString,
|
||||
path: r.URL.Path,
|
||||
}, renderPipeReader, pluginPipeWriter)
|
||||
pluginPipeWriter.Close()
|
||||
_ = pluginPipeWriter.Close()
|
||||
}()
|
||||
// Return minified HTML
|
||||
_ = 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()
|
||||
}()
|
||||
a.chainUiPlugins(plugins[1:], rc, reader, modified)
|
||||
_ = reader.Close()
|
||||
}
|
||||
|
||||
func (a *goBlog) checkRenderData(r *http.Request, data *renderData) {
|
||||
|
|
|
@ -173,7 +173,7 @@ func (a *goBlog) serveSitemapBlogPosts(w http.ResponseWriter, r *http.Request) {
|
|||
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()
|
||||
go func() {
|
||||
_, _ = io.WriteString(pw, xml.Header)
|
||||
|
|
9
tts.go
9
tts.go
|
@ -75,6 +75,7 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
|
|||
_ = phw.Close()
|
||||
}()
|
||||
postHtmlText, err := htmlTextFromReader(phr)
|
||||
_ = phr.CloseWithError(err)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -82,14 +83,13 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
|
|||
|
||||
// Create TTS audio for each part
|
||||
partReaders := []io.Reader{}
|
||||
partWriters := []*io.PipeWriter{}
|
||||
var g errgroup.Group
|
||||
for _, part := range parts {
|
||||
part := part
|
||||
pr, pw := io.Pipe()
|
||||
defer func() {
|
||||
pw.Close()
|
||||
}()
|
||||
partReaders = append(partReaders, pr)
|
||||
partWriters = append(partWriters, pw)
|
||||
g.Go(func() error {
|
||||
// Build SSML
|
||||
ssml := "<speak>" + html.EscapeString(part) + "<break time=\"500ms\"/></speak>"
|
||||
|
@ -105,6 +105,9 @@ func (a *goBlog) createPostTTSAudio(p *post) error {
|
|||
defer bufferpool.Put(buf)
|
||||
hash := sha256.New()
|
||||
err = mp3merge.MergeMP3(io.MultiWriter(buf, hash), partReaders...)
|
||||
for _, pw := range partWriters {
|
||||
_ = pw.CloseWithError(err)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ EXTRA="
|
|||
github.com/cretz/bine@master
|
||||
github.com/tkrajina/gpxgo@master
|
||||
github.com/yuin/goldmark-emoji@master
|
||||
willnorris.com/go/microformats@main
|
||||
github.com/traefik/yaegi@master
|
||||
"
|
||||
|
||||
|
|
4
utils.go
4
utils.go
|
@ -289,7 +289,9 @@ func cleanHTMLText(s string) string {
|
|||
// Clean HTML with UGC policy and return text
|
||||
pr, pw := io.Pipe()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue