diff --git a/.gitignore b/.gitignore index e636d91..593ea84 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ config.yml config_test.yml .idea/ /bin/* -*.xml \ No newline at end of file +*.xml +/vendor diff --git a/error_handler.go b/error_handler.go new file mode 100644 index 0000000..a629066 --- /dev/null +++ b/error_handler.go @@ -0,0 +1,113 @@ +package main + +import ( + "fmt" + "net/http" + "runtime/debug" + + "github.com/getsentry/raven-go" + "github.com/gin-gonic/gin" + "github.com/pkg/errors" +) + +type ( + ErrorHandlerFunc func(recovery interface{}, c *gin.Context) +) + +func ErrorHandler(handlers ...ErrorHandlerFunc) gin.HandlerFunc { + return func(c *gin.Context) { + defer func() { + rec := recover() + for _, handler := range handlers { + handler(rec, c) + } + + if rec != nil || len(c.Errors) > 0 { + c.Abort() + } + }() + + c.Next() + } +} + +func ErrorResponseHandler() ErrorHandlerFunc { + return func(recovery interface{}, c *gin.Context) { + publicErrors := c.Errors.ByType(gin.ErrorTypePublic) + privateLen := len(c.Errors.ByType(gin.ErrorTypePrivate)) + publicLen := len(publicErrors) + + if privateLen == 0 && publicLen == 0 && recovery == nil { + return + } + + messagesLen := publicLen + if privateLen > 0 || recovery != nil { + messagesLen++ + } + + messages := make([]string, messagesLen) + index := 0 + for _, err := range publicErrors { + messages[index] = err.Error() + index++ + } + + if privateLen > 0 || recovery != nil { + messages[index] = "Something went wrong" + } + + c.JSON(http.StatusInternalServerError, gin.H{"error": messages}) + } +} + +func ErrorCaptureHandler(client *raven.Client, errorsStacktrace bool) ErrorHandlerFunc { + return func(recovery interface{}, c *gin.Context) { + tags := map[string]string{ + "endpoint": c.Request.RequestURI, + } + + if recovery != nil { + stacktrace := raven.NewStacktrace(4, 3, nil) + recStr := fmt.Sprint(recovery) + err := errors.New(recStr) + go client.CaptureMessageAndWait( + recStr, + tags, + raven.NewException(err, stacktrace), + raven.NewHttp(c.Request), + ) + } + + for _, err := range c.Errors { + if errorsStacktrace { + stacktrace := NewRavenStackTrace(client, err.Err, 0) + go client.CaptureMessageAndWait( + err.Error(), + tags, + raven.NewException(err.Err, stacktrace), + raven.NewHttp(c.Request), + ) + } else { + go client.CaptureErrorAndWait(err.Err, tags) + } + } + } +} + +func PanicLogger() ErrorHandlerFunc { + return func(recovery interface{}, c *gin.Context) { + if recovery != nil { + logger.Error(recovery) + debug.PrintStack() + } + } +} + +func ErrorLogger() ErrorHandlerFunc { + return func(recovery interface{}, c *gin.Context) { + for _, err := range c.Errors { + logger.Error(err.Err) + } + } +} diff --git a/errors.go b/errors.go deleted file mode 100644 index a221b5d..0000000 --- a/errors.go +++ /dev/null @@ -1,211 +0,0 @@ -package main - -import ( - "fmt" - "net/http" - "runtime" - "runtime/debug" - - "github.com/getsentry/raven-go" - "github.com/gin-gonic/gin" - "github.com/pkg/errors" -) - -type ( - ErrorHandlerFunc func(recovery interface{}, c *gin.Context) -) - -func ErrorHandler(handlers ...ErrorHandlerFunc) gin.HandlerFunc { - return func(c *gin.Context) { - defer func() { - rec := recover() - for _, handler := range handlers { - handler(rec, c) - } - - if rec != nil || len(c.Errors) > 0 { - c.Abort() - } - }() - - c.Next() - } -} - -func ErrorResponseHandler() ErrorHandlerFunc { - return func(recovery interface{}, c *gin.Context) { - publicErrors := c.Errors.ByType(gin.ErrorTypePublic) - privateLen := len(c.Errors.ByType(gin.ErrorTypePrivate)) - publicLen := len(publicErrors) - - if privateLen == 0 && publicLen == 0 && recovery == nil { - return - } - - messagesLen := publicLen - if privateLen > 0 || recovery != nil { - messagesLen++ - } - - messages := make([]string, messagesLen) - index := 0 - for _, err := range publicErrors { - messages[index] = err.Error() - index++ - } - - if privateLen > 0 || recovery != nil { - messages[index] = "Something went wrong" - } - - c.JSON(http.StatusInternalServerError, gin.H{"error": messages}) - } -} - -func ErrorCaptureHandler(client *raven.Client, errorsStacktrace bool) ErrorHandlerFunc { - return func(recovery interface{}, c *gin.Context) { - tags := map[string]string{ - "endpoint": c.Request.RequestURI, - } - - if recovery != nil { - stacktrace := raven.NewStacktrace(4, 3, nil) - recStr := fmt.Sprint(recovery) - err := errors.New(recStr) - go client.CaptureMessageAndWait( - recStr, - tags, - raven.NewException(err, stacktrace), - raven.NewHttp(c.Request), - ) - } - - for _, err := range c.Errors { - if errorsStacktrace { - stacktrace := NewRavenStackTrace(client, err.Err, 0) - go client.CaptureMessageAndWait( - err.Error(), - tags, - raven.NewException(err.Err, stacktrace), - raven.NewHttp(c.Request), - ) - } else { - go client.CaptureErrorAndWait(err.Err, tags) - } - } - } -} - -func PanicLogger() ErrorHandlerFunc { - return func(recovery interface{}, c *gin.Context) { - if recovery != nil { - fmt.Printf("===\n%+v\n", recovery) - debug.PrintStack() - } - } -} - -func ErrorLogger() ErrorHandlerFunc { - return func(recovery interface{}, c *gin.Context) { - for _, err := range c.Errors { - fmt.Printf("===\n%+v\n", err.Err) - } - } -} - -func NewRavenStackTrace(client *raven.Client, myerr error, skip int) *raven.Stacktrace { - st := getErrorStackTraceConverted(myerr, 3, client.IncludePaths()) - if st == nil { - st = raven.NewStacktrace(skip, 3, client.IncludePaths()) - } - return st -} - -func getErrorStackTraceConverted(err error, context int, appPackagePrefixes []string) *raven.Stacktrace { - st := getErrorCauseStackTrace(err) - if st == nil { - return nil - } - return convertStackTrace(st, context, appPackagePrefixes) -} - -func getErrorCauseStackTrace(err error) errors.StackTrace { - // This code is inspired by github.com/pkg/errors.Cause(). - var st errors.StackTrace - for err != nil { - s := getErrorStackTrace(err) - if s != nil { - st = s - } - err = getErrorCause(err) - } - return st -} - -func convertStackTrace(st errors.StackTrace, context int, appPackagePrefixes []string) *raven.Stacktrace { - // This code is borrowed from github.com/getsentry/raven-go.NewStacktrace(). - var frames []*raven.StacktraceFrame - for _, f := range st { - frame := convertFrame(f, context, appPackagePrefixes) - if frame != nil { - frames = append(frames, frame) - } - } - if len(frames) == 0 { - return nil - } - for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 { - frames[i], frames[j] = frames[j], frames[i] - } - return &raven.Stacktrace{Frames: frames} -} - -func convertFrame(f errors.Frame, context int, appPackagePrefixes []string) *raven.StacktraceFrame { - // This code is borrowed from github.com/pkg/errors.Frame. - pc := uintptr(f) - 1 - fn := runtime.FuncForPC(pc) - var file string - var line int - if fn != nil { - file, line = fn.FileLine(pc) - } else { - file = "unknown" - } - return raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes) -} - -func getErrorStackTrace(err error) errors.StackTrace { - ster, ok := err.(interface { - StackTrace() errors.StackTrace - }) - if !ok { - return nil - } - return ster.StackTrace() -} - -func getErrorCause(err error) error { - cer, ok := err.(interface { - Cause() error - }) - if !ok { - return nil - } - return cer.Cause() -} - -type errorResponse struct { - Errors []string `json:"errors"` -} - -func NotFound(errors ...string) (int, interface{}) { - return http.StatusNotFound, errorResponse{ - Errors: errors, - } -} - -func BadRequest(errors ...string) (int, interface{}) { - return http.StatusBadRequest, errorResponse{ - Errors: errors, - } -} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..932cc6f --- /dev/null +++ b/go.mod @@ -0,0 +1,63 @@ +module github.com/retailcrm/mg-transport-telegram + +require ( + cloud.google.com/go v0.26.0 // indirect + github.com/Microsoft/go-winio v0.4.9 // indirect + github.com/aws/aws-sdk-go v1.15.12 + github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261 // indirect + github.com/davecgh/go-spew v1.1.0 // indirect + github.com/denisenkom/go-mssqldb v0.0.0-20180707235734-242fa5aa1b45 // indirect + github.com/docker/distribution v2.6.2+incompatible // indirect + github.com/docker/docker v1.13.1 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.3.3 // indirect + github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 // indirect + github.com/getsentry/raven-go v0.0.0-20180801005657-7535a8fa2ace + github.com/gin-contrib/multitemplate v0.0.0-20180607024123-41d1d62d1df3 + github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 // indirect + github.com/gin-gonic/gin v1.3.0 + github.com/go-playground/locales v0.12.1 // indirect + github.com/go-playground/universal-translator v0.16.0 // indirect + github.com/go-sql-driver/mysql v1.4.0 // indirect + github.com/go-telegram-bot-api/telegram-bot-api v0.0.0-20180602093832-4c16a90966d1 + github.com/golang-migrate/migrate v3.4.0+incompatible + github.com/golang/protobuf v1.1.0 // indirect + github.com/google/go-cmp v0.2.0 // indirect + github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 // indirect + github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect + github.com/h2non/gock v1.0.9 + github.com/jessevdk/go-flags v1.4.0 + github.com/jinzhu/gorm v1.9.1 + github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect + github.com/jinzhu/now v0.0.0-20180511015916-ed742868f2ae // indirect + github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b // indirect + github.com/jtolds/gls v4.2.1+incompatible // indirect + github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 // indirect + github.com/mattn/go-isatty v0.0.3 // indirect + github.com/mattn/go-sqlite3 v1.9.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v0.0.0-20180718012357-94122c33edd3 // indirect + github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect + github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 + github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 + github.com/pkg/errors v0.8.0 + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/retailcrm/api-client-go v1.0.4 + github.com/retailcrm/mg-transport-api-client-go v1.1.0 + github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 // indirect + github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a // indirect + github.com/stevvooe/resumable v0.0.0-20170302213456-2aaf90b2ceea // indirect + github.com/stretchr/testify v1.2.2 + github.com/technoweenie/multipartstreamer v1.0.1 // indirect + github.com/ugorji/go v1.1.1 // indirect + golang.org/x/crypto v0.0.0-20180808211826-de0752318171 // indirect + golang.org/x/net v0.0.0-20180811021610-c39426892332 // indirect + golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect + golang.org/x/sys v0.0.0-20180815093151-14742f9018cd // indirect + golang.org/x/text v0.3.0 + google.golang.org/appengine v1.1.0 // indirect + gopkg.in/go-playground/assert.v1 v1.2.1 // indirect + gopkg.in/go-playground/validator.v8 v8.18.2 // indirect + gopkg.in/go-playground/validator.v9 v9.21.0 + gopkg.in/yaml.v2 v2.2.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..937f70c --- /dev/null +++ b/go.sum @@ -0,0 +1,127 @@ +cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY= +github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Microsoft/go-winio v0.4.9 h1:3RbgqgGVqmcpbOiwrjbVtDHLlJBGF6aE+yHmNtBNsFQ= +github.com/Microsoft/go-winio v0.4.9/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/aws/aws-sdk-go v1.15.12 h1:iHNrTnYV37nlnL3zX7R3sniehzrTvCjRgtHqhf31VLI= +github.com/aws/aws-sdk-go v1.15.12/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261 h1:6/yVvBsKeAw05IUj4AzvrxaCnDjN4nUqKjW9+w5wixg= +github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denisenkom/go-mssqldb v0.0.0-20180707235734-242fa5aa1b45 h1:UW8VerkZA1zCt3uWhQ2wbMae76OLn7s7Utz8wyKtJUk= +github.com/denisenkom/go-mssqldb v0.0.0-20180707235734-242fa5aa1b45/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= +github.com/docker/distribution v2.6.2+incompatible h1:4FI6af79dfCS/CYb+RRtkSHw3q1L/bnDjG1PcPZtQhM= +github.com/docker/distribution v2.6.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.13.1 h1:5VBhsO6ckUxB0A8CE5LlUJdXzik9cbEbBTQ/ggeml7M= +github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= +github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= +github.com/getsentry/raven-go v0.0.0-20180801005657-7535a8fa2ace h1:M5ZUuRO+XFqhTa9PlaqyWgfzMNWKSraCWm7z4PzM1GA= +github.com/getsentry/raven-go v0.0.0-20180801005657-7535a8fa2ace/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= +github.com/gin-contrib/multitemplate v0.0.0-20180607024123-41d1d62d1df3 h1:nKrMd5DcMWMxZbGzSEscZ7zsnA0vLf46rGKV1R7c4Kc= +github.com/gin-contrib/multitemplate v0.0.0-20180607024123-41d1d62d1df3/go.mod h1:62qM8p4crGvNKE413gTzn4eMFin1VOJfMDWMRzHdvqM= +github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7 h1:AzN37oI0cOS+cougNAV9szl6CVoj2RYwzS3DpUQNtlY= +github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= +github.com/gin-gonic/gin v1.3.0 h1:kCmZyPklC0gVdL728E6Aj20uYBJV93nj/TkwBTKhFbs= +github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y= +github.com/go-ini/ini v1.25.4 h1:Mujh4R/dH6YL8bxuISne3xX2+qcQ9p0IxKAP6ExWoUo= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-playground/locales v0.12.1 h1:2FITxuFt/xuCNP1Acdhv62OzaCiviiE4kotfhkmOqEc= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0 h1:X++omBR/4cE2MNg91AoC3rmGrCjJ8eAeUP/K/EKx4DM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= +github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-telegram-bot-api/telegram-bot-api v0.0.0-20180602093832-4c16a90966d1 h1:FlRoyZCY3snE+M9jTruqOzPwZg8KIwQBXr//t215K8E= +github.com/go-telegram-bot-api/telegram-bot-api v0.0.0-20180602093832-4c16a90966d1/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= +github.com/golang-migrate/migrate v3.4.0+incompatible h1:9yjg5lYsbeEpWXGc80RylvPMKZ0tZEGsyO3CpYLK3jU= +github.com/golang-migrate/migrate v3.4.0+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk= +github.com/golang/protobuf v1.1.0 h1:0iH4Ffd/meGoXqF2lSAhZHt8X+cPgkfn/cb6Cce5Vpc= +github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0= +github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ= +github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/h2non/gock v1.0.9 h1:17gCehSo8ZOgEsFKpQgqHiR7VLyjxdAG3lkhVvO9QZU= +github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE= +github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jinzhu/gorm v1.9.1 h1:lDSDtsCt5AGGSKTs8AHlSDbbgif4G4+CKJ8ETBDVHTA= +github.com/jinzhu/gorm v1.9.1/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v0.0.0-20180511015916-ed742868f2ae h1:8bBMcboXYVuo0WYH+rPe5mB8obO89a993hdTZ3phTjc= +github.com/jinzhu/now v0.0.0-20180511015916-ed742868f2ae/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b h1:X61dhFTE1Au92SvyF8HyAwdjWqiSdfBgFR7wTxC0+uU= +github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE= +github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 h1:it29sI2IM490luSc3RAhp5WuCYnc6RtbfLVAB7nmC5M= +github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= +github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180718012357-94122c33edd3 h1:YFBuDro+e1UCqlJpDWGucQaO/UNhBX1GlS8Du0GNfPw= +github.com/modern-go/reflect2 v0.0.0-20180718012357-94122c33edd3/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= +github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5 h1:/TjjTS4kg7vC+05gD0LE4+97f/+PRFICnK/7wJPk7kE= +github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5/go.mod h1:4Opqa6/HIv0lhG3WRAkqzO0afezkRhxXI0P8EJkqeRU= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/retailcrm/api-client-go v1.0.4 h1:pYlkdQhesc8MN/huU4qp9XpLLRxfr0SIICf2RmEVnoA= +github.com/retailcrm/api-client-go v1.0.4/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c= +github.com/retailcrm/mg-transport-api-client-go v1.1.0 h1:hIxiOhVwA2jil3XcHKX9pxXnoEaq8LdYapD/RPwJcy8= +github.com/retailcrm/mg-transport-api-client-go v1.1.0/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI= +github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9 h1:lXQ+j+KwZcbwrbgU0Rp4Eglg3EJLHbuZU3BbOqAGBmg= +github.com/smartystreets/assertions v0.0.0-20180803164922-886ec427f6b9/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a h1:JSvGDIbmil4Ui/dDdFBExb7/cmkNjyX5F97oglmvCDo= +github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= +github.com/stevvooe/resumable v0.0.0-20170302213456-2aaf90b2ceea h1:KR90QmB10LunzqE3lvSRq0epy66wkQi2bDmkJdkkxi8= +github.com/stevvooe/resumable v0.0.0-20170302213456-2aaf90b2ceea/go.mod h1:1pdIZTAHUz+HDKDVZ++5xg/duPlhKAIzw9qy42CWYp4= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM= +github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= +github.com/ugorji/go v1.1.1 h1:gmervu+jDMvXTbcHQ0pd2wee85nEoE0BsVyEuzkfK8w= +github.com/ugorji/go v1.1.1/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ= +golang.org/x/crypto v0.0.0-20180808211826-de0752318171 h1:vYogbvSFj2YXcjQxFHu/rASSOt9sLytpCaSkiwQ135I= +golang.org/x/crypto v0.0.0-20180808211826-de0752318171/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180811021610-c39426892332 h1:efGso+ep0DjyCBJPjvoz0HI6UldX4Md2F1rZFe1ir0E= +golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180815093151-14742f9018cd h1:Vdp9FdQnZJQQF78wgpudgkchp80Nu37AWr8+mprtgAo= +golang.org/x/sys v0.0.0-20180815093151-14742f9018cd/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ= +gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= +gopkg.in/go-playground/validator.v9 v9.21.0 h1:wSDJGBpQBYC1wLpVnGHLmshm2JicoSNdrb38Zj+8yHI= +gopkg.in/go-playground/validator.v9 v9.21.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/locale.go b/locale.go new file mode 100644 index 0000000..40e7fe8 --- /dev/null +++ b/locale.go @@ -0,0 +1,55 @@ +package main + +import ( + "io/ioutil" + + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" + "gopkg.in/yaml.v2" +) + +var ( + localizer *i18n.Localizer + bundle = &i18n.Bundle{DefaultLanguage: language.English} + matcher = language.NewMatcher([]language.Tag{ + language.English, + language.Russian, + language.Spanish, + }) +) + +func loadTranslateFile() { + bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal) + files, err := ioutil.ReadDir("translate") + if err != nil { + panic(err) + } + for _, f := range files { + if !f.IsDir() { + bundle.MustLoadMessageFile("translate/" + f.Name()) + } + } +} + +func setLocale(al string) { + tag, _ := language.MatchStrings(matcher, al) + localizer = i18n.NewLocalizer(bundle, tag.String()) +} + +func getLocalizedMessage(messageID string) string { + return localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: messageID}) +} + +func getLocale() map[string]string { + return map[string]string{ + "ButtonSave": getLocalizedMessage("button_save"), + "ApiKey": getLocalizedMessage("api_key"), + "TabSettings": getLocalizedMessage("tab_settings"), + "TabBots": getLocalizedMessage("tab_bots"), + "TableName": getLocalizedMessage("table_name"), + "TableToken": getLocalizedMessage("table_token"), + "AddBot": getLocalizedMessage("add_bot"), + "TableDelete": getLocalizedMessage("table_delete"), + "Title": getLocalizedMessage("title"), + } +} diff --git a/main.go b/main.go index a4572d2..c0b3b86 100644 --- a/main.go +++ b/main.go @@ -5,9 +5,7 @@ import ( "github.com/jessevdk/go-flags" - "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/op/go-logging" - "golang.org/x/text/language" ) // Options struct @@ -24,13 +22,6 @@ var ( options Options parser = flags.NewParser(&options, flags.Default) tokenCounter uint32 - localizer *i18n.Localizer - bundle = &i18n.Bundle{DefaultLanguage: language.English} - matcher = language.NewMatcher([]language.Tag{ - language.English, - language.Russian, - language.Spanish, - }) ) func main() { diff --git a/routing.go b/routing.go index a588288..24985ae 100644 --- a/routing.go +++ b/routing.go @@ -1,10 +1,9 @@ package main import ( - "encoding/json" - "fmt" "net/http" - "regexp" + + "fmt" "github.com/gin-gonic/gin" "github.com/go-telegram-bot-api/telegram-bot-api" @@ -13,17 +12,11 @@ import ( ) func connectHandler(c *gin.Context) { - rx := regexp.MustCompile(`/+$`) - ra := rx.ReplaceAllString(c.Query("account"), ``) - p := Connection{ - APIURL: ra, - } - res := struct { - Conn *Connection + Conn Connection Locale map[string]string }{ - &p, + c.MustGet("account").(Connection), getLocale(), } @@ -31,18 +24,7 @@ func connectHandler(c *gin.Context) { } func addBotHandler(c *gin.Context) { - var b Bot - - if err := c.ShouldBindJSON(&b); err != nil { - c.Error(err) - return - } - - if b.Token == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("no_bot_token")}) - return - } - + b := c.MustGet("bot").(Bot) cl, err := getBotByToken(b.Token) if err != nil { c.Error(err) @@ -106,23 +88,11 @@ func addBotHandler(c *gin.Context) { return } - jsonString, err := json.Marshal(b) - if err != nil { - c.Error(err) - return - } - - c.JSON(http.StatusCreated, jsonString) + c.JSON(http.StatusCreated, b) } func deleteBotHandler(c *gin.Context) { - var b Bot - - if err := c.ShouldBindJSON(&b); err != nil { - c.Error(err) - return - } - + b := c.MustGet("bot").(Bot) conn := getConnectionById(b.ConnectionID) if conn.MGURL == "" || conn.MGToken == "" { c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("not_found_account")}) @@ -173,13 +143,7 @@ func settingsHandler(c *gin.Context) { } func saveHandler(c *gin.Context) { - var conn Connection - - if err := c.BindJSON(&conn); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) - return - } - + conn := c.MustGet("connection").(Connection) _, err, code := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { c.AbortWithStatusJSON(code, gin.H{"error": err.Error()}) @@ -196,14 +160,7 @@ func saveHandler(c *gin.Context) { } func createHandler(c *gin.Context) { - var conn Connection - - if err := c.BindJSON(&conn); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) - return - } - - conn.ClientID = GenerateToken() + conn := c.MustGet("connection").(Connection) cl := getConnectionByURL(conn.APIURL) if cl.ID != 0 { @@ -217,37 +174,8 @@ func createHandler(c *gin.Context) { return } - integration := v5.IntegrationModule{ - Code: transport, - IntegrationCode: transport, - Active: true, - Name: "Telegram", - ClientID: conn.ClientID, - Logo: fmt.Sprintf( - "https://%s/static/telegram_logo.svg", - config.HTTPServer.Host, - ), - BaseURL: fmt.Sprintf( - "https://%s", - config.HTTPServer.Host, - ), - AccountURL: fmt.Sprintf( - "https://%s/settings/%s", - config.HTTPServer.Host, - conn.ClientID, - ), - Actions: map[string]string{"activity": "/actions/activity"}, - Integrations: &v5.Integrations{ - MgTransport: &v5.MgTransport{ - WebhookUrl: fmt.Sprintf( - "https://%s/webhook/", - config.HTTPServer.Host, - ), - }, - }, - } - - data, status, errr := client.IntegrationModuleEdit(integration) + conn.ClientID = GenerateToken() + data, status, errr := client.IntegrationModuleEdit(getIntegrationModule(conn.ClientID)) if errr.RuntimeErr != nil { c.Error(errr.RuntimeErr) return @@ -306,3 +234,35 @@ func activityHandler(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"success": true}) } + +func getIntegrationModule(clientId string) v5.IntegrationModule { + return v5.IntegrationModule{ + Code: transport, + IntegrationCode: transport, + Active: true, + Name: "Telegram", + ClientID: clientId, + Logo: fmt.Sprintf( + "https://%s/static/telegram_logo.svg", + config.HTTPServer.Host, + ), + BaseURL: fmt.Sprintf( + "https://%s", + config.HTTPServer.Host, + ), + AccountURL: fmt.Sprintf( + "https://%s/settings/%s", + config.HTTPServer.Host, + clientId, + ), + Actions: map[string]string{"activity": "/actions/activity"}, + Integrations: &v5.Integrations{ + MgTransport: &v5.MgTransport{ + WebhookUrl: fmt.Sprintf( + "https://%s/webhook/", + config.HTTPServer.Host, + ), + }, + }, + } +} diff --git a/routing_test.go b/routing_test.go index 555e6bf..cb3d279 100644 --- a/routing_test.go +++ b/routing_test.go @@ -10,15 +10,19 @@ import ( "strings" "testing" + "github.com/gin-gonic/gin" "github.com/h2non/gock" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +var router *gin.Engine + func init() { config = LoadConfig("config_test.yml") orm = NewDb(config) logger = newLogger() + router = setup() c := Connection{ ID: 1, @@ -41,9 +45,8 @@ func TestRouting_connectHandler(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.HandlerFunc(connectHandler) + router.ServeHTTP(rr, req) - handler.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) } @@ -83,8 +86,7 @@ func TestRouting_addBotHandler(t *testing.T) { t.Fatal(err) } rr := httptest.NewRecorder() - handler := http.HandlerFunc(addBotHandler) - handler.ServeHTTP(rr, req) + router.ServeHTTP(rr, req) require.Equal(t, http.StatusCreated, rr.Code, fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusCreated)) @@ -120,8 +122,7 @@ func TestRouting_deleteBotHandler(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.HandlerFunc(deleteBotHandler) - handler.ServeHTTP(rr, req) + router.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) @@ -134,8 +135,7 @@ func TestRouting_settingsHandler(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.HandlerFunc(makeHandler(settingsHandler)) - handler.ServeHTTP(rr, req) + router.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) @@ -160,8 +160,7 @@ func TestRouting_saveHandler(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.HandlerFunc(saveHandler) - handler.ServeHTTP(rr, req) + router.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) @@ -177,8 +176,7 @@ func TestRouting_activityHandler(t *testing.T) { } rr := httptest.NewRecorder() - handler := http.HandlerFunc(activityHandler) - handler.ServeHTTP(rr, req) + router.ServeHTTP(rr, req) assert.Equal(t, http.StatusOK, rr.Code, fmt.Sprintf("handler returned wrong status code: got %v want %v", rr.Code, http.StatusOK)) diff --git a/run.go b/run.go index 24455d2..8258d94 100644 --- a/run.go +++ b/run.go @@ -1,20 +1,17 @@ package main import ( + "net/http" "os" "os/signal" + "regexp" "syscall" - "io/ioutil" - "github.com/getsentry/raven-go" "github.com/gin-contrib/multitemplate" "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" _ "github.com/golang-migrate/migrate/database/postgres" _ "github.com/golang-migrate/migrate/source/file" - "gopkg.in/go-playground/validator.v9" - "gopkg.in/yaml.v2" ) func init() { @@ -57,12 +54,7 @@ func start() { func setup() *gin.Engine { loadTranslateFile() - - binding.Validator = new(defaultValidator) - - if v, ok := binding.Validator.Engine().(*validator.Validate); ok { - v.RegisterValidation("validatecrmurl", validateCrmURL) - } + setValidation() if config.Debug == false { gin.SetMode(gin.ReleaseMode) @@ -94,12 +86,12 @@ func setup() *gin.Engine { r.Use(ErrorHandler(errorHandlers...)) - r.GET("/", connectHandler) + r.GET("/", checkAccountForRequest(), connectHandler) r.GET("/settings/:uid", settingsHandler) - r.POST("/save/", saveHandler) - r.POST("/create/", createHandler) - r.POST("/add-bot/", addBotHandler) - r.POST("/delete-bot/", deleteBotHandler) + r.POST("/save/", checkConnectionForRequest(), saveHandler) + r.POST("/create/", checkConnectionForRequest(), createHandler) + r.POST("/add-bot/", checkBotForRequest(), addBotHandler) + r.POST("/delete-bot/", checkBotForRequest(), deleteBotHandler) r.POST("/actions/activity", activityHandler) r.POST("/telegram/:token", telegramWebhookHandler) r.POST("/webhook/", mgWebhookHandler) @@ -114,15 +106,45 @@ func createHTMLRender() multitemplate.Renderer { return r } -func loadTranslateFile() { - bundle.RegisterUnmarshalFunc("yml", yaml.Unmarshal) - files, err := ioutil.ReadDir("translate") - if err != nil { - logger.Error(err) - } - for _, f := range files { - if !f.IsDir() { - bundle.MustLoadMessageFile("translate/" + f.Name()) +func checkAccountForRequest() gin.HandlerFunc { + return func(c *gin.Context) { + rx := regexp.MustCompile(`/+$`) + ra := rx.ReplaceAllString(c.Query("account"), ``) + p := Connection{ + APIURL: ra, } + + c.Set("account", p) + } +} + +func checkBotForRequest() gin.HandlerFunc { + return func(c *gin.Context) { + var b Bot + + if err := c.ShouldBindJSON(&b); err != nil { + c.Error(err) + return + } + + if b.Token == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("no_bot_token")}) + return + } + + c.Set("bot", b) + } +} + +func checkConnectionForRequest() gin.HandlerFunc { + return func(c *gin.Context) { + var conn Connection + + if err := c.BindJSON(&conn); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) + return + } + + c.Set("connection", conn) } } diff --git a/stacktrace.go b/stacktrace.go new file mode 100644 index 0000000..f9efc14 --- /dev/null +++ b/stacktrace.go @@ -0,0 +1,89 @@ +package main + +import ( + "runtime" + + "github.com/getsentry/raven-go" + "github.com/pkg/errors" +) + +func NewRavenStackTrace(client *raven.Client, myerr error, skip int) *raven.Stacktrace { + st := getErrorStackTraceConverted(myerr, 3, client.IncludePaths()) + if st == nil { + st = raven.NewStacktrace(skip, 3, client.IncludePaths()) + } + return st +} + +func getErrorStackTraceConverted(err error, context int, appPackagePrefixes []string) *raven.Stacktrace { + st := getErrorCauseStackTrace(err) + if st == nil { + return nil + } + return convertStackTrace(st, context, appPackagePrefixes) +} + +func getErrorCauseStackTrace(err error) errors.StackTrace { + // This code is inspired by github.com/pkg/errors.Cause(). + var st errors.StackTrace + for err != nil { + s := getErrorStackTrace(err) + if s != nil { + st = s + } + err = getErrorCause(err) + } + return st +} + +func convertStackTrace(st errors.StackTrace, context int, appPackagePrefixes []string) *raven.Stacktrace { + // This code is borrowed from github.com/getsentry/raven-go.NewStacktrace(). + var frames []*raven.StacktraceFrame + for _, f := range st { + frame := convertFrame(f, context, appPackagePrefixes) + if frame != nil { + frames = append(frames, frame) + } + } + if len(frames) == 0 { + return nil + } + for i, j := 0, len(frames)-1; i < j; i, j = i+1, j-1 { + frames[i], frames[j] = frames[j], frames[i] + } + return &raven.Stacktrace{Frames: frames} +} + +func convertFrame(f errors.Frame, context int, appPackagePrefixes []string) *raven.StacktraceFrame { + // This code is borrowed from github.com/pkg/errors.Frame. + pc := uintptr(f) - 1 + fn := runtime.FuncForPC(pc) + var file string + var line int + if fn != nil { + file, line = fn.FileLine(pc) + } else { + file = "unknown" + } + return raven.NewStacktraceFrame(pc, file, line, context, appPackagePrefixes) +} + +func getErrorStackTrace(err error) errors.StackTrace { + ster, ok := err.(interface { + StackTrace() errors.StackTrace + }) + if !ok { + return nil + } + return ster.StackTrace() +} + +func getErrorCause(err error) error { + cer, ok := err.(interface { + Cause() error + }) + if !ok { + return nil + } + return cer.Cause() +} diff --git a/static/script.js b/static/script.js index 15a798f..2ebef7c 100644 --- a/static/script.js +++ b/static/script.js @@ -74,14 +74,14 @@ function send(url, data, callback) { } function getBotTemplate(data) { - let bot = JSON.parse(data); + // let bot = JSON.parse(data); tmpl = ` - ${bot.name} - ${bot.token} + ${data.name} + ${data.token} diff --git a/telegram.go b/telegram.go index 81915c8..61296e5 100644 --- a/telegram.go +++ b/telegram.go @@ -126,7 +126,7 @@ func telegramWebhookHandler(c *gin.Context) { data, st, err := client.Messages(snd) if err != nil { - logger.Error(token, err.Error(), st, data) + logger.Error(b.Token, err.Error(), st, data) c.Error(err) return } @@ -155,7 +155,7 @@ func telegramWebhookHandler(c *gin.Context) { data, st, err := client.UpdateMessages(snd) if err != nil { - logger.Error(token, err.Error(), st, data) + logger.Error(b.Token, err.Error(), st, data) c.Error(err) return } @@ -165,21 +165,19 @@ func telegramWebhookHandler(c *gin.Context) { } } - c.AbortWithStatus(http.StatusOK) + c.JSON(http.StatusOK, gin.H{}) } func mgWebhookHandler(c *gin.Context) { clientID := c.GetHeader("Clientid") if clientID == "" { - logger.Error("mgWebhookHandler clientID is empty") c.AbortWithStatus(http.StatusBadRequest) return } conn := getConnection(clientID) if !conn.Active { - logger.Error(conn.ClientID, "mgWebhookHandler: connection deactivated") - c.JSON(http.StatusBadRequest, gin.H{"error": "Connection deactivated"}) + c.AbortWithStatus(http.StatusBadRequest) return } @@ -198,8 +196,7 @@ func mgWebhookHandler(c *gin.Context) { b := getBot(conn.ID, msg.Data.ChannelID) if b.ID == 0 { - logger.Error(msg.Data.ChannelID, "mgWebhookHandler: missing or deactivated") - c.JSON(http.StatusBadRequest, gin.H{"error": "missing or deactivated"}) + c.AbortWithStatus(http.StatusBadRequest) return } @@ -221,7 +218,7 @@ func mgWebhookHandler(c *gin.Context) { m.ReplyToMessageID = qid } - msg, err := bot.Send(m) + msgSend, err := bot.Send(m) if err != nil { logger.Error(err) c.AbortWithStatus(http.StatusBadRequest) @@ -229,14 +226,14 @@ func mgWebhookHandler(c *gin.Context) { } if config.Debug { - logger.Debugf("mgWebhookHandler sent %v", msg) + logger.Debugf("mgWebhookHandler sent %v", msgSend) } - c.JSON(http.StatusOK, gin.H{"external_message_id": strconv.Itoa(msg.MessageID)}) + c.JSON(http.StatusOK, gin.H{"external_message_id": strconv.Itoa(msgSend.MessageID)}) } if msg.Type == "message_updated" { - msg, err := bot.Send(tgbotapi.NewEditMessageText(cid, uid, msg.Data.Content)) + msgSend, err := bot.Send(tgbotapi.NewEditMessageText(cid, uid, msg.Data.Content)) if err != nil { logger.Error(err) c.AbortWithStatus(http.StatusBadRequest) @@ -244,14 +241,14 @@ func mgWebhookHandler(c *gin.Context) { } if config.Debug { - logger.Debugf("mgWebhookHandler update %v", msg) + logger.Debugf("mgWebhookHandler update %v", msgSend) } - c.JSON(http.StatusOK, gin.H{"message": "Message updated"}) + c.AbortWithStatus(http.StatusOK) } if msg.Type == "message_deleted" { - msg, err := bot.Send(tgbotapi.NewDeleteMessage(cid, uid)) + msgSend, err := bot.Send(tgbotapi.NewDeleteMessage(cid, uid)) if err != nil { logger.Error(err) c.AbortWithStatus(http.StatusBadRequest) @@ -259,10 +256,10 @@ func mgWebhookHandler(c *gin.Context) { } if config.Debug { - logger.Debugf("mgWebhookHandler delete %v", msg) + logger.Debugf("mgWebhookHandler delete %v", msgSend) } - c.JSON(http.StatusOK, gin.H{"message": "Message deleted"}) + c.JSON(http.StatusOK, gin.H{}) } } diff --git a/utils.go b/utils.go index 0f314d7..c37272f 100644 --- a/utils.go +++ b/utils.go @@ -11,32 +11,8 @@ import ( "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/retailcrm/api-client-go/v5" - "golang.org/x/text/language" ) -func setLocale(al string) { - tag, _ := language.MatchStrings(matcher, al) - localizer = i18n.NewLocalizer(bundle, tag.String()) -} - -func getLocale() map[string]string { - return map[string]string{ - "ButtonSave": getLocalizedMessage("button_save"), - "ApiKey": getLocalizedMessage("api_key"), - "TabSettings": getLocalizedMessage("tab_settings"), - "TabBots": getLocalizedMessage("tab_bots"), - "TableName": getLocalizedMessage("table_name"), - "TableToken": getLocalizedMessage("table_token"), - "AddBot": getLocalizedMessage("add_bot"), - "TableDelete": getLocalizedMessage("table_delete"), - "Title": getLocalizedMessage("title"), - } -} - -func getLocalizedMessage(messageID string) string { - return localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: messageID}) -} - // GenerateToken function func GenerateToken() string { c := atomic.AddUint32(&tokenCounter, 1) diff --git a/validator.go b/validator.go index f235ad1..219749b 100644 --- a/validator.go +++ b/validator.go @@ -50,6 +50,14 @@ func kindOfData(data interface{}) reflect.Kind { return valueType } +func setValidation() { + binding.Validator = new(defaultValidator) + + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + v.RegisterValidation("validatecrmurl", validateCrmURL) + } +} + func validateCrmURL(field validator.FieldLevel) bool { regCommandName := regexp.MustCompile(`https://?[\da-z.-]+\.(retailcrm\.(ru|pro)|ecomlogic\.com)`)