diff --git a/cmd/owl/web/handler.go b/cmd/owl/web/handler.go
index 4c5a160..5b0355a 100644
--- a/cmd/owl/web/handler.go
+++ b/cmd/owl/web/handler.go
@@ -8,6 +8,7 @@ import (
"os"
"path"
"strings"
+ "time"
"github.com/julienschmidt/httprouter"
)
@@ -249,6 +250,78 @@ func postMediaHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Requ
}
}
+func userMicropubHandler(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 {
+ println("Error getting user: ", err.Error())
+ notFoundHandler(repo)(w, r)
+ return
+ }
+
+ // parse request form
+ err = r.ParseForm()
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("Bad request"))
+ return
+ }
+
+ // verify access token
+ token := r.Header.Get("Authorization")
+ if token == "" {
+ token = r.Form.Get("access_token")
+ } else {
+ token = strings.TrimPrefix(token, "Bearer ")
+ }
+
+ valid, _ := user.ValidateAccessToken(token)
+ if !valid {
+ w.WriteHeader(http.StatusUnauthorized)
+ w.Write([]byte("Unauthorized"))
+ return
+ }
+
+ h := r.Form.Get("h")
+ content := r.Form.Get("content")
+ name := r.Form.Get("name")
+ inReplyTo := r.Form.Get("in-reply-to")
+
+ if h != "entry" {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("Bad request. h must be entry. Got " + h))
+ return
+ }
+ if content == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ w.Write([]byte("Bad request. content is required"))
+ return
+ }
+
+ // create post
+ post, err := user.CreateNewPostFull(
+ owl.PostMeta{
+ Title: name,
+ Reply: owl.Reply{
+ Url: inReplyTo,
+ },
+ Date: time.Now(),
+ },
+ content,
+ )
+
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ w.Write([]byte("Internal server error"))
+ return
+ }
+
+ w.Header().Add("Location", post.FullUrl())
+ w.WriteHeader(http.StatusCreated)
+
+ }
+}
+
func userMediaHandler(repo *owl.Repository) func(http.ResponseWriter, *http.Request, httprouter.Params) {
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
filepath := ps.ByName("filepath")
diff --git a/cmd/owl/web/micropub_test.go b/cmd/owl/web/micropub_test.go
new file mode 100644
index 0000000..d9574ff
--- /dev/null
+++ b/cmd/owl/web/micropub_test.go
@@ -0,0 +1,183 @@
+package web_test
+
+import (
+ "h4kor/owl-blogs"
+ main "h4kor/owl-blogs/cmd/owl/web"
+ "h4kor/owl-blogs/test/assertions"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func TestMicropubMinimalArticle(t *testing.T) {
+ repo, user := getSingleUserTestRepo()
+ user.ResetPassword("testpassword")
+
+ code, _ := user.GenerateAuthCode(
+ "test", "test", "test", "test", "test",
+ )
+ token, _, _ := user.GenerateAccessToken(owl.AuthCode{
+ Code: code,
+ ClientId: "test",
+ RedirectUri: "test",
+ CodeChallenge: "test",
+ CodeChallengeMethod: "test",
+ Scope: "test",
+ })
+
+ // Create Request and Response
+ form := url.Values{}
+ form.Add("h", "entry")
+ form.Add("name", "Test Article")
+ form.Add("content", "Test Content")
+
+ req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode()))
+ req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
+ req.Header.Add("Authorization", "Bearer "+token)
+ assertions.AssertNoError(t, err, "Error creating request")
+ rr := httptest.NewRecorder()
+ router := main.SingleUserRouter(&repo)
+ router.ServeHTTP(rr, req)
+
+ assertions.AssertStatus(t, rr, http.StatusCreated)
+}
+
+func TestMicropubWithoutName(t *testing.T) {
+ repo, user := getSingleUserTestRepo()
+ user.ResetPassword("testpassword")
+
+ code, _ := user.GenerateAuthCode(
+ "test", "test", "test", "test", "test",
+ )
+ token, _, _ := user.GenerateAccessToken(owl.AuthCode{
+ Code: code,
+ ClientId: "test",
+ RedirectUri: "test",
+ CodeChallenge: "test",
+ CodeChallengeMethod: "test",
+ Scope: "test",
+ })
+
+ // Create Request and Response
+ form := url.Values{}
+ form.Add("h", "entry")
+ form.Add("content", "Test Content")
+
+ req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode()))
+ req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
+ req.Header.Add("Authorization", "Bearer "+token)
+ assertions.AssertNoError(t, err, "Error creating request")
+ rr := httptest.NewRecorder()
+ router := main.SingleUserRouter(&repo)
+ router.ServeHTTP(rr, req)
+
+ assertions.AssertStatus(t, rr, http.StatusCreated)
+ loc_header := rr.Header().Get("Location")
+ assertions.Assert(t, loc_header != "", "Location header should be set")
+}
+
+func TestMicropubAccessTokenInBody(t *testing.T) {
+ repo, user := getSingleUserTestRepo()
+ user.ResetPassword("testpassword")
+
+ code, _ := user.GenerateAuthCode(
+ "test", "test", "test", "test", "test",
+ )
+ token, _, _ := user.GenerateAccessToken(owl.AuthCode{
+ Code: code,
+ ClientId: "test",
+ RedirectUri: "test",
+ CodeChallenge: "test",
+ CodeChallengeMethod: "test",
+ Scope: "test",
+ })
+
+ // Create Request and Response
+ form := url.Values{}
+ form.Add("h", "entry")
+ form.Add("content", "Test Content")
+ form.Add("access_token", token)
+
+ req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode()))
+ req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
+ assertions.AssertNoError(t, err, "Error creating request")
+ rr := httptest.NewRecorder()
+ router := main.SingleUserRouter(&repo)
+ router.ServeHTTP(rr, req)
+
+ assertions.AssertStatus(t, rr, http.StatusCreated)
+ loc_header := rr.Header().Get("Location")
+ assertions.Assert(t, loc_header != "", "Location header should be set")
+}
+
+// func TestMicropubAccessTokenInBoth(t *testing.T) {
+// repo, user := getSingleUserTestRepo()
+// user.ResetPassword("testpassword")
+
+// code, _ := user.GenerateAuthCode(
+// "test", "test", "test", "test", "test",
+// )
+// token, _, _ := user.GenerateAccessToken(owl.AuthCode{
+// Code: code,
+// ClientId: "test",
+// RedirectUri: "test",
+// CodeChallenge: "test",
+// CodeChallengeMethod: "test",
+// Scope: "test",
+// })
+
+// // Create Request and Response
+// form := url.Values{}
+// form.Add("h", "entry")
+// form.Add("content", "Test Content")
+// form.Add("access_token", token)
+
+// req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode()))
+// req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+// req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
+// req.Header.Add("Authorization", "Bearer "+token)
+// assertions.AssertNoError(t, err, "Error creating request")
+// rr := httptest.NewRecorder()
+// router := main.SingleUserRouter(&repo)
+// router.ServeHTTP(rr, req)
+
+// assertions.AssertStatus(t, rr, http.StatusBadRequest)
+// }
+
+func TestMicropubNoAccessToken(t *testing.T) {
+ repo, user := getSingleUserTestRepo()
+ user.ResetPassword("testpassword")
+
+ code, _ := user.GenerateAuthCode(
+ "test", "test", "test", "test", "test",
+ )
+ user.GenerateAccessToken(owl.AuthCode{
+ Code: code,
+ ClientId: "test",
+ RedirectUri: "test",
+ CodeChallenge: "test",
+ CodeChallengeMethod: "test",
+ Scope: "test",
+ })
+
+ // Create Request and Response
+ form := url.Values{}
+ form.Add("h", "entry")
+ form.Add("content", "Test Content")
+
+ req, err := http.NewRequest("POST", user.MicropubUrl(), strings.NewReader(form.Encode()))
+ req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
+ req.Header.Add("Content-Length", strconv.Itoa(len(form.Encode())))
+ assertions.AssertNoError(t, err, "Error creating request")
+ rr := httptest.NewRecorder()
+ router := main.SingleUserRouter(&repo)
+ router.ServeHTTP(rr, req)
+
+ assertions.AssertStatus(t, rr, http.StatusUnauthorized)
+}
diff --git a/cmd/owl/web/server.go b/cmd/owl/web/server.go
index 4967735..d828508 100644
--- a/cmd/owl/web/server.go
+++ b/cmd/owl/web/server.go
@@ -23,6 +23,7 @@ func Router(repo *owl.Repository) http.Handler {
router.GET("/user/:user/posts/:post/", postHandler(repo))
router.GET("/user/:user/posts/:post/media/*filepath", postMediaHandler(repo))
router.POST("/user/:user/webmention/", userWebmentionHandler(repo))
+ router.POST("/user/:user/micropub/", userMicropubHandler(repo))
router.GET("/user/:user/.well-known/oauth-authorization-server", userAuthMetadataHandler(repo))
router.NotFound = http.HandlerFunc(notFoundHandler(repo))
return router
@@ -41,6 +42,7 @@ func SingleUserRouter(repo *owl.Repository) http.Handler {
router.GET("/posts/:post/", postHandler(repo))
router.GET("/posts/:post/media/*filepath", postMediaHandler(repo))
router.POST("/webmention/", userWebmentionHandler(repo))
+ router.POST("/micropub/", userMicropubHandler(repo))
router.GET("/.well-known/oauth-authorization-server", userAuthMetadataHandler(repo))
router.NotFound = http.HandlerFunc(notFoundHandler(repo))
return router
diff --git a/embed/initial/base.html b/embed/initial/base.html
index 78ad250..5fee043 100644
--- a/embed/initial/base.html
+++ b/embed/initial/base.html
@@ -31,6 +31,7 @@
+
{{ end }}