From 747b73ff87fbd5b20d38c5a7241261f4c15b6915 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Tue, 14 Aug 2018 17:49:01 +0300 Subject: [PATCH 1/9] add gin, errors handler and minor fixes --- config.go | 1 + config.yml.dist | 4 + config_test.yml.dist | 4 + database.go | 2 - errors.go | 211 +++++++++++++++++++ main.go | 20 +- models.go | 4 +- routing.go | 470 +++++++++---------------------------------- run.go | 88 +++++++- static/script.js | 23 +-- telegram.go | 154 ++++---------- token.go | 17 -- utils.go | 93 +++++++++ validator.go | 57 ++++++ 14 files changed, 610 insertions(+), 538 deletions(-) create mode 100644 errors.go delete mode 100644 token.go create mode 100644 utils.go create mode 100644 validator.go diff --git a/config.go b/config.go index 917e0dd..a9a67c0 100644 --- a/config.go +++ b/config.go @@ -17,6 +17,7 @@ type TransportConfig struct { Debug bool `yaml:"debug"` UpdateInterval int `yaml:"update_interval"` ConfigAWS ConfigAWS `yaml:"config_aws"` + Credentials []string `yaml:"credentials"` } // ConfigAWS struct diff --git a/config.yml.dist b/config.yml.dist index 04b3e09..67265f4 100644 --- a/config.yml.dist +++ b/config.yml.dist @@ -20,3 +20,7 @@ config_aws: bucket: ~ folder_name: ~ content_type: image/jpeg + +credentials: + - "/api/integration-modules/{code}" + - "/api/integration-modules/{code}/edit" diff --git a/config_test.yml.dist b/config_test.yml.dist index 208cc8f..b9870d5 100644 --- a/config_test.yml.dist +++ b/config_test.yml.dist @@ -20,3 +20,7 @@ config_aws: bucket: ~ folder_name: ~ content_type: image/jpeg + +credentials: + - "/api/integration-modules/{code}" + - "/api/integration-modules/{code}/edit" diff --git a/database.go b/database.go index db2cc8c..1080fd8 100644 --- a/database.go +++ b/database.go @@ -3,7 +3,6 @@ package main import ( "time" - "github.com/getsentry/raven-go" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" ) @@ -17,7 +16,6 @@ type Orm struct { func NewDb(config *TransportConfig) *Orm { db, err := gorm.Open("postgres", config.Database.Connection) if err != nil { - raven.CaptureErrorAndWait(err, nil) panic(err) } diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..a221b5d --- /dev/null +++ b/errors.go @@ -0,0 +1,211 @@ +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/main.go b/main.go index 48e6de6..a4572d2 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,9 @@ 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 @@ -16,11 +18,19 @@ type Options struct { const transport = "mg-telegram" var ( - config *TransportConfig - orm *Orm - logger *logging.Logger - options Options - parser = flags.NewParser(&options, flags.Default) + config *TransportConfig + orm *Orm + logger *logging.Logger + 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/models.go b/models.go index 90497be..7a118f0 100644 --- a/models.go +++ b/models.go @@ -6,8 +6,8 @@ import "time" type Connection struct { ID int `gorm:"primary_key"` ClientID string `gorm:"client_id type:varchar(70);not null;unique" json:"clientId,omitempty"` - APIKEY string `gorm:"api_key type:varchar(100);not null" json:"api_key,omitempty"` - APIURL string `gorm:"api_url type:varchar(255);not null" json:"api_url,omitempty"` + APIKEY string `gorm:"api_key type:varchar(100);not null" json:"api_key,omitempty" binding:"required"` + APIURL string `gorm:"api_url type:varchar(255);not null" json:"api_url,omitempty" binding:"required,validatecrmurl"` MGURL string `gorm:"mg_url type:varchar(255);not null;" json:"mg_url,omitempty"` MGToken string `gorm:"mg_token type:varchar(100);not null;unique" json:"mg_token,omitempty"` CreatedAt time.Time diff --git a/routing.go b/routing.go index 6df7272..a588288 100644 --- a/routing.go +++ b/routing.go @@ -2,163 +2,62 @@ package main import ( "encoding/json" - "errors" "fmt" - "html/template" - "io/ioutil" "net/http" "regexp" - "strings" - "github.com/getsentry/raven-go" + "github.com/gin-gonic/gin" "github.com/go-telegram-bot-api/telegram-bot-api" - "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/retailcrm/api-client-go/v5" "github.com/retailcrm/mg-transport-api-client-go/v1" - "golang.org/x/text/language" - "gopkg.in/yaml.v2" ) -var ( - validPath = regexp.MustCompile(`^/(save|settings|telegram)/([a-zA-Z0-9-:_+]+)$`) - localizer *i18n.Localizer - bundle = &i18n.Bundle{DefaultLanguage: language.English} - matcher = language.NewMatcher([]language.Tag{ - language.English, - language.Russian, - language.Spanish, - }) -) - -func init() { - 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 setLocale(al string) { - tag, _ := language.MatchStrings(matcher, al) - localizer = i18n.NewLocalizer(bundle, tag.String()) -} - -// Response struct -type Response struct { - Success bool `json:"success"` - Error string `json:"error"` -} - -func setWrapperRoutes() { - http.HandleFunc("/", connectHandler) - http.HandleFunc("/settings/", makeHandler(settingsHandler)) - http.HandleFunc("/save/", saveHandler) - http.HandleFunc("/create/", createHandler) - http.HandleFunc("/actions/activity", activityHandler) - http.HandleFunc("/add-bot/", addBotHandler) - http.HandleFunc("/delete-bot/", deleteBotHandler) -} - -func renderTemplate(w http.ResponseWriter, tmpl string, c interface{}) { - tm, err := template.ParseFiles("templates/layout.html", "templates/"+tmpl+".html") - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - err = tm.Execute(w, &c) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} - -func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - m := validPath.FindStringSubmatch(r.URL.Path) - if m == nil { - http.NotFound(w, r) - return - } - raven.CapturePanic(func() { - fn(w, r, m[2]) - }, nil) - } -} - -func connectHandler(w http.ResponseWriter, r *http.Request) { - setLocale(r.Header.Get("Accept-Language")) - - account := r.URL.Query() +func connectHandler(c *gin.Context) { rx := regexp.MustCompile(`/+$`) - ra := rx.ReplaceAllString(account.Get("account"), ``) + ra := rx.ReplaceAllString(c.Query("account"), ``) p := Connection{ APIURL: ra, } res := struct { Conn *Connection - Locale map[string]interface{} + Locale map[string]string }{ &p, - map[string]interface{}{ - "ButtonSave": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "button_save"}), - "ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}), - "Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}), - }, + getLocale(), } - renderTemplate(w, "home", &res) + + c.HTML(http.StatusOK, "home", &res) } -func addBotHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - setLocale(r.Header.Get("Accept-Language")) - body, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(err.Error()) - return - } - +func addBotHandler(c *gin.Context) { var b Bot - err = json.Unmarshal(body, &b) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(err.Error()) + if err := c.ShouldBindJSON(&b); err != nil { + c.Error(err) return } if b.Token == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "no_bot_token"}), http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("no_bot_token")}) return } cl, err := getBotByToken(b.Token) if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(err.Error()) + c.Error(err) return } if cl.ID != 0 { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "bot_already_created"}), http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("bot_already_created")}) return } bot, err := tgbotapi.NewBotAPI(b.Token) if err != nil { logger.Error(b.Token, err.Error()) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_token"}), http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_token")}) return } @@ -167,17 +66,17 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { wr, err := bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) if err != nil { logger.Error(b.Token, err.Error()) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_webhook"}), http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) return } if !wr.Ok { logger.Error(b.Token, wr.ErrorCode, wr.Result) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_webhook"}), http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) return } - b.Name = GetBotName(bot) + b.Name = bot.Self.FirstName ch := v1.Channel{ Type: "telegram", @@ -189,92 +88,72 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { }, } - c := getConnectionById(b.ConnectionID) + conn := getConnectionById(b.ConnectionID) - var client = v1.New(c.MGURL, c.MGToken) + var client = v1.New(conn.MGURL, conn.MGToken) data, status, err := client.ActivateTransportChannel(ch) if status != http.StatusCreated { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activating_channel"}), http.StatusBadRequest) - logger.Error(c.APIURL, status, err.Error(), data) + logger.Error(conn.APIURL, status, err.Error(), data) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activating_channel")}) return } b.Channel = data.ChannelID - err = c.createBot(b) + err = conn.createBot(b) if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) + c.Error(err) return } jsonString, err := json.Marshal(b) if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) + c.Error(err) return } - w.WriteHeader(http.StatusCreated) - w.Write(jsonString) + c.JSON(http.StatusCreated, jsonString) } -func deleteBotHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - setLocale(r.Header.Get("Accept-Language")) - body, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - logger.Error(err.Error()) - return - } - +func deleteBotHandler(c *gin.Context) { var b Bot - err = json.Unmarshal(body, &b) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - logger.Error(err.Error()) + if err := c.ShouldBindJSON(&b); err != nil { + c.Error(err) return } - c := getConnectionById(b.ConnectionID) - if c.MGURL == "" || c.MGToken == "" { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) + conn := getConnectionById(b.ConnectionID) + if conn.MGURL == "" || conn.MGToken == "" { + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("not_found_account")}) logger.Error(b.ID, "MGURL or MGToken is empty") return } - var client = v1.New(c.MGURL, c.MGToken) + var client = v1.New(conn.MGURL, conn.MGToken) data, status, err := client.DeactivateTransportChannel(getBotChannelByToken(b.Token)) if status > http.StatusOK { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_deactivating_channel"}), http.StatusBadRequest) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_deactivating_channel")}) logger.Error(b.ID, status, err.Error(), data) return } err = b.deleteBot() if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - logger.Error(b.ID, err.Error()) + c.Error(err) return } - w.WriteHeader(http.StatusOK) + c.JSON(http.StatusOK, gin.H{}) } -func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { - setLocale(r.Header.Get("Accept-Language")) +func settingsHandler(c *gin.Context) { + uid := c.Param("uid") p := getConnection(uid) if p.ID == 0 { - http.Redirect(w, r, "/", http.StatusFound) + c.Redirect(http.StatusFound, "/") return } @@ -283,111 +162,58 @@ func settingsHandler(w http.ResponseWriter, r *http.Request, uid string) { res := struct { Conn *Connection Bots Bots - Locale map[string]interface{} + Locale map[string]string }{ p, bots, - map[string]interface{}{ - "ButtonSave": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "button_save"}), - "ApiKey": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "api_key"}), - "TabSettings": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "tab_settings"}), - "TabBots": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "tab_bots"}), - "TableName": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_name"}), - "TableToken": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_token"}), - "AddBot": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "add_bot"}), - "TableDelete": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "table_delete"}), - "Title": localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "title"}), - }, + getLocale(), } - renderTemplate(w, "form", res) + c.HTML(http.StatusOK, "form", &res) } -func saveHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - setLocale(r.Header.Get("Accept-Language")) +func saveHandler(c *gin.Context) { + var conn Connection - body, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + if err := c.BindJSON(&conn); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) return } - var c Connection - - err = json.Unmarshal(body, &c) + _, err, code := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + c.AbortWithStatusJSON(code, gin.H{"error": err.Error()}) return } - err = validateCrmSettings(c) + err = conn.saveConnection() if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - logger.Error(c.APIURL, err.Error()) + c.Error(err) return } - _, err, code := getAPIClient(c.APIURL, c.APIKEY) - if err != nil { - http.Error(w, err.Error(), code) - return - } - - err = c.saveConnection() - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) - return - } - - w.WriteHeader(http.StatusOK) - w.Write([]byte(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "successful"}))) + c.JSON(http.StatusOK, gin.H{"message": getLocalizedMessage("successful")}) } -func createHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - setLocale(r.Header.Get("Accept-Language")) +func createHandler(c *gin.Context) { + var conn Connection - body, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - logger.Error(err.Error()) + if err := c.BindJSON(&conn); err != nil { + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) return } - var c Connection + conn.ClientID = GenerateToken() - err = json.Unmarshal(body, &c) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) - return - } - - c.ClientID = GenerateToken() - - err = validateCrmSettings(c) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - logger.Error(c.APIURL, err.Error()) - return - } - - cl := getConnectionByURL(c.APIURL) + cl := getConnectionByURL(conn.APIURL) if cl.ID != 0 { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "connection_already_created"}), http.StatusBadRequest) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")}) return } - client, err, code := getAPIClient(c.APIURL, c.APIKEY) + client, err, _ := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { - http.Error(w, err.Error(), code) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } @@ -396,7 +222,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { IntegrationCode: transport, Active: true, Name: "Telegram", - ClientID: c.ClientID, + ClientID: conn.ClientID, Logo: fmt.Sprintf( "https://%s/static/telegram_logo.svg", config.HTTPServer.Host, @@ -408,7 +234,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { AccountURL: fmt.Sprintf( "https://%s/settings/%s", config.HTTPServer.Host, - c.ClientID, + conn.ClientID, ), Actions: map[string]string{"activity": "/actions/activity"}, Integrations: &v5.Integrations{ @@ -423,172 +249,60 @@ func createHandler(w http.ResponseWriter, r *http.Request) { data, status, errr := client.IntegrationModuleEdit(integration) if errr.RuntimeErr != nil { - raven.CaptureErrorAndWait(errr.RuntimeErr, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_integration"}), http.StatusInternalServerError) - logger.Error(c.APIURL, status, errr.RuntimeErr, data) + c.Error(errr.RuntimeErr) return } if status >= http.StatusBadRequest { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_activity_mg"}), http.StatusBadRequest) - logger.Error(c.APIURL, status, errr.ApiErr, data) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activity_mg")}) + logger.Error(conn.APIURL, status, errr.ApiErr, data) return } - c.MGURL = data.Info["baseUrl"] - c.MGToken = data.Info["token"] - c.Active = true + conn.MGURL = data.Info["baseUrl"] + conn.MGToken = data.Info["token"] + conn.Active = true - err = c.createConnection() + err = conn.createConnection() if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) + c.Error(err) return } - res := struct { - Url string - Message string - }{ - Url: "/settings/" + c.ClientID, - Message: localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "successful"}), - } - - jss, err := json.Marshal(res) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_connection"}), http.StatusInternalServerError) - logger.Error(c.APIURL, err.Error()) - return - } - - w.WriteHeader(http.StatusFound) - w.Write(jss) + c.JSON( + http.StatusCreated, + gin.H{ + "url": "/settings/" + conn.ClientID, + "message": getLocalizedMessage("successful"), + }, + ) } -func activityHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - setLocale(r.Header.Get("Accept-Language")) - w.Header().Set("Content-Type", "application/json") - res := Response{Success: false} +func activityHandler(c *gin.Context) { + var rec v5.ActivityCallback - if r.Method != http.MethodPost { - res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "set_method"}) - jsonString, err := json.Marshal(res) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - w.Write(jsonString) + if err := c.ShouldBindJSON(&rec); err != nil { + c.Error(err) return } - r.ParseForm() - var rec v5.Activity - - err := json.Unmarshal([]byte(r.FormValue("activity")), &rec) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "wrong_data"}) - jsonString, err := json.Marshal(res) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - w.Write(jsonString) + conn := getConnection(rec.ClientId) + if conn.ID == 0 { + c.AbortWithStatusJSON(http.StatusBadRequest, + gin.H{ + "success": false, + "error": "Wrong data", + }, + ) return } - c := getConnection(r.FormValue("clientId")) - c.Active = rec.Active && !rec.Freeze + conn.Active = rec.Activity.Active && !rec.Activity.Freeze - if err := c.setConnectionActivity(); err != nil { - raven.CaptureErrorAndWait(err, nil) - res.Error = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "wrong_data"}) - jsonString, err := json.Marshal(res) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - w.Write(jsonString) + if err := conn.setConnectionActivity(); err != nil { + c.Error(err) return } - res.Success = true - jsonString, err := json.Marshal(res) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - - w.Write(jsonString) -} - -func validateCrmSettings(c Connection) error { - if c.APIURL == "" || c.APIKEY == "" { - return errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "missing_url_key"})) - } - - if res, _ := regexp.MatchString(`https://?[\da-z\.-]+\.(retailcrm\.(ru|pro)|ecomlogic\.com)`, c.APIURL); !res { - return errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url"})) - } - - return nil -} - -func getAPIClient(url, key string) (*v5.Client, error, int) { - client := v5.New(url, key) - - cr, status, errr := client.APICredentials() - if errr.RuntimeErr != nil { - raven.CaptureErrorAndWait(errr.RuntimeErr, nil) - logger.Error(url, status, errr.RuntimeErr, cr) - return nil, errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"})), http.StatusInternalServerError - - } - - if !cr.Success { - logger.Error(url, status, errr.ApiErr, cr) - return nil, errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"})), http.StatusBadRequest - } - - if res := checkCredentials(cr.Credentials); len(res) != 0 { - logger.Error(url, status, res) - return nil, - errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{ - MessageID: "missing_credentials", - TemplateData: map[string]interface{}{ - "Credentials": strings.Join(res, ", "), - }, - })), - http.StatusBadRequest - } - - return client, nil, 0 -} - -func checkCredentials(credential []string) []string { - rc := []string{ - "/api/integration-modules/{code}", - "/api/integration-modules/{code}/edit", - } - - for kn, vn := range rc { - for _, vc := range credential { - if vn == vc { - if len(rc) == 1 { - rc = rc[:0] - break - } - rc = append(rc[:kn], rc[kn+1:]...) - } - } - } - - return rc + c.JSON(http.StatusOK, gin.H{"success": true}) } diff --git a/run.go b/run.go index c553730..24455d2 100644 --- a/run.go +++ b/run.go @@ -1,15 +1,20 @@ package main import ( - "net/http" - "os" "os/signal" "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() { @@ -28,7 +33,6 @@ func (x *RunCommand) Execute(args []string) error { config = LoadConfig(options.Config) orm = NewDb(config) logger = newLogger() - raven.SetDSN(config.SentryDSN) go start() @@ -47,8 +51,78 @@ func (x *RunCommand) Execute(args []string) error { } func start() { - setWrapperRoutes() - setTransportRoutes() - http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) - http.ListenAndServe(config.HTTPServer.Listen, nil) + routing := setup() + routing.Run(config.HTTPServer.Listen) +} + +func setup() *gin.Engine { + loadTranslateFile() + + binding.Validator = new(defaultValidator) + + if v, ok := binding.Validator.Engine().(*validator.Validate); ok { + v.RegisterValidation("validatecrmurl", validateCrmURL) + } + + if config.Debug == false { + gin.SetMode(gin.ReleaseMode) + } + + r := gin.Default() + + if config.Debug { + r.Use(gin.Logger()) + } + + r.Static("/static", "./static") + r.HTMLRender = createHTMLRender() + + r.Use(func(c *gin.Context) { + setLocale(c.GetHeader("Accept-Language")) + }) + + errorHandlers := []ErrorHandlerFunc{ + PanicLogger(), + ErrorLogger(), + ErrorResponseHandler(), + } + sentry, _ := raven.New(config.SentryDSN) + + if sentry != nil { + errorHandlers = append(errorHandlers, ErrorCaptureHandler(sentry, false)) + } + + r.Use(ErrorHandler(errorHandlers...)) + + r.GET("/", 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("/actions/activity", activityHandler) + r.POST("/telegram/:token", telegramWebhookHandler) + r.POST("/webhook/", mgWebhookHandler) + + return r +} + +func createHTMLRender() multitemplate.Renderer { + r := multitemplate.NewRenderer() + r.AddFromFiles("home", "templates/layout.html", "templates/home.html") + r.AddFromFiles("form", "templates/layout.html", "templates/form.html") + 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()) + } + } } diff --git a/static/script.js b/static/script.js index 7519274..15a798f 100644 --- a/static/script.js +++ b/static/script.js @@ -3,8 +3,12 @@ $('#save-crm').on("submit", function(e) { send( $(this).attr('action'), formDataToObj($(this).serializeArray()), - function () { - return 0; + function (data) { + sessionStorage.setItem("createdMsg", data.message); + + document.location.replace( + location.protocol.concat("//").concat(window.location.host) + data.url + ); } ) }); @@ -15,7 +19,7 @@ $("#save").on("submit", function(e) { $(this).attr('action'), formDataToObj($(this).serializeArray()), function (data) { - M.toast({html: data}); + M.toast({html: data.message}); } ) }); @@ -62,17 +66,8 @@ function send(url, data, callback) { type: "POST", success: callback, error: function (res){ - if (res.status < 400) { - if (res.responseText) { - let resObj = JSON.parse(res.responseText); - sessionStorage.setItem("createdMsg", resObj.Message); - - document.location.replace( - location.protocol.concat("//").concat(window.location.host) + resObj.Url - ); - } - } else { - M.toast({html: res.responseText}) + if (res.status >= 400) { + M.toast({html: res.responseJSON.error}) } } }); diff --git a/telegram.go b/telegram.go index b0f192f..81915c8 100644 --- a/telegram.go +++ b/telegram.go @@ -1,10 +1,8 @@ package main import ( - "encoding/json" "errors" "fmt" - "io/ioutil" "net/http" "strconv" "time" @@ -13,73 +11,46 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/s3/s3manager" - "github.com/getsentry/raven-go" + "github.com/gin-gonic/gin" "github.com/go-telegram-bot-api/telegram-bot-api" - "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/retailcrm/mg-transport-api-client-go/v1" ) -func setTransportRoutes() { - http.HandleFunc("/telegram/", makeHandler(telegramWebhookHandler)) - http.HandleFunc("/webhook/", mgWebhookHandler) -} - -// GetBotName function -func GetBotName(bot *tgbotapi.BotAPI) string { - return bot.Self.FirstName -} - -func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string) { - defer r.Body.Close() +func telegramWebhookHandler(c *gin.Context) { + token := c.Param("token") b, err := getBotByToken(token) if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(token, err.Error()) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } if b.ID == 0 { - logger.Error(token, "telegramWebhookHandler: missing or deactivated") - w.WriteHeader(http.StatusOK) + c.AbortWithStatus(http.StatusOK) return } - c := getConnectionById(b.ConnectionID) - if !c.Active { - logger.Error(c.ClientID, "telegramWebhookHandler: connection deactivated") - w.WriteHeader(http.StatusOK) + conn := getConnectionById(b.ConnectionID) + if !conn.Active { + c.AbortWithStatus(http.StatusOK) return } var update tgbotapi.Update - - bytes, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(token, err) - w.WriteHeader(http.StatusInternalServerError) + if err := c.ShouldBindJSON(&update); err != nil { + c.Error(err) return } if config.Debug { - logger.Debugf("telegramWebhookHandler: %v", string(bytes)) + logger.Debugf("mgWebhookHandler request: %v", update) } - err = json.Unmarshal(bytes, &update) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(token, err) - w.WriteHeader(http.StatusInternalServerError) - return - } - - var client = v1.New(c.MGURL, c.MGToken) + var client = v1.New(conn.MGURL, conn.MGToken) if update.Message != nil { if update.Message.Text == "" { setLocale(update.Message.From.LanguageCode) - update.Message.Text = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: getMessageID(update.Message)}) + update.Message.Text = getLocalizedMessage(getMessageID(update.Message)) } nickname := update.Message.From.UserName @@ -92,18 +63,14 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string if user.Expired(config.UpdateInterval) || user.ID == 0 { fileID, fileURL, err := GetFileIDAndURL(b.Token, update.Message.From.ID) if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } if fileID != user.UserPhotoID && fileURL != "" { picURL, err := UploadUserAvatar(fileURL) if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } @@ -117,9 +84,7 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string err = user.save() if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } } @@ -161,9 +126,8 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string data, st, err := client.Messages(snd) if err != nil { - raven.CaptureErrorAndWait(err, nil) logger.Error(token, err.Error(), st, data) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } @@ -175,7 +139,7 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string if update.EditedMessage != nil { if update.EditedMessage.Text == "" { setLocale(update.EditedMessage.From.LanguageCode) - update.EditedMessage.Text = localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: getMessageID(update.Message)}) + update.EditedMessage.Text = getLocalizedMessage(getMessageID(update.Message)) } snd := v1.UpdateData{ @@ -191,9 +155,8 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string data, st, err := client.UpdateMessages(snd) if err != nil { - raven.CaptureErrorAndWait(err, nil) logger.Error(token, err.Error(), st, data) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } @@ -202,63 +165,47 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string } } - w.WriteHeader(http.StatusOK) + c.AbortWithStatus(http.StatusOK) } -func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - clientID := r.Header.Get("Clientid") +func mgWebhookHandler(c *gin.Context) { + clientID := c.GetHeader("Clientid") if clientID == "" { logger.Error("mgWebhookHandler clientID is empty") - w.WriteHeader(http.StatusBadRequest) + c.AbortWithStatus(http.StatusBadRequest) return } - c := getConnection(clientID) - if !c.Active { - logger.Error(c.ClientID, "mgWebhookHandler: connection deactivated") - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("Connection deactivated")) + conn := getConnection(clientID) + if !conn.Active { + logger.Error(conn.ClientID, "mgWebhookHandler: connection deactivated") + c.JSON(http.StatusBadRequest, gin.H{"error": "Connection deactivated"}) return } - bytes, err := ioutil.ReadAll(r.Body) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) + var msg v1.WebhookRequest + if err := c.ShouldBindJSON(&msg); err != nil { + c.Error(err) return } if config.Debug { - logger.Debugf("mgWebhookHandler request: %v", string(bytes)) - } - - var msg v1.WebhookRequest - err = json.Unmarshal(bytes, &msg) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) - return + logger.Debugf("mgWebhookHandler request: %v", msg) } uid, _ := strconv.Atoi(msg.Data.ExternalMessageID) cid, _ := strconv.ParseInt(msg.Data.ExternalChatID, 10, 64) - b := getBot(c.ID, msg.Data.ChannelID) + b := getBot(conn.ID, msg.Data.ChannelID) if b.ID == 0 { logger.Error(msg.Data.ChannelID, "mgWebhookHandler: missing or deactivated") - w.WriteHeader(http.StatusBadRequest) - w.Write([]byte("missing or deactivated")) + c.JSON(http.StatusBadRequest, gin.H{"error": "missing or deactivated"}) return } bot, err := tgbotapi.NewBotAPI(b.Token) if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } @@ -268,9 +215,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { if msg.Data.QuoteExternalID != "" { qid, err := strconv.Atoi(msg.Data.QuoteExternalID) if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - w.WriteHeader(http.StatusInternalServerError) + c.Error(err) return } m.ReplyToMessageID = qid @@ -278,9 +223,8 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { msg, err := bot.Send(m) if err != nil { - raven.CaptureErrorAndWait(err, nil) logger.Error(err) - w.WriteHeader(http.StatusBadRequest) + c.AbortWithStatus(http.StatusBadRequest) return } @@ -288,27 +232,14 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { logger.Debugf("mgWebhookHandler sent %v", msg) } - rsp, err := json.Marshal(map[string]string{"external_message_id": strconv.Itoa(msg.MessageID)}) - if err != nil { - raven.CaptureErrorAndWait(err, nil) - logger.Error(err) - return - } - - if config.Debug { - logger.Debugf("mgWebhookHandler sent response %v", string(rsp)) - } - - w.WriteHeader(http.StatusOK) - w.Write(rsp) + c.JSON(http.StatusOK, gin.H{"external_message_id": strconv.Itoa(msg.MessageID)}) } if msg.Type == "message_updated" { msg, err := bot.Send(tgbotapi.NewEditMessageText(cid, uid, msg.Data.Content)) if err != nil { - raven.CaptureErrorAndWait(err, nil) logger.Error(err) - w.WriteHeader(http.StatusBadRequest) + c.AbortWithStatus(http.StatusBadRequest) return } @@ -316,16 +247,14 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { logger.Debugf("mgWebhookHandler update %v", msg) } - w.WriteHeader(http.StatusOK) - w.Write([]byte("Message updated")) + c.JSON(http.StatusOK, gin.H{"message": "Message updated"}) } if msg.Type == "message_deleted" { msg, err := bot.Send(tgbotapi.NewDeleteMessage(cid, uid)) if err != nil { - raven.CaptureErrorAndWait(err, nil) logger.Error(err) - w.WriteHeader(http.StatusBadRequest) + c.AbortWithStatus(http.StatusBadRequest) return } @@ -333,8 +262,7 @@ func mgWebhookHandler(w http.ResponseWriter, r *http.Request) { logger.Debugf("mgWebhookHandler delete %v", msg) } - w.WriteHeader(http.StatusOK) - w.Write([]byte("Message deleted")) + c.JSON(http.StatusOK, gin.H{"message": "Message deleted"}) } } diff --git a/token.go b/token.go deleted file mode 100644 index 748a391..0000000 --- a/token.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "crypto/sha256" - "fmt" - "sync/atomic" - "time" -) - -var tokenCounter uint32 - -// GenerateToken function -func GenerateToken() string { - c := atomic.AddUint32(&tokenCounter, 1) - - return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%d%d", time.Now().UnixNano(), c)))) -} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..0f314d7 --- /dev/null +++ b/utils.go @@ -0,0 +1,93 @@ +package main + +import ( + "crypto/sha256" + "errors" + "fmt" + "net/http" + "strings" + "sync/atomic" + "time" + + "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) + + return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%d%d", time.Now().UnixNano(), c)))) +} + +func getAPIClient(url, key string) (*v5.Client, error, int) { + client := v5.New(url, key) + + cr, status, e := client.APICredentials() + if e.RuntimeErr != nil { + logger.Error(url, status, e.RuntimeErr, cr) + return nil, errors.New(getLocalizedMessage("not_found_account")), http.StatusInternalServerError + + } + + if !cr.Success { + logger.Error(url, status, e.ApiErr, cr) + return nil, errors.New(getLocalizedMessage("incorrect_url_key")), http.StatusBadRequest + } + + if res := checkCredentials(cr.Credentials); len(res) != 0 { + logger.Error(url, status, res) + return nil, + errors.New(localizer.MustLocalize(&i18n.LocalizeConfig{ + MessageID: "missing_credentials", + TemplateData: map[string]interface{}{ + "Credentials": strings.Join(res, ", "), + }, + })), + http.StatusBadRequest + } + + return client, nil, 0 +} + +func checkCredentials(credential []string) []string { + rc := config.Credentials + + for _, vc := range credential { + for kn, vn := range rc { + if vn == vc { + if len(rc) == 1 { + rc = rc[:0] + break + } + rc = append(rc[:kn], rc[kn+1:]...) + } + } + } + + return rc +} diff --git a/validator.go b/validator.go new file mode 100644 index 0000000..f235ad1 --- /dev/null +++ b/validator.go @@ -0,0 +1,57 @@ +package main + +import ( + "reflect" + "regexp" + "sync" + + "github.com/gin-gonic/gin/binding" + "gopkg.in/go-playground/validator.v9" +) + +type defaultValidator struct { + once sync.Once + validate *validator.Validate +} + +var _ binding.StructValidator = &defaultValidator{} + +func (v *defaultValidator) ValidateStruct(obj interface{}) error { + + if kindOfData(obj) == reflect.Struct { + v.lazyinit() + if err := v.validate.Struct(obj); err != nil { + return error(err) + } + } + + return nil +} + +func (v *defaultValidator) Engine() interface{} { + v.lazyinit() + return v.validate +} + +func (v *defaultValidator) lazyinit() { + v.once.Do(func() { + v.validate = validator.New() + v.validate.SetTagName("binding") + }) +} + +func kindOfData(data interface{}) reflect.Kind { + value := reflect.ValueOf(data) + valueType := value.Kind() + + if valueType == reflect.Ptr { + valueType = value.Elem().Kind() + } + return valueType +} + +func validateCrmURL(field validator.FieldLevel) bool { + regCommandName := regexp.MustCompile(`https://?[\da-z.-]+\.(retailcrm\.(ru|pro)|ecomlogic\.com)`) + + return regCommandName.Match([]byte(field.Field().Interface().(string))) +} From e6855456b0861d922eb4983b2795cdfa5ecc4b0d Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Wed, 15 Aug 2018 17:56:36 +0300 Subject: [PATCH 2/9] add go.mod, improve test, minor fixes --- .gitignore | 3 +- error_handler.go | 113 +++++++++++++++++++++++++ errors.go | 211 ----------------------------------------------- go.mod | 63 ++++++++++++++ go.sum | 127 ++++++++++++++++++++++++++++ locale.go | 55 ++++++++++++ main.go | 9 -- routing.go | 126 ++++++++++------------------ routing_test.go | 22 +++-- run.go | 72 ++++++++++------ stacktrace.go | 89 ++++++++++++++++++++ static/script.js | 8 +- telegram.go | 31 ++++--- utils.go | 24 ------ validator.go | 8 ++ 15 files changed, 575 insertions(+), 386 deletions(-) create mode 100644 error_handler.go delete mode 100644 errors.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 locale.go create mode 100644 stacktrace.go 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)`) From a0a2b783ca6fc41b93f38926b8c613285848ac2b Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Thu, 16 Aug 2018 18:13:01 +0300 Subject: [PATCH 3/9] improve config, templates, routing --- .gitignore | 1 + config.go | 7 +++ config.yml.dist | 5 ++ config_test.yml.dist | 5 ++ error_handler.go | 6 +- go.sum | 127 ------------------------------------------- log.go | 2 +- main.go | 2 - routing.go | 31 ++++++----- run.go | 9 ++- templates/form.html | 2 +- utils.go | 5 +- 12 files changed, 48 insertions(+), 154 deletions(-) delete mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 593ea84..34107d5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ config_test.yml /bin/* *.xml /vendor +go.sum diff --git a/config.go b/config.go index a9a67c0..220901a 100644 --- a/config.go +++ b/config.go @@ -18,6 +18,13 @@ type TransportConfig struct { UpdateInterval int `yaml:"update_interval"` ConfigAWS ConfigAWS `yaml:"config_aws"` Credentials []string `yaml:"credentials"` + TransportInfo TransportInfo `yaml:"transport_info"` +} + +type TransportInfo struct { + Name string `yaml:"name"` + Code string `yaml:"code"` + LogoPath string `yaml:"logo_path"` } // ConfigAWS struct diff --git a/config.yml.dist b/config.yml.dist index 67265f4..17c5134 100644 --- a/config.yml.dist +++ b/config.yml.dist @@ -5,6 +5,11 @@ http_server: host: ~ listen: :3001 +transport_info: + name: Telegram + code: mg-telegram + logo_path: /static/telegram_logo.svg + sentry_dsn: ~ log_level: 5 diff --git a/config_test.yml.dist b/config_test.yml.dist index b9870d5..034613a 100644 --- a/config_test.yml.dist +++ b/config_test.yml.dist @@ -5,6 +5,11 @@ http_server: host: ~ listen: :3002 +transport_info: + name: Telegram + code: mg-telegram + logo_path: /static/telegram_logo.svg + sentry_dsn: ~ log_level: 5 diff --git a/error_handler.go b/error_handler.go index a629066..4f0b05e 100644 --- a/error_handler.go +++ b/error_handler.go @@ -54,7 +54,7 @@ func ErrorResponseHandler() ErrorHandlerFunc { } if privateLen > 0 || recovery != nil { - messages[index] = "Something went wrong" + messages[index] = getLocalizedMessage("error_save") } c.JSON(http.StatusInternalServerError, gin.H{"error": messages}) @@ -98,7 +98,7 @@ func ErrorCaptureHandler(client *raven.Client, errorsStacktrace bool) ErrorHandl func PanicLogger() ErrorHandlerFunc { return func(recovery interface{}, c *gin.Context) { if recovery != nil { - logger.Error(recovery) + logger.Error(c.Request.RequestURI, recovery) debug.PrintStack() } } @@ -107,7 +107,7 @@ func PanicLogger() ErrorHandlerFunc { func ErrorLogger() ErrorHandlerFunc { return func(recovery interface{}, c *gin.Context) { for _, err := range c.Errors { - logger.Error(err.Err) + logger.Error(c.Request.RequestURI, err.Err) } } } diff --git a/go.sum b/go.sum deleted file mode 100644 index 937f70c..0000000 --- a/go.sum +++ /dev/null @@ -1,127 +0,0 @@ -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/log.go b/log.go index dff865b..eaa19cb 100644 --- a/log.go +++ b/log.go @@ -11,7 +11,7 @@ var logFormat = logging.MustStringFormatter( ) func newLogger() *logging.Logger { - logger := logging.MustGetLogger(transport) + logger := logging.MustGetLogger(config.TransportInfo.Code) logBackend := logging.NewLogBackend(os.Stdout, "", 0) formatBackend := logging.NewBackendFormatter(logBackend, logFormat) backend1Leveled := logging.AddModuleLevel(logBackend) diff --git a/main.go b/main.go index c0b3b86..cc880dd 100644 --- a/main.go +++ b/main.go @@ -13,8 +13,6 @@ type Options struct { Config string `short:"c" long:"config" default:"config.yml" description:"Path to configuration file"` } -const transport = "mg-telegram" - var ( config *TransportConfig orm *Orm diff --git a/routing.go b/routing.go index 24985ae..d4191bf 100644 --- a/routing.go +++ b/routing.go @@ -38,8 +38,8 @@ func addBotHandler(c *gin.Context) { bot, err := tgbotapi.NewBotAPI(b.Token) if err != nil { - logger.Error(b.Token, err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_token")}) + logger.Error(b.Token, err.Error()) return } @@ -47,14 +47,14 @@ func addBotHandler(c *gin.Context) { wr, err := bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) if err != nil { - logger.Error(b.Token, err.Error()) c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) + logger.Error(b.Token, err.Error()) return } if !wr.Ok { - logger.Error(b.Token, wr.ErrorCode, wr.Result) c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) + logger.Error(b.Token, wr.ErrorCode, wr.Result) return } @@ -75,8 +75,8 @@ func addBotHandler(c *gin.Context) { var client = v1.New(conn.MGURL, conn.MGToken) data, status, err := client.ActivateTransportChannel(ch) if status != http.StatusCreated { - logger.Error(conn.APIURL, status, err.Error(), data) c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activating_channel")}) + logger.Error(conn.APIURL, status, err.Error(), data) return } @@ -146,7 +146,7 @@ func saveHandler(c *gin.Context) { conn := c.MustGet("connection").(Connection) _, err, code := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { - c.AbortWithStatusJSON(code, gin.H{"error": err.Error()}) + c.JSON(code, gin.H{"error": err.Error()}) return } @@ -164,13 +164,17 @@ func createHandler(c *gin.Context) { cl := getConnectionByURL(conn.APIURL) if cl.ID != 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")}) + c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")}) return } - client, err, _ := getAPIClient(conn.APIURL, conn.APIKEY) + client, err, code := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + if code == http.StatusInternalServerError { + c.Error(err) + } else { + c.JSON(code, gin.H{"error": err.Error()}) + } return } @@ -216,7 +220,7 @@ func activityHandler(c *gin.Context) { conn := getConnection(rec.ClientId) if conn.ID == 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, + c.JSON(http.StatusBadRequest, gin.H{ "success": false, "error": "Wrong data", @@ -237,14 +241,15 @@ func activityHandler(c *gin.Context) { func getIntegrationModule(clientId string) v5.IntegrationModule { return v5.IntegrationModule{ - Code: transport, - IntegrationCode: transport, + Code: config.TransportInfo.Code, + IntegrationCode: config.TransportInfo.Code, Active: true, - Name: "Telegram", + Name: config.TransportInfo.Name, ClientID: clientId, Logo: fmt.Sprintf( - "https://%s/static/telegram_logo.svg", + "https://%s%s", config.HTTPServer.Host, + config.TransportInfo.LogoPath, ), BaseURL: fmt.Sprintf( "https://%s", diff --git a/run.go b/run.go index 8258d94..a544a09 100644 --- a/run.go +++ b/run.go @@ -75,13 +75,12 @@ func setup() *gin.Engine { errorHandlers := []ErrorHandlerFunc{ PanicLogger(), - ErrorLogger(), ErrorResponseHandler(), } - sentry, _ := raven.New(config.SentryDSN) + sentry, _ := raven.New(config.SentryDSN) if sentry != nil { - errorHandlers = append(errorHandlers, ErrorCaptureHandler(sentry, false)) + errorHandlers = append(errorHandlers, ErrorCaptureHandler(sentry, true)) } r.Use(ErrorHandler(errorHandlers...)) @@ -128,7 +127,7 @@ func checkBotForRequest() gin.HandlerFunc { } if b.Token == "" { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("no_bot_token")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("no_bot_token")}) return } @@ -140,7 +139,7 @@ func checkConnectionForRequest() gin.HandlerFunc { return func(c *gin.Context) { var conn Connection - if err := c.BindJSON(&conn); err != nil { + if err := c.ShouldBindJSON(&conn); err != nil { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) return } diff --git a/templates/form.html b/templates/form.html index ae66278..455882a 100644 --- a/templates/form.html +++ b/templates/form.html @@ -9,7 +9,7 @@
- +
diff --git a/utils.go b/utils.go index c37272f..6382896 100644 --- a/utils.go +++ b/utils.go @@ -26,7 +26,7 @@ func getAPIClient(url, key string) (*v5.Client, error, int) { cr, status, e := client.APICredentials() if e.RuntimeErr != nil { logger.Error(url, status, e.RuntimeErr, cr) - return nil, errors.New(getLocalizedMessage("not_found_account")), http.StatusInternalServerError + return nil, e.RuntimeErr, http.StatusInternalServerError } @@ -51,7 +51,8 @@ func getAPIClient(url, key string) (*v5.Client, error, int) { } func checkCredentials(credential []string) []string { - rc := config.Credentials + rc := make([]string, len(config.Credentials)) + copy(rc, config.Credentials) for _, vc := range credential { for kn, vn := range rc { From 3e8fb7c1a9f7c63f9d554d3b5c0f4fedc4ba39d0 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Fri, 17 Aug 2018 13:36:25 +0300 Subject: [PATCH 4/9] improve Makefile, docker-compose --- .gitignore | 1 - Makefile | 7 +-- docker-compose-test.yml | 4 +- docker-compose.yml | 4 +- go.sum | 127 ++++++++++++++++++++++++++++++++++++++++ routing.go | 24 ++++---- 6 files changed, 147 insertions(+), 20 deletions(-) create mode 100644 go.sum diff --git a/.gitignore b/.gitignore index 34107d5..593ea84 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,3 @@ config_test.yml /bin/* *.xml /vendor -go.sum diff --git a/Makefile b/Makefile index 8f47b24..9c5e35f 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ export GOPATH := $(GOPATH):$(ROOT_DIR) build: deps fmt @echo "==> Building" - @go build -o $(BIN) -ldflags "-X common.build=${REVISION}" . + @cd $(ROOT_DIR) && CGO_ENABLED=0 go build -o $(BIN) -ldflags "-X common.build=${REVISION}" . @echo $(BIN) run: migrate @@ -36,10 +36,7 @@ fmt: deps: @echo "==> Installing dependencies" - $(eval DEPS:=$(shell cd $(SRC_DIR) \ - && go list -f '{{join .Imports "\n"}}{{ "\n" }}{{join .TestImports "\n"}}' ./... \ - | sort | uniq | tr '\r' '\n' | paste -sd ' ' -)) - @go get -d -v $(DEPS) + @go mod tidy migrate: build ${BIN} --config $(CONFIG_FILE) migrate -p $(MIGRATIONS_DIR) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 60c96e9..33f9776 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -11,9 +11,11 @@ services: - ${POSTGRES_ADDRESS:-127.0.0.1:5434}:${POSTGRES_PORT:-5432} mg_telegram_test: - image: golang:1.9.3-stretch + image: golang:1.11beta3-stretch working_dir: /mgtg user: ${UID:-1000}:${GID:-1000} + environment: + GOCACHE: /go volumes: - ./:/mgtg/ - ./static:/static/ diff --git a/docker-compose.yml b/docker-compose.yml index 2ebc45e..02e3670 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,9 +11,11 @@ services: - ${POSTGRES_ADDRESS:-127.0.0.1:5434}:${POSTGRES_PORT:-5432} mg_telegram: - image: golang:1.9.3-stretch + image: golang:1.11beta3-stretch working_dir: /mgtg user: ${UID:-1000}:${GID:-1000} + environment: + GOCACHE: /go volumes: - ./:/mgtg - ./static:/static/ 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/routing.go b/routing.go index d4191bf..86b0444 100644 --- a/routing.go +++ b/routing.go @@ -32,13 +32,13 @@ func addBotHandler(c *gin.Context) { } if cl.ID != 0 { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("bot_already_created")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("bot_already_created")}) return } bot, err := tgbotapi.NewBotAPI(b.Token) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_token")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_token")}) logger.Error(b.Token, err.Error()) return } @@ -47,13 +47,13 @@ func addBotHandler(c *gin.Context) { wr, err := bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) if err != nil { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) logger.Error(b.Token, err.Error()) return } if !wr.Ok { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) logger.Error(b.Token, wr.ErrorCode, wr.Result) return } @@ -75,7 +75,7 @@ func addBotHandler(c *gin.Context) { var client = v1.New(conn.MGURL, conn.MGToken) data, status, err := client.ActivateTransportChannel(ch) if status != http.StatusCreated { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activating_channel")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activating_channel")}) logger.Error(conn.APIURL, status, err.Error(), data) return } @@ -95,7 +95,7 @@ func deleteBotHandler(c *gin.Context) { 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")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("not_found_account")}) logger.Error(b.ID, "MGURL or MGToken is empty") return } @@ -104,7 +104,7 @@ func deleteBotHandler(c *gin.Context) { data, status, err := client.DeactivateTransportChannel(getBotChannelByToken(b.Token)) if status > http.StatusOK { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_deactivating_channel")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_deactivating_channel")}) logger.Error(b.ID, status, err.Error(), data) return } @@ -146,7 +146,7 @@ func saveHandler(c *gin.Context) { conn := c.MustGet("connection").(Connection) _, err, code := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { - c.JSON(code, gin.H{"error": err.Error()}) + c.AbortWithStatusJSON(code, gin.H{"error": err.Error()}) return } @@ -164,7 +164,7 @@ func createHandler(c *gin.Context) { cl := getConnectionByURL(conn.APIURL) if cl.ID != 0 { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")}) return } @@ -173,7 +173,7 @@ func createHandler(c *gin.Context) { if code == http.StatusInternalServerError { c.Error(err) } else { - c.JSON(code, gin.H{"error": err.Error()}) + c.AbortWithStatusJSON(code, gin.H{"error": err.Error()}) } return } @@ -186,7 +186,7 @@ func createHandler(c *gin.Context) { } if status >= http.StatusBadRequest { - c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activity_mg")}) + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activity_mg")}) logger.Error(conn.APIURL, status, errr.ApiErr, data) return } @@ -220,7 +220,7 @@ func activityHandler(c *gin.Context) { conn := getConnection(rec.ClientId) if conn.ID == 0 { - c.JSON(http.StatusBadRequest, + c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{ "success": false, "error": "Wrong data", From 30b00da6bc681b51e0fa636dc44f7d29589bd82c Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Fri, 17 Aug 2018 16:31:55 +0300 Subject: [PATCH 5/9] add src dir, improve makefile --- Makefile | 10 +- config.go => src/config.go | 0 database.go => src/database.go | 0 error_handler.go => src/error_handler.go | 0 locale.go => src/locale.go | 0 log.go => src/log.go | 0 main.go => src/main.go | 0 migrate.go => src/migrate.go | 0 models.go => src/models.go | 0 repository.go => src/repository.go | 0 routing.go => src/routing.go | 252 +++++++++++++++- routing_test.go => src/routing_test.go | 0 run.go => src/run.go | 0 stacktrace.go => src/stacktrace.go | 0 src/telegram.go | 57 ++++ utils.go => src/utils.go | 43 +++ validator.go => src/validator.go | 0 telegram.go | 357 ----------------------- 18 files changed, 352 insertions(+), 367 deletions(-) rename config.go => src/config.go (100%) rename database.go => src/database.go (100%) rename error_handler.go => src/error_handler.go (100%) rename locale.go => src/locale.go (100%) rename log.go => src/log.go (100%) rename main.go => src/main.go (100%) rename migrate.go => src/migrate.go (100%) rename models.go => src/models.go (100%) rename repository.go => src/repository.go (100%) rename routing.go => src/routing.go (52%) rename routing_test.go => src/routing_test.go (100%) rename run.go => src/run.go (100%) rename stacktrace.go => src/stacktrace.go (100%) create mode 100644 src/telegram.go rename utils.go => src/utils.go (56%) rename validator.go => src/validator.go (100%) delete mode 100644 telegram.go diff --git a/Makefile b/Makefile index 9c5e35f..f737613 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,14 @@ ROOT_DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -SRC_DIR=$(ROOT_DIR) +SRC_DIR=$(ROOT_DIR)/src MIGRATIONS_DIR=$(ROOT_DIR)/migrations CONFIG_FILE=$(ROOT_DIR)/config.yml CONFIG_TEST_FILE=$(ROOT_DIR)/config_test.yml BIN=$(ROOT_DIR)/bin/transport REVISION=$(shell git describe --tags 2>/dev/null || git log --format="v0.0-%h" -n 1 || echo "v0.0-unknown") -ifndef GOPATH - $(error GOPATH must be defined) -endif - -export GOPATH := $(GOPATH):$(ROOT_DIR) - build: deps fmt @echo "==> Building" - @cd $(ROOT_DIR) && CGO_ENABLED=0 go build -o $(BIN) -ldflags "-X common.build=${REVISION}" . + @cd $(SRC_DIR) && CGO_ENABLED=0 go build -o $(BIN) -ldflags "-X common.build=${REVISION}" . @echo $(BIN) run: migrate diff --git a/config.go b/src/config.go similarity index 100% rename from config.go rename to src/config.go diff --git a/database.go b/src/database.go similarity index 100% rename from database.go rename to src/database.go diff --git a/error_handler.go b/src/error_handler.go similarity index 100% rename from error_handler.go rename to src/error_handler.go diff --git a/locale.go b/src/locale.go similarity index 100% rename from locale.go rename to src/locale.go diff --git a/log.go b/src/log.go similarity index 100% rename from log.go rename to src/log.go diff --git a/main.go b/src/main.go similarity index 100% rename from main.go rename to src/main.go diff --git a/migrate.go b/src/migrate.go similarity index 100% rename from migrate.go rename to src/migrate.go diff --git a/models.go b/src/models.go similarity index 100% rename from models.go rename to src/models.go diff --git a/repository.go b/src/repository.go similarity index 100% rename from repository.go rename to src/repository.go diff --git a/routing.go b/src/routing.go similarity index 52% rename from routing.go rename to src/routing.go index 86b0444..03b3216 100644 --- a/routing.go +++ b/src/routing.go @@ -1,9 +1,10 @@ package main import ( - "net/http" - "fmt" + "net/http" + "strconv" + "time" "github.com/gin-gonic/gin" "github.com/go-telegram-bot-api/telegram-bot-api" @@ -271,3 +272,250 @@ func getIntegrationModule(clientId string) v5.IntegrationModule { }, } } + +func telegramWebhookHandler(c *gin.Context) { + token := c.Param("token") + b, err := getBotByToken(token) + if err != nil { + c.Error(err) + return + } + + if b.ID == 0 { + c.AbortWithStatus(http.StatusOK) + return + } + + conn := getConnectionById(b.ConnectionID) + if !conn.Active { + c.AbortWithStatus(http.StatusOK) + return + } + + var update tgbotapi.Update + if err := c.ShouldBindJSON(&update); err != nil { + c.Error(err) + return + } + + if config.Debug { + logger.Debugf("mgWebhookHandler request: %v", update) + } + + var client = v1.New(conn.MGURL, conn.MGToken) + + if update.Message != nil { + if update.Message.Text == "" { + setLocale(update.Message.From.LanguageCode) + update.Message.Text = getLocalizedMessage(getMessageID(update.Message)) + } + + nickname := update.Message.From.UserName + user := getUserByExternalID(update.Message.From.ID) + + if update.Message.From.UserName == "" { + nickname = update.Message.From.FirstName + } + + if user.Expired(config.UpdateInterval) || user.ID == 0 { + fileID, fileURL, err := GetFileIDAndURL(b.Token, update.Message.From.ID) + if err != nil { + c.Error(err) + return + } + + if fileID != user.UserPhotoID && fileURL != "" { + picURL, err := UploadUserAvatar(fileURL) + if err != nil { + c.Error(err) + return + } + + user.UserPhotoID = fileID + user.UserPhotoURL = picURL + } + + if user.ExternalID == 0 { + user.ExternalID = update.Message.From.ID + } + + err = user.save() + if err != nil { + c.Error(err) + return + } + } + + lang := update.Message.From.LanguageCode + + if len(update.Message.From.LanguageCode) > 2 { + lang = update.Message.From.LanguageCode[:2] + } + + if config.Debug { + logger.Debugf("telegramWebhookHandler user %v", user) + } + + snd := v1.SendData{ + Message: v1.SendMessage{ + Message: v1.Message{ + ExternalID: strconv.Itoa(update.Message.MessageID), + Type: "text", + Text: update.Message.Text, + }, + SentAt: time.Now(), + }, + User: v1.User{ + ExternalID: strconv.Itoa(update.Message.From.ID), + Nickname: nickname, + Firstname: update.Message.From.FirstName, + Avatar: user.UserPhotoURL, + Lastname: update.Message.From.LastName, + Language: lang, + }, + Channel: b.Channel, + ExternalChatID: strconv.FormatInt(update.Message.Chat.ID, 10), + } + + if update.Message.ReplyToMessage != nil { + snd.Quote = &v1.SendMessageRequestQuote{ExternalID: strconv.Itoa(update.Message.ReplyToMessage.MessageID)} + } + + data, st, err := client.Messages(snd) + if err != nil { + logger.Error(b.Token, err.Error(), st, data) + c.Error(err) + return + } + + if config.Debug { + logger.Debugf("telegramWebhookHandler Type: SendMessage, Bot: %v, Message: %v, Response: %v", b.ID, snd, data) + } + } + + if update.EditedMessage != nil { + if update.EditedMessage.Text == "" { + setLocale(update.EditedMessage.From.LanguageCode) + update.EditedMessage.Text = getLocalizedMessage(getMessageID(update.Message)) + } + + snd := v1.UpdateData{ + Message: v1.UpdateMessage{ + Message: v1.Message{ + ExternalID: strconv.Itoa(update.EditedMessage.MessageID), + Type: "text", + Text: update.EditedMessage.Text, + }, + }, + Channel: b.Channel, + } + + data, st, err := client.UpdateMessages(snd) + if err != nil { + logger.Error(b.Token, err.Error(), st, data) + c.Error(err) + return + } + + if config.Debug { + logger.Debugf("telegramWebhookHandler Type: UpdateMessage, Bot: %v, Message: %v, Response: %v", b.ID, snd, data) + } + } + + c.JSON(http.StatusOK, gin.H{}) +} + +func mgWebhookHandler(c *gin.Context) { + clientID := c.GetHeader("Clientid") + if clientID == "" { + c.AbortWithStatus(http.StatusBadRequest) + return + } + + conn := getConnection(clientID) + if !conn.Active { + c.AbortWithStatus(http.StatusBadRequest) + return + } + + var msg v1.WebhookRequest + if err := c.ShouldBindJSON(&msg); err != nil { + c.Error(err) + return + } + + if config.Debug { + logger.Debugf("mgWebhookHandler request: %v", msg) + } + + uid, _ := strconv.Atoi(msg.Data.ExternalMessageID) + cid, _ := strconv.ParseInt(msg.Data.ExternalChatID, 10, 64) + + b := getBot(conn.ID, msg.Data.ChannelID) + if b.ID == 0 { + c.AbortWithStatus(http.StatusBadRequest) + return + } + + bot, err := tgbotapi.NewBotAPI(b.Token) + if err != nil { + c.Error(err) + return + } + + if msg.Type == "message_sent" { + m := tgbotapi.NewMessage(cid, msg.Data.Content) + + if msg.Data.QuoteExternalID != "" { + qid, err := strconv.Atoi(msg.Data.QuoteExternalID) + if err != nil { + c.Error(err) + return + } + m.ReplyToMessageID = qid + } + + msgSend, err := bot.Send(m) + if err != nil { + logger.Error(err) + c.AbortWithStatus(http.StatusBadRequest) + return + } + + if config.Debug { + logger.Debugf("mgWebhookHandler sent %v", msgSend) + } + + c.JSON(http.StatusOK, gin.H{"external_message_id": strconv.Itoa(msgSend.MessageID)}) + } + + if msg.Type == "message_updated" { + msgSend, err := bot.Send(tgbotapi.NewEditMessageText(cid, uid, msg.Data.Content)) + if err != nil { + logger.Error(err) + c.AbortWithStatus(http.StatusBadRequest) + return + } + + if config.Debug { + logger.Debugf("mgWebhookHandler update %v", msgSend) + } + + c.AbortWithStatus(http.StatusOK) + } + + if msg.Type == "message_deleted" { + msgSend, err := bot.Send(tgbotapi.NewDeleteMessage(cid, uid)) + if err != nil { + logger.Error(err) + c.AbortWithStatus(http.StatusBadRequest) + return + } + + if config.Debug { + logger.Debugf("mgWebhookHandler delete %v", msgSend) + } + + c.JSON(http.StatusOK, gin.H{}) + } +} diff --git a/routing_test.go b/src/routing_test.go similarity index 100% rename from routing_test.go rename to src/routing_test.go diff --git a/run.go b/src/run.go similarity index 100% rename from run.go rename to src/run.go diff --git a/stacktrace.go b/src/stacktrace.go similarity index 100% rename from stacktrace.go rename to src/stacktrace.go diff --git a/src/telegram.go b/src/telegram.go new file mode 100644 index 0000000..f191c93 --- /dev/null +++ b/src/telegram.go @@ -0,0 +1,57 @@ +package main + +import "github.com/go-telegram-bot-api/telegram-bot-api" + +//GetFileIDAndURL function +func GetFileIDAndURL(token string, userID int) (fileID, fileURL string, err error) { + bot, err := tgbotapi.NewBotAPI(token) + if err != nil { + return + } + + bot.Debug = config.Debug + + res, err := bot.GetUserProfilePhotos( + tgbotapi.UserProfilePhotosConfig{ + UserID: userID, + Limit: 1, + }, + ) + if err != nil { + return + } + + if config.Debug { + logger.Debugf("GetFileIDAndURL Photos: %v", res.Photos) + } + + if len(res.Photos) > 0 { + fileID = res.Photos[0][len(res.Photos[0])-1].FileID + fileURL, err = bot.GetFileDirectURL(fileID) + } + + return +} + +func getMessageID(data *tgbotapi.Message) string { + switch { + case data.Sticker != nil: + return "sticker" + case data.Audio != nil: + return "audio" + case data.Contact != nil: + return "contact" + case data.Document != nil: + return "document" + case data.Location != nil: + return "location" + case data.Video != nil: + return "video" + case data.Voice != nil: + return "voice" + case data.Photo != nil: + return "photo" + default: + return "undefined" + } +} diff --git a/utils.go b/src/utils.go similarity index 56% rename from utils.go rename to src/utils.go index 6382896..18fd9ce 100644 --- a/utils.go +++ b/src/utils.go @@ -9,6 +9,10 @@ import ( "sync/atomic" "time" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/nicksnyder/go-i18n/v2/i18n" "github.com/retailcrm/api-client-go/v5" ) @@ -68,3 +72,42 @@ func checkCredentials(credential []string) []string { return rc } + +//UploadUserAvatar function +func UploadUserAvatar(url string) (picURLs3 string, err error) { + s3Config := &aws.Config{ + Credentials: credentials.NewStaticCredentials( + config.ConfigAWS.AccessKeyID, + config.ConfigAWS.SecretAccessKey, + ""), + Region: aws.String(config.ConfigAWS.Region), + } + + s := session.Must(session.NewSession(s3Config)) + uploader := s3manager.NewUploader(s) + + resp, err := http.Get(url) + if err != nil { + return + } + defer resp.Body.Close() + + if resp.StatusCode >= http.StatusBadRequest { + return "", errors.New(fmt.Sprintf("get: %v code: %v", url, resp.StatusCode)) + } + + result, err := uploader.Upload(&s3manager.UploadInput{ + Bucket: aws.String(config.ConfigAWS.Bucket), + Key: aws.String(fmt.Sprintf("%v/%v.jpg", config.ConfigAWS.FolderName, GenerateToken())), + Body: resp.Body, + ContentType: aws.String(config.ConfigAWS.ContentType), + ACL: aws.String("public-read"), + }) + if err != nil { + return + } + + picURLs3 = result.Location + + return +} diff --git a/validator.go b/src/validator.go similarity index 100% rename from validator.go rename to src/validator.go diff --git a/telegram.go b/telegram.go deleted file mode 100644 index 61296e5..0000000 --- a/telegram.go +++ /dev/null @@ -1,357 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "net/http" - "strconv" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3/s3manager" - "github.com/gin-gonic/gin" - "github.com/go-telegram-bot-api/telegram-bot-api" - "github.com/retailcrm/mg-transport-api-client-go/v1" -) - -func telegramWebhookHandler(c *gin.Context) { - token := c.Param("token") - b, err := getBotByToken(token) - if err != nil { - c.Error(err) - return - } - - if b.ID == 0 { - c.AbortWithStatus(http.StatusOK) - return - } - - conn := getConnectionById(b.ConnectionID) - if !conn.Active { - c.AbortWithStatus(http.StatusOK) - return - } - - var update tgbotapi.Update - if err := c.ShouldBindJSON(&update); err != nil { - c.Error(err) - return - } - - if config.Debug { - logger.Debugf("mgWebhookHandler request: %v", update) - } - - var client = v1.New(conn.MGURL, conn.MGToken) - - if update.Message != nil { - if update.Message.Text == "" { - setLocale(update.Message.From.LanguageCode) - update.Message.Text = getLocalizedMessage(getMessageID(update.Message)) - } - - nickname := update.Message.From.UserName - user := getUserByExternalID(update.Message.From.ID) - - if update.Message.From.UserName == "" { - nickname = update.Message.From.FirstName - } - - if user.Expired(config.UpdateInterval) || user.ID == 0 { - fileID, fileURL, err := GetFileIDAndURL(b.Token, update.Message.From.ID) - if err != nil { - c.Error(err) - return - } - - if fileID != user.UserPhotoID && fileURL != "" { - picURL, err := UploadUserAvatar(fileURL) - if err != nil { - c.Error(err) - return - } - - user.UserPhotoID = fileID - user.UserPhotoURL = picURL - } - - if user.ExternalID == 0 { - user.ExternalID = update.Message.From.ID - } - - err = user.save() - if err != nil { - c.Error(err) - return - } - } - - lang := update.Message.From.LanguageCode - - if len(update.Message.From.LanguageCode) > 2 { - lang = update.Message.From.LanguageCode[:2] - } - - if config.Debug { - logger.Debugf("telegramWebhookHandler user %v", user) - } - - snd := v1.SendData{ - Message: v1.SendMessage{ - Message: v1.Message{ - ExternalID: strconv.Itoa(update.Message.MessageID), - Type: "text", - Text: update.Message.Text, - }, - SentAt: time.Now(), - }, - User: v1.User{ - ExternalID: strconv.Itoa(update.Message.From.ID), - Nickname: nickname, - Firstname: update.Message.From.FirstName, - Avatar: user.UserPhotoURL, - Lastname: update.Message.From.LastName, - Language: lang, - }, - Channel: b.Channel, - ExternalChatID: strconv.FormatInt(update.Message.Chat.ID, 10), - } - - if update.Message.ReplyToMessage != nil { - snd.Quote = &v1.SendMessageRequestQuote{ExternalID: strconv.Itoa(update.Message.ReplyToMessage.MessageID)} - } - - data, st, err := client.Messages(snd) - if err != nil { - logger.Error(b.Token, err.Error(), st, data) - c.Error(err) - return - } - - if config.Debug { - logger.Debugf("telegramWebhookHandler Type: SendMessage, Bot: %v, Message: %v, Response: %v", b.ID, snd, data) - } - } - - if update.EditedMessage != nil { - if update.EditedMessage.Text == "" { - setLocale(update.EditedMessage.From.LanguageCode) - update.EditedMessage.Text = getLocalizedMessage(getMessageID(update.Message)) - } - - snd := v1.UpdateData{ - Message: v1.UpdateMessage{ - Message: v1.Message{ - ExternalID: strconv.Itoa(update.EditedMessage.MessageID), - Type: "text", - Text: update.EditedMessage.Text, - }, - }, - Channel: b.Channel, - } - - data, st, err := client.UpdateMessages(snd) - if err != nil { - logger.Error(b.Token, err.Error(), st, data) - c.Error(err) - return - } - - if config.Debug { - logger.Debugf("telegramWebhookHandler Type: UpdateMessage, Bot: %v, Message: %v, Response: %v", b.ID, snd, data) - } - } - - c.JSON(http.StatusOK, gin.H{}) -} - -func mgWebhookHandler(c *gin.Context) { - clientID := c.GetHeader("Clientid") - if clientID == "" { - c.AbortWithStatus(http.StatusBadRequest) - return - } - - conn := getConnection(clientID) - if !conn.Active { - c.AbortWithStatus(http.StatusBadRequest) - return - } - - var msg v1.WebhookRequest - if err := c.ShouldBindJSON(&msg); err != nil { - c.Error(err) - return - } - - if config.Debug { - logger.Debugf("mgWebhookHandler request: %v", msg) - } - - uid, _ := strconv.Atoi(msg.Data.ExternalMessageID) - cid, _ := strconv.ParseInt(msg.Data.ExternalChatID, 10, 64) - - b := getBot(conn.ID, msg.Data.ChannelID) - if b.ID == 0 { - c.AbortWithStatus(http.StatusBadRequest) - return - } - - bot, err := tgbotapi.NewBotAPI(b.Token) - if err != nil { - c.Error(err) - return - } - - if msg.Type == "message_sent" { - m := tgbotapi.NewMessage(cid, msg.Data.Content) - - if msg.Data.QuoteExternalID != "" { - qid, err := strconv.Atoi(msg.Data.QuoteExternalID) - if err != nil { - c.Error(err) - return - } - m.ReplyToMessageID = qid - } - - msgSend, err := bot.Send(m) - if err != nil { - logger.Error(err) - c.AbortWithStatus(http.StatusBadRequest) - return - } - - if config.Debug { - logger.Debugf("mgWebhookHandler sent %v", msgSend) - } - - c.JSON(http.StatusOK, gin.H{"external_message_id": strconv.Itoa(msgSend.MessageID)}) - } - - if msg.Type == "message_updated" { - msgSend, err := bot.Send(tgbotapi.NewEditMessageText(cid, uid, msg.Data.Content)) - if err != nil { - logger.Error(err) - c.AbortWithStatus(http.StatusBadRequest) - return - } - - if config.Debug { - logger.Debugf("mgWebhookHandler update %v", msgSend) - } - - c.AbortWithStatus(http.StatusOK) - } - - if msg.Type == "message_deleted" { - msgSend, err := bot.Send(tgbotapi.NewDeleteMessage(cid, uid)) - if err != nil { - logger.Error(err) - c.AbortWithStatus(http.StatusBadRequest) - return - } - - if config.Debug { - logger.Debugf("mgWebhookHandler delete %v", msgSend) - } - - c.JSON(http.StatusOK, gin.H{}) - } -} - -//GetFileIDAndURL function -func GetFileIDAndURL(token string, userID int) (fileID, fileURL string, err error) { - bot, err := tgbotapi.NewBotAPI(token) - if err != nil { - return - } - - bot.Debug = config.Debug - - res, err := bot.GetUserProfilePhotos( - tgbotapi.UserProfilePhotosConfig{ - UserID: userID, - Limit: 1, - }, - ) - if err != nil { - return - } - - if config.Debug { - logger.Debugf("GetFileIDAndURL Photos: %v", res.Photos) - } - - if len(res.Photos) > 0 { - fileID = res.Photos[0][len(res.Photos[0])-1].FileID - fileURL, err = bot.GetFileDirectURL(fileID) - } - - return -} - -//UploadUserAvatar function -func UploadUserAvatar(url string) (picURLs3 string, err error) { - s3Config := &aws.Config{ - Credentials: credentials.NewStaticCredentials( - config.ConfigAWS.AccessKeyID, - config.ConfigAWS.SecretAccessKey, - ""), - Region: aws.String(config.ConfigAWS.Region), - } - - s := session.Must(session.NewSession(s3Config)) - uploader := s3manager.NewUploader(s) - - resp, err := http.Get(url) - if err != nil { - return - } - defer resp.Body.Close() - - if resp.StatusCode >= http.StatusBadRequest { - return "", errors.New(fmt.Sprintf("get: %v code: %v", url, resp.StatusCode)) - } - - result, err := uploader.Upload(&s3manager.UploadInput{ - Bucket: aws.String(config.ConfigAWS.Bucket), - Key: aws.String(fmt.Sprintf("%v/%v.jpg", config.ConfigAWS.FolderName, GenerateToken())), - Body: resp.Body, - ContentType: aws.String(config.ConfigAWS.ContentType), - ACL: aws.String("public-read"), - }) - if err != nil { - return - } - - picURLs3 = result.Location - - return -} - -func getMessageID(data *tgbotapi.Message) string { - switch { - case data.Sticker != nil: - return "sticker" - case data.Audio != nil: - return "audio" - case data.Contact != nil: - return "contact" - case data.Document != nil: - return "document" - case data.Location != nil: - return "location" - case data.Video != nil: - return "video" - case data.Voice != nil: - return "voice" - case data.Photo != nil: - return "photo" - default: - return "undefined" - } -} From bc269a0d2bf7a2318217a0808a66c35a52f00c5e Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Mon, 20 Aug 2018 13:57:14 +0300 Subject: [PATCH 6/9] improve validation --- go.mod | 5 +---- go.sum | 6 ------ src/validator.go | 53 ++++++------------------------------------------ 3 files changed, 7 insertions(+), 57 deletions(-) diff --git a/go.mod b/go.mod index 932cc6f..137e980 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,6 @@ require ( 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 @@ -57,7 +55,6 @@ require ( 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/go-playground/validator.v8 v8.18.2 gopkg.in/yaml.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index 937f70c..ba5c476 100644 --- a/go.sum +++ b/go.sum @@ -32,10 +32,6 @@ 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= @@ -121,7 +117,5 @@ gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXa 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/src/validator.go b/src/validator.go index 219749b..b44c17b 100644 --- a/src/validator.go +++ b/src/validator.go @@ -3,63 +3,22 @@ package main import ( "reflect" "regexp" - "sync" "github.com/gin-gonic/gin/binding" - "gopkg.in/go-playground/validator.v9" + "gopkg.in/go-playground/validator.v8" ) -type defaultValidator struct { - once sync.Once - validate *validator.Validate -} - -var _ binding.StructValidator = &defaultValidator{} - -func (v *defaultValidator) ValidateStruct(obj interface{}) error { - - if kindOfData(obj) == reflect.Struct { - v.lazyinit() - if err := v.validate.Struct(obj); err != nil { - return error(err) - } - } - - return nil -} - -func (v *defaultValidator) Engine() interface{} { - v.lazyinit() - return v.validate -} - -func (v *defaultValidator) lazyinit() { - v.once.Do(func() { - v.validate = validator.New() - v.validate.SetTagName("binding") - }) -} - -func kindOfData(data interface{}) reflect.Kind { - value := reflect.ValueOf(data) - valueType := value.Kind() - - if valueType == reflect.Ptr { - valueType = value.Elem().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 { +func validateCrmURL( + v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value, + field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string, +) bool { regCommandName := regexp.MustCompile(`https://?[\da-z.-]+\.(retailcrm\.(ru|pro)|ecomlogic\.com)`) - return regCommandName.Match([]byte(field.Field().Interface().(string))) + return regCommandName.Match([]byte(field.Interface().(string))) } From 51329ddd545b855091f35771aa12edecfbb15de6 Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Mon, 20 Aug 2018 13:51:45 +0300 Subject: [PATCH 7/9] add features and minor fixes --- go.mod | 23 +++++++++++++---------- go.sum | 49 +++++++++++++++++++++++++++++-------------------- src/routing.go | 31 ++++++++++++++++--------------- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/go.mod b/go.mod index 137e980..2b8524c 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,10 @@ 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/Microsoft/go-winio v0.4.10 // indirect + github.com/aws/aws-sdk-go v1.15.16 github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261 // indirect - github.com/davecgh/go-spew v1.1.0 // indirect + github.com/davecgh/go-spew v1.1.1 // 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 @@ -19,17 +19,19 @@ require ( 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/golang/protobuf v1.2.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/gopherjs/gopherjs v0.0.0-20180820052304-89baedc74dd7 // 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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b // indirect github.com/jtolds/gls v4.2.1+incompatible // indirect + github.com/kr/pretty v0.1.0 // 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 @@ -41,19 +43,20 @@ require ( 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/retailcrm/mg-transport-api-client-go v1.1.2 + github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf // 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/crypto v0.0.0-20180820150726-614d502a4dac // indirect + golang.org/x/net v0.0.0-20180821023952-922f4815f713 // indirect golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect - golang.org/x/sys v0.0.0-20180815093151-14742f9018cd // indirect + golang.org/x/sys v0.0.0-20180821044426-4ea2f632f6e9 // indirect golang.org/x/text v0.3.0 google.golang.org/appengine v1.1.0 // indirect + gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/go-playground/assert.v1 v1.2.1 // indirect gopkg.in/go-playground/validator.v8 v8.18.2 gopkg.in/yaml.v2 v2.2.1 diff --git a/go.sum b/go.sum index ba5c476..b48ffa7 100644 --- a/go.sum +++ b/go.sum @@ -2,14 +2,14 @@ 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/Microsoft/go-winio v0.4.10 h1:NrhPZI+cp3Fjmm5t/PZkVuir43JIRLZG/PSKK7atSfw= +github.com/Microsoft/go-winio v0.4.10/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/aws/aws-sdk-go v1.15.16 h1:h3Wt98XmE8BWKZKLNhw1bl+ABVMvdWwetYM/rs88jMY= +github.com/aws/aws-sdk-go v1.15.16/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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/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= @@ -38,14 +38,14 @@ github.com/go-telegram-bot-api/telegram-bot-api v0.0.0-20180602093832-4c16a90966 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/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.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/gopherjs/gopherjs v0.0.0-20180820052304-89baedc74dd7 h1:WF7x3tAe0mEb4wf/yhSThHwZYQIjVmEGSbAH9hzOeZQ= +github.com/gopherjs/gopherjs v0.0.0-20180820052304-89baedc74dd7/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= @@ -58,10 +58,17 @@ github.com/jinzhu/now v0.0.0-20180511015916-ed742868f2ae h1:8bBMcboXYVuo0WYH+rPe 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/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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= @@ -84,10 +91,10 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb 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/retailcrm/mg-transport-api-client-go v1.1.2 h1:PL/IjYhsiD3LZ08YXTZGbwLmnOL1ulOUr8wGWE28g9A= +github.com/retailcrm/mg-transport-api-client-go v1.1.2/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI= +github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf h1:6V1qxN6Usn4jy8unvggSJz/NC790tefw8Zdy6OZS5co= +github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/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= @@ -98,14 +105,14 @@ github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQ 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/crypto v0.0.0-20180820150726-614d502a4dac h1:7d7lG9fHOLdL6jZPtnV4LpI41SbohIJ1Atq7U991dMg= +golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/net v0.0.0-20180821023952-922f4815f713 h1:rMJUcaDGbG+X967I4zGKCq5laYqcGKJmpB+3jhpOhPw= +golang.org/x/net v0.0.0-20180821023952-922f4815f713/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/sys v0.0.0-20180821044426-4ea2f632f6e9 h1:0RHCP7KEw0rDuVXXaT2gfV77uu6lTKa5aItB+EoFbQk= +golang.org/x/sys v0.0.0-20180821044426-4ea2f632f6e9/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= @@ -113,6 +120,8 @@ google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRS 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/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/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= diff --git a/src/routing.go b/src/routing.go index 03b3216..8172040 100644 --- a/src/routing.go +++ b/src/routing.go @@ -47,15 +47,9 @@ func addBotHandler(c *gin.Context) { bot.Debug = config.Debug wr, err := bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) - if err != nil { + if err != nil || !wr.Ok { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) - logger.Error(b.Token, err.Error()) - return - } - - if !wr.Ok { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) - logger.Error(b.Token, wr.ErrorCode, wr.Result) + logger.Error(b.Token, err.Error(), wr) return } @@ -63,11 +57,18 @@ func addBotHandler(c *gin.Context) { ch := v1.Channel{ Type: "telegram", - Events: []string{ - "message_sent", - "message_updated", - "message_deleted", - "message_read", + Settings: v1.ChannelSettings{ + SpamAllowed: false, + Status: v1.Status{ + Delivered: v1.ChannelFeatureNone, + Read: v1.ChannelFeatureNone, + }, + Text: v1.ChannelSettingsText{ + Creating: v1.ChannelFeatureBoth, + Editing: v1.ChannelFeatureBoth, + Quoting: v1.ChannelFeatureBoth, + Deleting: v1.ChannelFeatureSend, + }, }, } @@ -97,7 +98,6 @@ func deleteBotHandler(c *gin.Context) { conn := getConnectionById(b.ConnectionID) if conn.MGURL == "" || conn.MGToken == "" { c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("not_found_account")}) - logger.Error(b.ID, "MGURL or MGToken is empty") return } @@ -459,7 +459,8 @@ func mgWebhookHandler(c *gin.Context) { bot, err := tgbotapi.NewBotAPI(b.Token) if err != nil { - c.Error(err) + logger.Error(b, err) + c.AbortWithStatus(http.StatusBadRequest) return } From 8983640ab29ea7a6f939776d883790720fbf868f Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Thu, 23 Aug 2018 13:11:40 +0300 Subject: [PATCH 8/9] improve test, minor fixes --- Makefile | 6 +++--- go.mod | 8 +++++--- go.sum | 5 +++++ src/routing_test.go | 24 ++++++++++++++++++++++-- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index f737613..b0d91a4 100644 --- a/Makefile +++ b/Makefile @@ -17,16 +17,16 @@ run: migrate test: deps fmt @echo "==> Running tests" - @cd $(SRC_DIR) && go test ./... -v -cpu 2 + @cd $(ROOT_DIR) && go test ./... -v -cpu 2 jenkins_test: deps @echo "==> Running tests (result in test-report.xml)" @go get -v -u github.com/jstemmer/go-junit-report - @cd $(SRC_DIR) && go test ./... -v -cpu 2 -cover -race | go-junit-report -set-exit-code > $(SRC_DIR)/test-report.xml + @cd $(ROOT_DIR) && go test ./... -v -cpu 2 -cover -race | go-junit-report -set-exit-code > $(ROOT_DIR)/test-report.xml fmt: @echo "==> Running gofmt" - @gofmt -l -s -w $(SRC_DIR) + @gofmt -l -s -w $(ROOT_DIR) deps: @echo "==> Installing dependencies" diff --git a/go.mod b/go.mod index 2b8524c..ea551bb 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/retailcrm/mg-transport-telegram require ( cloud.google.com/go v0.26.0 // indirect github.com/Microsoft/go-winio v0.4.10 // indirect - github.com/aws/aws-sdk-go v1.15.16 + github.com/aws/aws-sdk-go v1.15.18 github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/denisenkom/go-mssqldb v0.0.0-20180707235734-242fa5aa1b45 // indirect @@ -16,6 +16,7 @@ require ( 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-ini/ini v1.38.2 // 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 @@ -32,7 +33,8 @@ require ( github.com/json-iterator/go v0.0.0-20180806060727-1624edc4454b // indirect github.com/jtolds/gls v4.2.1+incompatible // indirect github.com/kr/pretty v0.1.0 // indirect - github.com/lib/pq v0.0.0-20180523175426-90697d60dd84 // indirect + github.com/kr/pty v1.1.2 // indirect + github.com/lib/pq v1.0.0 // 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 @@ -53,7 +55,7 @@ require ( golang.org/x/crypto v0.0.0-20180820150726-614d502a4dac // indirect golang.org/x/net v0.0.0-20180821023952-922f4815f713 // indirect golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect - golang.org/x/sys v0.0.0-20180821044426-4ea2f632f6e9 // indirect + golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339 // indirect golang.org/x/text v0.3.0 google.golang.org/appengine v1.1.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect diff --git a/go.sum b/go.sum index b48ffa7..26b0136 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,7 @@ github.com/Microsoft/go-winio v0.4.10 h1:NrhPZI+cp3Fjmm5t/PZkVuir43JIRLZG/PSKK7a github.com/Microsoft/go-winio v0.4.10/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/aws/aws-sdk-go v1.15.16 h1:h3Wt98XmE8BWKZKLNhw1bl+ABVMvdWwetYM/rs88jMY= github.com/aws/aws-sdk-go v1.15.16/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/aws/aws-sdk-go v1.15.18/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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -32,6 +33,7 @@ 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-ini/ini v1.38.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= 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= @@ -67,10 +69,12 @@ github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVY github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.2/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 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/lib/pq v1.0.0/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= @@ -113,6 +117,7 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6Zh golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180821044426-4ea2f632f6e9 h1:0RHCP7KEw0rDuVXXaT2gfV77uu6lTKa5aItB+EoFbQk= golang.org/x/sys v0.0.0-20180821044426-4ea2f632f6e9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180821140842-3b58ed4ad339/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= diff --git a/src/routing_test.go b/src/routing_test.go index cb3d279..43f6226 100644 --- a/src/routing_test.go +++ b/src/routing_test.go @@ -7,11 +7,13 @@ import ( "net/http" "net/http/httptest" "net/url" + "os" "strings" "testing" "github.com/gin-gonic/gin" "github.com/h2non/gock" + "github.com/retailcrm/mg-transport-api-client-go/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -19,11 +21,11 @@ import ( var router *gin.Engine func init() { + os.Chdir("../") config = LoadConfig("config_test.yml") orm = NewDb(config) logger = newLogger() router = setup() - c := Connection{ ID: 1, ClientID: "123123", @@ -54,6 +56,24 @@ func TestRouting_connectHandler(t *testing.T) { func TestRouting_addBotHandler(t *testing.T) { defer gock.Off() + ch := v1.Channel{ + Type: "telegram", + Settings: v1.ChannelSettings{ + SpamAllowed: false, + Status: v1.Status{ + Delivered: v1.ChannelFeatureNone, + Read: v1.ChannelFeatureNone, + }, + Text: v1.ChannelSettingsText{ + Creating: v1.ChannelFeatureBoth, + Editing: v1.ChannelFeatureBoth, + Quoting: v1.ChannelFeatureBoth, + Deleting: v1.ChannelFeatureSend, + }, + }, + } + + outgoing, _ := json.Marshal(ch) p := url.Values{"url": {"https://" + config.HTTPServer.Host + "/telegram/123123:Qwerty"}} gock.New("https://api.telegram.org"). @@ -75,7 +95,7 @@ func TestRouting_addBotHandler(t *testing.T) { gock.New("https://test.retailcrm.pro"). Post("/api/transport/v1/channels"). - BodyString(`{"ID":0,"Type":"telegram","Events":["message_sent","message_updated","message_deleted","message_read"]}`). + JSON([]byte(outgoing)). MatchHeader("Content-Type", "application/json"). MatchHeader("X-Transport-Token", "test-token"). Reply(201). From 87777dc95b1923efeb03eff4017c76c105456e5e Mon Sep 17 00:00:00 2001 From: DmitryZagorulko Date: Thu, 23 Aug 2018 15:52:32 +0300 Subject: [PATCH 9/9] add ErrorResponse struct --- src/error.go | 15 +++++++++++++++ src/routing.go | 26 +++++++++++++++----------- src/routing_test.go | 4 ++-- src/run.go | 5 ++--- 4 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 src/error.go diff --git a/src/error.go b/src/error.go new file mode 100644 index 0000000..0377d8a --- /dev/null +++ b/src/error.go @@ -0,0 +1,15 @@ +package main + +import ( + "net/http" +) + +type ErrorResponse struct { + Error string `json:"error"` +} + +func BadRequest(error string) (int, interface{}) { + return http.StatusBadRequest, ErrorResponse{ + Error: getLocalizedMessage(error), + } +} diff --git a/src/routing.go b/src/routing.go index 8172040..379ee85 100644 --- a/src/routing.go +++ b/src/routing.go @@ -33,13 +33,13 @@ func addBotHandler(c *gin.Context) { } if cl.ID != 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("bot_already_created")}) + c.AbortWithStatusJSON(BadRequest("bot_already_created")) return } bot, err := tgbotapi.NewBotAPI(b.Token) if err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_token")}) + c.AbortWithStatusJSON(BadRequest("incorrect_token")) logger.Error(b.Token, err.Error()) return } @@ -48,7 +48,7 @@ func addBotHandler(c *gin.Context) { wr, err := bot.SetWebhook(tgbotapi.NewWebhook("https://" + config.HTTPServer.Host + "/telegram/" + bot.Token)) if err != nil || !wr.Ok { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_creating_webhook")}) + c.AbortWithStatusJSON(BadRequest("error_creating_webhook")) logger.Error(b.Token, err.Error(), wr) return } @@ -60,14 +60,14 @@ func addBotHandler(c *gin.Context) { Settings: v1.ChannelSettings{ SpamAllowed: false, Status: v1.Status{ - Delivered: v1.ChannelFeatureNone, + Delivered: v1.ChannelFeatureSend, Read: v1.ChannelFeatureNone, }, Text: v1.ChannelSettingsText{ Creating: v1.ChannelFeatureBoth, Editing: v1.ChannelFeatureBoth, Quoting: v1.ChannelFeatureBoth, - Deleting: v1.ChannelFeatureSend, + Deleting: v1.ChannelFeatureReceive, }, }, } @@ -77,7 +77,7 @@ func addBotHandler(c *gin.Context) { var client = v1.New(conn.MGURL, conn.MGToken) data, status, err := client.ActivateTransportChannel(ch) if status != http.StatusCreated { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activating_channel")}) + c.AbortWithStatusJSON(BadRequest("error_activating_channel")) logger.Error(conn.APIURL, status, err.Error(), data) return } @@ -97,7 +97,7 @@ func deleteBotHandler(c *gin.Context) { b := c.MustGet("bot").(Bot) conn := getConnectionById(b.ConnectionID) if conn.MGURL == "" || conn.MGToken == "" { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("not_found_account")}) + c.AbortWithStatusJSON(BadRequest("not_found_account")) return } @@ -105,7 +105,7 @@ func deleteBotHandler(c *gin.Context) { data, status, err := client.DeactivateTransportChannel(getBotChannelByToken(b.Token)) if status > http.StatusOK { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_deactivating_channel")}) + c.AbortWithStatusJSON(BadRequest("error_deactivating_channel")) logger.Error(b.ID, status, err.Error(), data) return } @@ -147,7 +147,11 @@ func saveHandler(c *gin.Context) { conn := c.MustGet("connection").(Connection) _, err, code := getAPIClient(conn.APIURL, conn.APIKEY) if err != nil { - c.AbortWithStatusJSON(code, gin.H{"error": err.Error()}) + if code == http.StatusInternalServerError { + c.Error(err) + } else { + c.AbortWithStatusJSON(BadRequest(err.Error())) + } return } @@ -165,7 +169,7 @@ func createHandler(c *gin.Context) { cl := getConnectionByURL(conn.APIURL) if cl.ID != 0 { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")}) + c.AbortWithStatusJSON(BadRequest("connection_already_created")) return } @@ -187,7 +191,7 @@ func createHandler(c *gin.Context) { } if status >= http.StatusBadRequest { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activity_mg")}) + c.AbortWithStatusJSON(BadRequest("error_activity_mg")) logger.Error(conn.APIURL, status, errr.ApiErr, data) return } diff --git a/src/routing_test.go b/src/routing_test.go index 43f6226..f6483dc 100644 --- a/src/routing_test.go +++ b/src/routing_test.go @@ -61,14 +61,14 @@ func TestRouting_addBotHandler(t *testing.T) { Settings: v1.ChannelSettings{ SpamAllowed: false, Status: v1.Status{ - Delivered: v1.ChannelFeatureNone, + Delivered: v1.ChannelFeatureSend, Read: v1.ChannelFeatureNone, }, Text: v1.ChannelSettingsText{ Creating: v1.ChannelFeatureBoth, Editing: v1.ChannelFeatureBoth, Quoting: v1.ChannelFeatureBoth, - Deleting: v1.ChannelFeatureSend, + Deleting: v1.ChannelFeatureReceive, }, }, } diff --git a/src/run.go b/src/run.go index a544a09..abf8936 100644 --- a/src/run.go +++ b/src/run.go @@ -1,7 +1,6 @@ package main import ( - "net/http" "os" "os/signal" "regexp" @@ -127,7 +126,7 @@ func checkBotForRequest() gin.HandlerFunc { } if b.Token == "" { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("no_bot_token")}) + c.AbortWithStatusJSON(BadRequest("no_bot_token")) return } @@ -140,7 +139,7 @@ func checkConnectionForRequest() gin.HandlerFunc { var conn Connection if err := c.ShouldBindJSON(&conn); err != nil { - c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")}) + c.AbortWithStatusJSON(BadRequest("incorrect_url_key")) return }