Add back old middlewares
This commit is contained in:
parent
736c904f65
commit
490d13e333
|
@ -3,6 +3,7 @@ package app
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
@ -114,3 +115,17 @@ func mkjson(c *fasthttp.RequestCtx, data interface{}) {
|
||||||
func b2s(b []byte) string {
|
func b2s(b []byte) string {
|
||||||
return *(*string)(unsafe.Pointer(&b))
|
return *(*string)(unsafe.Pointer(&b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// s2b converts string to a byte slice without memory allocation.
|
||||||
|
//
|
||||||
|
// Note it may break if string and/or slice header will change
|
||||||
|
// in the future go versions.
|
||||||
|
func s2b(s string) []byte {
|
||||||
|
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||||
|
bh := reflect.SliceHeader{
|
||||||
|
Data: sh.Data,
|
||||||
|
Len: sh.Len,
|
||||||
|
Cap: sh.Len,
|
||||||
|
}
|
||||||
|
return *(*[]byte)(unsafe.Pointer(&bh))
|
||||||
|
}
|
||||||
|
|
104
app/recovery.go
104
app/recovery.go
|
@ -1,104 +0,0 @@
|
||||||
package app
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
raven "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() {
|
|
||||||
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple"})
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
ravenUser := &raven.User{
|
|
||||||
Username: 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)
|
|
||||||
}
|
|
||||||
fmt.Println(err)
|
|
||||||
client.CaptureError(
|
|
||||||
err,
|
|
||||||
flags,
|
|
||||||
st,
|
|
||||||
ravenHTTP,
|
|
||||||
ravenUser,
|
|
||||||
)
|
|
||||||
c.AbortWithStatus(http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
if !onlyCrashes {
|
|
||||||
for _, item := range c.Errors {
|
|
||||||
var err = error(item)
|
|
||||||
if item.Type == gin.ErrorTypePrivate {
|
|
||||||
err = item.Err
|
|
||||||
}
|
|
||||||
fmt.Println(err)
|
|
||||||
client.CaptureError(
|
|
||||||
err,
|
|
||||||
flags,
|
|
||||||
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() }
|
|
|
@ -1,6 +1,11 @@
|
||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/buaazp/fasthttprouter"
|
"github.com/buaazp/fasthttprouter"
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"github.com/valyala/fasthttp"
|
"github.com/valyala/fasthttp"
|
||||||
|
@ -12,14 +17,85 @@ type router struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r router) Method(path string, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) {
|
func (r router) Method(path string, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) {
|
||||||
r.r.GET(path, Method(f, privilegesNeeded...))
|
r.r.GET(path, wrap(Method(f, privilegesNeeded...)))
|
||||||
}
|
}
|
||||||
func (r router) POSTMethod(path string, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) {
|
func (r router) POSTMethod(path string, f func(md common.MethodData) common.CodeMessager, privilegesNeeded ...int) {
|
||||||
r.r.POST(path, Method(f, privilegesNeeded...))
|
r.r.POST(path, wrap(Method(f, privilegesNeeded...)))
|
||||||
}
|
}
|
||||||
func (r router) Peppy(path string, a func(c *fasthttp.RequestCtx, db *sqlx.DB)) {
|
func (r router) Peppy(path string, a func(c *fasthttp.RequestCtx, db *sqlx.DB)) {
|
||||||
r.r.GET(path, PeppyMethod(a))
|
r.r.GET(path, wrap(PeppyMethod(a)))
|
||||||
}
|
}
|
||||||
func (r router) GET(path string, handle fasthttp.RequestHandler) {
|
func (r router) GET(path string, handle fasthttp.RequestHandler) {
|
||||||
r.r.GET(path, handle)
|
r.r.GET(path, wrap(handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// \x1b is escape code for ESC
|
||||||
|
// <ESC>[<n>m is escape sequence for a certain colour
|
||||||
|
// no IP is written out because of the hundreds of possible ways to pass IPs
|
||||||
|
// to a request when using a reverse proxy
|
||||||
|
// this is partly inspired from gin, though made even more simplistic.
|
||||||
|
fmtString = "%s | %15s |\x1b[%sm %3d \x1b[0m %-7s %s\n"
|
||||||
|
// a kind of human readable RFC3339
|
||||||
|
timeFormat = "2006-01-02 15:04:05"
|
||||||
|
// color reference
|
||||||
|
// http://misc.flogisoft.com/bash/tip_colors_and_formatting
|
||||||
|
colorOk = "42" // green
|
||||||
|
colorError = "41" // red
|
||||||
|
)
|
||||||
|
|
||||||
|
// wrap returns a function that wraps around handle, providing middleware
|
||||||
|
// functionality to apply to all API calls, which is to say:
|
||||||
|
// - logging
|
||||||
|
// - panic recovery (reporting to sentry)
|
||||||
|
// - gzipping
|
||||||
|
func wrap(handle fasthttp.RequestHandler) fasthttp.RequestHandler {
|
||||||
|
return func(c *fasthttp.RequestCtx) {
|
||||||
|
start := time.Now()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
common.Err(c, err)
|
||||||
|
c.SetStatusCode(500)
|
||||||
|
c.SetBodyString(`{ "code": 500, "message": "something really bad happened" }`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch color to colorError if statusCode is in [500;600)
|
||||||
|
color := colorOk
|
||||||
|
statusCode := c.Response.StatusCode()
|
||||||
|
if statusCode >= 500 && statusCode < 600 {
|
||||||
|
color = colorError
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Contains(c.Request.Header.Peek("Accept-Encoding"), s2b("gzip")) {
|
||||||
|
c.Response.Header.Add("Content-Encoding", "gzip")
|
||||||
|
c.Response.Header.Add("Vary", "Accept-Encoding")
|
||||||
|
b := c.Response.Body()
|
||||||
|
c.Response.ResetBody()
|
||||||
|
fasthttp.WriteGzip(c.Response.BodyWriter(), b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// print stuff
|
||||||
|
fmt.Printf(
|
||||||
|
fmtString,
|
||||||
|
time.Now().Format(timeFormat),
|
||||||
|
time.Since(start).String(),
|
||||||
|
color,
|
||||||
|
statusCode,
|
||||||
|
c.Method(),
|
||||||
|
c.Path(),
|
||||||
|
)
|
||||||
|
}()
|
||||||
|
|
||||||
|
handle(c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,8 @@ var randomSentences = [...]string{
|
||||||
}
|
}
|
||||||
|
|
||||||
func surpriseMe() string {
|
func surpriseMe() string {
|
||||||
return randomSentences[rn.Intn(len(randomSentences))] + " " + kaomojis[rn.Intn(len(kaomojis))]
|
n := int(time.Now().UnixNano())
|
||||||
|
return randomSentences[n%len(randomSentences)] + " " + kaomojis[n%len(kaomojis)]
|
||||||
}
|
}
|
||||||
|
|
||||||
type pingResponse struct {
|
type pingResponse struct {
|
||||||
|
|
|
@ -47,36 +47,30 @@ func (md MethodData) ClientIP() string {
|
||||||
// Err logs an error. If RavenClient is set, it will use the client to report
|
// Err logs an error. If RavenClient is set, it will use the client to report
|
||||||
// the error to sentry, otherwise it will just write the error to stdout.
|
// the error to sentry, otherwise it will just write the error to stdout.
|
||||||
func (md MethodData) Err(err error) {
|
func (md MethodData) Err(err error) {
|
||||||
if RavenClient == nil {
|
user := &raven.User{
|
||||||
fmt.Println("ERROR!!!!")
|
|
||||||
fmt.Println(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create stacktrace
|
|
||||||
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple", "git.zxq.co/ripple"})
|
|
||||||
|
|
||||||
// Generate tags for error
|
|
||||||
tags := map[string]string{
|
|
||||||
"endpoint": b2s(md.Ctx.RequestURI()),
|
|
||||||
"token": md.User.Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
RavenClient.CaptureError(
|
|
||||||
err,
|
|
||||||
tags,
|
|
||||||
st,
|
|
||||||
generateRavenHTTP(md.Ctx),
|
|
||||||
&raven.User{
|
|
||||||
ID: strconv.Itoa(md.User.UserID),
|
ID: strconv.Itoa(md.User.UserID),
|
||||||
Username: md.User.Value,
|
Username: md.User.Value,
|
||||||
IP: md.Ctx.RemoteAddr().String(),
|
IP: md.Ctx.RemoteAddr().String(),
|
||||||
},
|
}
|
||||||
)
|
// Generate tags for error
|
||||||
|
tags := map[string]string{
|
||||||
|
"endpoint": string(md.Ctx.RequestURI()),
|
||||||
|
"token": md.User.Value,
|
||||||
|
}
|
||||||
|
_err(err, tags, user, md.Ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Err for peppy API calls
|
// Err for peppy API calls
|
||||||
func Err(c *fasthttp.RequestCtx, err error) {
|
func Err(c *fasthttp.RequestCtx, err error) {
|
||||||
|
// Generate tags for error
|
||||||
|
tags := map[string]string{
|
||||||
|
"endpoint": string(c.RequestURI()),
|
||||||
|
}
|
||||||
|
|
||||||
|
_err(err, tags, nil, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _err(err error, tags map[string]string, user *raven.User, c *fasthttp.RequestCtx) {
|
||||||
if RavenClient == nil {
|
if RavenClient == nil {
|
||||||
fmt.Println("ERROR!!!!")
|
fmt.Println("ERROR!!!!")
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -86,16 +80,15 @@ func Err(c *fasthttp.RequestCtx, err error) {
|
||||||
// Create stacktrace
|
// Create stacktrace
|
||||||
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple", "git.zxq.co/ripple"})
|
st := raven.NewStacktrace(0, 3, []string{"zxq.co/ripple", "git.zxq.co/ripple"})
|
||||||
|
|
||||||
// Generate tags for error
|
ifaces := []raven.Interface{st, generateRavenHTTP(c)}
|
||||||
tags := map[string]string{
|
if user != nil {
|
||||||
"endpoint": b2s(c.RequestURI()),
|
ifaces = append(ifaces, user)
|
||||||
}
|
}
|
||||||
|
|
||||||
RavenClient.CaptureError(
|
RavenClient.CaptureError(
|
||||||
err,
|
err,
|
||||||
tags,
|
tags,
|
||||||
st,
|
ifaces...,
|
||||||
generateRavenHTTP(c),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user