diff --git a/docker-compose.yml b/docker-compose.yml index e79c46b..bbe9880 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: user: ${UID:-1000}:${GID:-1000} volumes: - ./:/mg_telegram/ + - ./static:/static/ links: - postgres ports: diff --git a/models.go b/models.go index 7388fcb..0187538 100644 --- a/models.go +++ b/models.go @@ -5,26 +5,27 @@ import "time" // Connection model type Connection struct { ID int `gorm:"primary_key"` - ClientID string `gorm:"client_id" json:"clientId,omitempty"` - APIKEY string `gorm:"api_key" json:"api_key,omitempty"` - APIURL string `gorm:"api_url" json:"api_url,omitempty"` - MGURL string `gorm:"mg_url" json:"mg_url,omitempty"` - MGToken string `gorm:"mg_token" json:"mg_token,omitempty"` + ClientID string `gorm:"client_id type:varchar(70);not null;unique" json:"clientId,omitempty"` + APIKEY string `gorm:"api_key type:varchar(100);not null;unique" json:"api_key,omitempty"` + APIURL string `gorm:"api_url type:varchar(100);not null;unique" json:"api_url,omitempty"` + MGURL string `gorm:"mg_url type:varchar(100);unique" json:"mg_url,omitempty"` + MGToken string `gorm:"mg_token type:varchar(100)" json:"mg_token,omitempty"` CreatedAt time.Time UpdatedAt time.Time - Active bool `json:"active,omitempty"` + Active bool `json:"active,omitempty"` + Bots []Bot `gorm:"foreignkey:ConnectionID"` } // Bot model type Bot struct { - ID int `gorm:"primary_key"` - ClientID string `gorm:"client_id" json:"clientId,omitempty"` - Channel uint64 `json:"channel,omitempty"` - Token string `json:"token,omitempty"` - Name string `json:"name,omitempty"` - CreatedAt time.Time - UpdatedAt time.Time - Active bool `json:"active,omitempty"` + ID int `gorm:"primary_key"` + ConnectionID int `gorm:"connection_id" json:"connectionId,omitempty"` + Channel uint64 `json:"channel,omitempty"` + Token string `gorm:"token type:varchar(100);not null;unique" json:"token,omitempty"` + Name string `gorm:"name type:varchar(40)" json:"name,omitempty"` + CreatedAt time.Time + UpdatedAt time.Time + Active bool `json:"active,omitempty"` } //Bots list diff --git a/repository.go b/repository.go index 609ada7..96d77d8 100644 --- a/repository.go +++ b/repository.go @@ -26,6 +26,10 @@ func (c *Connection) saveConnection() error { return orm.DB.Model(c).Where("client_id = ?", c.ClientID).Update(c).Error } +func (c *Connection) createBot(b Bot) error { + return orm.DB.Model(c).Association("Bots").Append(&b).Error +} + func getBotByToken(token string) *Bot { var bot Bot orm.DB.First(&bot, "token = ?", token) @@ -33,10 +37,6 @@ func getBotByToken(token string) *Bot { return &bot } -func (b *Bot) createBot() error { - return orm.DB.Create(b).Error -} - func (b *Bot) setBotActivity() error { return orm.DB.Model(b).Where("token = ?", b.Token).Update("Active", !b.Active).Error } @@ -49,5 +49,13 @@ func getBotChannelByToken(token string) uint64 { } func (b *Bots) getBotsByClientID(uid string) error { - return orm.DB.Where("client_id = ?", uid).Find(b).Error + var c Connection + return orm.DB.First(&c, "client_id = ?", uid).Association("Bots").Find(b).Error +} + +func getConnectionById(id int) *Connection { + var connection Connection + orm.DB.First(&connection, "id = ?", id) + + return &connection } diff --git a/routing.go b/routing.go index 8f3cf8f..ccf1f4d 100644 --- a/routing.go +++ b/routing.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "net/http" "regexp" + "strings" "github.com/getsentry/raven-go" "github.com/go-telegram-bot-api/telegram-bot-api" @@ -19,7 +20,6 @@ import ( ) var ( - templates = template.Must(template.ParseFiles("templates/layout.html", "templates/form.html", "templates/home.html")) validPath = regexp.MustCompile(`^/(save|settings|telegram)/([a-zA-Z0-9-:_+]+)$`) localizer *i18n.Localizer bundle = &i18n.Bundle{DefaultLanguage: language.English} @@ -114,6 +114,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { 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 } @@ -123,6 +124,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { 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 } @@ -131,10 +133,10 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { return } - c := getConnection(b.ClientID) + c := getConnectionById(b.ConnectionID) if c.MGURL == "" || c.MGToken == "" { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.ClientID, "MGURL or MGToken is empty") + logger.Error(b.Token, "MGURL or MGToken is empty") return } @@ -166,13 +168,6 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { return } - _, err = bot.GetWebhookInfo() - if err != nil { - logger.Error(b.Token, err.Error()) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_creating_webhook"}), http.StatusBadRequest) - return - } - b.Name = GetBotName(bot) ch := v1.Channel{ @@ -196,7 +191,7 @@ func addBotHandler(w http.ResponseWriter, r *http.Request) { b.Channel = data.ChannelID b.Active = true - err = b.createBot() + err = c.createBot(b) if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_adding_bot"}), http.StatusInternalServerError) @@ -221,6 +216,7 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } @@ -230,13 +226,14 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { if err != nil { raven.CaptureErrorAndWait(err, nil) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) + logger.Error(err.Error()) return } - c := getConnection(b.ClientID) + c := getConnectionById(b.ConnectionID) if c.MGURL == "" || c.MGToken == "" { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusBadRequest) - logger.Error(b.ClientID, "MGURL or MGToken is empty") + logger.Error(b.ID, "MGURL or MGToken is empty") return } @@ -257,14 +254,14 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { data, status, err := client.DeactivateTransportChannel(ch.ID) if status > http.StatusOK { http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_deactivating_channel"}), http.StatusBadRequest) - logger.Error(b.ClientID, status, err.Error(), data) + logger.Error(b.ID, status, err.Error(), data) return } } else { 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(b.ClientID, status, err.Error(), data) + logger.Error(b.ID, status, err.Error(), data) return } } @@ -272,7 +269,7 @@ func activityBotHandler(w http.ResponseWriter, r *http.Request) { err = b.setBotActivity() if err != nil { raven.CaptureErrorAndWait(err, nil) - logger.Error(b.ClientID, err.Error()) + logger.Error(b.ID, err.Error()) http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "error_save"}), http.StatusInternalServerError) return } @@ -345,6 +342,12 @@ func saveHandler(w http.ResponseWriter, r *http.Request) { 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) @@ -391,24 +394,12 @@ func createHandler(w http.ResponseWriter, r *http.Request) { return } - client := v5.New(c.APIURL, c.APIKEY) - - cr, status, errr := client.APICredentials() - if errr.RuntimeErr != nil { - raven.CaptureErrorAndWait(errr.RuntimeErr, nil) - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found_account"}), http.StatusInternalServerError) - logger.Error(c.APIURL, status, errr.RuntimeErr, cr) + client, err, code := getAPIClient(c.APIURL, c.APIKEY) + if err != nil { + http.Error(w, err.Error(), code) return } - if !cr.Success { - http.Error(w, localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_url_key"}), http.StatusBadRequest) - logger.Error(c.APIURL, status, errr.ApiErr, cr) - return - } - - //TODO: проверка на необходимые методы cr.Credentials - integration := v5.IntegrationModule{ Code: transport, IntegrationCode: transport, @@ -416,7 +407,7 @@ func createHandler(w http.ResponseWriter, r *http.Request) { Name: "Telegram", ClientID: c.ClientID, Logo: fmt.Sprintf( - "https://%s/web/telegram_logo.svg", + "https://%s/static/telegram_logo.svg", config.HTTPServer.Host, ), BaseURL: fmt.Sprintf( @@ -564,3 +555,55 @@ func validateCrmSettings(c Connection) error { 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 +} diff --git a/run.go b/run.go index 34fe7d8..51a13fe 100644 --- a/run.go +++ b/run.go @@ -49,6 +49,6 @@ func (x *RunCommand) Execute(args []string) error { func start() { setWrapperRoutes() setTransportRoutes() - http.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir("web")))) + http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) http.ListenAndServe(config.HTTPServer.Listen, nil) } diff --git a/web/font.css b/static/font.css similarity index 100% rename from web/font.css rename to static/font.css diff --git a/web/font.woff2 b/static/font.woff2 similarity index 100% rename from web/font.woff2 rename to static/font.woff2 diff --git a/web/jquery-3.3.1.min.js b/static/jquery-3.3.1.min.js similarity index 100% rename from web/jquery-3.3.1.min.js rename to static/jquery-3.3.1.min.js diff --git a/web/materialize.min.css b/static/materialize.min.css similarity index 100% rename from web/materialize.min.css rename to static/materialize.min.css diff --git a/web/materialize.min.js b/static/materialize.min.js similarity index 100% rename from web/materialize.min.js rename to static/materialize.min.js diff --git a/web/script.js b/static/script.js similarity index 93% rename from web/script.js rename to static/script.js index e78d8bf..bde8d25 100644 --- a/web/script.js +++ b/static/script.js @@ -24,7 +24,10 @@ $("#add-bot").on("submit", function(e) { e.preventDefault(); send( $(this).attr('action'), - formDataToObj($(this).serializeArray()), + { + connectionId: parseInt($(this).find('input[name=connectionId]').val()), + token: $(this).find('input[name=token]').val(), + }, function (data) { let bots = $("#bots"); if (bots.hasClass("hide")) { @@ -42,7 +45,7 @@ $(document).on("click", ".activity-bot", function(e) { { token: but.attr("data-token"), active: (but.attr("data-activity") === 'true'), - clientId: $('input[name=clientId]').val(), + connectionId: parseInt($('input[name=connectionId]').val()), }, function () { if (but.attr("data-activity") === 'true') { diff --git a/web/style.css b/static/style.css similarity index 100% rename from web/style.css rename to static/style.css diff --git a/web/telegram_logo.svg b/static/telegram_logo.svg similarity index 100% rename from web/telegram_logo.svg rename to static/telegram_logo.svg diff --git a/telegram.go b/telegram.go index d60c872..a00fe9a 100644 --- a/telegram.go +++ b/telegram.go @@ -45,7 +45,7 @@ func telegramWebhookHandler(w http.ResponseWriter, r *http.Request, token string return } - c := getConnection(b.ClientID) + c := getConnectionById(b.ConnectionID) if c.MGURL == "" || c.MGToken == "" { logger.Error(token, "MGURL or MGToken is empty") w.WriteHeader(http.StatusBadRequest) diff --git a/templates/form.html b/templates/form.html index 49dc0dd..2e235c1 100644 --- a/templates/form.html +++ b/templates/form.html @@ -9,7 +9,7 @@