Improve error rendering

This commit is contained in:
Jan-Lukas Else 2020-12-24 10:09:34 +01:00
parent 59388e8654
commit 9e4a03549f
20 changed files with 109 additions and 92 deletions

View File

@ -74,13 +74,13 @@ func initActivityPub() error {
func apHandleWebfinger(w http.ResponseWriter, r *http.Request) { func apHandleWebfinger(w http.ResponseWriter, r *http.Request) {
re, err := regexp.Compile(`^acct:(.*)@` + regexp.QuoteMeta(appConfig.Server.Domain) + `$`) re, err := regexp.Compile(`^acct:(.*)@` + regexp.QuoteMeta(appConfig.Server.Domain) + `$`)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
name := re.ReplaceAllString(r.URL.Query().Get("resource"), "$1") name := re.ReplaceAllString(r.URL.Query().Get("resource"), "$1")
blog := appConfig.Blogs[name] blog := appConfig.Blogs[name]
if blog == nil { if blog == nil {
http.Error(w, "Not found", http.StatusNotFound) serveError(w, r, "Blog not found", http.StatusNotFound)
return return
} }
w.Header().Set(contentType, "application/jrd+json"+charsetUtf8Suffix) w.Header().Set(contentType, "application/jrd+json"+charsetUtf8Suffix)
@ -100,7 +100,7 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
blogName := chi.URLParam(r, "blog") blogName := chi.URLParam(r, "blog")
blog := appConfig.Blogs[blogName] blog := appConfig.Blogs[blogName]
if blog == nil { if blog == nil {
http.Error(w, "Inbox not found", http.StatusNotFound) serveError(w, r, "Inbox not found", http.StatusNotFound)
return return
} }
blogIri := blog.apIri() blogIri := blog.apIri()
@ -108,7 +108,7 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
requestActor, requestKey, requestActorStatus, err := apVerifySignature(r) requestActor, requestKey, requestActorStatus, err := apVerifySignature(r)
if err != nil { if err != nil {
// Send 401 because signature could not be verified // Send 401 because signature could not be verified
http.Error(w, err.Error(), http.StatusUnauthorized) serveError(w, r, err.Error(), http.StatusUnauthorized)
return return
} }
if requestActorStatus != 0 { if requestActorStatus != 0 {
@ -122,7 +122,7 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
http.Error(w, "Error when trying to get request actor", http.StatusBadRequest) serveError(w, r, "Error when trying to get request actor", http.StatusBadRequest)
return return
} }
// Parse activity // Parse activity
@ -130,17 +130,17 @@ func apHandleInbox(w http.ResponseWriter, r *http.Request) {
err = json.NewDecoder(r.Body).Decode(&activity) err = json.NewDecoder(r.Body).Decode(&activity)
_ = r.Body.Close() _ = r.Body.Close()
if err != nil { if err != nil {
http.Error(w, "Failed to decode body", http.StatusBadRequest) serveError(w, r, "Failed to decode body", http.StatusBadRequest)
return return
} }
// Get and check activity actor // Get and check activity actor
activityActor, ok := activity["actor"].(string) activityActor, ok := activity["actor"].(string)
if !ok { if !ok {
http.Error(w, "actor in activity is no string", http.StatusBadRequest) serveError(w, r, "actor in activity is no string", http.StatusBadRequest)
return return
} }
if activityActor != requestActor.ID { if activityActor != requestActor.ID {
http.Error(w, "Request actor isn't activity actor", http.StatusForbidden) serveError(w, r, "Request actor isn't activity actor", http.StatusForbidden)
return return
} }
// Do // Do

View File

@ -119,10 +119,10 @@ func (p *post) toASNote() *asNote {
return as return as
} }
func (b *configBlog) serveActivityStreams(blog string, w http.ResponseWriter) { func (b *configBlog) serveActivityStreams(blog string, w http.ResponseWriter, r *http.Request) {
publicKeyDer, err := x509.MarshalPKIXPublicKey(&apPrivateKey.PublicKey) publicKeyDer, err := x509.MarshalPKIXPublicKey(&apPrivateKey.PublicKey)
if err != nil { if err != nil {
http.Error(w, "Failed to marshal public key", http.StatusInternalServerError) serveError(w, r, "Failed to marshal public key", http.StatusInternalServerError)
return return
} }
// Send JSON // Send JSON

8
api.go
View File

@ -17,12 +17,12 @@ func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
}() }()
bodyContent, err := ioutil.ReadAll(r.Body) bodyContent, err := ioutil.ReadAll(r.Body)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
p, aliases, err := parseHugoFile(string(bodyContent)) p, aliases, err := parseHugoFile(string(bodyContent))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
p.Blog = blog p.Blog = blog
@ -31,7 +31,7 @@ func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
p.Slug = slug p.Slug = slug
err = p.replace() err = p.replace()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
aliases = append(aliases, alias) aliases = append(aliases, alias)
@ -51,7 +51,7 @@ func apiPostCreateHugo(w http.ResponseWriter, r *http.Request) {
p.Parameters["aliases"] = aliases p.Parameters["aliases"] = aliases
err = p.replace() err = p.replace()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
} }

View File

@ -82,7 +82,7 @@ func checkLogin(w http.ResponseWriter, r *http.Request) bool {
// Set basic auth // Set basic auth
req.SetBasicAuth(r.FormValue("username"), r.FormValue("password")) req.SetBasicAuth(r.FormValue("username"), r.FormValue("password"))
// Send cookie // Send cookie
sendTokenCookie(w) sendTokenCookie(w, r)
// Serve original request // Serve original request
d.ServeHTTP(w, req) d.ServeHTTP(w, req)
return true return true
@ -90,11 +90,11 @@ func checkLogin(w http.ResponseWriter, r *http.Request) bool {
return false return false
} }
func sendTokenCookie(w http.ResponseWriter) { func sendTokenCookie(w http.ResponseWriter, r *http.Request) {
expiration := time.Now().Add(7 * 24 * time.Hour) expiration := time.Now().Add(7 * 24 * time.Hour)
tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{ExpiresAt: expiration.Unix()}).SignedString(jwtKey()) tokenString, err := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{ExpiresAt: expiration.Unix()}).SignedString(jwtKey())
if err != nil { if err != nil {
http.Error(w, "failed to sign JWT", http.StatusInternalServerError) serveError(w, r, "Failed to sign JWT", http.StatusInternalServerError)
return return
} }
http.SetCookie(w, &http.Cookie{ http.SetCookie(w, &http.Cookie{

View File

@ -3,7 +3,7 @@ package main
import "net/http" import "net/http"
func serveCustomPage(blog *configBlog, page *customPage) func(w http.ResponseWriter, r *http.Request) { func serveCustomPage(blog *configBlog, page *customPage) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, _ *http.Request) {
if appConfig.Cache != nil && appConfig.Cache.Enable && page.Cache { if appConfig.Cache != nil && appConfig.Cache.Enable && page.Cache {
if page.CacheExpiration != 0 { if page.CacheExpiration != 0 {
setInternalCacheExpirationHeader(w, page.CacheExpiration) setInternalCacheExpirationHeader(w, page.CacheExpiration)

View File

@ -10,7 +10,7 @@ import (
const editorPath = "/editor" const editorPath = "/editor"
func serveEditor(w http.ResponseWriter, r *http.Request) { func serveEditor(w http.ResponseWriter, _ *http.Request) {
render(w, templateEditor, &renderData{}) render(w, templateEditor, &renderData{})
} }
@ -20,12 +20,12 @@ func serveEditorPost(w http.ResponseWriter, r *http.Request) {
case "loadupdate": case "loadupdate":
parsedURL, err := url.Parse(r.FormValue("url")) parsedURL, err := url.Parse(r.FormValue("url"))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
post, err := getPost(parsedURL.Path) post, err := getPost(parsedURL.Path)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
mf := post.toMfItem() mf := post.toMfItem()
@ -49,12 +49,12 @@ func serveEditorPost(w http.ResponseWriter, r *http.Request) {
} }
jsonBytes, err := json.Marshal(mf) jsonBytes, err := json.Marshal(mf)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
req, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(jsonBytes)) req, err := http.NewRequest(http.MethodPost, "", bytes.NewReader(jsonBytes))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
req.Header.Set(contentType, contentTypeJSON) req.Header.Set(contentType, contentTypeJSON)
@ -62,7 +62,7 @@ func serveEditorPost(w http.ResponseWriter, r *http.Request) {
case "upload": case "upload":
editorMicropubPost(w, r, true) editorMicropubPost(w, r, true)
default: default:
http.Error(w, "unknown editoraction", http.StatusBadRequest) serveError(w, r, "Unknown editoraction", http.StatusBadRequest)
} }
return return
} }

View File

@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
) )
type errorData struct { type errorData struct {
@ -11,11 +12,23 @@ type errorData struct {
} }
func serve404(w http.ResponseWriter, r *http.Request) { func serve404(w http.ResponseWriter, r *http.Request) {
serveError(w, r, fmt.Sprintf("%s was not found", r.RequestURI), http.StatusNotFound)
}
func serveError(w http.ResponseWriter, r *http.Request, message string, status int) {
if !strings.Contains(strings.ToLower(r.Header.Get("Accept")), contentTypeHTML) {
http.Error(w, message, status)
return
}
title := fmt.Sprintf("%d %s", status, http.StatusText(status))
if message == "" {
message = http.StatusText(status)
}
render(w, templateError, &renderData{ render(w, templateError, &renderData{
Data: &errorData{ Data: &errorData{
Title: "404 Not Found", Title: title,
Message: fmt.Sprintf("`%s` was not found", r.RequestURI), Message: message,
}, },
}) })
w.WriteHeader(http.StatusNotFound) w.WriteHeader(status)
} }

View File

@ -81,7 +81,7 @@ func generateFeed(blog string, f feedType, w http.ResponseWriter, r *http.Reques
} }
if err != nil { if err != nil {
w.Header().Del(contentType) w.Header().Del(contentType)
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)

View File

@ -296,6 +296,10 @@ func buildHandler() (http.Handler, error) {
// Check redirects, then serve 404 // Check redirects, then serve 404
r.With(checkRegexRedirects, cacheMiddleware, minifier.Middleware).NotFound(serve404) r.With(checkRegexRedirects, cacheMiddleware, minifier.Middleware).NotFound(serve404)
r.With(minifier.Middleware).MethodNotAllowed(func(rw http.ResponseWriter, r *http.Request) {
serveError(rw, r, "", http.StatusMethodNotAllowed)
})
return r, nil return r, nil
} }

View File

@ -14,7 +14,7 @@ func checkIndieAuth(next http.Handler) http.Handler {
} }
tokenData, err := verifyIndieAuthToken(bearerToken) tokenData, err := verifyIndieAuthToken(bearerToken)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized) serveError(w, r, err.Error(), http.StatusUnauthorized)
return return
} }
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "scope", strings.Join(tokenData.Scopes, " ")))) next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), "scope", strings.Join(tokenData.Scopes, " "))))

View File

@ -35,18 +35,18 @@ func indieAuthRequest(w http.ResponseWriter, r *http.Request) {
State: r.Form.Get("state"), State: r.Form.Get("state"),
} }
if rt := r.Form.Get("response_type"); rt != "code" && rt != "id" && rt != "" { if rt := r.Form.Get("response_type"); rt != "code" && rt != "id" && rt != "" {
http.Error(w, "response_type must be code", http.StatusBadRequest) serveError(w, r, "response_type must be code", http.StatusBadRequest)
return return
} }
if scope := r.Form.Get("scope"); scope != "" { if scope := r.Form.Get("scope"); scope != "" {
data.Scopes = strings.Split(scope, " ") data.Scopes = strings.Split(scope, " ")
} }
if !isValidProfileURL(data.ClientID) || !isValidProfileURL(data.RedirectURI) { if !isValidProfileURL(data.ClientID) || !isValidProfileURL(data.RedirectURI) {
http.Error(w, "client_id and redirect_uri need to by valid URLs", http.StatusBadRequest) serveError(w, r, "client_id and redirect_uri need to by valid URLs", http.StatusBadRequest)
return return
} }
if data.State == "" { if data.State == "" {
http.Error(w, "state must not be empty", http.StatusBadRequest) serveError(w, r, "state must not be empty", http.StatusBadRequest)
return return
} }
render(w, "indieauth", &renderData{ render(w, "indieauth", &renderData{
@ -90,7 +90,7 @@ func indieAuthAccept(w http.ResponseWriter, r *http.Request) {
data.code = fmt.Sprintf("%x", sha.Sum(nil)) data.code = fmt.Sprintf("%x", sha.Sum(nil))
err := data.saveAuthorization() err := data.saveAuthorization()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, data.RedirectURI+"?code="+data.code+"&state="+data.State, http.StatusFound) http.Redirect(w, r, data.RedirectURI+"?code="+data.code+"&state="+data.State, http.StatusFound)
@ -114,11 +114,11 @@ func indieAuthVerification(w http.ResponseWriter, r *http.Request) {
} }
valid, err := data.verifyAuthorization() valid, err := data.verifyAuthorization()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
if !valid { if !valid {
http.Error(w, "Authentication not valid", http.StatusForbidden) serveError(w, r, "Authentication not valid", http.StatusForbidden)
return return
} }
res := &tokenResponse{ res := &tokenResponse{
@ -128,7 +128,7 @@ func indieAuthVerification(w http.ResponseWriter, r *http.Request) {
err = json.NewEncoder(w).Encode(res) err = json.NewEncoder(w).Encode(res)
if err != nil { if err != nil {
w.Header().Del(contentType) w.Header().Del(contentType)
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
} }
@ -138,7 +138,7 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
// Token verification // Token verification
data, err := verifyIndieAuthToken(r.Header.Get("Authorization")) data, err := verifyIndieAuthToken(r.Header.Get("Authorization"))
if err != nil { if err != nil {
http.Error(w, "Invalid token or token not found", http.StatusUnauthorized) serveError(w, r, "Invalid token or token not found", http.StatusUnauthorized)
return return
} }
res := &tokenResponse{ res := &tokenResponse{
@ -150,7 +150,7 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
err = json.NewEncoder(w).Encode(res) err = json.NewEncoder(w).Encode(res)
if err != nil { if err != nil {
w.Header().Del(contentType) w.Header().Del(contentType)
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
return return
@ -171,15 +171,15 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
} }
valid, err := data.verifyAuthorization() valid, err := data.verifyAuthorization()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
if !valid { if !valid {
http.Error(w, "Authentication not valid", http.StatusForbidden) serveError(w, r, "Authentication not valid", http.StatusForbidden)
return return
} }
if len(data.Scopes) < 1 { if len(data.Scopes) < 1 {
http.Error(w, "No scope", http.StatusBadRequest) serveError(w, r, "No scope", http.StatusBadRequest)
return return
} }
data.time = time.Now() data.time = time.Now()
@ -188,7 +188,7 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
data.token = fmt.Sprintf("%x", sha.Sum(nil)) data.token = fmt.Sprintf("%x", sha.Sum(nil))
err = data.saveToken() err = data.saveToken()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
res := &tokenResponse{ res := &tokenResponse{
@ -201,12 +201,12 @@ func indieAuthToken(w http.ResponseWriter, r *http.Request) {
err = json.NewEncoder(w).Encode(res) err = json.NewEncoder(w).Encode(res)
if err != nil { if err != nil {
w.Header().Del(contentType) w.Header().Del(contentType)
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
return return
} }
http.Error(w, "", http.StatusBadRequest) serveError(w, r, "", http.StatusBadRequest)
return return
} }
} }

View File

@ -36,12 +36,12 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
if urlString := r.URL.Query().Get("url"); urlString != "" { if urlString := r.URL.Query().Get("url"); urlString != "" {
u, err := url.Parse(r.URL.Query().Get("url")) u, err := url.Parse(r.URL.Query().Get("url"))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
p, err := getPost(u.Path) p, err := getPost(u.Path)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
mf = p.toMfItem() mf = p.toMfItem()
@ -53,7 +53,7 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
offset: offset, offset: offset,
}) })
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
list := map[string][]*microformatItem{} list := map[string][]*microformatItem{}
@ -70,7 +70,7 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
for blog := range appConfig.Blogs { for blog := range appConfig.Blogs {
values, err := allTaxonomyValues(blog, appConfig.Micropub.CategoryParam) values, err := allTaxonomyValues(blog, appConfig.Micropub.CategoryParam)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
allCategories = append(allCategories, values...) allCategories = append(allCategories, values...)
@ -81,7 +81,7 @@ func serveMicropubQuery(w http.ResponseWriter, r *http.Request) {
"categories": allCategories, "categories": allCategories,
}) })
default: default:
w.WriteHeader(http.StatusNotFound) serve404(w, r)
} }
} }
@ -124,38 +124,38 @@ func serveMicropubPost(w http.ResponseWriter, r *http.Request) {
err = r.ParseForm() err = r.ParseForm()
} }
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
if action := micropubAction(r.Form.Get("action")); action != "" { if action := micropubAction(r.Form.Get("action")); action != "" {
u, err := url.Parse(r.Form.Get("url")) u, err := url.Parse(r.Form.Get("url"))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
if action == actionDelete { if action == actionDelete {
micropubDelete(w, r, u) micropubDelete(w, r, u)
return return
} }
http.Error(w, "Action not supported", http.StatusNotImplemented) serveError(w, r, "Action not supported", http.StatusNotImplemented)
return return
} }
p, err = convertMPValueMapToPost(r.Form) p, err = convertMPValueMapToPost(r.Form)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
} else if strings.Contains(ct, contentTypeJSON) { } else if strings.Contains(ct, contentTypeJSON) {
parsedMfItem := &microformatItem{} parsedMfItem := &microformatItem{}
err := json.NewDecoder(r.Body).Decode(parsedMfItem) err := json.NewDecoder(r.Body).Decode(parsedMfItem)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
if parsedMfItem.Action != "" { if parsedMfItem.Action != "" {
u, err := url.Parse(parsedMfItem.URL) u, err := url.Parse(parsedMfItem.URL)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
if parsedMfItem.Action == actionDelete { if parsedMfItem.Action == actionDelete {
@ -166,25 +166,25 @@ func serveMicropubPost(w http.ResponseWriter, r *http.Request) {
micropubUpdate(w, r, u, parsedMfItem) micropubUpdate(w, r, u, parsedMfItem)
return return
} }
http.Error(w, "Action not supported", http.StatusNotImplemented) serveError(w, r, "Action not supported", http.StatusNotImplemented)
return return
} }
p, err = convertMPMfToPost(parsedMfItem) p, err = convertMPMfToPost(parsedMfItem)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
} else { } else {
http.Error(w, "wrong content type", http.StatusBadRequest) serveError(w, r, "wrong content type", http.StatusBadRequest)
return return
} }
if !strings.Contains(r.Context().Value("scope").(string), "create") { if !strings.Contains(r.Context().Value("scope").(string), "create") {
http.Error(w, "create scope missing", http.StatusForbidden) serveError(w, r, "create scope missing", http.StatusForbidden)
return return
} }
err := p.create() err := p.create()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, p.fullURL(), http.StatusAccepted) http.Redirect(w, r, p.fullURL(), http.StatusAccepted)
@ -418,11 +418,11 @@ func (p *post) computeExtraPostParameters() error {
func micropubDelete(w http.ResponseWriter, r *http.Request, u *url.URL) { func micropubDelete(w http.ResponseWriter, r *http.Request, u *url.URL) {
if !strings.Contains(r.Context().Value("scope").(string), "delete") { if !strings.Contains(r.Context().Value("scope").(string), "delete") {
http.Error(w, "delete scope missing", http.StatusForbidden) serveError(w, r, "delete scope missing", http.StatusForbidden)
return return
} }
if err := deletePost(u.Path); err != nil { if err := deletePost(u.Path); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
http.Redirect(w, r, u.String(), http.StatusNoContent) http.Redirect(w, r, u.String(), http.StatusNoContent)
@ -431,12 +431,12 @@ func micropubDelete(w http.ResponseWriter, r *http.Request, u *url.URL) {
func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *microformatItem) { func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *microformatItem) {
if !strings.Contains(r.Context().Value("scope").(string), "update") { if !strings.Contains(r.Context().Value("scope").(string), "update") {
http.Error(w, "update scope missing", http.StatusForbidden) serveError(w, r, "update scope missing", http.StatusForbidden)
return return
} }
p, err := getPost(u.Path) p, err := getPost(u.Path)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
if mf.Replace != nil { if mf.Replace != nil {
@ -550,12 +550,12 @@ func micropubUpdate(w http.ResponseWriter, r *http.Request, u *url.URL, mf *micr
} }
err = p.computeExtraPostParameters() err = p.computeExtraPostParameters()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
err = p.replace() err = p.replace()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, p.fullURL(), http.StatusNoContent) http.Redirect(w, r, p.fullURL(), http.StatusNoContent)

View File

@ -22,25 +22,25 @@ const micropubMediaSubPath = "/media"
func serveMicropubMedia(w http.ResponseWriter, r *http.Request) { func serveMicropubMedia(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Context().Value("scope").(string), "media") { if !strings.Contains(r.Context().Value("scope").(string), "media") {
http.Error(w, "media scope missing", http.StatusForbidden) serveError(w, r, "media scope missing", http.StatusForbidden)
return return
} }
if appConfig.Micropub.MediaStorage == nil { if appConfig.Micropub.MediaStorage == nil {
http.Error(w, "Not configured", http.StatusNotImplemented) serveError(w, r, "Not configured", http.StatusNotImplemented)
return return
} }
if ct := r.Header.Get(contentType); !strings.Contains(ct, contentTypeMultipartForm) { if ct := r.Header.Get(contentType); !strings.Contains(ct, contentTypeMultipartForm) {
http.Error(w, "wrong content-type", http.StatusBadRequest) serveError(w, r, "wrong content-type", http.StatusBadRequest)
return return
} }
err := r.ParseMultipartForm(0) err := r.ParseMultipartForm(0)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
file, header, err := r.FormFile("file") file, header, err := r.FormFile("file")
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
defer func() { _ = file.Close() }() defer func() { _ = file.Close() }()
@ -48,7 +48,7 @@ func serveMicropubMedia(w http.ResponseWriter, r *http.Request) {
defer func() { _ = hashFile.Close() }() defer func() { _ = hashFile.Close() }()
fileName, err := getSHA256(hashFile) fileName, err := getSHA256(hashFile)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
fileExtension := filepath.Ext(header.Filename) fileExtension := filepath.Ext(header.Filename)
@ -65,13 +65,13 @@ func serveMicropubMedia(w http.ResponseWriter, r *http.Request) {
fileName += strings.ToLower(fileExtension) fileName += strings.ToLower(fileExtension)
location, err := appConfig.Micropub.MediaStorage.uploadToBunny(fileName, file) location, err := appConfig.Micropub.MediaStorage.uploadToBunny(fileName, file)
if err != nil { if err != nil {
http.Error(w, "failed to upload original file: "+err.Error(), http.StatusInternalServerError) serveError(w, r, "failed to upload original file: "+err.Error(), http.StatusInternalServerError)
return return
} }
if appConfig.Micropub.MediaStorage.TinifyKey != "" { if appConfig.Micropub.MediaStorage.TinifyKey != "" {
compressedLocation, err := appConfig.Micropub.MediaStorage.tinify(location) compressedLocation, err := appConfig.Micropub.MediaStorage.tinify(location)
if err != nil { if err != nil {
http.Error(w, "failed to compress file: "+err.Error(), http.StatusInternalServerError) serveError(w, r, "failed to compress file: "+err.Error(), http.StatusInternalServerError)
return return
} else if compressedLocation != "" { } else if compressedLocation != "" {
location = compressedLocation location = compressedLocation

View File

@ -22,7 +22,7 @@ func allPostAliases() ([]string, error) {
func servePostAlias(w http.ResponseWriter, r *http.Request) { func servePostAlias(w http.ResponseWriter, r *http.Request) {
row, err := appDbQueryRow("select path from post_parameters where parameter = 'aliases' and value = @alias", sql.Named("alias", r.URL.Path)) row, err := appDbQueryRow("select path from post_parameters where parameter = 'aliases' and value = @alias", sql.Named("alias", r.URL.Path))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
var path string var path string
@ -31,7 +31,7 @@ func servePostAlias(w http.ResponseWriter, r *http.Request) {
serve404(w, r) serve404(w, r)
return return
} else if err != nil { } else if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, path, http.StatusFound) http.Redirect(w, r, path, http.StatusFound)

View File

@ -41,7 +41,7 @@ func servePost(w http.ResponseWriter, r *http.Request) {
serve404(w, r) serve404(w, r)
return return
} else if err != nil { } else if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
if as { if as {
@ -95,7 +95,7 @@ func serveHome(blog string, path string) func(w http.ResponseWriter, r *http.Req
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
as := strings.HasSuffix(r.URL.Path, ".as") as := strings.HasSuffix(r.URL.Path, ".as")
if as { if as {
appConfig.Blogs[blog].serveActivityStreams(blog, w) appConfig.Blogs[blog].serveActivityStreams(blog, w, r)
return return
} }
serveIndex(&indexConfig{ serveIndex(&indexConfig{
@ -202,7 +202,7 @@ func serveIndex(ic *indexConfig) func(w http.ResponseWriter, r *http.Request) {
var posts []*post var posts []*post
err := p.Results(&posts) err := p.Results(&posts)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
// Meta // Meta

View File

@ -13,7 +13,7 @@ func serveSearch(blog string, path string) func(w http.ResponseWriter, r *http.R
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm() err := r.ParseForm()
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
if q := r.Form.Get("q"); q != "" { if q := r.Form.Get("q"); q != "" {

View File

@ -13,7 +13,7 @@ const sitemapPath = "/sitemap.xml"
func serveSitemap(w http.ResponseWriter, r *http.Request) { func serveSitemap(w http.ResponseWriter, r *http.Request) {
posts, err := getPosts(&postsRequestConfig{}) posts, err := getPosts(&postsRequestConfig{})
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
sm := sitemap.New() sm := sitemap.New()

View File

@ -6,7 +6,7 @@ func serveTaxonomy(blog string, tax *taxonomy) func(w http.ResponseWriter, r *ht
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
allValues, err := allTaxonomyValues(blog, tax.Name) allValues, err := allTaxonomyValues(blog, tax.Name)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
render(w, templateTaxonomy, &renderData{ render(w, templateTaxonomy, &renderData{

View File

@ -5,7 +5,7 @@
{{ define "main" }} {{ define "main" }}
<main> <main>
{{ with .Data.Title }}<h1>{{ . }}</h1>{{ end }} {{ with .Data.Title }}<h1>{{ . }}</h1>{{ end }}
{{ with .Data.Message }}{{ md . }}{{ end }} {{ with .Data.Message }}<p class="monospace">{{ . }}</p>{{ end }}
</main> </main>
{{ end }} {{ end }}

View File

@ -45,15 +45,15 @@ func initWebmention() error {
func handleWebmention(w http.ResponseWriter, r *http.Request) { func handleWebmention(w http.ResponseWriter, r *http.Request) {
m, err := extractMention(r) m, err := extractMention(r)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
if !isAllowedHost(httptest.NewRequest(http.MethodGet, m.Target, nil), r.URL.Host, appConfig.Server.Domain) { if !isAllowedHost(httptest.NewRequest(http.MethodGet, m.Target, nil), r.URL.Host, appConfig.Server.Domain) {
http.Error(w, "target not allowed", http.StatusBadRequest) serveError(w, r, "target not allowed", http.StatusBadRequest)
return return
} }
if err = queueMention(m); err != nil { if err = queueMention(m); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
w.WriteHeader(http.StatusAccepted) w.WriteHeader(http.StatusAccepted)
@ -85,14 +85,14 @@ func webmentionAdmin(w http.ResponseWriter, r *http.Request) {
status: webmentionStatusVerified, status: webmentionStatusVerified,
}) })
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
approved, err := getWebmentions(&webmentionsRequestConfig{ approved, err := getWebmentions(&webmentionsRequestConfig{
status: webmentionStatusApproved, status: webmentionStatusApproved,
}) })
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
render(w, "webmentionadmin", &renderData{ render(w, "webmentionadmin", &renderData{
@ -106,12 +106,12 @@ func webmentionAdmin(w http.ResponseWriter, r *http.Request) {
func webmentionAdminDelete(w http.ResponseWriter, r *http.Request) { func webmentionAdminDelete(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("mentionid")) id, err := strconv.Atoi(r.FormValue("mentionid"))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
err = deleteWebmention(id) err = deleteWebmention(id)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
purgeCache() purgeCache()
@ -122,12 +122,12 @@ func webmentionAdminDelete(w http.ResponseWriter, r *http.Request) {
func webmentionAdminApprove(w http.ResponseWriter, r *http.Request) { func webmentionAdminApprove(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.FormValue("mentionid")) id, err := strconv.Atoi(r.FormValue("mentionid"))
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) serveError(w, r, err.Error(), http.StatusBadRequest)
return return
} }
err = approveWebmention(id) err = approveWebmention(id)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) serveError(w, r, err.Error(), http.StatusInternalServerError)
return return
} }
purgeCache() purgeCache()