Compare commits
No commits in common. "ceab45fefabe30d669472ac4f98fc50be5b4679b" and "f8cc9f6aa7c9b3e6e88cd74d8dc87b44dfa3f725" have entirely different histories.
ceab45fefa
...
f8cc9f6aa7
@ -190,9 +190,7 @@ func (a *api) blogHandler() http.Handler {
|
|||||||
|
|
||||||
mux.Handle("/posts/", http.StripPrefix("/posts",
|
mux.Handle("/posts/", http.StripPrefix("/posts",
|
||||||
apiutil.MethodMux(map[string]http.Handler{
|
apiutil.MethodMux(map[string]http.Handler{
|
||||||
"GET": a.getPostsHandler(),
|
"GET": a.renderPostHandler(),
|
||||||
"EDIT": a.editPostHandler(false),
|
|
||||||
"MANAGE": a.managePostsHandler(),
|
|
||||||
"POST": a.postPostHandler(),
|
"POST": a.postPostHandler(),
|
||||||
"DELETE": a.deletePostHandler(false),
|
"DELETE": a.deletePostHandler(false),
|
||||||
"PREVIEW": a.previewPostHandler(),
|
"PREVIEW": a.previewPostHandler(),
|
||||||
@ -202,7 +200,6 @@ func (a *api) blogHandler() http.Handler {
|
|||||||
mux.Handle("/assets/", http.StripPrefix("/assets",
|
mux.Handle("/assets/", http.StripPrefix("/assets",
|
||||||
apiutil.MethodMux(map[string]http.Handler{
|
apiutil.MethodMux(map[string]http.Handler{
|
||||||
"GET": a.getPostAssetHandler(),
|
"GET": a.getPostAssetHandler(),
|
||||||
"MANAGE": a.managePostAssetsHandler(),
|
|
||||||
"POST": a.postPostAssetHandler(),
|
"POST": a.postPostAssetHandler(),
|
||||||
"DELETE": a.deletePostAssetHandler(),
|
"DELETE": a.deletePostAssetHandler(),
|
||||||
}),
|
}),
|
||||||
@ -214,8 +211,7 @@ func (a *api) blogHandler() http.Handler {
|
|||||||
authMiddleware(a.auther)(
|
authMiddleware(a.auther)(
|
||||||
|
|
||||||
apiutil.MethodMux(map[string]http.Handler{
|
apiutil.MethodMux(map[string]http.Handler{
|
||||||
"EDIT": a.editPostHandler(true),
|
"GET": a.renderDraftPostHandler(),
|
||||||
"MANAGE": a.manageDraftPostsHandler(),
|
|
||||||
"POST": a.postDraftPostHandler(),
|
"POST": a.postDraftPostHandler(),
|
||||||
"DELETE": a.deletePostHandler(true),
|
"DELETE": a.deletePostHandler(true),
|
||||||
"PREVIEW": a.previewPostHandler(),
|
"PREVIEW": a.previewPostHandler(),
|
||||||
@ -231,21 +227,17 @@ func (a *api) blogHandler() http.Handler {
|
|||||||
mux.Handle("/feed.xml", a.renderFeedHandler())
|
mux.Handle("/feed.xml", a.renderFeedHandler())
|
||||||
mux.Handle("/", a.renderIndexHandler())
|
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{
|
h := apiutil.MethodMux(map[string]http.Handler{
|
||||||
"GET": applyMiddlewares(mux, readOnlyMiddlewares...),
|
"GET": applyMiddlewares(
|
||||||
"MANAGE": applyMiddlewares(mux, readOnlyMiddlewares...),
|
mux,
|
||||||
"EDIT": applyMiddlewares(mux, readOnlyMiddlewares...),
|
logReqMiddleware, // only log GETs on cache miss
|
||||||
"*": applyMiddlewares(mux, readWriteMiddlewares...),
|
cacheMiddleware(cache),
|
||||||
|
),
|
||||||
|
"*": applyMiddlewares(
|
||||||
|
mux,
|
||||||
|
purgeCacheOnOKMiddleware(cache),
|
||||||
|
authMiddleware(a.auther),
|
||||||
|
),
|
||||||
})
|
})
|
||||||
|
|
||||||
return h
|
return h
|
||||||
@ -255,30 +247,26 @@ func (a *api) handler() http.Handler {
|
|||||||
|
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
mux.Handle("/api/", applyMiddlewares(
|
mux.Handle("/api/", http.StripPrefix("/api", a.apiHandler()))
|
||||||
http.StripPrefix("/api", a.apiHandler()),
|
|
||||||
logReqMiddleware,
|
|
||||||
))
|
|
||||||
|
|
||||||
mux.Handle("/", a.blogHandler())
|
mux.Handle("/", a.blogHandler())
|
||||||
|
|
||||||
h := applyMiddlewares(
|
h := apiutil.MethodMux(map[string]http.Handler{
|
||||||
apiutil.MethodMux(map[string]http.Handler{
|
"GET": applyMiddlewares(
|
||||||
"GET": applyMiddlewares(
|
mux,
|
||||||
mux,
|
),
|
||||||
),
|
"*": applyMiddlewares(
|
||||||
"*": applyMiddlewares(
|
mux,
|
||||||
mux,
|
a.checkCSRFMiddleware,
|
||||||
a.checkCSRFMiddleware,
|
addResponseHeadersMiddleware(map[string]string{
|
||||||
addResponseHeadersMiddleware(map[string]string{
|
"Cache-Control": "no-store, max-age=0",
|
||||||
"Cache-Control": "no-store, max-age=0",
|
"Pragma": "no-cache",
|
||||||
"Pragma": "no-cache",
|
"Expires": "0",
|
||||||
"Expires": "0",
|
}),
|
||||||
}),
|
logReqMiddleware,
|
||||||
),
|
),
|
||||||
}),
|
})
|
||||||
setLoggerMiddleware(a.params.Logger),
|
|
||||||
)
|
h = setLoggerMiddleware(a.params.Logger)(h)
|
||||||
|
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
@ -120,9 +120,6 @@ func RandStr(numBytes int) string {
|
|||||||
//
|
//
|
||||||
// If the method "*" is defined then all methods not defined will be directed to
|
// If the method "*" is defined then all methods not defined will be directed to
|
||||||
// that handler, and 405 Method Not Allowed is never returned.
|
// 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 {
|
func MethodMux(handlers map[string]http.Handler) http.Handler {
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
@ -130,7 +127,7 @@ func MethodMux(handlers map[string]http.Handler) http.Handler {
|
|||||||
method := strings.ToUpper(r.Method)
|
method := strings.ToUpper(r.Method)
|
||||||
formMethod := strings.ToUpper(r.FormValue("method"))
|
formMethod := strings.ToUpper(r.FormValue("method"))
|
||||||
|
|
||||||
if formMethod != "" {
|
if method == "POST" && formMethod != "" {
|
||||||
method = formMethod
|
method = formMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,9 +59,9 @@ func resizeImage(out io.Writer, in io.Reader, maxWidth float64) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) managePostAssetsHandler() http.Handler {
|
func (a *api) renderPostAssetsIndexHandler() http.Handler {
|
||||||
|
|
||||||
tpl := a.mustParseBasedTpl("post-assets-manage.html")
|
tpl := a.mustParseBasedTpl("assets.html")
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
@ -86,10 +86,17 @@ func (a *api) managePostAssetsHandler() http.Handler {
|
|||||||
|
|
||||||
func (a *api) getPostAssetHandler() http.Handler {
|
func (a *api) getPostAssetHandler() http.Handler {
|
||||||
|
|
||||||
|
renderIndexHandler := a.renderPostAssetsIndexHandler()
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
id := filepath.Base(r.URL.Path)
|
id := filepath.Base(r.URL.Path)
|
||||||
|
|
||||||
|
if id == "/" {
|
||||||
|
renderIndexHandler.ServeHTTP(rw, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
maxWidth, err := apiutil.StrToInt(r.FormValue("w"), 0)
|
maxWidth, err := apiutil.StrToInt(r.FormValue("w"), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiutil.BadRequest(rw, r, fmt.Errorf("invalid w parameter: %w", err))
|
apiutil.BadRequest(rw, r, fmt.Errorf("invalid w parameter: %w", err))
|
||||||
@ -165,7 +172,7 @@ func (a *api) postPostAssetHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a.executeRedirectTpl(rw, r, a.manageAssetsURL(false))
|
a.executeRedirectTpl(rw, r, a.assetsURL(false))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,6 +199,6 @@ func (a *api) deletePostAssetHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a.executeRedirectTpl(rw, r, a.manageAssetsURL(false))
|
a.executeRedirectTpl(rw, r, a.assetsURL(false))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,77 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
||||||
"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *api) manageDraftPostsHandler() http.Handler {
|
func (a *api) renderDraftPostHandler() http.Handler {
|
||||||
|
|
||||||
tpl := a.mustParseBasedTpl("draft-posts-manage.html")
|
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")
|
||||||
const pageCount = 20
|
const pageCount = 20
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
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)
|
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiutil.BadRequest(
|
apiutil.BadRequest(
|
||||||
@ -68,6 +125,6 @@ func (a *api) postDraftPostHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a.executeRedirectTpl(rw, r, a.editDraftPostURL(p.ID, false))
|
a.executeRedirectTpl(rw, r, a.draftURL(p.ID, false)+"?edit")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/http/apiutil"
|
||||||
|
"github.com/mediocregopher/blog.mediocregopher.com/srv/post"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (a *api) renderIndexHandler() http.Handler {
|
func (a *api) renderIndexHandler() http.Handler {
|
||||||
@ -31,6 +35,61 @@ func (a *api) renderIndexHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
executeTemplate(rw, r, tpl, nil)
|
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
||||||
|
if err != nil {
|
||||||
|
apiutil.BadRequest(
|
||||||
|
rw, r, fmt.Errorf("invalid page number: %w", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tag := r.FormValue("tag")
|
||||||
|
|
||||||
|
var (
|
||||||
|
posts []post.StoredPost
|
||||||
|
hasMore bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if tag == "" {
|
||||||
|
posts, hasMore, err = a.params.PostStore.Get(page, pageCount)
|
||||||
|
} else {
|
||||||
|
posts, err = a.params.PostStore.GetByTag(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
apiutil.InternalServerError(
|
||||||
|
rw, r, fmt.Errorf("fetching page %d of posts: %w", page, err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tags, err := a.params.PostStore.GetTags()
|
||||||
|
if err != nil {
|
||||||
|
apiutil.InternalServerError(
|
||||||
|
rw, r, fmt.Errorf("fething tags: %w", err),
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tplPayload := struct {
|
||||||
|
Posts []post.StoredPost
|
||||||
|
PrevPage, NextPage int
|
||||||
|
Tags []string
|
||||||
|
}{
|
||||||
|
Posts: posts,
|
||||||
|
PrevPage: -1,
|
||||||
|
NextPage: -1,
|
||||||
|
Tags: tags,
|
||||||
|
}
|
||||||
|
|
||||||
|
if page > 0 {
|
||||||
|
tplPayload.PrevPage = page - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasMore {
|
||||||
|
tplPayload.NextPage = page + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
executeTemplate(rw, r, tpl, tplPayload)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -123,88 +123,26 @@ func (a *api) postToPostTplPayload(storedPost post.StoredPost) (postTplPayload,
|
|||||||
return tplPayload, nil
|
return tplPayload, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) getPostsHandler() http.Handler {
|
func (a *api) renderPostHandler() http.Handler {
|
||||||
|
|
||||||
tpl := a.mustParseBasedTpl("posts.html")
|
|
||||||
getPostHandler := a.getPostHandler()
|
|
||||||
const pageCount = 20
|
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
||||||
|
|
||||||
id := filepath.Base(r.URL.Path)
|
|
||||||
|
|
||||||
if id != "/" {
|
|
||||||
getPostHandler.ServeHTTP(rw, r)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
|
||||||
if err != nil {
|
|
||||||
apiutil.BadRequest(
|
|
||||||
rw, r, fmt.Errorf("invalid page number: %w", err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tag := r.FormValue("tag")
|
|
||||||
|
|
||||||
var (
|
|
||||||
posts []post.StoredPost
|
|
||||||
hasMore bool
|
|
||||||
)
|
|
||||||
|
|
||||||
if tag == "" {
|
|
||||||
posts, hasMore, err = a.params.PostStore.Get(page, pageCount)
|
|
||||||
} else {
|
|
||||||
posts, err = a.params.PostStore.GetByTag(tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
apiutil.InternalServerError(
|
|
||||||
rw, r, fmt.Errorf("fetching page %d of posts: %w", page, err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tags, err := a.params.PostStore.GetTags()
|
|
||||||
if err != nil {
|
|
||||||
apiutil.InternalServerError(
|
|
||||||
rw, r, fmt.Errorf("fething tags: %w", err),
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
tplPayload := struct {
|
|
||||||
Posts []post.StoredPost
|
|
||||||
PrevPage, NextPage int
|
|
||||||
Tags []string
|
|
||||||
}{
|
|
||||||
Posts: posts,
|
|
||||||
PrevPage: -1,
|
|
||||||
NextPage: -1,
|
|
||||||
Tags: tags,
|
|
||||||
}
|
|
||||||
|
|
||||||
if page > 0 {
|
|
||||||
tplPayload.PrevPage = page - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if hasMore {
|
|
||||||
tplPayload.NextPage = page + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
executeTemplate(rw, r, tpl, tplPayload)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) getPostHandler() http.Handler {
|
|
||||||
|
|
||||||
tpl := a.mustParseBasedTpl("post.html")
|
tpl := a.mustParseBasedTpl("post.html")
|
||||||
|
renderPostsIndexHandler := a.renderPostsIndexHandler()
|
||||||
|
renderEditPostHandler := a.renderEditPostHandler(false)
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
id := strings.TrimSuffix(filepath.Base(r.URL.Path), ".html")
|
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)
|
storedPost, err := a.params.PostStore.GetByID(id)
|
||||||
|
|
||||||
if errors.Is(err, post.ErrPostNotFound) {
|
if errors.Is(err, post.ErrPostNotFound) {
|
||||||
@ -233,13 +171,19 @@ func (a *api) getPostHandler() http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) managePostsHandler() http.Handler {
|
func (a *api) renderPostsIndexHandler() http.Handler {
|
||||||
|
|
||||||
tpl := a.mustParseBasedTpl("posts-manage.html")
|
renderEditPostHandler := a.renderEditPostHandler(false)
|
||||||
|
tpl := a.mustParseBasedTpl("posts.html")
|
||||||
const pageCount = 20
|
const pageCount = 20
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
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)
|
page, err := apiutil.StrToInt(r.FormValue("p"), 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiutil.BadRequest(
|
apiutil.BadRequest(
|
||||||
@ -277,26 +221,20 @@ func (a *api) managePostsHandler() http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) editPostHandler(isDraft bool) http.Handler {
|
func (a *api) renderEditPostHandler(isDraft bool) http.Handler {
|
||||||
|
|
||||||
tpl := a.mustParseBasedTpl("post-edit.html")
|
tpl := a.mustParseBasedTpl("edit-post.html")
|
||||||
|
|
||||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
id := filepath.Base(r.URL.Path)
|
id := filepath.Base(r.URL.Path)
|
||||||
|
|
||||||
if id == "/" && !isDraft {
|
var storedPost post.StoredPost
|
||||||
http.Error(rw, "Post id required", 400)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
storedPost post.StoredPost
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if id != "/" {
|
if id != "/" {
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
if isDraft {
|
if isDraft {
|
||||||
storedPost.Post, err = a.params.PostDraftStore.GetByID(id)
|
storedPost.Post, err = a.params.PostDraftStore.GetByID(id)
|
||||||
} else {
|
} else {
|
||||||
@ -312,6 +250,10 @@ func (a *api) editPostHandler(isDraft bool) http.Handler {
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else if !isDraft {
|
||||||
|
http.Error(rw, "Post ID required in URL", 400)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
tags, err := a.params.PostStore.GetTags()
|
tags, err := a.params.PostStore.GetTags()
|
||||||
@ -406,7 +348,7 @@ func (a *api) postPostHandler() http.Handler {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
a.executeRedirectTpl(rw, r, a.editPostURL(p.ID, false))
|
a.executeRedirectTpl(rw, r, a.postURL(p.ID, false))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,9 +382,9 @@ func (a *api) deletePostHandler(isDraft bool) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isDraft {
|
if isDraft {
|
||||||
a.executeRedirectTpl(rw, r, a.manageDraftPostsURL(false))
|
a.executeRedirectTpl(rw, r, a.draftsURL(false))
|
||||||
} else {
|
} else {
|
||||||
a.executeRedirectTpl(rw, r, a.managePostsURL(false))
|
a.executeRedirectTpl(rw, r, a.postsURL(false))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -49,31 +49,19 @@ func (a *api) postURL(id string, abs bool) string {
|
|||||||
return a.blogURL(path, abs)
|
return a.blogURL(path, abs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) editPostURL(id string, abs bool) string {
|
func (a *api) postsURL(abs bool) string {
|
||||||
return a.postURL(id, abs) + "?method=edit"
|
return a.blogURL("posts", abs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) managePostsURL(abs bool) string {
|
func (a *api) assetsURL(abs bool) string {
|
||||||
return a.blogURL("posts?method=manage", abs)
|
return a.blogURL("assets", abs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) manageAssetsURL(abs bool) string {
|
func (a *api) draftURL(id string, abs bool) string {
|
||||||
return a.blogURL("assets?method=manage", abs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *api) draftPostURL(id string, abs bool) string {
|
|
||||||
path := filepath.Join("drafts", id)
|
path := filepath.Join("drafts", id)
|
||||||
return a.blogURL(path, abs)
|
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 {
|
func (a *api) draftsURL(abs bool) string {
|
||||||
return a.blogURL("drafts", abs)
|
return a.blogURL("drafts", abs)
|
||||||
}
|
}
|
||||||
@ -100,7 +88,7 @@ func (a *api) tplFuncs() template.FuncMap {
|
|||||||
return a.blogURL(path, false)
|
return a.blogURL(path, false)
|
||||||
},
|
},
|
||||||
"DraftURL": func(id string) string {
|
"DraftURL": func(id string) string {
|
||||||
return a.draftPostURL(id, false)
|
return a.draftURL(id, false)
|
||||||
},
|
},
|
||||||
"DateTimeFormat": func(t time.Time) string {
|
"DateTimeFormat": func(t time.Time) string {
|
||||||
return t.Format("2006-01-02")
|
return t.Format("2006-01-02")
|
||||||
|
@ -7,9 +7,9 @@ mostly left open to inspection, but you will not able to change
|
|||||||
anything without providing credentials.
|
anything without providing credentials.
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{{ BlogURL "posts?method=manage" }}">Posts</a></li>
|
<li><a href="{{ BlogURL "posts" }}">Posts</a></li>
|
||||||
<li><a href="{{ BlogURL "assets?method=manage" }}">Assets</a></li>
|
<li><a href="{{ BlogURL "assets" }}">Assets</a></li>
|
||||||
<li><a href="{{ BlogURL "drafts?method=manage" }}">Drafts</a> (private)</li>
|
<li><a href="{{ BlogURL "drafts" }}">Drafts</a> (private)</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
@ -102,17 +102,14 @@
|
|||||||
-
|
-
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<strong>mediocregopher</strong>'s lil web corner
|
welcome to <strong>mediocregopher</strong>'s lil internet corner
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
<a href="{{ BlogURL "/" }}">Home</a>
|
<a href="{{ BlogURL "/" }}">Posts</a>
|
||||||
//
|
|
||||||
<a href="{{ BlogURL "/posts" }}">Posts</a>
|
|
||||||
/
|
/
|
||||||
<a href="{{ BlogURL "follow" }}">Follow</a>
|
<a href="{{ BlogURL "follow" }}">Follow</a>
|
||||||
|
(<a href="{{ BlogURL "feed.xml" }}">RSS</a>)
|
||||||
/
|
/
|
||||||
<a href="{{ BlogURL "feed.xml" }}">RSS</a>
|
|
||||||
//
|
|
||||||
<a href="{{ StaticURL "wtfpl.txt" }}">License</a>
|
<a href="{{ StaticURL "wtfpl.txt" }}">License</a>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@ -137,7 +134,7 @@
|
|||||||
|
|
||||||
.fuck-it-up {
|
.fuck-it-up {
|
||||||
|
|
||||||
color: var(--nc-tx-1);
|
color: var(--nc-lk-1);
|
||||||
font-family:Courier,monospace;
|
font-family:Courier,monospace;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size:0.18rem;
|
font-size:0.18rem;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
<h1>Drafts</h1>
|
<h1>Drafts</h1>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ BlogURL "drafts" }}?method=edit">New Draft</a>
|
<a href="{{ BlogURL "drafts/" }}?edit">New Draft</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{{ if ge .Payload.PrevPage 0 }}
|
{{ if ge .Payload.PrevPage 0 }}
|
||||||
@ -20,9 +20,9 @@
|
|||||||
|
|
||||||
{{ range .Payload.Posts }}
|
{{ range .Payload.Posts }}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ .Title }}</td>
|
<td><a href="{{ DraftURL .ID }}">{{ .Title }}</a></td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ DraftURL .ID }}?method=edit">
|
<a href="{{ DraftURL .ID }}?edit">
|
||||||
Edit
|
Edit
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
<p>
|
<p>
|
||||||
{{ if .Payload.IsDraft }}
|
{{ if .Payload.IsDraft }}
|
||||||
<a href="{{ BlogURL "drafts?method=manage" }}">
|
<a href="{{ BlogURL "drafts/" }}">
|
||||||
Back to Drafts
|
Back to Drafts
|
||||||
</a>
|
</a>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<a href="{{ BlogURL "posts?method=manage" }}">
|
<a href="{{ BlogURL "posts/" }}">
|
||||||
Back to Posts
|
Back to Posts
|
||||||
</a>
|
</a>
|
||||||
{{ end }}
|
{{ end }}
|
@ -1,51 +1,34 @@
|
|||||||
{{ define "body" }}
|
{{ define "body" }}
|
||||||
|
|
||||||
|
{{ if ge .Payload.PrevPage 0 }}
|
||||||
<p>
|
<p>
|
||||||
Hi! I'm Brian, and this here's my little corner of the web. Here I write
|
<a href="?p={{ .Payload.PrevPage}}">< < Previous Page</a>
|
||||||
<a href="{{ BlogURL "posts" }}">posts</a>
|
|
||||||
about projects I'm working on and things that interest me (which you can
|
|
||||||
<a href="{{ BlogURL "follow" }}">follow</a>,
|
|
||||||
if you like). Beyond that I've linked to various related links related to me
|
|
||||||
below.
|
|
||||||
</p>
|
</p>
|
||||||
|
{{ else }}
|
||||||
|
<p>
|
||||||
|
Welcome to the Mediocre Blog! Posts are listed in chronological order. If
|
||||||
|
you aren't sure of where to start I recommend picking at random.
|
||||||
|
</p>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
<h2>Social</h2>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://matrix.to/#/@mediocregopher:waffle.farm">@mediocregopher:waffle.farm</a> is my matrix handle.</li>
|
|
||||||
<li><a href="https://social.cryptic.io/@mediocregopher">@mediocregopher@social.cryptic.io</a> is my mastodon handle.</li>
|
|
||||||
<li><a href="https://bgpicciano.com">bgpicciano.com</a> is my cover site/resume.</li>
|
|
||||||
<li><a href="mailto:mediocregopher@gmail.com">mediocregopher@gmail.com</a> is my email.</li>
|
|
||||||
<li><a href="https://news.cryptic.io">Cryptic News</a> aggregates interesting blogs.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2>Dev</h2>
|
|
||||||
<ul>
|
<ul>
|
||||||
|
{{ range .Payload.Posts }}
|
||||||
<li>
|
<li>
|
||||||
<a href="https://code.betamike.com/mediocregopher">Gitea</a>
|
<strong><a href="{{ PostURL .ID }}">
|
||||||
and
|
{{ DateTimeFormat .PublishedAt }} / {{ .Title }}
|
||||||
<a href="https://github.com/mediocregopher">GitHub</a>
|
</a></strong>
|
||||||
are for open-source code I've written.
|
{{ if .Description }}
|
||||||
|
<br/><em>{{ .Description }}</em>
|
||||||
|
{{ end }}
|
||||||
</li>
|
</li>
|
||||||
<li><a href="https://github.com/mediocregopher/radix">radix</a> is for using redis with go.</li>
|
{{ end }}
|
||||||
<li><a href="https://code.betamike.com/cryptic-io/cryptic-net">cryptic-net</a> implements the foundation of a community infrastucture.</li>
|
|
||||||
<li><a href="https://code.betamike.com/mediocregopher/mediocre-blog">mediocre-blog</a> is the CMS I designed from scratch to run this site.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>Art</h2>
|
{{ if ge .Payload.NextPage 0 }}
|
||||||
<ul>
|
<p>
|
||||||
<li><a href="https://opensea.io/mediocregopher?tab=created">OpenSea</a> lists some NFTs I've made.</li>
|
<a href="?p={{ .Payload.NextPage}}">Next Page > ></a>
|
||||||
<li><a href="https://exchange.art/artists/mediocregopher/series">Exchange.art</a> lists others.</li>
|
</p>
|
||||||
</ul>
|
{{ end }}
|
||||||
|
|
||||||
<hr/>
|
|
||||||
|
|
||||||
<p>I'm not affiliated with these, but they're worth listing.</p>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://search.marginalia.nu/">Marginalia</a> reminds me of the old internet.</li>
|
|
||||||
<li><a href="https://drewdevault.com/2020/11/01/What-is-Gemini-anyway.html">Gemini</a> is a protocol I soon hope to add to my blog.</li>
|
|
||||||
<li><a href="https://www.nts.live/">NTS</a> is a great internet radio station.</li>
|
|
||||||
<li><a href="https://yamakan.place/palestine/#">Radio alHara</a> is another great internet radio station.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
|
@ -1,47 +0,0 @@
|
|||||||
{{ define "body" }}
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<a href="{{ BlogURL "admin" }}">Back to Admin</a>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h1>Posts</h1>
|
|
||||||
|
|
||||||
{{ if ge .Payload.PrevPage 0 }}
|
|
||||||
<p>
|
|
||||||
<a href="?p={{ .Payload.PrevPage}}">< < Previous Page</a>
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
<table>
|
|
||||||
|
|
||||||
{{ range .Payload.Posts }}
|
|
||||||
<tr>
|
|
||||||
<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 }}?method=edit">
|
|
||||||
Edit
|
|
||||||
</a>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<form
|
|
||||||
action="{{ PostURL .ID }}?method=delete"
|
|
||||||
method="POST"
|
|
||||||
>
|
|
||||||
<input type="submit" value="Delete" />
|
|
||||||
</form>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
</table>
|
|
||||||
|
|
||||||
{{ if ge .Payload.NextPage 0 }}
|
|
||||||
<p>
|
|
||||||
<a href="?p={{ .Payload.NextPage}}">Next Page > ></a>
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
{{ template "base.html" . }}
|
|
@ -1,28 +1,40 @@
|
|||||||
{{ define "body" }}
|
{{ define "body" }}
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="{{ BlogURL "admin" }}">Back to Admin</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h1>Posts</h1>
|
||||||
|
|
||||||
{{ if ge .Payload.PrevPage 0 }}
|
{{ if ge .Payload.PrevPage 0 }}
|
||||||
<p>
|
<p>
|
||||||
<a href="?p={{ .Payload.PrevPage}}">< < Previous Page</a>
|
<a href="?p={{ .Payload.PrevPage}}">< < Previous Page</a>
|
||||||
</p>
|
</p>
|
||||||
{{ else }}
|
|
||||||
<p>
|
|
||||||
Posts are listed in chronological order. If you aren't sure of where to
|
|
||||||
start I recommend picking at random.
|
|
||||||
</p>
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
<ul>
|
<table>
|
||||||
{{ range .Payload.Posts }}
|
|
||||||
<li>
|
{{ range .Payload.Posts }}
|
||||||
<strong><a href="{{ PostURL .ID }}">
|
<tr>
|
||||||
{{ DateTimeFormat .PublishedAt }} / {{ .Title }}
|
<td>{{ .PublishedAt.Local.Format "2006-01-02 15:04:05 MST" }}</td>
|
||||||
</a></strong>
|
<td><a href="{{ PostURL .ID }}">{{ .Title }}</a></td>
|
||||||
{{ if .Description }}
|
<td>
|
||||||
<br/><em>{{ .Description }}</em>
|
<a href="{{ PostURL .ID }}?edit">
|
||||||
{{ end }}
|
Edit
|
||||||
</li>
|
</a>
|
||||||
{{ end }}
|
</td>
|
||||||
</ul>
|
<td>
|
||||||
|
<form
|
||||||
|
action="{{ PostURL .ID }}?method=delete"
|
||||||
|
method="POST"
|
||||||
|
>
|
||||||
|
<input type="submit" value="Delete" />
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
{{ if ge .Payload.NextPage 0 }}
|
{{ if ge .Payload.NextPage 0 }}
|
||||||
<p>
|
<p>
|
||||||
|
Loading…
Reference in New Issue
Block a user