forked from wgroeneveld/go-jamming
revert to embedded anonymous images if downloading pictures fails
This commit is contained in:
parent
64c6065d74
commit
00f927886d
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
|
@ -2,22 +2,47 @@ package pictures
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"brainbaking.com/go-jamming/db"
|
"brainbaking.com/go-jamming/db"
|
||||||
|
_ "embed"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"net/http"
|
"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.
|
// Handle handles picture GET calls.
|
||||||
// It does not validate the picture query as it's part of a composite key anyway.
|
// It does not validate the picture query as it's part of a composite key anyway.
|
||||||
func Handle(repo db.MentionRepo) http.HandlerFunc {
|
func Handle(repo db.MentionRepo) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
picDomain := mux.Vars(r)["picture"]
|
picDomain := mux.Vars(r)["picture"]
|
||||||
|
if picDomain == Anonymous {
|
||||||
|
servePicture(w, anonymous)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
picData := repo.GetPicture(picDomain)
|
picData := repo.GetPicture(picDomain)
|
||||||
if picData == nil {
|
if picData == nil {
|
||||||
http.NotFound(w, r)
|
http.NotFound(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
w.WriteHeader(http.StatusOK)
|
servePicture(w, picData)
|
||||||
// TODO response headers? is this a jpeg, png, gif, webm? should we?
|
|
||||||
w.Write(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)
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package recv
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"brainbaking.com/go-jamming/app/mf"
|
"brainbaking.com/go-jamming/app/mf"
|
||||||
|
"brainbaking.com/go-jamming/app/pictures"
|
||||||
"brainbaking.com/go-jamming/common"
|
"brainbaking.com/go-jamming/common"
|
||||||
"brainbaking.com/go-jamming/db"
|
"brainbaking.com/go-jamming/db"
|
||||||
"brainbaking.com/go-jamming/rest"
|
"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) {
|
func (recv *Receiver) saveAuthorPictureLocally(indieweb *mf.IndiewebData) {
|
||||||
_, picData, err := recv.RestClient.GetBody(indieweb.Author.Picture)
|
_, picData, err := recv.RestClient.GetBody(indieweb.Author.Picture)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
srcDomain := rest.Domain(indieweb.Source)
|
srcDomain := rest.Domain(indieweb.Source)
|
||||||
_, dberr := recv.Repo.SavePicture(picData, srcDomain)
|
_, dberr := recv.Repo.SavePicture(picData, srcDomain)
|
||||||
if dberr != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,48 @@ var conf = &common.Config{
|
||||||
ConString: ":memory:",
|
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) {
|
func TestReceive(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
label string
|
label string
|
||||||
|
|
|
@ -50,7 +50,7 @@ func RelPathGetBodyFunc(t *testing.T, relPath string) func(string) (http.Header,
|
||||||
mockfile := relPath + strings.ReplaceAll(url, "https://brainbaking.com/", "")
|
mockfile := relPath + strings.ReplaceAll(url, "https://brainbaking.com/", "")
|
||||||
html, err := ioutil.ReadFile(mockfile)
|
html, err := ioutil.ReadFile(mockfile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
headerData, headerFileErr := ioutil.ReadFile(strings.ReplaceAll(mockfile, ".html", "-headers.json"))
|
headerData, headerFileErr := ioutil.ReadFile(strings.ReplaceAll(mockfile, ".html", "-headers.json"))
|
||||||
|
|
Loading…
Reference in New Issue