diff --git a/app/method.go b/app/method.go index 6721bd7..74c25f3 100644 --- a/app/method.go +++ b/app/method.go @@ -1,7 +1,9 @@ package app import ( + "crypto/md5" "encoding/json" + "fmt" "io/ioutil" "regexp" @@ -34,6 +36,7 @@ func initialCaretaker(c *gin.Context, f func(md common.MethodData) common.CodeMe default: token, _ = c.Cookie("X-Ripple-Token") } + c.Set("token", fmt.Sprintf("%x", md5.Sum([]byte(token)))) md := common.MethodData{ DB: db, diff --git a/app/recovery.go b/app/recovery.go new file mode 100644 index 0000000..e55ad60 --- /dev/null +++ b/app/recovery.go @@ -0,0 +1,99 @@ +package app + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + + "github.com/getsentry/raven-go" + "github.com/gin-gonic/gin" +) + +// Recovery is a better sentry logger. +func Recovery(client *raven.Client, onlyCrashes bool) gin.HandlerFunc { + return func(c *gin.Context) { + var requestBody []byte + + defer func() { + tokenRaw, ex := c.Get("token") + var token string + if ex { + token = tokenRaw.(string) + } + + ravenHTTP := raven.NewHttp(c.Request) + if len(requestBody) != 0 { + ravenHTTP.Data = string(requestBody) + } + + stackTrace := raven.NewStacktrace(0, 3, []string{"git.zxq.co/ripple"}) + + ravenUser := &raven.User{ + Username: "token " + token, + IP: c.Request.RemoteAddr, + } + + flags := map[string]string{ + "endpoint": c.Request.RequestURI, + "token": token, + } + + if rval := recover(); rval != nil { + var err error + switch rval := rval.(type) { + case string: + err = errors.New(rval) + case error: + err = rval + default: + err = fmt.Errorf("%v - %#v", rval, rval) + } + client.CaptureError( + err, + flags, + stackTrace, + ravenHTTP, + ravenUser, + ) + c.AbortWithStatus(http.StatusInternalServerError) + } + if !onlyCrashes { + for _, item := range c.Errors { + client.CaptureError( + item, + flags, + stackTrace, + ravenHTTP, + ravenUser, + ) + } + } + }() + + if c.Request.Method == "POST" && c.Request.URL.Path != "/tokens" && + c.Request.URL.Path != "/tokens/new" { + var err error + requestBody, err = ioutil.ReadAll(c.Request.Body) + if err != nil { + c.Error(err) + } + c.Request.Body = fakeBody{ + r: bytes.NewReader(requestBody), + orig: c.Request.Body, + } + } + + c.Next() + } +} + +type fakeBody struct { + r io.Reader + orig io.ReadCloser +} + +func (f fakeBody) Read(p []byte) (int, error) { return f.r.Read(p) } +func (f fakeBody) Close() error { return f.orig.Close() } diff --git a/app/start.go b/app/start.go index d65ef38..cd81ccd 100644 --- a/app/start.go +++ b/app/start.go @@ -10,7 +10,6 @@ import ( "git.zxq.co/ripple/rippleapi/common" "github.com/getsentry/raven-go" "github.com/gin-gonic/contrib/gzip" - "github.com/gin-gonic/contrib/sentry" "github.com/gin-gonic/gin" ) @@ -27,7 +26,7 @@ func Start(conf common.Conf, dbO *sql.DB) *gin.Engine { if err != nil { fmt.Println(err) } else { - r.Use(sentry.Recovery(ravenClient, false)) + r.Use(Recovery(ravenClient, false)) } }