WIP receiving webmentionsi. #7
This commit is contained in:
parent
179559250a
commit
691158cd0e
|
@ -26,6 +26,9 @@ Each directory in the `/users/` directory of a repository is considered a user.
|
||||||
\- media/
|
\- media/
|
||||||
-- Contains all media files used in the blog post.
|
-- Contains all media files used in the blog post.
|
||||||
-- All files in this folder will be publicly available
|
-- All files in this folder will be publicly available
|
||||||
|
\- webmention/
|
||||||
|
\- <hash>.yml
|
||||||
|
-- Contains data for a received webmention
|
||||||
\- meta/
|
\- meta/
|
||||||
\- base.html
|
\- base.html
|
||||||
-- The template used to render all sites
|
-- The template used to render all sites
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
)
|
)
|
||||||
|
@ -56,6 +57,59 @@ func userIndexHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Requ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func userWebmentionHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
|
user, err := getUserFromRepo(repo, ps)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte("User not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte("Unable to parse form data"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
params := r.PostForm
|
||||||
|
target := params["target"]
|
||||||
|
source := params["source"]
|
||||||
|
if len(target) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte("No target provided"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(source) == 0 {
|
||||||
|
w.WriteHeader(http.StatusBadRequest)
|
||||||
|
w.Write([]byte("No source provided"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(target[0], "/")
|
||||||
|
if len(parts) < 2 {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte("Not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
postId := parts[len(parts)-2]
|
||||||
|
post, err := user.GetPost(postId)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusNotFound)
|
||||||
|
w.Write([]byte("Post not found"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = post.AddWebmention(source[0])
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("Unable to process webmention"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(http.StatusAccepted)
|
||||||
|
w.Write([]byte(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func userRSSHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
func userRSSHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
|
||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
user, err := getUserFromRepo(repo, ps)
|
user, err := getUserFromRepo(repo, ps)
|
||||||
|
|
|
@ -14,6 +14,7 @@ func Router(repo *owl.Repository) http.Handler {
|
||||||
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
||||||
router.GET("/", repoIndexHandler(repo))
|
router.GET("/", repoIndexHandler(repo))
|
||||||
router.GET("/user/:user/", userIndexHandler(repo))
|
router.GET("/user/:user/", userIndexHandler(repo))
|
||||||
|
router.POST("/user/:user/webmention/", userWebmentionHandler(repo))
|
||||||
router.GET("/user/:user/index.xml", userRSSHandler(repo))
|
router.GET("/user/:user/index.xml", userRSSHandler(repo))
|
||||||
router.GET("/user/:user/posts/:post/", postHandler(repo))
|
router.GET("/user/:user/posts/:post/", postHandler(repo))
|
||||||
router.GET("/user/:user/posts/:post/media/*filepath", postMediaHandler(repo))
|
router.GET("/user/:user/posts/:post/media/*filepath", postMediaHandler(repo))
|
||||||
|
@ -25,6 +26,7 @@ func SingleUserRouter(repo *owl.Repository) http.Handler {
|
||||||
router := httprouter.New()
|
router := httprouter.New()
|
||||||
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
router.ServeFiles("/static/*filepath", http.Dir(repo.StaticDir()))
|
||||||
router.GET("/", userIndexHandler(repo))
|
router.GET("/", userIndexHandler(repo))
|
||||||
|
router.POST("/webmention/", userWebmentionHandler(repo))
|
||||||
router.GET("/index.xml", userRSSHandler(repo))
|
router.GET("/index.xml", userRSSHandler(repo))
|
||||||
router.GET("/posts/:post/", postHandler(repo))
|
router.GET("/posts/:post/", postHandler(repo))
|
||||||
router.GET("/posts/:post/media/*filepath", postMediaHandler(repo))
|
router.GET("/posts/:post/media/*filepath", postMediaHandler(repo))
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package main_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
main "h4kor/owl-blogs/cmd/owl-web"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWebmentionHandleAccepts(t *testing.T) {
|
||||||
|
repo := getTestRepo()
|
||||||
|
user, _ := repo.CreateUser("test-1")
|
||||||
|
post, _ := user.CreateNewPost("post-1")
|
||||||
|
|
||||||
|
target := post.FullUrl()
|
||||||
|
source := "https://example.com"
|
||||||
|
data := url.Values{}
|
||||||
|
data.Set("target", target)
|
||||||
|
data.Set("source", source)
|
||||||
|
|
||||||
|
// Create Request and Response
|
||||||
|
req, err := http.NewRequest("POST", user.UrlPath()+"webmention/", strings.NewReader(data.Encode()))
|
||||||
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := main.Router(&repo)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if status := rr.Code; status != http.StatusAccepted {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, http.StatusAccepted)
|
||||||
|
t.Errorf("Body: %v", rr.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWebmentionWrittenToPost(t *testing.T) {
|
||||||
|
repo := getTestRepo()
|
||||||
|
user, _ := repo.CreateUser("test-1")
|
||||||
|
post, _ := user.CreateNewPost("post-1")
|
||||||
|
|
||||||
|
target := post.FullUrl()
|
||||||
|
source := "https://example.com"
|
||||||
|
data := url.Values{}
|
||||||
|
data.Set("target", target)
|
||||||
|
data.Set("source", source)
|
||||||
|
|
||||||
|
// Create Request and Response
|
||||||
|
req, err := http.NewRequest("POST", user.UrlPath()+"webmention/", strings.NewReader(data.Encode()))
|
||||||
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
|
req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rr := httptest.NewRecorder()
|
||||||
|
router := main.Router(&repo)
|
||||||
|
router.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
// Check the status code is what we expect.
|
||||||
|
if status := rr.Code; status != http.StatusAccepted {
|
||||||
|
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||||
|
status, http.StatusAccepted)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(post.Webmentions()) != 1 {
|
||||||
|
t.Errorf("no webmention written to post")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
post.go
18
post.go
|
@ -2,7 +2,10 @@ package owl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
|
@ -39,6 +42,10 @@ func (post Post) MediaDir() string {
|
||||||
return path.Join(post.Dir(), "media")
|
return path.Join(post.Dir(), "media")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (post Post) WebmentionDir() string {
|
||||||
|
return path.Join(post.Dir(), "webmention")
|
||||||
|
}
|
||||||
|
|
||||||
func (post Post) UrlPath() string {
|
func (post Post) UrlPath() string {
|
||||||
return post.user.UrlPath() + "posts/" + post.id + "/"
|
return post.user.UrlPath() + "posts/" + post.id + "/"
|
||||||
}
|
}
|
||||||
|
@ -139,3 +146,14 @@ func (post *Post) LoadMeta() error {
|
||||||
post.meta = meta
|
post.meta = meta
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (post *Post) AddWebmention(source string) error {
|
||||||
|
hash := sha256.Sum256([]byte(source))
|
||||||
|
hashStr := base64.URLEncoding.EncodeToString(hash[:])
|
||||||
|
data := "source: " + source
|
||||||
|
return os.WriteFile(path.Join(post.WebmentionDir(), hashStr+".yml"), []byte(data), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (post *Post) Webmentions() []string {
|
||||||
|
return listDir(post.WebmentionDir())
|
||||||
|
}
|
||||||
|
|
1
user.go
1
user.go
|
@ -150,6 +150,7 @@ func (user User) CreateNewPost(title string) (Post, error) {
|
||||||
os.WriteFile(post.ContentFile(), []byte(initial_content), 0644)
|
os.WriteFile(post.ContentFile(), []byte(initial_content), 0644)
|
||||||
// create media dir
|
// create media dir
|
||||||
os.Mkdir(post.MediaDir(), 0755)
|
os.Mkdir(post.MediaDir(), 0755)
|
||||||
|
os.Mkdir(post.WebmentionDir(), 0755)
|
||||||
return post, nil
|
return post, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue