display rest errors in logs

This commit is contained in:
Umputun
2018-01-01 16:16:11 -06:00
parent e83c552576
commit 689a983106
7 changed files with 96 additions and 60 deletions
+6 -5
View File
@@ -14,6 +14,7 @@ import (
"github.com/umputun/remark/app/migrator"
"github.com/umputun/remark/app/rest/auth"
"github.com/umputun/remark/app/rest/common"
"github.com/umputun/remark/app/store"
)
@@ -46,7 +47,7 @@ func (a *admin) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
err := a.dataService.Delete(locator, id)
if err != nil {
log.Printf("[WARN] can't delete comment, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't delete comment")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment")
return
}
a.respCache.Flush()
@@ -61,7 +62,7 @@ func (a *admin) setBlockCtrl(w http.ResponseWriter, r *http.Request) {
blockStatus := r.URL.Query().Get("block") == "1"
if err := a.dataService.SetBlock(store.Locator{SiteID: siteID}, userID, blockStatus); err != nil {
httpError(w, r, http.StatusBadRequest, err, "can't set blocking status")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set blocking status")
return
}
a.respCache.Flush()
@@ -76,7 +77,7 @@ func (a *admin) setPinCtrl(w http.ResponseWriter, r *http.Request) {
pinStatus := r.URL.Query().Get("pin") == "1"
if err := a.dataService.SetPin(locator, commentID, pinStatus); err != nil {
httpError(w, r, http.StatusBadRequest, err, "can't set pin status")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't set pin status")
return
}
a.respCache.Flush()
@@ -97,7 +98,7 @@ func (a *admin) exportCtrl(w http.ResponseWriter, r *http.Request) {
}
if err := a.exporter.Export(writer, siteID); err != nil {
httpError(w, r, http.StatusInternalServerError, err, "export failed")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "export failed")
}
}
@@ -106,7 +107,7 @@ func (a *admin) exportCtrl(w http.ResponseWriter, r *http.Request) {
func (a *admin) importCtrl(w http.ResponseWriter, r *http.Request) {
siteID := r.URL.Query().Get("site")
if err := a.importer.Import(r.Body, siteID); err != nil {
httpError(w, r, http.StatusBadRequest, err, "import failed")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "import failed")
}
a.respCache.Flush()
}
+10 -9
View File
@@ -16,6 +16,7 @@ import (
"github.com/gorilla/sessions"
"golang.org/x/oauth2"
"github.com/umputun/remark/app/rest/common"
"github.com/umputun/remark/app/store"
)
@@ -85,7 +86,7 @@ func (p Provider) LoginHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] login, %+v", session.Values)
if err := session.Save(r, w); err != nil {
http.Error(w, fmt.Sprintf("failed to save start, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to save state")
return
}
@@ -100,7 +101,7 @@ func (p Provider) AuthHandler(w http.ResponseWriter, r *http.Request) {
session, err := p.Get(r, "remark")
if err != nil {
http.Error(w, fmt.Sprintf("failed to get session, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to get session")
return
}
@@ -114,14 +115,14 @@ func (p Provider) AuthHandler(w http.ResponseWriter, r *http.Request) {
log.Printf("[DEBUG] auth, %+v", session.Values)
tok, err := p.conf.Exchange(context.Background(), r.URL.Query().Get("code"))
if err != nil {
http.Error(w, fmt.Sprintf("exchange failed, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "exchange failed")
return
}
client := p.conf.Client(context.Background(), tok)
uinfo, err := client.Get(p.InfoURL)
if err != nil {
http.Error(w, fmt.Sprintf("failed to get client info via %s, %s", p.InfoURL, err), http.StatusBadRequest)
common.SendErrorJSON(w, r, http.StatusBadRequest, err, fmt.Sprintf("failed to get client info via %s", p.InfoURL))
return
}
@@ -133,20 +134,20 @@ func (p Provider) AuthHandler(w http.ResponseWriter, r *http.Request) {
data, err := ioutil.ReadAll(uinfo.Body)
if err != nil {
http.Error(w, fmt.Sprintf("failed to read user info, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to read user info")
return
}
jData := map[string]interface{}{}
if e := json.Unmarshal(data, &jData); e != nil {
http.Error(w, fmt.Sprintf("failed to unmarshal user info, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to unmarshal user info")
return
}
log.Printf("[DEBUG] got raw user info %+v", jData)
session.Values["uinfo"] = p.MapUser(jData)
if err = session.Save(r, w); err != nil {
http.Error(w, fmt.Sprintf("failed to save user info, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to save user info")
return
}
@@ -165,7 +166,7 @@ func (p Provider) AuthHandler(w http.ResponseWriter, r *http.Request) {
func (p Provider) LogoutHandler(w http.ResponseWriter, r *http.Request) {
session, err := p.Get(r, "remark")
if err != nil {
http.Error(w, fmt.Sprintf("failed to get session, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "failed to get session")
return
}
@@ -178,7 +179,7 @@ func (p Provider) LogoutHandler(w http.ResponseWriter, r *http.Request) {
delete(session.Values, "state")
if err = session.Save(r, w); err != nil {
http.Error(w, fmt.Sprintf("failed to reset user info, %s", err), http.StatusInternalServerError)
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to reset user info")
return
}
log.Printf("[DEBUG] logout, %+v", session.Values)
+4 -21
View File
@@ -2,16 +2,14 @@ package auth
import (
"context"
"errors"
"net/http"
"github.com/gorilla/sessions"
"github.com/umputun/remark/app/rest/common"
"github.com/umputun/remark/app/store"
)
type contextKey string
// Mode defines behavior of Auth middleware
type Mode int
@@ -49,7 +47,7 @@ func Auth(sessionStore *sessions.FilesystemStore, admins []string, modes []Mode)
if inModes(Developer) {
user := devUser
ctx := r.Context()
ctx = context.WithValue(ctx, contextKey("user"), user)
ctx = context.WithValue(ctx, common.ContextKey("user"), user)
r = r.WithContext(ctx)
h.ServeHTTP(w, r)
return
@@ -82,7 +80,7 @@ func Auth(sessionStore *sessions.FilesystemStore, admins []string, modes []Mode)
}
ctx := r.Context()
ctx = context.WithValue(ctx, contextKey("user"), user)
ctx = context.WithValue(ctx, common.ContextKey("user"), user)
r = r.WithContext(ctx)
}
h.ServeHTTP(w, r)
@@ -96,7 +94,7 @@ func Auth(sessionStore *sessions.FilesystemStore, admins []string, modes []Mode)
func AdminOnly(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
user, err := GetUserInfo(r)
user, err := common.GetUserInfo(r)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
@@ -111,18 +109,3 @@ func AdminOnly(next http.Handler) http.Handler {
}
return http.HandlerFunc(fn)
}
// GetUserInfo extracts user, or and token from request's context
func GetUserInfo(r *http.Request) (user store.User, err error) {
ctx := r.Context()
if ctx == nil {
return store.User{}, errors.New("no info about user")
}
if u, ok := ctx.Value(contextKey("user")).(store.User); ok {
return u, nil
}
return store.User{}, errors.New("user can't be parsed")
}
+30
View File
@@ -0,0 +1,30 @@
package common
import (
"log"
"net/http"
"strings"
"github.com/go-chi/render"
)
// SendErrorJSON makes {error: blah, details: blah} json body and responds with error code
func SendErrorJSON(w http.ResponseWriter, r *http.Request, code int, err error, details string) {
logDetails(r, code, err, details)
render.Status(r, code)
render.JSON(w, r, map[string]interface{}{"error": err.Error(), "details": details})
}
// SendErrorText with simple text body and responds with error code
func SendErrorText(w http.ResponseWriter, r *http.Request, code int, text string) {
render.Status(r, code)
render.PlainText(w, r, text)
}
func logDetails(r *http.Request, code int, err error, details string) {
uinfoStr := ""
if user, err := GetUserInfo(r); err == nil {
uinfoStr = user.Name + "/" + user.ID + " - "
}
log.Printf("[DEBUG] %s - %v - %d - %s%s - %s", details, err, code, uinfoStr, strings.Split(r.RemoteAddr, ":")[0], r.URL)
}
+26
View File
@@ -0,0 +1,26 @@
package common
import (
"errors"
"net/http"
"github.com/umputun/remark/app/store"
)
// ContextKey is a type to match on context
type ContextKey string
// GetUserInfo returns user from request context
func GetUserInfo(r *http.Request) (user store.User, err error) {
ctx := r.Context()
if ctx == nil {
return store.User{}, errors.New("no info about user")
}
if u, ok := ctx.Value(ContextKey("user")).(store.User); ok {
return u, nil
}
return store.User{}, errors.New("user can't be parsed")
}
+2 -3
View File
@@ -16,8 +16,7 @@ import (
"github.com/didip/tollbooth"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/render"
"github.com/umputun/remark/app/rest/auth"
"github.com/umputun/remark/app/rest/common"
)
var org = "Umputun"
@@ -162,7 +161,7 @@ func Logger(flags ...LoggerFlag) func(http.Handler) http.Handler {
}
if inFlags(LogUser) {
u, err := auth.GetUserInfo(r)
u, err := common.GetUserInfo(r)
if err == nil && u.Name != "" {
user = fmt.Sprintf(" - %s %q", u.ID, u.Name)
}
+18 -22
View File
@@ -20,6 +20,7 @@ import (
"github.com/umputun/remark/app/migrator"
"github.com/umputun/remark/app/rest/auth"
"github.com/umputun/remark/app/rest/common"
"github.com/umputun/remark/app/rest/format"
"github.com/umputun/remark/app/store"
)
@@ -113,13 +114,13 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
comment := store.Comment{}
if err := render.DecodeJSON(r.Body, &comment); err != nil {
log.Printf("[WARN] can't bind request %s", err)
httpError(w, r, http.StatusBadRequest, err, "can't bind comment")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't bind comment")
return
}
user, err := auth.GetUserInfo(r)
user, err := common.GetUserInfo(r)
if err != nil { // this not suppose to happen (handled by Auth), just dbl-check
httpError(w, r, http.StatusUnauthorized, err, "can't get user info")
common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info")
return
}
@@ -133,14 +134,14 @@ func (s *Server) createCommentCtrl(w http.ResponseWriter, r *http.Request) {
// check if user blocked
if s.mod.checkBlocked(store.Locator{}, comment.User) {
log.Printf("[WARN] user %s rejected (blocked)", err)
httpError(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked")
common.SendErrorJSON(w, r, http.StatusForbidden, errors.New("rejected"), "user blocked")
return
}
id, err := s.DataService.Create(comment)
if err != nil {
log.Printf("[WARN] can't save comment, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't save comment")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't save comment")
return
}
@@ -160,7 +161,7 @@ func (s *Server) deleteCommentCtrl(w http.ResponseWriter, r *http.Request) {
err := s.DataService.Delete(locator, id)
if err != nil {
log.Printf("[WARN] can't delete comment %s %+v, %s", id, locator, err)
httpError(w, r, http.StatusInternalServerError, err, "can't delete comment")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't delete comment")
return
}
@@ -185,7 +186,7 @@ func (s *Server) findCommentsCtrl(w http.ResponseWriter, r *http.Request) {
comments, err := s.DataService.Find(store.Request{Locator: locator, Sort: r.URL.Query().Get("sort")})
if err != nil {
log.Printf("[WARN] can't get comments for %+v, %s", locator, err)
httpError(w, r, http.StatusInternalServerError, err, "can't load comments comment")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't load comments comment")
return
}
comments = s.mod.maskBlockedUsers(comments)
@@ -217,7 +218,7 @@ func (s *Server) lastCommentsCtrl(w http.ResponseWriter, r *http.Request) {
comments, err := s.DataService.Last(store.Locator{SiteID: r.URL.Query().Get("site")}, max)
if err != nil {
log.Printf("[WARN] can't get last comments, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't get last comments")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't get last comments")
return
}
comments = s.mod.maskBlockedUsers(comments)
@@ -238,7 +239,7 @@ func (s *Server) commentByIDCtrl(w http.ResponseWriter, r *http.Request) {
comment, err := s.DataService.GetByID(locator, id)
if err != nil {
log.Printf("[WARN] can't get comment, %s", err)
httpError(w, r, http.StatusInternalServerError, err, "can't get comment by id")
common.SendErrorJSON(w, r, http.StatusInternalServerError, err, "can't get comment by id")
return
}
render.Status(r, http.StatusOK)
@@ -261,7 +262,7 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
comments, err := s.DataService.GetByUser(store.Locator{SiteID: r.URL.Query().Get("site")}, userID)
if err != nil {
log.Printf("[WARN] can't get comment, %s", err)
httpError(w, r, http.StatusBadRequest, err, "can't get comment by user id")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get comment by user id")
return
}
@@ -272,9 +273,9 @@ func (s *Server) findUserCommentsCtrl(w http.ResponseWriter, r *http.Request) {
// GET /user - returns user info
func (s *Server) userInfoCtrl(w http.ResponseWriter, r *http.Request) {
user, err := auth.GetUserInfo(r)
user, err := common.GetUserInfo(r)
if err != nil {
httpError(w, r, http.StatusUnauthorized, err, "can't get user info")
common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info")
return
}
render.JSON(w, r, user)
@@ -285,7 +286,7 @@ func (s *Server) countCtrl(w http.ResponseWriter, r *http.Request) {
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
count, err := s.DataService.Count(locator)
if err != nil {
httpError(w, r, http.StatusBadRequest, err, "can't get count")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get count")
return
}
render.JSON(w, r, JSON{"count": count, "loc": locator})
@@ -296,7 +297,7 @@ func (s *Server) listCtrl(w http.ResponseWriter, r *http.Request) {
locator := store.Locator{SiteID: r.URL.Query().Get("site")}
posts, err := s.DataService.List(locator)
if err != nil {
httpError(w, r, http.StatusBadRequest, err, "can't get count")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get count")
return
}
render.JSON(w, r, JSON{"posts": posts, "loc": locator})
@@ -305,9 +306,9 @@ func (s *Server) listCtrl(w http.ResponseWriter, r *http.Request) {
// PUT /vote/{id}?site=siteID&url=post-url&vote=1 - vote for/against comment
func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
user, err := auth.GetUserInfo(r)
user, err := common.GetUserInfo(r)
if err != nil {
httpError(w, r, http.StatusUnauthorized, err, "can't get user info")
common.SendErrorJSON(w, r, http.StatusUnauthorized, err, "can't get user info")
return
}
locator := store.Locator{SiteID: r.URL.Query().Get("site"), URL: r.URL.Query().Get("url")}
@@ -319,18 +320,13 @@ func (s *Server) voteCtrl(w http.ResponseWriter, r *http.Request) {
comment, err := s.DataService.Vote(locator, id, user.ID, vote)
if err != nil {
log.Printf("[WARN] vote rejected for %s - %s, %s", user.ID, id, err)
httpError(w, r, http.StatusBadRequest, err, "can't vote for comment")
common.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't vote for comment")
return
}
s.respCache.Flush()
render.JSON(w, r, JSON{"id": comment.ID, "score": comment.Score})
}
func httpError(w http.ResponseWriter, r *http.Request, code int, err error, details string) {
render.Status(r, code)
render.JSON(w, r, JSON{"error": err.Error(), "details": details})
}
// renderJSONWithHTML allows html tags and forces charset=utf-8
func renderJSONWithHTML(w http.ResponseWriter, r *http.Request, v interface{}) {
buf := &bytes.Buffer{}