Remove webmentions
parent
9aebddd251
commit
7cbaf6ad31
1
main.go
1
main.go
|
@ -16,6 +16,5 @@ func main() {
|
|||
log.Println("Blog URL: " + BlogUrl)
|
||||
http.HandleFunc("/micropub", HandleMicroPub)
|
||||
http.HandleFunc("/media", HandleMedia)
|
||||
http.HandleFunc("/webmention", HandleWebmention)
|
||||
log.Fatal(http.ListenAndServe(":5555", nil))
|
||||
}
|
||||
|
|
161
webmention.go
161
webmention.go
|
@ -1,161 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
"willnorris.com/go/webmention"
|
||||
)
|
||||
|
||||
type Mention struct {
|
||||
Source string `json:"source"`
|
||||
Target string `json:"target"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
func HandleWebmention(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
_, _ = w.Write([]byte("The HTTP method is not allowed, make a POST request"))
|
||||
return
|
||||
}
|
||||
sourceUrl, err := url.Parse(r.FormValue("source"))
|
||||
if err != nil || !(sourceUrl.Scheme == "http" || sourceUrl.Scheme == "https") {
|
||||
err = errors.New("failed to parse webmention source URL")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
targetUrl, err := url.Parse(r.FormValue("target"))
|
||||
if err != nil || !(sourceUrl.Scheme == "http" || sourceUrl.Scheme == "https") {
|
||||
err = errors.New("failed to parse webmention target URL")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
// Check if urls don't equal
|
||||
if sourceUrl.String() == targetUrl.String() {
|
||||
err = errors.New("source and target equal")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
// Check if target is blog
|
||||
if !strings.HasPrefix(targetUrl.String(), strings.TrimSuffix(BlogUrl, "/")) {
|
||||
err = errors.New("wrong webmention target")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
// Check response code for source
|
||||
respCode, err := responseCodeForSource(sourceUrl.String())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
if respCode < 200 || respCode >= 300 {
|
||||
if respCode == 410 || respCode == 404 {
|
||||
// Delete mention, because source is gone
|
||||
go func() {
|
||||
e := deleteWebmention(sourceUrl.String(), targetUrl.String())
|
||||
if e != nil {
|
||||
fmt.Print("Tried to delete webmention", sourceUrl.String(), "but failed:", e.Error())
|
||||
}
|
||||
}()
|
||||
returnSuccess(targetUrl.String(), w, r)
|
||||
return
|
||||
} else {
|
||||
err = errors.New("source returned error")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
}
|
||||
// Check if source mentions target
|
||||
if !sourceMentionsTarget(sourceUrl.String(), targetUrl.String()) {
|
||||
err = errors.New("source doesn't mention target")
|
||||
go func() {
|
||||
// Try to delete webmention nevertheless
|
||||
e := deleteWebmention(sourceUrl.String(), targetUrl.String())
|
||||
if e != nil {
|
||||
fmt.Print("Tried to delete webmention (source doesn't mention target) ", sourceUrl.String(), "but failed:", e.Error())
|
||||
}
|
||||
}()
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
e := saveWebmention(&Mention{
|
||||
Source: sourceUrl.String(),
|
||||
Target: targetUrl.String(),
|
||||
Date: time.Now().Format("2006-01-02"),
|
||||
})
|
||||
if e != nil {
|
||||
fmt.Println("Failed to save webmention:", e.Error())
|
||||
}
|
||||
}()
|
||||
returnSuccess(targetUrl.String(), w, r)
|
||||
go func() {
|
||||
if SelectedNotificationServices != nil {
|
||||
SelectedNotificationServices.Post("New webmention: " + sourceUrl.String())
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func responseCodeForSource(source string) (int, error) {
|
||||
client := http.DefaultClient
|
||||
resp, err := client.Get(source)
|
||||
if err != nil || resp == nil {
|
||||
return 0, err
|
||||
}
|
||||
return resp.StatusCode, nil
|
||||
}
|
||||
|
||||
func sourceMentionsTarget(source string, target string) bool {
|
||||
client := webmention.New(nil)
|
||||
dl, err := client.DiscoverLinks(source, "")
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for _, link := range dl {
|
||||
if link == target {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func saveWebmention(mention *Mention) (err error) {
|
||||
bytesRepresentation, err := json.Marshal(mention)
|
||||
if err != nil {
|
||||
return errors.New("failed to marshal json before committing")
|
||||
}
|
||||
filePath := fmt.Sprintf("data/mentions/%x/%x.json", md5.Sum([]byte(strings.ReplaceAll(mention.Target, "/", ""))), md5.Sum([]byte(mention.Source)))
|
||||
err = SelectedStorage.UpdateFile(filePath, string(bytesRepresentation), "New webmention from "+mention.Source)
|
||||
return
|
||||
}
|
||||
|
||||
func deleteWebmention(source string, target string) (err error) {
|
||||
filePath := fmt.Sprintf("data/mentions/%x/%x.json", md5.Sum([]byte(strings.ReplaceAll(target, "/", ""))), md5.Sum([]byte(source)))
|
||||
err = SelectedStorage.DeleteFile(filePath, "Delete webmention from "+source)
|
||||
return
|
||||
}
|
||||
|
||||
func returnSuccess(target string, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Add("Location", target)
|
||||
if strings.Contains(r.Header.Get("Accept"), "text/html") {
|
||||
// Redirect browser
|
||||
w.WriteHeader(http.StatusSeeOther)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_responseCodeForSource(t *testing.T) {
|
||||
for _, code := range []int{200, 404} {
|
||||
t.Run(strconv.Itoa(code), func(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(code)
|
||||
}))
|
||||
defer ts.Close()
|
||||
got, err := responseCodeForSource(ts.URL)
|
||||
if err != nil || got != code {
|
||||
t.Errorf("Wrong response code: Got %d, but expected %d", got, code)
|
||||
}
|
||||
})
|
||||
}
|
||||
t.Run("Error", func(t *testing.T) {
|
||||
ts := httptest.NewUnstartedServer(nil)
|
||||
defer ts.Close()
|
||||
got, err := responseCodeForSource(ts.URL)
|
||||
if err == nil {
|
||||
t.Errorf("Error is nil")
|
||||
}
|
||||
if got != 0 {
|
||||
t.Errorf("Wrong response code: Got %d, but expected %d", got, 0)
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue