revert to embedded anonymous images if downloading pictures fails

This commit is contained in:
Wouter Groeneveld 2021-04-19 21:38:53 +02:00
parent 64c6065d74
commit 00f927886d
5 changed files with 80 additions and 6 deletions

BIN
app/pictures/anonymous.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,22 +2,47 @@ package pictures
import (
"brainbaking.com/go-jamming/db"
_ "embed"
"github.com/gorilla/mux"
"github.com/rs/zerolog/log"
"net/http"
)
//go:embed anonymous.jpg
var anonymous []byte
const (
Anonymous = "anonymous"
)
func init() {
if anonymous == nil {
log.Fatal().Msg("embedded anonymous image missing?")
}
}
// Handle handles picture GET calls.
// It does not validate the picture query as it's part of a composite key anyway.
func Handle(repo db.MentionRepo) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
picDomain := mux.Vars(r)["picture"]
if picDomain == Anonymous {
servePicture(w, anonymous)
return
}
picData := repo.GetPicture(picDomain)
if picData == nil {
http.NotFound(w, r)
return
}
w.WriteHeader(http.StatusOK)
// TODO response headers? is this a jpeg, png, gif, webm? should we?
w.Write(picData)
servePicture(w, picData)
}
}
// servePicture writes an OK and raw bytes.
// For some reason, headers - although they should be there - aren't needed?
func servePicture(w http.ResponseWriter, bytes []byte) {
w.WriteHeader(http.StatusOK)
w.Write(bytes)
}

View File

@ -2,6 +2,7 @@ package recv
import (
"brainbaking.com/go-jamming/app/mf"
"brainbaking.com/go-jamming/app/pictures"
"brainbaking.com/go-jamming/common"
"brainbaking.com/go-jamming/db"
"brainbaking.com/go-jamming/rest"
@ -97,16 +98,22 @@ func (recv *Receiver) parseBodyAsNonIndiewebSite(body string, wm mf.Mention) *mf
}
}
// saveAuthorPictureLocally tries to download the author picture.
// If it succeeds, it alters the picture path to a local /pictures/x one.
// If it fails, it falls back to a local default image.
// This *should* also validate image byte headers, like https://stackoverflow.com/questions/670546/determine-if-file-is-an-image
func (recv *Receiver) saveAuthorPictureLocally(indieweb *mf.IndiewebData) {
_, picData, err := recv.RestClient.GetBody(indieweb.Author.Picture)
if err != nil {
log.Warn().Err(err).Str("url", indieweb.Author.Picture).Msg("Unable to download author picture. Ignoring.")
log.Warn().Err(err).Str("url", indieweb.Author.Picture).Msg("Unable to download author picture. Reverting to anonymous.")
indieweb.Author.Picture = fmt.Sprintf("/pictures/%s", pictures.Anonymous)
return
}
srcDomain := rest.Domain(indieweb.Source)
_, dberr := recv.Repo.SavePicture(picData, srcDomain)
if dberr != nil {
log.Warn().Err(err).Str("url", indieweb.Author.Picture).Msg("Unable to save downloaded author picture. Ignoring.")
log.Warn().Err(err).Str("url", indieweb.Author.Picture).Msg("Unable to save downloaded author picture. Reverting to anonymous.")
indieweb.Author.Picture = fmt.Sprintf("/pictures/%s", pictures.Anonymous)
return
}

View File

@ -22,6 +22,48 @@ var conf = &common.Config{
ConString: ":memory:",
}
func TestSaveAuthorPictureLocally(t *testing.T) {
cases := []struct {
label string
pictureUrl string
expectedPictureUrl string
}{
{
"Absolute URL gets 'downloaded' and replaced by relative",
"https://brainbaking.com/picture.jpg",
"/pictures/brainbaking.com",
},
{
"Absolute URL gets replaced by anonymous if download fails",
"https://brainbaking.com/thedogatemypic-nowitsmissing-shiii.png",
"/pictures/anonymous",
},
}
for _, tc := range cases {
t.Run(tc.label, func(t *testing.T) {
repo := db.NewMentionRepo(conf)
recv := &Receiver{
Conf: conf,
Repo: repo,
RestClient: &mocks.RestClientMock{
GetBodyFunc: mocks.RelPathGetBodyFunc(t, "../../../mocks/"),
},
}
indieweb := &mf.IndiewebData{
Source: "https://brainbaking.com",
Author: mf.IndiewebAuthor{
Picture: tc.pictureUrl,
},
}
recv.saveAuthorPictureLocally(indieweb)
assert.Equal(t, tc.expectedPictureUrl, indieweb.Author.Picture)
})
}
}
func TestReceive(t *testing.T) {
cases := []struct {
label string

View File

@ -50,7 +50,7 @@ func RelPathGetBodyFunc(t *testing.T, relPath string) func(string) (http.Header,
mockfile := relPath + strings.ReplaceAll(url, "https://brainbaking.com/", "")
html, err := ioutil.ReadFile(mockfile)
if err != nil {
t.Error(err)
return nil, "", err
}
headerData, headerFileErr := ioutil.ReadFile(strings.ReplaceAll(mockfile, ".html", "-headers.json"))