display rest errors in logs
This commit is contained in:
+6
-5
@@ -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
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
@@ -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{}
|
||||
|
||||
Reference in New Issue
Block a user