summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Picciano <mediocregopher@gmail.com>2022-11-29 20:59:31 +0100
committerBrian Picciano <mediocregopher@gmail.com>2022-11-29 20:59:31 +0100
commit1f3ae665ed2e58ca572678ce7caf8b711f226392 (patch)
treee023602e07e2a80d24ef9d9527ddca0e1640e929
parent31f8f37c5ad3ad4ac7b3cc93d0257dd80c877c7c (diff)
Introduce EDIT and MANAGE methods
All admin "index" pages are moved under MANAGE, so that we can have (for example) and normal "GET /posts" page later which would replace the current index page, and potentially corresponding pages for the other categories. The EDIT method replaces the old `?edit` pattern, which normalizes how we differentiate page functionality generally.
-rw-r--r--src/http/api.go72
-rw-r--r--src/http/apiutil/apiutil.go5
-rw-r--r--src/http/assets.go15
-rw-r--r--src/http/drafts.go63
-rw-r--r--src/http/posts.go50
-rw-r--r--src/http/tpl.go24
-rw-r--r--src/http/tpl/admin.html6
-rw-r--r--src/http/tpl/draft-posts-manage.html (renamed from src/http/tpl/draft-posts.html)6
-rw-r--r--src/http/tpl/post-assets-manage.html (renamed from src/http/tpl/assets.html)0
-rw-r--r--src/http/tpl/post-edit.html (renamed from src/http/tpl/edit-post.html)4
-rw-r--r--src/http/tpl/posts-manage.html (renamed from src/http/tpl/posts.html)2
11 files changed, 97 insertions, 150 deletions
diff --git a/src/http/api.go b/src/http/api.go
index 01cad50..cbcd182 100644
--- a/src/http/api.go
+++ b/src/http/api.go
@@ -190,7 +190,9 @@ func (a *api) blogHandler() http.Handler {
mux.Handle("/posts/", http.StripPrefix("/posts",
apiutil.MethodMux(map[string]http.Handler{
- "GET": a.renderPostHandler(),
+ "GET": a.getPostHandler(),
+ "EDIT": a.editPostHandler(false),
+ "MANAGE": a.managePostsHandler(),
"POST": a.postPostHandler(),
"DELETE": a.deletePostHandler(false),
"PREVIEW": a.previewPostHandler(),
@@ -200,6 +202,7 @@ func (a *api) blogHandler() http.Handler {
mux.Handle("/assets/", http.StripPrefix("/assets",
apiutil.MethodMux(map[string]http.Handler{
"GET": a.getPostAssetHandler(),
+ "MANAGE": a.managePostAssetsHandler(),
"POST": a.postPostAssetHandler(),
"DELETE": a.deletePostAssetHandler(),
}),
@@ -211,7 +214,8 @@ func (a *api) blogHandler() http.Handler {
authMiddleware(a.auther)(
apiutil.MethodMux(map[string]http.Handler{
- "GET": a.renderDraftPostHandler(),
+ "EDIT": a.editPostHandler(true),
+ "MANAGE": a.manageDraftPostsHandler(),
"POST": a.postDraftPostHandler(),
"DELETE": a.deletePostHandler(true),
"PREVIEW": a.previewPostHandler(),
@@ -227,17 +231,21 @@ func (a *api) blogHandler() http.Handler {
mux.Handle("/feed.xml", a.renderFeedHandler())
mux.Handle("/", a.renderIndexHandler())
+ readOnlyMiddlewares := []middleware{
+ logReqMiddleware, // only log GETs on cache miss
+ cacheMiddleware(cache),
+ }
+
+ readWriteMiddlewares := []middleware{
+ purgeCacheOnOKMiddleware(cache),
+ authMiddleware(a.auther),
+ }
+
h := apiutil.MethodMux(map[string]http.Handler{
- "GET": applyMiddlewares(
- mux,
- logReqMiddleware, // only log GETs on cache miss
- cacheMiddleware(cache),
- ),
- "*": applyMiddlewares(
- mux,
- purgeCacheOnOKMiddleware(cache),
- authMiddleware(a.auther),
- ),
+ "GET": applyMiddlewares(mux, readOnlyMiddlewares...),
+ "MANAGE": applyMiddlewares(mux, readOnlyMiddlewares...),
+ "EDIT": applyMiddlewares(mux, readOnlyMiddlewares...),
+ "*": applyMiddlewares(mux, readWriteMiddlewares...),
})
return h
@@ -247,26 +255,30 @@ func (a *api) handler() http.Handler {
mux := http.NewServeMux()
- mux.Handle("/api/", http.StripPrefix("/api", a.apiHandler()))
- mux.Handle("/", a.blogHandler())
+ mux.Handle("/api/", applyMiddlewares(
+ http.StripPrefix("/api", a.apiHandler()),
+ logReqMiddleware,
+ ))
- h := apiutil.MethodMux(map[string]http.Handler{
- "GET": applyMiddlewares(
- mux,
- ),
- "*": applyMiddlewares(
- mux,
- a.checkCSRFMiddleware,
- addResponseHeadersMiddleware(map[string]string{
- "Cache-Control": "no-store, max-age=0",
- "Pragma": "no-cache",
- "Expires": "0",
- }),
- logReqMiddleware,
- ),
- })
+ mux.Handle("/", a.blogHandler())
- h = setLoggerMiddleware(a.params.Logger)(h)
+ h := applyMiddlewares(
+ apiutil.MethodMux(map[string]http.Handler{
+ "GET": applyMiddlewares(
+ mux,
+ ),
+ "*": applyMiddlewares(
+ mux,
+ a.checkCSRFMiddleware,
+ addResponseHeadersMiddleware(map[string]string{
+ "Cache-Control": "no-store, max-age=0",
+ "Pragma": "no-cache",
+ "Expires": "0",
+ }),
+ ),
+ }),
+ setLoggerMiddleware(a.params.Logger),
+ )
return h
}
diff --git a/src/http/apiutil/apiutil.go b/src/http/apiutil/apiutil.go
index fed6fb5..1fbadea 100644
--- a/src/http/apiutil/apiutil.go
+++ b/src/http/apiutil/apiutil.go
@@ -120,6 +120,9 @@ func RandStr(numBytes int) string {
//
// If the method "*" is defined then all methods not defined will be directed to
// that handler, and 405 Method Not Allowed is never returned.
+//
+// If the GET argument 'method' is present then the ToUpper of that is taken to
+// be the name of the method.
func MethodMux(handlers map[string]http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
@@ -127,7 +130,7 @@ func MethodMux(handlers map[string]http.Handler) http.Handler {
method := strings.ToUpper(r.Method)
formMethod := strings.ToUpper(r.FormValue("method"))
- if method == "POST" && formMethod != "" {
+ if formMethod != "" {
method = formMethod
}
diff --git a/src/http/assets.go b/src/http/assets.go
index 260b786..9c6c67e 100644
--- a/src/http/assets.go
+++ b/src/http/assets.go
@@ -59,9 +59,9 @@ func resizeImage(out io.Writer, in io.Reader, maxWidth float64) error {
}
}
-func (a *api) renderPostAssetsIndexHandler() http.Handler {
+func (a *api) managePostAssetsHandler() http.Handler {
- tpl := a.mustParseBasedTpl("assets.html")
+ tpl := a.mustParseBasedTpl("post-assets-manage.html")
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
@@ -86,17 +86,10 @@ func (a *api) renderPostAssetsIndexHandler() http.Handler {
func (a *api) getPostAssetHandler() http.Handler {
- renderIndexHandler := a.renderPostAssetsIndexHandler()
-
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
id := filepath.Base(r.URL.Path)
- if id == "/" {
- renderIndexHandler.ServeHTTP(rw, r)
- return
- }
-
maxWidth, err := apiutil.StrToInt(r.FormValue("w"), 0)
if err != nil {
apiutil.BadRequest(rw, r, fmt.Errorf("invalid w parameter: %w", err))
@@ -172,7 +165,7 @@ func (a *api) postPostAssetHandler() http.Handler {
return
}
- a.executeRedirectTpl(rw, r, a.assetsURL(false))
+ a.executeRedirectTpl(rw, r, a.manageAssetsURL(false))
})
}
@@ -199,6 +192,6 @@ func (a *api) deletePostAssetHandler() http.Handler {
return
}
- a.executeRedirectTpl(rw, r, a.assetsURL(false))
+ a.executeRedirectTpl(rw, r, a.manageAssetsURL(false))
})
}
diff --git a/src/http/drafts.go b/src/http/drafts.go
index cb776b0..4b98bc8 100644
--- a/src/http/drafts.go
+++ b/src/http/drafts.go
@@ -1,77 +1,20 @@
package http
import (
- "errors"
"fmt"
"net/http"
- "path/filepath"
- "strings"
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
)
-func (a *api) renderDraftPostHandler() http.Handler {
+func (a *api) manageDraftPostsHandler() http.Handler {
- tpl := a.mustParseBasedTpl("post.html")
- renderDraftPostsIndexHandler := a.renderDraftPostsIndexHandler()
- renderDraftEditPostHandler := a.renderEditPostHandler(true)
-
- return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
-
- id := strings.TrimSuffix(filepath.Base(r.URL.Path), ".html")
-
- if id == "/" {
- renderDraftPostsIndexHandler.ServeHTTP(rw, r)
- return
- }
-
- if _, ok := r.URL.Query()["edit"]; ok {
- renderDraftEditPostHandler.ServeHTTP(rw, r)
- return
- }
-
- p, err := a.params.PostDraftStore.GetByID(id)
-
- if errors.Is(err, post.ErrPostNotFound) {
- http.Error(rw, "Post not found", 404)
- return
- } else if err != nil {
- apiutil.InternalServerError(
- rw, r, fmt.Errorf("fetching post with id %q: %w", id, err),
- )
- return
- }
-
- tplPayload, err := a.postToPostTplPayload(post.StoredPost{Post: p})
-
- if err != nil {
- apiutil.InternalServerError(
- rw, r, fmt.Errorf(
- "generating template payload for post with id %q: %w",
- id, err,
- ),
- )
- return
- }
-
- executeTemplate(rw, r, tpl, tplPayload)
- })
-}
-
-func (a *api) renderDraftPostsIndexHandler() http.Handler {
-
- renderEditPostHandler := a.renderEditPostHandler(true)
- tpl := a.mustParseBasedTpl("draft-posts.html")
+ tpl := a.mustParseBasedTpl("draft-posts-manage.html")
const pageCount = 20
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- if _, ok := r.URL.Query()["edit"]; ok {
- renderEditPostHandler.ServeHTTP(rw, r)
- return
- }
-
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
if err != nil {
apiutil.BadRequest(
@@ -125,6 +68,6 @@ func (a *api) postDraftPostHandler() http.Handler {
return
}
- a.executeRedirectTpl(rw, r, a.draftURL(p.ID, false)+"?edit")
+ a.executeRedirectTpl(rw, r, a.editDraftPostURL(p.ID, false))
})
}
diff --git a/src/http/posts.go b/src/http/posts.go
index 09daac4..1950113 100644
--- a/src/http/posts.go
+++ b/src/http/posts.go
@@ -123,26 +123,14 @@ func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload,
return tplPayload, nil
}
-func (a *api) renderPostHandler() http.Handler {
+func (a *api) getPostHandler() http.Handler {
tpl := a.mustParseBasedTpl("post.html")
- renderPostsIndexHandler := a.renderPostsIndexHandler()
- renderEditPostHandler := a.renderEditPostHandler(false)
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
id := strings.TrimSuffix(filepath.Base(r.URL.Path), ".html")
- if id == "/" {
- renderPostsIndexHandler.ServeHTTP(rw, r)
- return
- }
-
- if _, ok := r.URL.Query()["edit"]; ok {
- renderEditPostHandler.ServeHTTP(rw, r)
- return
- }
-
storedPost, err := a.params.PostStore.GetByID(id)
if errors.Is(err, post.ErrPostNotFound) {
@@ -171,19 +159,13 @@ func (a *api) renderPostHandler() http.Handler {
})
}
-func (a *api) renderPostsIndexHandler() http.Handler {
+func (a *api) managePostsHandler() http.Handler {
- renderEditPostHandler := a.renderEditPostHandler(false)
- tpl := a.mustParseBasedTpl("posts.html")
+ tpl := a.mustParseBasedTpl("posts-manage.html")
const pageCount = 20
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
- if _, ok := r.URL.Query()["edit"]; ok {
- renderEditPostHandler.ServeHTTP(rw, r)
- return
- }
-
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
if err != nil {
apiutil.BadRequest(
@@ -221,20 +203,26 @@ func (a *api) renderPostsIndexHandler() http.Handler {
})
}
-func (a *api) renderEditPostHandler(isDraft bool) http.Handler {
+func (a *api) editPostHandler(isDraft bool) http.Handler {
- tpl := a.mustParseBasedTpl("edit-post.html")
+ tpl := a.mustParseBasedTpl("post-edit.html")
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
id := filepath.Base(r.URL.Path)
- var storedPost post.StoredPost
+ if id == "/" && !isDraft {
+ http.Error(rw, "Post id required", 400)
+ return
+ }
+
+ var (
+ storedPost post.StoredPost
+ err error
+ )
if id != "/" {
- var err error
-
if isDraft {
storedPost.Post, err = a.params.PostDraftStore.GetByID(id)
} else {
@@ -250,10 +238,6 @@ func (a *api) renderEditPostHandler(isDraft bool) http.Handler {
)
return
}
-
- } else if !isDraft {
- http.Error(rw, "Post ID required in URL", 400)
- return
}
tags, err := a.params.PostStore.GetTags()
@@ -348,7 +332,7 @@ func (a *api) postPostHandler() http.Handler {
return
}
- a.executeRedirectTpl(rw, r, a.postURL(p.ID, false))
+ a.executeRedirectTpl(rw, r, a.editPostURL(p.ID, false))
})
}
@@ -382,9 +366,9 @@ func (a *api) deletePostHandler(isDraft bool) http.Handler {
}
if isDraft {
- a.executeRedirectTpl(rw, r, a.draftsURL(false))
+ a.executeRedirectTpl(rw, r, a.manageDraftPostsURL(false))
} else {
- a.executeRedirectTpl(rw, r, a.postsURL(false))
+ a.executeRedirectTpl(rw, r, a.managePostsURL(false))
}
})
}
diff --git a/src/http/tpl.go b/src/http/tpl.go
index 3e1a2ba..a9f89d7 100644
--- a/src/http/tpl.go
+++ b/src/http/tpl.go
@@ -49,19 +49,31 @@ func (a *api) postURL(id string, abs bool) string {
return a.blogURL(path, abs)
}
-func (a *api) postsURL(abs bool) string {
- return a.blogURL("posts", abs)
+func (a *api) editPostURL(id string, abs bool) string {
+ return a.postURL(id, abs) + "?method=edit"
}
-func (a *api) assetsURL(abs bool) string {
- return a.blogURL("assets", abs)
+func (a *api) managePostsURL(abs bool) string {
+ return a.blogURL("posts?method=manage", abs)
}
-func (a *api) draftURL(id string, abs bool) string {
+func (a *api) manageAssetsURL(abs bool) string {
+ return a.blogURL("assets?method=manage", abs)
+}
+
+func (a *api) draftPostURL(id string, abs bool) string {
path := filepath.Join("drafts", id)
return a.blogURL(path, abs)
}
+func (a *api) editDraftPostURL(id string, abs bool) string {
+ return a.draftPostURL(id, abs) + "?method=edit"
+}
+
+func (a *api) manageDraftPostsURL(abs bool) string {
+ return a.blogURL("drafts", abs) + "?method=manage"
+}
+
func (a *api) draftsURL(abs bool) string {
return a.blogURL("drafts", abs)
}
@@ -88,7 +100,7 @@ func (a *api) tplFuncs() template.FuncMap {
return a.blogURL(path, false)
},
"DraftURL": func(id string) string {
- return a.draftURL(id, false)
+ return a.draftPostURL(id, false)
},
"DateTimeFormat": func(t time.Time) string {
return t.Format("2006-01-02")
diff --git a/src/http/tpl/admin.html b/src/http/tpl/admin.html
index f2ba4d6..510d705 100644
--- a/src/http/tpl/admin.html
+++ b/src/http/tpl/admin.html
@@ -7,9 +7,9 @@ mostly left open to inspection, but you will not able to change
anything without providing credentials.
<ul>
- <li><a href="{{ BlogURL "posts" }}">Posts</a></li>
- <li><a href="{{ BlogURL "assets" }}">Assets</a></li>
- <li><a href="{{ BlogURL "drafts" }}">Drafts</a> (private)</li>
+ <li><a href="{{ BlogURL "posts?method=manage" }}">Posts</a></li>
+ <li><a href="{{ BlogURL "assets?method=manage" }}">Assets</a></li>
+ <li><a href="{{ BlogURL "drafts?method=manage" }}">Drafts</a> (private)</li>
</ul>
{{ end }}
diff --git a/src/http/tpl/draft-posts.html b/src/http/tpl/draft-posts-manage.html
index 53261b9..12aadb2 100644
--- a/src/http/tpl/draft-posts.html
+++ b/src/http/tpl/draft-posts-manage.html
@@ -7,7 +7,7 @@
<h1>Drafts</h1>
<p>
- <a href="{{ BlogURL "drafts/" }}?edit">New Draft</a>
+ <a href="{{ BlogURL "drafts" }}?method=edit">New Draft</a>
</p>
{{ if ge .Payload.PrevPage 0 }}
@@ -20,9 +20,9 @@
{{ range .Payload.Posts }}
<tr>
- <td><a href="{{ DraftURL .ID }}">{{ .Title }}</a></td>
+ <td>{{ .Title }}</td>
<td>
- <a href="{{ DraftURL .ID }}?edit">
+ <a href="{{ DraftURL .ID }}?method=edit">
Edit
</a>
</td>
diff --git a/src/http/tpl/assets.html b/src/http/tpl/post-assets-manage.html
index f21717a..f21717a 100644
--- a/src/http/tpl/assets.html
+++ b/src/http/tpl/post-assets-manage.html
diff --git a/src/http/tpl/edit-post.html b/src/http/tpl/post-edit.html
index f8e2730..2813754 100644
--- a/src/http/tpl/edit-post.html
+++ b/src/http/tpl/post-edit.html
@@ -2,11 +2,11 @@
<p>
{{ if .Payload.IsDraft }}
- <a href="{{ BlogURL "drafts/" }}">
+ <a href="{{ BlogURL "drafts?method=manage" }}">
Back to Drafts
</a>
{{ else }}
- <a href="{{ BlogURL "posts/" }}">
+ <a href="{{ BlogURL "posts?method=manage" }}">
Back to Posts
</a>
{{ end }}
diff --git a/src/http/tpl/posts.html b/src/http/tpl/posts-manage.html
index fbeaa41..cfb2ec9 100644
--- a/src/http/tpl/posts.html
+++ b/src/http/tpl/posts-manage.html
@@ -19,7 +19,7 @@
<td>{{ .PublishedAt.Local.Format "2006-01-02 15:04:05 MST" }}</td>
<td><a href="{{ PostURL .ID }}">{{ .Title }}</a></td>
<td>
- <a href="{{ PostURL .ID }}?edit">
+ <a href="{{ PostURL .ID }}?method=edit">
Edit
</a>
</td>