forked from wgroeneveld/go-jamming
mail notifier, admin wrappers endpoints, first impl done
This commit is contained in:
parent
2e504eaa65
commit
6cc83620ba
|
@ -1,10 +1,10 @@
|
|||
package admin
|
||||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/common"
|
||||
"brainbaking.com/go-jamming/db"
|
||||
"brainbaking.com/go-jamming/rest"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"net/http"
|
||||
)
|
||||
|
@ -16,32 +16,39 @@ func HandleGet(repo db.MentionRepo) http.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO validate or not? see webmention.HandlePost
|
||||
// TODO unit tests
|
||||
// HandleApprove approves the Mention (by key in URL) and adds to the whitelist.
|
||||
// Returns 200 OK with approved source/target or 404 if key is invalid.
|
||||
func HandleApprove(c *common.Config, repo db.MentionRepo) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
wm := mf.Mention{
|
||||
Source: r.FormValue("source"),
|
||||
Target: r.FormValue("target"),
|
||||
key := mux.Vars(r)["key"]
|
||||
|
||||
approved := repo.Approve(key)
|
||||
if approved == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
repo.Approve(wm)
|
||||
c.AddToWhitelist(wm.SourceDomain())
|
||||
w.WriteHeader(200)
|
||||
c.AddToWhitelist(approved.AsMention().SourceDomain())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Approved: %s", approved.AsMention().String())
|
||||
}
|
||||
}
|
||||
|
||||
// HandleReject rejects the Mention (by key in URL) and adds to the blacklist.
|
||||
// Returns 200 OK with rejected source/target or 404 if key is invalid.
|
||||
func HandleReject(c *common.Config, repo db.MentionRepo) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
wm := mf.Mention{
|
||||
Source: r.FormValue("source"),
|
||||
Target: r.FormValue("target"),
|
||||
key := mux.Vars(r)["key"]
|
||||
|
||||
rejected := repo.Reject(key)
|
||||
if rejected == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
repo.Reject(wm)
|
||||
c.AddToBlacklist(wm.SourceDomain())
|
||||
w.WriteHeader(200)
|
||||
c.AddToBlacklist(rejected.AsMention().SourceDomain())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, "Rejected: %s", rejected.AsMention().String())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,8 @@ func (wm Mention) SourceDomain() string {
|
|||
// Key returns a unique string representation of the mention for use in storage.
|
||||
// TODO Profiling indicated that md5() consumes a lot of CPU power, so this could be replaced with db migration.
|
||||
func (wm Mention) Key() string {
|
||||
return fmt.Sprintf("%x", md5.Sum([]byte("source="+wm.Source+",target="+wm.Target)))
|
||||
key := fmt.Sprintf("%x", md5.Sum([]byte("source="+wm.Source+",target="+wm.Target)))
|
||||
return fmt.Sprintf("%s:%s", key, wm.TargetDomain())
|
||||
}
|
||||
|
||||
func (wm Mention) SourceUrl() *url.URL {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/common"
|
||||
"encoding/base64"
|
||||
"github.com/rs/zerolog/log"
|
||||
"net/mail"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
type MailNotifier struct {
|
||||
Conf *common.Config
|
||||
}
|
||||
|
||||
// sendMail is a utility function that sends a mail without authentication to localhost. Tested using postfix.
|
||||
// cheers https://github.com/gadelkareem/go-helpers/blob/master/helpers.go
|
||||
func sendMail(from, subject, body, toName, toAddress string) error {
|
||||
c, err := smtp.Dial("127.0.0.1:25")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
if err = c.Mail(from); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
to := (&mail.Address{toName, toAddress}).String()
|
||||
if err = c.Rcpt(to); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := c.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
msg := "To: " + to + "\r\n" +
|
||||
"From: " + from + "\r\n" +
|
||||
"Subject: " + subject + "\r\n" +
|
||||
"Content-Type: text/html; charset=\"UTF-8\"\r\n" +
|
||||
"Content-Transfer-Encoding: base64\r\n" +
|
||||
"\r\n" + base64.StdEncoding.EncodeToString([]byte(body))
|
||||
|
||||
_, err = w.Write([]byte(msg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.Quit()
|
||||
}
|
||||
|
||||
func (mn *MailNotifier) NotifyReceived(wm mf.Mention, indieweb *mf.IndiewebData) {
|
||||
err := sendMail(
|
||||
"admin@brainbaking.com",
|
||||
"Webmention in moderation from "+wm.SourceDomain(),
|
||||
BuildNotification(wm, indieweb, mn.Conf),
|
||||
"Go-Jamming User",
|
||||
"wouter@brainbaking.com")
|
||||
|
||||
if err != nil {
|
||||
log.Err(err).Msg("Unable to send notification mail, check localhost postfix settings?")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/common"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Notifier interface {
|
||||
NotifyReceived(wm mf.Mention, data *mf.IndiewebData)
|
||||
}
|
||||
|
||||
// BuildNotification returns a string representation of the Mention to notify the admin.
|
||||
func BuildNotification(wm mf.Mention, data *mf.IndiewebData, cnf *common.Config) string {
|
||||
enter := "\n"
|
||||
acceptUrl := fmt.Sprintf("%sadmin/approve/%s/%s", cnf.BaseURL, cnf.Token, wm.Key())
|
||||
rejectUrl := fmt.Sprintf("%sadmin/reject/%s/%s", cnf.BaseURL, cnf.Token, wm.Key())
|
||||
|
||||
return fmt.Sprintf("Hi admin, %s%s,A webmention was received: %sSource %s, Target %s%sContent: %s%s%sAccept? %s%sReject? %s%sCheerio, your go-jammin' thing.",
|
||||
enter, enter, enter,
|
||||
wm.Source, wm.Target, enter,
|
||||
data.Content, enter, enter,
|
||||
acceptUrl, enter, rejectUrl, enter)
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/common"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildNotification(t *testing.T) {
|
||||
wm := mf.Mention{
|
||||
Source: "https://brainbaking.com/valid-indieweb-source.html",
|
||||
Target: "https://brainbaking.com/valid-indieweb-target.html",
|
||||
}
|
||||
cnf := &common.Config{
|
||||
AllowedWebmentionSources: []string{
|
||||
"brainbaking.com",
|
||||
},
|
||||
BaseURL: "https://jam.brainbaking.com/",
|
||||
Token: "mytoken",
|
||||
Blacklist: []string{},
|
||||
Whitelist: []string{},
|
||||
}
|
||||
|
||||
expected := `Hi admin,
|
||||
|
||||
,A webmention was received:
|
||||
Source https://brainbaking.com/valid-indieweb-source.html, Target https://brainbaking.com/valid-indieweb-target.html
|
||||
Content: somecontent
|
||||
|
||||
Accept? https://jam.brainbaking.com/admin/approve/mytoken/19d462ddff3c3322c662dac3461324bb:brainbaking.com
|
||||
Reject? https://jam.brainbaking.com/admin/reject/mytoken/19d462ddff3c3322c662dac3461324bb:brainbaking.com
|
||||
Cheerio, your go-jammin' thing.`
|
||||
|
||||
result := BuildNotification(wm, &mf.IndiewebData{Content: "somecontent"}, cnf)
|
||||
assert.Equal(t, result, expected)
|
||||
}
|
|
@ -20,11 +20,11 @@ func (s *server) routes() {
|
|||
s.router.HandleFunc("/pingback", pingback.HandlePost(c, db)).Methods("POST")
|
||||
s.router.HandleFunc("/webmention", webmention.HandlePost(c, db)).Methods("POST")
|
||||
|
||||
s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandleGet(db))).Methods("GET")
|
||||
s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandlePut(c, db))).Methods("PUT")
|
||||
s.router.HandleFunc("/webmention/{domain}/{token}", s.authorizedOnly(webmention.HandleDelete(db))).Methods("DELETE")
|
||||
s.router.HandleFunc("/webmention/{domain}/{token}", s.domainAndTokenOnly(webmention.HandleGet(db))).Methods("GET")
|
||||
s.router.HandleFunc("/webmention/{domain}/{token}", s.domainAndTokenOnly(webmention.HandlePut(c, db))).Methods("PUT")
|
||||
s.router.HandleFunc("/webmention/{domain}/{token}", s.domainAndTokenOnly(webmention.HandleDelete(db))).Methods("DELETE")
|
||||
|
||||
s.router.HandleFunc("/admin/{domain}/{token}", s.authorizedOnly(admin.HandleGet(db))).Methods("GET")
|
||||
s.router.HandleFunc("/admin/approve/{token}", s.authorizedOnly(admin.HandleApprove(c, db))).Methods("POST")
|
||||
s.router.HandleFunc("/admin/reject/{token}", s.authorizedOnly(admin.HandleReject(c, db))).Methods("POST")
|
||||
s.router.HandleFunc("/admin/{domain}/{token}", s.domainAndTokenOnly(admin.HandleGet(db))).Methods("GET")
|
||||
s.router.HandleFunc("/admin/approve/{token}/{key}", s.authorizedOnly(admin.HandleApprove(c, db))).Methods("GET")
|
||||
s.router.HandleFunc("/admin/reject/{token}/{key}", s.authorizedOnly(admin.HandleReject(c, db))).Methods("GET")
|
||||
}
|
||||
|
|
|
@ -20,10 +20,25 @@ type server struct {
|
|||
repo db.MentionRepo
|
||||
}
|
||||
|
||||
func (s *server) domainAndTokenOnly(h http.HandlerFunc) http.HandlerFunc {
|
||||
return s.domainOnly(s.authorizedOnly(h))
|
||||
}
|
||||
|
||||
func (s *server) domainOnly(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
if !s.conf.IsAnAllowedDomain(vars["domain"]) {
|
||||
rest.Unauthorized(w)
|
||||
return
|
||||
}
|
||||
h(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) authorizedOnly(h http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
if vars["token"] != s.conf.Token || !s.conf.IsAnAllowedDomain(vars["domain"]) {
|
||||
if vars["token"] != s.conf.Token {
|
||||
rest.Unauthorized(w)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -34,13 +34,13 @@ func TestAuthorizedOnlyUnauthorizedWithWrongToken(t *testing.T) {
|
|||
assert.False(t, passed, "should not have called unauthorized func")
|
||||
}
|
||||
|
||||
func TestAuthorizedOnlyUnauthorizedWithWrongDomain(t *testing.T) {
|
||||
func TestDomainOnlyWithWrongDomain(t *testing.T) {
|
||||
srv := &server{
|
||||
conf: conf,
|
||||
}
|
||||
|
||||
passed := false
|
||||
handler := srv.authorizedOnly(func(writer http.ResponseWriter, request *http.Request) {
|
||||
handler := srv.domainOnly(func(writer http.ResponseWriter, request *http.Request) {
|
||||
passed = true
|
||||
})
|
||||
r, _ := http.NewRequest("PUT", "/whatever", nil)
|
||||
|
|
|
@ -2,6 +2,7 @@ package webmention
|
|||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/app/notifier"
|
||||
"brainbaking.com/go-jamming/app/webmention/recv"
|
||||
"brainbaking.com/go-jamming/app/webmention/send"
|
||||
"brainbaking.com/go-jamming/db"
|
||||
|
@ -88,6 +89,9 @@ func HandlePost(conf *common.Config, repo db.MentionRepo) http.HandlerFunc {
|
|||
RestClient: httpClient,
|
||||
Conf: conf,
|
||||
Repo: repo,
|
||||
Notifier: ¬ifier.MailNotifier{
|
||||
Conf: conf,
|
||||
},
|
||||
}
|
||||
|
||||
go recv.Receive(wm)
|
||||
|
|
|
@ -2,6 +2,7 @@ package recv
|
|||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/app/notifier"
|
||||
"brainbaking.com/go-jamming/common"
|
||||
"brainbaking.com/go-jamming/db"
|
||||
"brainbaking.com/go-jamming/rest"
|
||||
|
@ -16,6 +17,7 @@ import (
|
|||
|
||||
type Receiver struct {
|
||||
RestClient rest.Client
|
||||
Notifier notifier.Notifier
|
||||
Conf *common.Config
|
||||
Repo db.MentionRepo
|
||||
}
|
||||
|
@ -62,23 +64,30 @@ func (recv *Receiver) processSourceBody(body string, wm mf.Mention) {
|
|||
indieweb := recv.convertBodyToIndiewebData(body, wm, data)
|
||||
recv.processAuthorPicture(indieweb)
|
||||
|
||||
recv.saveMentionToDatabase(wm, indieweb)
|
||||
if recv.Conf.IsWhitelisted(wm.Source) {
|
||||
recv.processWhitelistedMention(wm, indieweb)
|
||||
} else {
|
||||
recv.processMentionInModeration(wm, indieweb)
|
||||
}
|
||||
}
|
||||
|
||||
func (recv *Receiver) saveMentionToDatabase(wm mf.Mention, indieweb *mf.IndiewebData) {
|
||||
if recv.Conf.IsWhitelisted(wm.Source) {
|
||||
key, err := recv.Repo.Save(wm, indieweb)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Stringer("wm", wm).Msg("Failed to save new mention to db")
|
||||
}
|
||||
log.Info().Str("key", key).Msg("OK: Webmention processed, in whitelist.")
|
||||
} else {
|
||||
key, err := recv.Repo.InModeration(wm, indieweb)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Stringer("wm", wm).Msg("Failed to save new mention to in moderation db")
|
||||
}
|
||||
log.Info().Str("key", key).Msg("OK: Webmention processed, in moderation.")
|
||||
func (recv *Receiver) processMentionInModeration(wm mf.Mention, indieweb *mf.IndiewebData) {
|
||||
key, err := recv.Repo.InModeration(wm, indieweb)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Stringer("wm", wm).Msg("Failed to save new mention to in moderation db")
|
||||
}
|
||||
if recv.Notifier != nil {
|
||||
recv.Notifier.NotifyReceived(wm, indieweb)
|
||||
}
|
||||
log.Info().Str("key", key).Msg("OK: Webmention processed, in moderation.")
|
||||
}
|
||||
|
||||
func (recv *Receiver) processWhitelistedMention(wm mf.Mention, indieweb *mf.IndiewebData) {
|
||||
key, err := recv.Repo.Save(wm, indieweb)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Stringer("wm", wm).Msg("Failed to save new mention to db")
|
||||
}
|
||||
log.Info().Str("key", key).Msg("OK: Webmention processed, in whitelist.")
|
||||
}
|
||||
|
||||
func (recv *Receiver) processAuthorPicture(indieweb *mf.IndiewebData) {
|
||||
|
|
|
@ -237,7 +237,7 @@ func TestReceiveTargetDoesNotExistAnymoreDeletesPossiblyOlderWebmention(t *testi
|
|||
assert.Empty(t, indb)
|
||||
}
|
||||
|
||||
func TestReceiveFromNotInWhitelistSavesInModeration(t *testing.T) {
|
||||
func TestReceiveFromNotInWhitelistSavesInModerationAndNotifies(t *testing.T) {
|
||||
wm := mf.Mention{
|
||||
Source: "https://brainbaking.com/valid-indieweb-source.html",
|
||||
Target: "https://brainbaking.com/valid-indieweb-target.html",
|
||||
|
@ -246,22 +246,30 @@ func TestReceiveFromNotInWhitelistSavesInModeration(t *testing.T) {
|
|||
AllowedWebmentionSources: []string{
|
||||
"brainbaking.com",
|
||||
},
|
||||
BaseURL: "https://jam.brainbaking.com/",
|
||||
Token: "mytoken",
|
||||
Blacklist: []string{},
|
||||
Whitelist: []string{},
|
||||
}
|
||||
repo := db.NewMentionRepo(cnf)
|
||||
t.Cleanup(db.Purge)
|
||||
notifierMock := &mocks.StringNotifier{
|
||||
Conf: cnf,
|
||||
Output: "",
|
||||
}
|
||||
receiver := &Receiver{
|
||||
Conf: cnf,
|
||||
Repo: repo,
|
||||
RestClient: &mocks.RestClientMock{
|
||||
GetBodyFunc: mocks.RelPathGetBodyFunc("../../../mocks/"),
|
||||
},
|
||||
Notifier: notifierMock,
|
||||
}
|
||||
|
||||
receiver.Receive(wm)
|
||||
assert.Empty(t, repo.GetAll("brainbaking.com").Data)
|
||||
assert.Equal(t, 1, len(repo.GetAllToModerate("brainbaking.com").Data))
|
||||
assert.Contains(t, notifierMock.Output, "Accept?")
|
||||
}
|
||||
|
||||
func TestReceiveFromBlacklistedDomainDoesNothing(t *testing.T) {
|
||||
|
|
|
@ -12,6 +12,8 @@ import (
|
|||
)
|
||||
|
||||
type Config struct {
|
||||
// BaseURL should end with a / and is used to build URLs in notifications
|
||||
BaseURL string `json:"baseURL"`
|
||||
Port int `json:"port"`
|
||||
Token string `json:"token"`
|
||||
UtcOffset int `json:"utcOffset"`
|
||||
|
@ -49,6 +51,9 @@ func (c *Config) missingKeys() []string {
|
|||
if c.Token == "" {
|
||||
keys = append(keys, "token")
|
||||
}
|
||||
if c.BaseURL == "" {
|
||||
keys = append(keys, "baseURL")
|
||||
}
|
||||
if len(c.AllowedWebmentionSources) == 0 {
|
||||
keys = append(keys, "allowedWebmentionSources")
|
||||
}
|
||||
|
@ -128,7 +133,8 @@ func config() *Config {
|
|||
}
|
||||
|
||||
func defaultConfig() *Config {
|
||||
return &Config{
|
||||
defaultConfig := &Config{
|
||||
BaseURL: "https://jam.brainbaking.com/",
|
||||
Port: 1337,
|
||||
Token: "miauwkes",
|
||||
UtcOffset: 60,
|
||||
|
@ -136,4 +142,6 @@ func defaultConfig() *Config {
|
|||
Blacklist: []string{"youtube.com"},
|
||||
Whitelist: []string{"brainbaking.com"},
|
||||
}
|
||||
defaultConfig.Save()
|
||||
return defaultConfig
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ func TestReadFromJsonWithCorrectJsonData(t *testing.T) {
|
|||
confString := `{
|
||||
"port": 1337,
|
||||
"host": "localhost",
|
||||
"baseURL": "https://jam.brainbaking.com/",
|
||||
"token": "miauwkes",
|
||||
"utcOffset": 60,
|
||||
"allowedWebmentionSources": [
|
||||
|
@ -57,6 +58,7 @@ func TestWhitelist(t *testing.T) {
|
|||
Whitelist: []string{
|
||||
"youtube.com",
|
||||
},
|
||||
BaseURL: "https://jam.brainbaking.com/",
|
||||
Port: 123,
|
||||
Token: "token",
|
||||
AllowedWebmentionSources: []string{"blah.com"},
|
||||
|
@ -79,6 +81,7 @@ func TestAddToBlacklistNotYetAddsToListAndSaves(t *testing.T) {
|
|||
Blacklist: []string{
|
||||
"youtube.com",
|
||||
},
|
||||
BaseURL: "https://jam.brainbaking.com/",
|
||||
Port: 123,
|
||||
Token: "token",
|
||||
AllowedWebmentionSources: []string{"blah.com"},
|
||||
|
|
|
@ -58,15 +58,18 @@ func lastSentKey(domain string) string {
|
|||
|
||||
// Delete removes a possibly present mention by key. Ignores but logs possible errors.
|
||||
func (r *mentionRepoBunt) Delete(wm mf.Mention) {
|
||||
key := r.mentionToKey(wm)
|
||||
r.deleteByKey(wm.Key())
|
||||
}
|
||||
|
||||
func (r *mentionRepoBunt) deleteByKey(key string) {
|
||||
err := r.db.Update(func(tx *buntdb.Tx) error {
|
||||
_, err := tx.Delete(key)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("key", key).Stringer("wm", wm).Msg("Unable to delete")
|
||||
log.Warn().Err(err).Str("key", key).Msg("Unable to delete")
|
||||
} else {
|
||||
log.Debug().Str("key", key).Stringer("wm", wm).Msg("Deleted.")
|
||||
log.Debug().Str("key", key).Msg("Deleted.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,11 +91,14 @@ func pictureKey(domain string) string {
|
|||
|
||||
// Save saves the mention by marshalling data. Returns the key or a marshal/persist error.
|
||||
func (r *mentionRepoBunt) Save(wm mf.Mention, data *mf.IndiewebData) (string, error) {
|
||||
return r.saveByKey(wm.Key(), data)
|
||||
}
|
||||
|
||||
func (r *mentionRepoBunt) saveByKey(key string, data *mf.IndiewebData) (string, error) {
|
||||
jsonData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
key := r.mentionToKey(wm)
|
||||
err = r.db.Update(func(tx *buntdb.Tx) error {
|
||||
_, _, err := tx.Set(key, string(jsonData), nil)
|
||||
return err
|
||||
|
@ -103,14 +109,10 @@ func (r *mentionRepoBunt) Save(wm mf.Mention, data *mf.IndiewebData) (string, er
|
|||
return key, nil
|
||||
}
|
||||
|
||||
func (r *mentionRepoBunt) mentionToKey(wm mf.Mention) string {
|
||||
return fmt.Sprintf("%s:%s", wm.Key(), wm.TargetDomain())
|
||||
}
|
||||
|
||||
// Get returns a single unmarshalled json value based on the mention key.
|
||||
// It returns the unmarshalled result or nil if something went wrong.
|
||||
func (r *mentionRepoBunt) Get(wm mf.Mention) *mf.IndiewebData {
|
||||
return r.getByKey(r.mentionToKey(wm))
|
||||
return r.getByKey(wm.Key())
|
||||
}
|
||||
|
||||
func (r *mentionRepoBunt) getByKey(key string) *mf.IndiewebData {
|
||||
|
|
|
@ -7,21 +7,41 @@ import (
|
|||
)
|
||||
|
||||
type MentionRepo interface {
|
||||
// InModeration saves the mention data to the in moderation db to approve or reject later.
|
||||
// Returns the key or a marshal/persist error.
|
||||
InModeration(key mf.Mention, data *mf.IndiewebData) (string, error)
|
||||
// Save saves the mention to the approved db.
|
||||
// Returns the key or a marshal/persist error.
|
||||
Save(key mf.Mention, data *mf.IndiewebData) (string, error)
|
||||
// Delete removes a possibly present mention from the approved db by key.
|
||||
// Ignores but logs possible errors.
|
||||
Delete(key mf.Mention)
|
||||
Approve(key mf.Mention)
|
||||
Reject(key mf.Mention)
|
||||
// Approve saves the mention to the approved database and deletes the one in moderation.
|
||||
// If the key is invalid, it returns nil.
|
||||
Approve(key string) *mf.IndiewebData
|
||||
// Reject removes the in moderation key from the db and returns the deleted entry
|
||||
// If the key is invalid, it returns nil.
|
||||
Reject(key string) *mf.IndiewebData
|
||||
|
||||
// Get returns a single unmarshalled json value based on the approved mention key in the db.
|
||||
// It returns the unmarshalled result or nil if something went wrong.
|
||||
Get(key mf.Mention) *mf.IndiewebData
|
||||
// GetAll returns a wrapped data result for all approved mentions for a particular domain.
|
||||
GetAll(domain string) mf.IndiewebDataResult
|
||||
// GetAll returns a wrapped data result for all to approve mentions for a particular domain.
|
||||
GetAllToModerate(domain string) mf.IndiewebDataResult
|
||||
|
||||
// CleanupSpam removes potential blacklisted spam from the approved database by checking the url of each entry.
|
||||
CleanupSpam(domain string, blacklist []string)
|
||||
|
||||
// SavePicture saves the picture byte data in the approved database and returns a key or error.
|
||||
SavePicture(bytes string, domain string) (string, error)
|
||||
// GetPicture returns a byte slice (or nil if unknown) from the approved database for a particular source domain.
|
||||
GetPicture(domain string) []byte
|
||||
// LastSentMention fetches the last known RSS link where mentions were sent from the approved db.
|
||||
// Returns an empty string if an error occured.
|
||||
LastSentMention(domain string) string
|
||||
// UpdateLastSentMention updates the last sent mention link in the approved db. Logs but ignores errors.
|
||||
UpdateLastSentMention(domain string, lastSent string)
|
||||
}
|
||||
|
||||
|
@ -30,6 +50,7 @@ type MentionRepoWrapper struct {
|
|||
approvedRepo *mentionRepoBunt
|
||||
}
|
||||
|
||||
// Save saves the data to the
|
||||
func (m MentionRepoWrapper) Save(key mf.Mention, data *mf.IndiewebData) (string, error) {
|
||||
return m.approvedRepo.Save(key, data)
|
||||
}
|
||||
|
@ -46,14 +67,17 @@ func (m MentionRepoWrapper) Delete(key mf.Mention) {
|
|||
m.approvedRepo.Delete(key)
|
||||
}
|
||||
|
||||
func (m MentionRepoWrapper) Approve(keyInModeration mf.Mention) {
|
||||
toApprove := m.toApproveRepo.Get(keyInModeration)
|
||||
m.Save(keyInModeration, toApprove)
|
||||
m.toApproveRepo.Delete(keyInModeration)
|
||||
func (m MentionRepoWrapper) Approve(keyInModeration string) *mf.IndiewebData {
|
||||
toApprove := m.toApproveRepo.getByKey(keyInModeration)
|
||||
m.approvedRepo.saveByKey(keyInModeration, toApprove)
|
||||
m.toApproveRepo.deleteByKey(keyInModeration)
|
||||
return toApprove
|
||||
}
|
||||
|
||||
func (m MentionRepoWrapper) Reject(keyInModeration mf.Mention) {
|
||||
m.toApproveRepo.Delete(keyInModeration)
|
||||
func (m MentionRepoWrapper) Reject(keyInModeration string) *mf.IndiewebData {
|
||||
toReject := m.toApproveRepo.getByKey(keyInModeration)
|
||||
m.toApproveRepo.deleteByKey(keyInModeration)
|
||||
return toReject
|
||||
}
|
||||
|
||||
func (m MentionRepoWrapper) CleanupSpam(domain string, blacklist []string) {
|
||||
|
|
|
@ -15,7 +15,23 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func TestApproveCases(t *testing.T) {
|
||||
func TestRejectUnknownKeyReturnsNil(t *testing.T) {
|
||||
repo := NewMentionRepo(repoCnf)
|
||||
t.Cleanup(Purge)
|
||||
|
||||
result := repo.Reject("fuckballz")
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestApproveUnknownKeyReturnsNil(t *testing.T) {
|
||||
repo := NewMentionRepo(repoCnf)
|
||||
t.Cleanup(Purge)
|
||||
|
||||
result := repo.Approve("fuckballz")
|
||||
assert.Nil(t, result)
|
||||
}
|
||||
|
||||
func TestApproveAndRejectCases(t *testing.T) {
|
||||
cases := []struct {
|
||||
label string
|
||||
approve bool
|
||||
|
@ -42,6 +58,7 @@ func TestApproveCases(t *testing.T) {
|
|||
defer Purge()
|
||||
|
||||
wm := mf.Mention{
|
||||
Source: "https://jefklakscodex.com/dinges",
|
||||
Target: "https://brainbaking.com/sjiekedinges.html",
|
||||
}
|
||||
data := &mf.IndiewebData{
|
||||
|
@ -50,9 +67,9 @@ func TestApproveCases(t *testing.T) {
|
|||
repo.InModeration(wm, data)
|
||||
|
||||
if tc.approve {
|
||||
repo.Approve(wm)
|
||||
repo.Approve(wm.Key())
|
||||
} else {
|
||||
repo.Reject(wm)
|
||||
repo.Reject(wm.Key())
|
||||
}
|
||||
|
||||
allWms := repo.GetAll("brainbaking.com")
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package mocks
|
||||
|
||||
import (
|
||||
"brainbaking.com/go-jamming/app/mf"
|
||||
"brainbaking.com/go-jamming/app/notifier"
|
||||
"brainbaking.com/go-jamming/common"
|
||||
)
|
||||
|
||||
type StringNotifier struct {
|
||||
Output string
|
||||
Conf *common.Config
|
||||
}
|
||||
|
||||
func (sn *StringNotifier) NotifyReceived(wm mf.Mention, indieweb *mf.IndiewebData) {
|
||||
sn.Output = notifier.BuildNotification(wm, indieweb, sn.Conf)
|
||||
}
|
Loading…
Reference in New Issue