refactored webmentions to use a single file for in and out going mentions
This commit is contained in:
parent
1fc6b9e9d2
commit
c0151dbc15
|
@ -116,7 +116,7 @@ func userWebmentionHandler(repo *owl.Repository) func(http.ResponseWriter, *http
|
|||
w.Write([]byte("Post not found"))
|
||||
return
|
||||
}
|
||||
err = post.AddWebmention(source[0])
|
||||
err = post.AddIncomingWebmention(source[0])
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("Unable to process webmention"))
|
||||
|
|
|
@ -75,7 +75,7 @@ func TestWebmentionWrittenToPost(t *testing.T) {
|
|||
// Check the status code is what we expect.
|
||||
assertStatus(t, rr, http.StatusAccepted)
|
||||
|
||||
if len(post.Webmentions()) != 1 {
|
||||
if len(post.IncomingWebmentions()) != 1 {
|
||||
t.Errorf("no webmention written to post")
|
||||
}
|
||||
}
|
||||
|
|
222
post.go
222
post.go
|
@ -2,9 +2,7 @@ package owl
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
|
@ -34,8 +32,9 @@ type PostMeta struct {
|
|||
Draft bool `yaml:"draft"`
|
||||
}
|
||||
|
||||
type PostStatus struct {
|
||||
Webmentions []WebmentionOut
|
||||
type PostWebmetions struct {
|
||||
Incoming []WebmentionIn `ymal:"incoming"`
|
||||
Outgoing []WebmentionOut `ymal:"outgoing"`
|
||||
}
|
||||
|
||||
func (post Post) Id() string {
|
||||
|
@ -46,18 +45,14 @@ func (post Post) Dir() string {
|
|||
return path.Join(post.user.Dir(), "public", post.id)
|
||||
}
|
||||
|
||||
func (post Post) StatusFile() string {
|
||||
return path.Join(post.Dir(), "status.yml")
|
||||
func (post Post) WebmentionsFile() string {
|
||||
return path.Join(post.Dir(), "webmentions.yml")
|
||||
}
|
||||
|
||||
func (post Post) MediaDir() string {
|
||||
return path.Join(post.Dir(), "media")
|
||||
}
|
||||
|
||||
func (post Post) WebmentionDir() string {
|
||||
return path.Join(post.Dir(), "webmention")
|
||||
}
|
||||
|
||||
func (post Post) UrlPath() string {
|
||||
return post.user.UrlPath() + "posts/" + post.id + "/"
|
||||
}
|
||||
|
@ -91,35 +86,35 @@ func (post Post) Content() []byte {
|
|||
return data
|
||||
}
|
||||
|
||||
func (post Post) Status() PostStatus {
|
||||
func (post Post) Webmentions() PostWebmetions {
|
||||
// read status file
|
||||
// return parsed webmentions
|
||||
fileName := post.StatusFile()
|
||||
fileName := post.WebmentionsFile()
|
||||
if !fileExists(fileName) {
|
||||
return PostStatus{}
|
||||
return PostWebmetions{}
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return PostStatus{}
|
||||
return PostWebmetions{}
|
||||
}
|
||||
|
||||
status := PostStatus{}
|
||||
err = yaml.Unmarshal(data, &status)
|
||||
webmentions := PostWebmetions{}
|
||||
err = yaml.Unmarshal(data, &webmentions)
|
||||
if err != nil {
|
||||
return PostStatus{}
|
||||
return PostWebmetions{}
|
||||
}
|
||||
|
||||
return status
|
||||
return webmentions
|
||||
}
|
||||
|
||||
func (post Post) PersistStatus(status PostStatus) error {
|
||||
data, err := yaml.Marshal(status)
|
||||
func (post Post) PersistWebmentions(webmentions PostWebmetions) error {
|
||||
data, err := yaml.Marshal(webmentions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.WriteFile(post.StatusFile(), data, 0644)
|
||||
err = os.WriteFile(post.WebmentionsFile(), data, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -195,103 +190,85 @@ func (post *Post) LoadMeta() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (post *Post) WebmentionFile(source string) string {
|
||||
|
||||
hash := sha256.Sum256([]byte(source))
|
||||
hashStr := base64.URLEncoding.EncodeToString(hash[:])
|
||||
return path.Join(post.WebmentionDir(), hashStr+".yml")
|
||||
}
|
||||
|
||||
func (post *Post) PersistWebmention(webmention WebmentionIn) error {
|
||||
// ensure dir exists
|
||||
os.MkdirAll(post.WebmentionDir(), 0755)
|
||||
|
||||
// write to file
|
||||
fileName := post.WebmentionFile(webmention.Source)
|
||||
data, err := yaml.Marshal(webmention)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(fileName, data, 0644)
|
||||
}
|
||||
|
||||
func (post *Post) Webmention(source string) (WebmentionIn, error) {
|
||||
// ensure dir exists
|
||||
os.MkdirAll(post.WebmentionDir(), 0755)
|
||||
|
||||
// Check if file exists
|
||||
fileName := post.WebmentionFile(source)
|
||||
if !fileExists(fileName) {
|
||||
// return error if file doesn't exist
|
||||
return WebmentionIn{}, fmt.Errorf("Webmention file not found: %s", source)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(fileName)
|
||||
if err != nil {
|
||||
return WebmentionIn{}, err
|
||||
}
|
||||
|
||||
mention := WebmentionIn{}
|
||||
err = yaml.Unmarshal(data, &mention)
|
||||
if err != nil {
|
||||
return WebmentionIn{}, err
|
||||
}
|
||||
|
||||
return mention, nil
|
||||
}
|
||||
|
||||
func (post *Post) AddWebmention(source string) error {
|
||||
// Check if file already exists
|
||||
_, err := post.Webmention(source)
|
||||
if err != nil {
|
||||
webmention := WebmentionIn{
|
||||
Source: source,
|
||||
}
|
||||
defer post.EnrichWebmention(source)
|
||||
return post.PersistWebmention(webmention)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (post *Post) AddOutgoingWebmention(target string) error {
|
||||
status := post.Status()
|
||||
|
||||
// Check if file already exists
|
||||
_, err := post.Webmention(target)
|
||||
if err != nil {
|
||||
webmention := WebmentionOut{
|
||||
Target: target,
|
||||
}
|
||||
// if target is not in status, add it
|
||||
for _, t := range status.Webmentions {
|
||||
if t.Target == webmention.Target {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
status.Webmentions = append(status.Webmentions, webmention)
|
||||
}
|
||||
|
||||
return post.PersistStatus(status)
|
||||
}
|
||||
|
||||
func (post *Post) UpdateOutgoingWebmention(webmention *WebmentionOut) error {
|
||||
status := post.Status()
|
||||
func (post *Post) PersistIncomingWebmention(webmention WebmentionIn) error {
|
||||
wms := post.Webmentions()
|
||||
|
||||
// if target is not in status, add it
|
||||
replaced := false
|
||||
for i, t := range status.Webmentions {
|
||||
if t.Target == webmention.Target {
|
||||
status.Webmentions[i] = *webmention
|
||||
for i, t := range wms.Incoming {
|
||||
if t.Source == webmention.Source {
|
||||
wms.Incoming[i] = webmention
|
||||
replaced = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !replaced {
|
||||
status.Webmentions = append(status.Webmentions, *webmention)
|
||||
wms.Incoming = append(wms.Incoming, webmention)
|
||||
}
|
||||
|
||||
return post.PersistStatus(status)
|
||||
return post.PersistWebmentions(wms)
|
||||
}
|
||||
|
||||
func (post *Post) Webmention(source string) (WebmentionIn, error) {
|
||||
wms := post.Webmentions()
|
||||
for _, wm := range wms.Incoming {
|
||||
if wm.Source == source {
|
||||
return wm, nil
|
||||
}
|
||||
}
|
||||
return WebmentionIn{}, errors.New("not found")
|
||||
}
|
||||
|
||||
func (post *Post) AddIncomingWebmention(source string) error {
|
||||
// Check if file already exists
|
||||
_, err := post.Webmention(source)
|
||||
if err != nil {
|
||||
wms := post.Webmentions()
|
||||
wms.Incoming = append(wms.Incoming, WebmentionIn{
|
||||
Source: source,
|
||||
})
|
||||
defer post.EnrichWebmention(source)
|
||||
return post.PersistWebmentions(wms)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (post *Post) AddOutgoingWebmention(target string) error {
|
||||
wms := post.Webmentions()
|
||||
|
||||
// Check if file already exists
|
||||
for _, wm := range wms.Outgoing {
|
||||
if wm.Target == target {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
webmention := WebmentionOut{
|
||||
Target: target,
|
||||
}
|
||||
wms.Outgoing = append(wms.Outgoing, webmention)
|
||||
return post.PersistWebmentions(wms)
|
||||
}
|
||||
|
||||
func (post *Post) UpdateOutgoingWebmention(webmention *WebmentionOut) error {
|
||||
wms := post.Webmentions()
|
||||
|
||||
// if target is not in status, add it
|
||||
replaced := false
|
||||
for i, t := range wms.Outgoing {
|
||||
if t.Target == webmention.Target {
|
||||
wms.Outgoing[i] = *webmention
|
||||
replaced = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !replaced {
|
||||
wms.Outgoing = append(wms.Outgoing, *webmention)
|
||||
}
|
||||
|
||||
return post.PersistWebmentions(wms)
|
||||
}
|
||||
|
||||
func (post *Post) EnrichWebmention(source string) error {
|
||||
|
@ -304,35 +281,18 @@ func (post *Post) EnrichWebmention(source string) error {
|
|||
entry, err := post.user.repo.Parser.ParseHEntry(resp)
|
||||
if err == nil {
|
||||
webmention.Title = entry.Title
|
||||
return post.PersistWebmention(webmention)
|
||||
return post.PersistIncomingWebmention(webmention)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (post *Post) Webmentions() []WebmentionIn {
|
||||
// ensure dir exists
|
||||
os.MkdirAll(post.WebmentionDir(), 0755)
|
||||
files := listDir(post.WebmentionDir())
|
||||
webmentions := []WebmentionIn{}
|
||||
for _, file := range files {
|
||||
data, err := os.ReadFile(path.Join(post.WebmentionDir(), file))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
mention := WebmentionIn{}
|
||||
err = yaml.Unmarshal(data, &mention)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
webmentions = append(webmentions, mention)
|
||||
}
|
||||
|
||||
return webmentions
|
||||
func (post *Post) IncomingWebmentions() []WebmentionIn {
|
||||
return post.Webmentions().Incoming
|
||||
}
|
||||
|
||||
func (post *Post) ApprovedWebmentions() []WebmentionIn {
|
||||
webmentions := post.Webmentions()
|
||||
webmentions := post.IncomingWebmentions()
|
||||
approved := []WebmentionIn{}
|
||||
for _, webmention := range webmentions {
|
||||
if webmention.ApprovalStatus == "approved" {
|
||||
|
@ -348,9 +308,7 @@ func (post *Post) ApprovedWebmentions() []WebmentionIn {
|
|||
}
|
||||
|
||||
func (post *Post) OutgoingWebmentions() []WebmentionOut {
|
||||
status := post.Status()
|
||||
return status.Webmentions
|
||||
|
||||
return post.Webmentions().Outgoing
|
||||
}
|
||||
|
||||
// ScanForLinks scans the post content for links and adds them to the
|
||||
|
|
68
post_test.go
68
post_test.go
|
@ -167,18 +167,18 @@ func TestLoadMeta(t *testing.T) {
|
|||
/// Webmention
|
||||
///
|
||||
|
||||
func TestPersistWebmention(t *testing.T) {
|
||||
func TestPersistIncomingWebmention(t *testing.T) {
|
||||
repo := getTestRepo(owl.RepoConfig{})
|
||||
user, _ := repo.CreateUser("testuser")
|
||||
post, _ := user.CreateNewPost("testpost")
|
||||
webmention := owl.WebmentionIn{
|
||||
Source: "http://example.com/source",
|
||||
}
|
||||
err := post.PersistWebmention(webmention)
|
||||
err := post.PersistIncomingWebmention(webmention)
|
||||
if err != nil {
|
||||
t.Errorf("Got error: %v", err)
|
||||
}
|
||||
mentions := post.Webmentions()
|
||||
mentions := post.IncomingWebmentions()
|
||||
if len(mentions) != 1 {
|
||||
t.Errorf("Expected 1 webmention, got %d", len(mentions))
|
||||
}
|
||||
|
@ -188,74 +188,64 @@ func TestPersistWebmention(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAddWebmentionCreatesFile(t *testing.T) {
|
||||
func TestAddIncomingWebmentionCreatesFile(t *testing.T) {
|
||||
repo := getTestRepo(owl.RepoConfig{})
|
||||
repo.HttpClient = &MockHttpClient{}
|
||||
repo.Parser = &MockHtmlParser{}
|
||||
user, _ := repo.CreateUser("testuser")
|
||||
post, _ := user.CreateNewPost("testpost")
|
||||
|
||||
err := post.AddWebmention("https://example.com")
|
||||
err := post.AddIncomingWebmention("https://example.com")
|
||||
if err != nil {
|
||||
t.Errorf("Got Error: %v", err)
|
||||
}
|
||||
|
||||
mentions := post.Webmentions()
|
||||
mentions := post.IncomingWebmentions()
|
||||
if len(mentions) != 1 {
|
||||
t.Errorf("Expected 1 webmention, got %d", len(mentions))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddWebmentionNotOverwritingFile(t *testing.T) {
|
||||
func TestAddIncomingWebmentionNotOverwritingWebmention(t *testing.T) {
|
||||
repo := getTestRepo(owl.RepoConfig{})
|
||||
repo.HttpClient = &MockHttpClient{}
|
||||
repo.Parser = &MockHtmlParser{}
|
||||
user, _ := repo.CreateUser("testuser")
|
||||
post, _ := user.CreateNewPost("testpost")
|
||||
|
||||
post.AddWebmention("https://example.com")
|
||||
dir, _ := os.Open(post.WebmentionDir())
|
||||
defer dir.Close()
|
||||
files, _ := dir.Readdirnames(-1)
|
||||
post.PersistIncomingWebmention(owl.WebmentionIn{
|
||||
Source: "https://example.com",
|
||||
ApprovalStatus: "approved",
|
||||
})
|
||||
|
||||
if len(files) != 1 {
|
||||
t.Error("No file created for webmention")
|
||||
post.AddIncomingWebmention("https://example.com")
|
||||
|
||||
mentions := post.IncomingWebmentions()
|
||||
if len(mentions) != 1 {
|
||||
t.Errorf("Expected 1 webmention, got %d", len(mentions))
|
||||
}
|
||||
|
||||
content := "url: https://example.com\n"
|
||||
content += "verified: true"
|
||||
os.WriteFile(path.Join(post.WebmentionDir(), files[0]), []byte(content), 0644)
|
||||
|
||||
post.AddWebmention("https://example.com")
|
||||
|
||||
fileContent, _ := os.ReadFile(path.Join(post.WebmentionDir(), files[0]))
|
||||
if string(fileContent) != content {
|
||||
t.Error("File content was modified.")
|
||||
t.Errorf("Got: %v", fileContent)
|
||||
t.Errorf("Expected: %v", content)
|
||||
if mentions[0].ApprovalStatus != "approved" {
|
||||
t.Errorf("Expected approval status: %s, got %s", "approved", mentions[0].ApprovalStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddWebmentionAddsParsedTitle(t *testing.T) {
|
||||
func TestAddIncomingWebmentionAddsParsedTitle(t *testing.T) {
|
||||
repo := getTestRepo(owl.RepoConfig{})
|
||||
repo.HttpClient = &MockHttpClient{}
|
||||
repo.Parser = &MockHtmlParser{}
|
||||
user, _ := repo.CreateUser("testuser")
|
||||
post, _ := user.CreateNewPost("testpost")
|
||||
|
||||
post.AddWebmention("https://example.com")
|
||||
dir, _ := os.Open(post.WebmentionDir())
|
||||
defer dir.Close()
|
||||
files, _ := dir.Readdirnames(-1)
|
||||
post.AddIncomingWebmention("https://example.com")
|
||||
|
||||
if len(files) != 1 {
|
||||
t.Error("No file created for webmention")
|
||||
mentions := post.IncomingWebmentions()
|
||||
if len(mentions) != 1 {
|
||||
t.Errorf("Expected 1 webmention, got %d", len(mentions))
|
||||
}
|
||||
|
||||
fileContent, _ := os.ReadFile(path.Join(post.WebmentionDir(), files[0]))
|
||||
if !strings.Contains(string(fileContent), "Mock Title") {
|
||||
t.Error("File not containing the title.")
|
||||
t.Errorf("Got: %v", string(fileContent))
|
||||
if mentions[0].Title != "Mock Title" {
|
||||
t.Errorf("Expected title: %s, got %s", "Mock Title", mentions[0].Title)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -268,25 +258,25 @@ func TestApprovedWebmentions(t *testing.T) {
|
|||
ApprovalStatus: "approved",
|
||||
RetrievedAt: time.Now(),
|
||||
}
|
||||
post.PersistWebmention(webmention)
|
||||
post.PersistIncomingWebmention(webmention)
|
||||
webmention = owl.WebmentionIn{
|
||||
Source: "http://example.com/source2",
|
||||
ApprovalStatus: "",
|
||||
RetrievedAt: time.Now().Add(time.Hour * -1),
|
||||
}
|
||||
post.PersistWebmention(webmention)
|
||||
post.PersistIncomingWebmention(webmention)
|
||||
webmention = owl.WebmentionIn{
|
||||
Source: "http://example.com/source3",
|
||||
ApprovalStatus: "approved",
|
||||
RetrievedAt: time.Now().Add(time.Hour * -2),
|
||||
}
|
||||
post.PersistWebmention(webmention)
|
||||
post.PersistIncomingWebmention(webmention)
|
||||
webmention = owl.WebmentionIn{
|
||||
Source: "http://example.com/source4",
|
||||
ApprovalStatus: "rejected",
|
||||
RetrievedAt: time.Now().Add(time.Hour * -3),
|
||||
}
|
||||
post.PersistWebmention(webmention)
|
||||
post.PersistIncomingWebmention(webmention)
|
||||
|
||||
webmentions := post.ApprovedWebmentions()
|
||||
if len(webmentions) != 2 {
|
||||
|
|
|
@ -187,13 +187,13 @@ func TestRenderPostAddsLinksToApprovedWebmention(t *testing.T) {
|
|||
ApprovalStatus: "approved",
|
||||
RetrievedAt: time.Now().Add(time.Hour * -2),
|
||||
}
|
||||
post.PersistWebmention(webmention)
|
||||
post.PersistIncomingWebmention(webmention)
|
||||
webmention = owl.WebmentionIn{
|
||||
Source: "http://example.com/source4",
|
||||
ApprovalStatus: "rejected",
|
||||
RetrievedAt: time.Now().Add(time.Hour * -3),
|
||||
}
|
||||
post.PersistWebmention(webmention)
|
||||
post.PersistIncomingWebmention(webmention)
|
||||
|
||||
result, _ := owl.RenderPost(&post)
|
||||
if !strings.Contains(result, "http://example.com/source3") {
|
||||
|
|
Loading…
Reference in New Issue