mirror of
https://github.com/retailcrm/mg-bot-helper.git
synced 2025-04-20 01:10:58 +00:00
WIP: migrate to mg-transport-core
This commit is contained in:
parent
cceb2b8949
commit
dc5893ae63
41 changed files with 1017 additions and 1043 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -3,4 +3,6 @@ config_test.yml
|
|||
.idea/
|
||||
/bin/*
|
||||
*.xml
|
||||
/vendor
|
||||
/vendor
|
||||
src/main-packr.go
|
||||
src/packrd/*
|
|
@ -1,11 +1,7 @@
|
|||
FROM golang:1.11-stretch
|
||||
FROM golang:1.13-stretch
|
||||
|
||||
WORKDIR /
|
||||
ADD ./bin/bot /
|
||||
ADD ./templates/ /templates/
|
||||
ADD ./static/ /static/
|
||||
ADD ./translate/ /translate/
|
||||
ADD ./migrations/ /migrations/
|
||||
|
||||
EXPOSE 3001
|
||||
|
||||
|
|
27
Makefile
27
Makefile
|
@ -1,14 +1,16 @@
|
|||
ROOT_DIR=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
SRC_DIR=$(ROOT_DIR)/src
|
||||
MIGRATIONS_DIR=$(ROOT_DIR)/migrations
|
||||
MIGRATIONS_DIR=$(ROOT_DIR)/src/migrations
|
||||
CONFIG_FILE=$(ROOT_DIR)/config.yml
|
||||
CONFIG_TEST_FILE=$(ROOT_DIR)/config_test.yml
|
||||
BIN=$(ROOT_DIR)/bin/bot
|
||||
GO_BIN_DIR=$(shell go env GOPATH)/bin
|
||||
REVISION=$(shell git describe --tags 2>/dev/null || git log --format="v0.0-%h" -n 1 || echo "v0.0-unknown")
|
||||
|
||||
build: deps fmt
|
||||
build: packr_install deps fmt
|
||||
@echo "==> Building"
|
||||
@cd $(SRC_DIR) && CGO_ENABLED=0 go build -o $(BIN) -ldflags "-X common.build=${REVISION}" .
|
||||
@cd $(SRC_DIR) && $(GO_BIN_DIR)/packr2 && CGO_ENABLED=0 go build -o $(BIN) -ldflags "-X common.build=${REVISION}" .
|
||||
@cd $(SRC_DIR) && $(GO_BIN_DIR)/packr2 clean
|
||||
@echo $(BIN)
|
||||
|
||||
run: migrate
|
||||
|
@ -34,11 +36,26 @@ deps:
|
|||
@echo "==> Installing dependencies"
|
||||
@go mod tidy
|
||||
|
||||
migration: transport_tool_install
|
||||
@$(GO_BIN_DIR)/transport-core-tool migration -d $(MIGRATIONS_DIR)
|
||||
|
||||
migrate: build
|
||||
${BIN} --config $(CONFIG_FILE) migrate -p $(MIGRATIONS_DIR)
|
||||
${BIN} --config $(CONFIG_FILE) migrate
|
||||
|
||||
migrate_test: build
|
||||
@${BIN} --config $(CONFIG_TEST_FILE) migrate -p $(MIGRATIONS_DIR)
|
||||
@${BIN} --config $(CONFIG_TEST_FILE) migrate
|
||||
|
||||
migrate_down: build
|
||||
@${BIN} --config $(CONFIG_FILE) migrate -v down
|
||||
|
||||
transport_tool_install:
|
||||
ifeq (, $(shell command -v transport-core-tool 2> /dev/null))
|
||||
@echo "==> Installing migration generator..."
|
||||
@go get -u github.com/retailcrm/mg-transport-core/cmd/transport-core-tool
|
||||
endif
|
||||
|
||||
packr_install:
|
||||
ifeq (, $(shell command -v packr2 2> /dev/null))
|
||||
@go get github.com/gobuffalo/packr/v2/packr2@v2.7.1
|
||||
endif
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ services:
|
|||
- ${POSTGRES_ADDRESS:-127.0.0.1:5434}:${POSTGRES_PORT:-5432}
|
||||
|
||||
mg_bot_test:
|
||||
image: golang:1.11-stretch
|
||||
image: golang:1.13-stretch
|
||||
working_dir: /mg-bot
|
||||
user: ${UID:-1000}:${GID:-1000}
|
||||
environment:
|
||||
|
@ -19,7 +19,9 @@ services:
|
|||
volumes:
|
||||
- ./:/mg-bot/
|
||||
- ./static:/static/
|
||||
- /home/pavel/mg-transports/mg-transport-core:/mg-transport-core
|
||||
links:
|
||||
- postgres_test
|
||||
ports:
|
||||
- ${mg_bot_ADDRESS:-3002}:3002
|
||||
command: "make jenkins_test"
|
|
@ -11,7 +11,7 @@ services:
|
|||
- ${POSTGRES_ADDRESS:-127.0.0.1:5434}:${POSTGRES_PORT:-5432}
|
||||
|
||||
mg_bot:
|
||||
image: golang:1.11-stretch
|
||||
image: golang:1.13-stretch
|
||||
working_dir: /mg-bot
|
||||
user: ${UID:-1000}:${GID:-1000}
|
||||
environment:
|
||||
|
@ -19,6 +19,7 @@ services:
|
|||
volumes:
|
||||
- ./:/mg-bot/
|
||||
- ./static:/static/
|
||||
- /home/pavel/mg-transports/mg-transport-core:/mg-transport-core
|
||||
links:
|
||||
- postgres
|
||||
ports:
|
||||
|
|
70
go.mod
70
go.mod
|
@ -1,60 +1,26 @@
|
|||
module github.com/retailcrm/mg-bot-helper
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.26.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.4.11 // indirect
|
||||
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-20180901172138-1eb28afdf9b6 // 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-20180903072508-084a9de9eb03
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20180827023943-5799bbbb6dce
|
||||
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/golang-migrate/migrate v3.4.0+incompatible
|
||||
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/getsentry/raven-go v0.2.0
|
||||
github.com/gin-contrib/secure v0.0.0-20191217051405-b0e643eac591
|
||||
github.com/gin-gonic/gin v1.5.0
|
||||
github.com/gobuffalo/packr/v2 v2.7.1
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/h2non/gock v1.0.9
|
||||
github.com/h2non/gock v1.0.10
|
||||
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/joho/godotenv v1.2.0 // indirect
|
||||
github.com/json-iterator/go v1.1.5 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/lib/pq v1.0.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.4 // 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 v1.0.1 // indirect
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 // indirect
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.0-beta.5
|
||||
github.com/jinzhu/gorm v1.9.11
|
||||
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.1.2
|
||||
github.com/retailcrm/api-client-go v1.3.0
|
||||
github.com/retailcrm/mg-bot-api-client-go v1.0.16
|
||||
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/ugorji/go v1.1.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20180830192347-182538f80094 // indirect
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d // indirect
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f // indirect
|
||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9 // 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 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.21.0
|
||||
gopkg.in/yaml.v2 v2.2.1
|
||||
github.com/retailcrm/mg-transport-core v1.2.0
|
||||
github.com/rs/cors v1.7.0
|
||||
github.com/stretchr/testify v1.4.0
|
||||
golang.org/x/text v0.3.2
|
||||
gopkg.in/gormigrate.v1 v1.6.0
|
||||
gopkg.in/yaml.v2 v2.2.7
|
||||
)
|
||||
|
||||
replace github.com/retailcrm/mg-transport-core => /mg-transport-core
|
||||
|
||||
go 1.13
|
||||
|
|
334
go.sum
334
go.sum
|
@ -1,120 +1,340 @@
|
|||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.33.1/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||
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.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3 h1:CWUqKXe0s8A2z6qCgkP4Kru7wC11YoAnoupUKFDnH08=
|
||||
github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/aws/aws-sdk-go v1.25.14 h1:hEsU+cukBOQe1wRRuvEgG+y6AVCyS2eyHWuTefhGxTY=
|
||||
github.com/aws/aws-sdk-go v1.25.14/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67 h1:8k9FLYBLKT+9v2HQJ/a95ZemmTx+/ltJcAiRhVushG8=
|
||||
github.com/certifi/gocertifi v0.0.0-20190905060710-a5e0173ced67/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
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-20180901172138-1eb28afdf9b6 h1:BZGp1dbKFjqlGmxEpwkDpCWNxVwEYnUPoncIzLiHlPo=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20180901172138-1eb28afdf9b6/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:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
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/denisenkom/go-mssqldb v0.0.0-20181014144952-4e0d7dc8888f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190830225923-3302f0226fbd h1:DoaaxHqzWPQCWKSTmsi8UDSiFqxbfue+Xt+qi/BFKb8=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190830225923-3302f0226fbd/go.mod h1:uU0N10vx1abI4qeVe79CxepBP6PPREVTgMS5Gx6/mOk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
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-20180903072508-084a9de9eb03 h1:G/9fPivTr5EiyqE9OlW65iMRUxFXMGRHgZFGo50uG8Q=
|
||||
github.com/getsentry/raven-go v0.0.0-20180903072508-084a9de9eb03/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20180827023943-5799bbbb6dce h1:KqeVCdb+M2iwyF6GzdYxTazfE1cE+133RXuGaZ5Sc1E=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20180827023943-5799bbbb6dce/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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
|
||||
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20190914010127-bba2ccfe37ec h1:mfeHJfPwsXp/iovjrTwxtkTMRAIXZu6Uxg6O95En1+Y=
|
||||
github.com/gin-contrib/multitemplate v0.0.0-20190914010127-bba2ccfe37ec/go.mod h1:2tmLQ8sVzr2XKwquGd7zNq3zB6fGyjJL+47JoxoF8yM=
|
||||
github.com/gin-contrib/secure v0.0.0-20191217051405-b0e643eac591 h1:j2ZjjWGWVV/0cBJ/pjjk0jJnCdkmwykLTSiFxUC/ppc=
|
||||
github.com/gin-contrib/secure v0.0.0-20191217051405-b0e643eac591/go.mod h1:6kseOBFrSR3Is/kM1jDhCg/WsXAMvKJkuPvG9dGph/c=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/gin-gonic/gin v1.5.0 h1:fi+bqFAx/oLK54somfCtEZs9HeH1LHVoEPUgARpTqyc=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
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/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
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/golang-migrate/migrate v3.4.0+incompatible h1:9yjg5lYsbeEpWXGc80RylvPMKZ0tZEGsyO3CpYLK3jU=
|
||||
github.com/golang-migrate/migrate v3.4.0+incompatible/go.mod h1:IsVUlFN5puWOmXrqjgGUfIRIbU7mr8oNBE2tyERd9Wk=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
|
||||
github.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=
|
||||
github.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o=
|
||||
github.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=
|
||||
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
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/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
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/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/h2non/gock v1.0.9 h1:17gCehSo8ZOgEsFKpQgqHiR7VLyjxdAG3lkhVvO9QZU=
|
||||
github.com/h2non/gock v1.0.9/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
|
||||
github.com/h2non/gock v1.0.10 h1:EzHYzKKSLN4xk0w193uAy3tp8I3+L1jmaI2Mjg4lCgU=
|
||||
github.com/h2non/gock v1.0.10/go.mod h1:CZMcB0Lg5IWnr9bF79pPMg9WeV6WumxQiUJ1UvdO1iE=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
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/gorm v1.9.2/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo=
|
||||
github.com/jinzhu/gorm v1.9.11 h1:gaHGvE+UnWGlbWG4Y3FUwY1EcZ5n6S9WtqBA/uySMLE=
|
||||
github.com/jinzhu/gorm v1.9.11/go.mod h1:bu/pK8szGZ2puuErfU0RwyeNdsf3e6nCX/noXaVxkfw=
|
||||
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/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc=
|
||||
github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/json-iterator/go v1.1.5 h1:gL2yXlmiIo4+t+y32d4WGwOjKGYcGOuyrg46vadswDE=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v0.0.0-20181116074157-8ec929ed50c3/go.mod h1:oHTiXerJ20+SfYcrdlBO7rzZRJWGwSTQ0iUY2jI6Gfc=
|
||||
github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
||||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
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/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
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/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
|
||||
github.com/mattn/go-isatty v0.0.4/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/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
|
||||
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
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-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
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/nicksnyder/go-i18n/v2 v2.0.2 h1:KsHGcTByIM0mHZKQGy0nlJLOjPNjQ6MVib/3PvsBDNY=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.0.2/go.mod h1:JXS4+OKhbcwDoVTEj0sLFWL1vOwec2g/YBAxZ9owJqY=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
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/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
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/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/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.1.2 h1:bgd3EpS1o3IffgO4p+QOj7Mn+eg6HRd7bIlA5IXDkhU=
|
||||
github.com/retailcrm/api-client-go v1.1.2/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/retailcrm/api-client-go v1.3.0 h1:8cEtLZ9gk+nTEVuzA/wzQhb8tRfaxfCQHLdPtO+/gek=
|
||||
github.com/retailcrm/api-client-go v1.3.0/go.mod h1:QRoPE2SM6ST7i2g0yEdqm7Iw98y7cYuq3q14Ot+6N8c=
|
||||
github.com/retailcrm/mg-bot-api-client-go v1.0.16 h1:l7xzGp0IQTR+jJ//x3vDBz/jHnOG71MhNQtSGWq3rj8=
|
||||
github.com/retailcrm/mg-bot-api-client-go v1.0.16/go.mod h1:lJD4+WLi9CiOk4/2GUvmJ6LG4168eoilXAbfT61yK1U=
|
||||
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50 h1:4bT0pPowCpQImewr+BjzfUKcuFW+KVyB8d1OF3b6oTI=
|
||||
github.com/stevvooe/resumable v0.0.0-20180830230917-22b14a53ba50/go.mod h1:1pdIZTAHUz+HDKDVZ++5xg/duPlhKAIzw9qy42CWYp4=
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.32 h1:IBPltSoD5q2PPZJbNC/prK5F9rEVPXVx/ZzDpi7HKhs=
|
||||
github.com/retailcrm/mg-transport-api-client-go v1.1.32/go.mod h1:AWV6BueE28/6SCoyfKURTo4lF0oXYoOKmHTzehd5vAI=
|
||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.5.0 h1:Usqs0/lDK/NqTkvrmKSwA/3XkZAs7ZAW/eLeQ2MVBTw=
|
||||
github.com/rogpeppe/go-internal v1.5.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
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-20180830192347-182538f80094 h1:rVTAlhYa4+lCfNxmAIEOGQRoD23UqP72M3+rSWVGDTg=
|
||||
golang.org/x/crypto v0.0.0-20180830192347-182538f80094/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181112202954-3d3f9f413869/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190506204251-e1dfcc566284/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
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-20180903190138-2b024373dcd9 h1:lkiLiLBHGoH3XnqSLUIaBsilGMUjI+Uy2Xu2JLUtTas=
|
||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 h1:2AmBLzhAfXj+2HCW09VCkJtHIYgHTIPcTeYqgP7Bwt0=
|
||||
golang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
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/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.2 h1:icxYLlYflpazIV3ufMoNB9h9SYMQ37DZ8CTwkU4pnOs=
|
||||
gopkg.in/go-playground/validator.v9 v9.30.2/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/gormigrate.v1 v1.6.0 h1:XpYM6RHQPmzwY7Uyu+t+xxMXc86JYFJn4nEc9HzQjsI=
|
||||
gopkg.in/gormigrate.v1 v1.6.0/go.mod h1:Lf00lQrHqfSYWiTtPcyQabsDdM6ejZaMgV0OU6JMSlw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
DROP TABLE connection;
|
|
@ -1,17 +0,0 @@
|
|||
create table connection
|
||||
(
|
||||
id serial not null constraint connection_pkey primary key,
|
||||
client_id varchar(70) not null,
|
||||
api_key varchar(100) not null,
|
||||
api_url varchar(255) not null,
|
||||
mg_url varchar(255) not null,
|
||||
mg_token varchar(100) not null,
|
||||
commands jsonb,
|
||||
created_at timestamp with time zone,
|
||||
updated_at timestamp with time zone,
|
||||
active boolean,
|
||||
lang varchar(2) not null
|
||||
);
|
||||
|
||||
alter table connection
|
||||
add constraint connection_key unique (client_id, mg_token);
|
|
@ -1 +0,0 @@
|
|||
alter table connection drop column currency
|
|
@ -1 +0,0 @@
|
|||
alter table connection add column currency varchar(12);
|
|
@ -1,64 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
// BotConfig struct
|
||||
type BotConfig struct {
|
||||
Version string `yaml:"version"`
|
||||
LogLevel logging.Level `yaml:"log_level"`
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
SentryDSN string `yaml:"sentry_dsn"`
|
||||
HTTPServer HTTPServerConfig `yaml:"http_server"`
|
||||
Debug bool `yaml:"debug"`
|
||||
BotInfo BotInfo `yaml:"bot_info"`
|
||||
}
|
||||
|
||||
type BotInfo struct {
|
||||
Name string `yaml:"name"`
|
||||
Code string `yaml:"code"`
|
||||
LogoPath string `yaml:"logo_path"`
|
||||
}
|
||||
|
||||
// DatabaseConfig struct
|
||||
type DatabaseConfig struct {
|
||||
Connection string `yaml:"connection"`
|
||||
Logging bool `yaml:"logging"`
|
||||
TablePrefix string `yaml:"table_prefix"`
|
||||
MaxOpenConnections int `yaml:"max_open_connections"`
|
||||
MaxIdleConnections int `yaml:"max_idle_connections"`
|
||||
ConnectionLifetime int `yaml:"connection_lifetime"`
|
||||
}
|
||||
|
||||
// HTTPServerConfig struct
|
||||
type HTTPServerConfig struct {
|
||||
Host string `yaml:"host"`
|
||||
Listen string `yaml:"listen"`
|
||||
}
|
||||
|
||||
// LoadConfig read configuration file
|
||||
func LoadConfig(path string) *BotConfig {
|
||||
var err error
|
||||
|
||||
path, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
source, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var c BotConfig
|
||||
if err = yaml.Unmarshal(source, &c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
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] = getLocalizedMessage("error_save")
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
var (
|
||||
ok bool
|
||||
conn Connection
|
||||
)
|
||||
|
||||
connection, ok := c.Get("connection")
|
||||
if ok {
|
||||
conn = connection.(Connection)
|
||||
}
|
||||
|
||||
if conn.APIURL != "" {
|
||||
tags["crm"] = conn.APIURL
|
||||
}
|
||||
|
||||
if conn.ClientID != "" {
|
||||
tags["clientID"] = conn.ClientID
|
||||
}
|
||||
|
||||
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(c.Request.RequestURI, recovery)
|
||||
debug.PrintStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ErrorLogger() ErrorHandlerFunc {
|
||||
return func(recovery interface{}, c *gin.Context) {
|
||||
for _, err := range c.Errors {
|
||||
logger.Error(c.Request.RequestURI, err.Err)
|
||||
}
|
||||
}
|
||||
}
|
36
src/helpers.go
Normal file
36
src/helpers.go
Normal file
|
@ -0,0 +1,36 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/retailcrm/api-client-go/v5"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
)
|
||||
|
||||
func getIntegrationModule(clientId string) v5.IntegrationModule {
|
||||
return v5.IntegrationModule{
|
||||
Code: models.GetConfig().BotInfo.Code,
|
||||
IntegrationCode: models.GetConfig().BotInfo.Code,
|
||||
Active: true,
|
||||
Name: models.GetConfig().BotInfo.Name,
|
||||
ClientID: clientId,
|
||||
Logo: fmt.Sprintf(
|
||||
"https://%s%s",
|
||||
models.GetConfig().HTTPServer.Host,
|
||||
models.GetConfig().BotInfo.LogoPath,
|
||||
),
|
||||
BaseURL: fmt.Sprintf(
|
||||
"https://%s",
|
||||
models.GetConfig().HTTPServer.Host,
|
||||
),
|
||||
AccountURL: fmt.Sprintf(
|
||||
"https://%s/settings/%s",
|
||||
models.GetConfig().HTTPServer.Host,
|
||||
clientId,
|
||||
),
|
||||
Actions: map[string]string{"activity": "/actions/activity"},
|
||||
Integrations: &v5.Integrations{
|
||||
MgBot: &v5.MgBot{},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -2,57 +2,20 @@ package main
|
|||
|
||||
import (
|
||||
"html/template"
|
||||
"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]interface{} {
|
||||
func old_getLocale() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"Version": config.Version,
|
||||
"ButtonSave": getLocalizedMessage("button_save"),
|
||||
"ApiKey": getLocalizedMessage("api_key"),
|
||||
"TabSettings": getLocalizedMessage("tab_settings"),
|
||||
"TabBots": getLocalizedMessage("tab_bots"),
|
||||
"TableUrl": getLocalizedMessage("table_url"),
|
||||
"TableActivity": getLocalizedMessage("table_activity"),
|
||||
"Title": getLocalizedMessage("title"),
|
||||
"Language": getLocalizedMessage("language"),
|
||||
"CRMLink": template.HTML(getLocalizedMessage("crm_link")),
|
||||
"DocLink": template.HTML(getLocalizedMessage("doc_link")),
|
||||
"Version": "version here",
|
||||
"ButtonSave": app.GetLocalizedMessage("button_save"),
|
||||
"ApiKey": app.GetLocalizedMessage("api_key"),
|
||||
"TabSettings": app.GetLocalizedMessage("tab_settings"),
|
||||
"TabBots": app.GetLocalizedMessage("tab_bots"),
|
||||
"TableUrl": app.GetLocalizedMessage("table_url"),
|
||||
"TableActivity": app.GetLocalizedMessage("table_activity"),
|
||||
"Title": app.GetLocalizedMessage("title"),
|
||||
"Language": app.GetLocalizedMessage("language"),
|
||||
"CRMLink": template.HTML(app.GetLocalizedMessage("crm_link")),
|
||||
"DocLink": template.HTML(app.GetLocalizedMessage("doc_link")),
|
||||
}
|
||||
}
|
||||
|
|
22
src/log.go
22
src/log.go
|
@ -1,22 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/op/go-logging"
|
||||
)
|
||||
|
||||
var logFormat = logging.MustStringFormatter(
|
||||
`%{time:2006-01-02 15:04:05.000} %{level:.4s} => %{message}`,
|
||||
)
|
||||
|
||||
func newLogger() *logging.Logger {
|
||||
logger := logging.MustGetLogger(config.BotInfo.Code)
|
||||
logBackend := logging.NewLogBackend(os.Stdout, "", 0)
|
||||
formatBackend := logging.NewBackendFormatter(logBackend, logFormat)
|
||||
backend1Leveled := logging.AddModuleLevel(logBackend)
|
||||
backend1Leveled.SetLevel(config.LogLevel, "")
|
||||
logging.SetBackend(formatBackend)
|
||||
|
||||
return logger
|
||||
}
|
43
src/main.go
43
src/main.go
|
@ -3,22 +3,34 @@ package main
|
|||
import (
|
||||
"os"
|
||||
|
||||
"github.com/gobuffalo/packr/v2"
|
||||
"github.com/jessevdk/go-flags"
|
||||
"github.com/op/go-logging"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
// Options struct
|
||||
type Options struct {
|
||||
Config string `short:"c" long:"config" default:"config.yml" description:"Path to configuration file"`
|
||||
Config func(string) `short:"c" long:"config" default:"models.GetConfig().yml" description:"Path to configuration file"`
|
||||
}
|
||||
|
||||
// DefaultFatalError for panics and other uncatched errors
|
||||
const DefaultFatalError = "error_save"
|
||||
|
||||
var (
|
||||
config *BotConfig
|
||||
orm *Orm
|
||||
logger *logging.Logger
|
||||
options Options
|
||||
tokenCounter uint32
|
||||
options = Options{
|
||||
Config: func(s string) {
|
||||
if app == nil {
|
||||
initVariables(s)
|
||||
}
|
||||
},
|
||||
}
|
||||
app *core.Engine
|
||||
parser = flags.NewParser(&options, flags.Default)
|
||||
static *packr.Box
|
||||
templates *packr.Box
|
||||
translations *packr.Box
|
||||
wm *WorkersManager
|
||||
currency = map[string]string{
|
||||
"Российский рубль": "rub",
|
||||
"Гри́вня": "uah",
|
||||
|
@ -29,6 +41,23 @@ var (
|
|||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
static = packr.New("assets", "./../static")
|
||||
templates = packr.New("templates", "./../templates")
|
||||
translations = packr.New("translations", "./../translate")
|
||||
}
|
||||
|
||||
func initVariables(configPath string) {
|
||||
app = core.New().WithCookieSessions()
|
||||
app.Config = (&models.Config{}).LoadConfig(configPath)
|
||||
app.DefaultError = DefaultFatalError
|
||||
app.TranslationsBox = translations
|
||||
app.Prepare()
|
||||
// WorkerManager uses app.Config and app.Logger() under the hood - that's why it should be initialized here
|
||||
wm = NewWorkersManager()
|
||||
models.SetApp(app)
|
||||
}
|
||||
|
||||
func main() {
|
||||
if _, err := parser.Parse(); err != nil {
|
||||
if flagsErr, ok := err.(*flags.Error); ok && flagsErr.Type == flags.ErrHelp {
|
||||
|
|
|
@ -1,83 +1,64 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang-migrate/migrate"
|
||||
_ "github.com/retailcrm/mg-bot-helper/src/migrations"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
func init() {
|
||||
parser.AddCommand("migrate",
|
||||
_, err := parser.AddCommand("migrate",
|
||||
"Migrate database to defined migrations version",
|
||||
"Migrate database to defined migrations version.",
|
||||
&MigrateCommand{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// MigrateCommand struct
|
||||
type MigrateCommand struct {
|
||||
Version string `short:"v" long:"version" default:"up" description:"Migrate to defined migrations version. Allowed: up, down, next, prev and integer value."`
|
||||
Path string `short:"p" long:"path" default:"" description:"Path to migrations files."`
|
||||
Version string `short:"v" long:"version" default:"up" description:"Migrate to defined migrations version. Allowed: up, down, next, prev and migration version."`
|
||||
}
|
||||
|
||||
// Execute method
|
||||
func (x *MigrateCommand) Execute(args []string) error {
|
||||
botConfig := LoadConfig(options.Config)
|
||||
core.Migrations().SetDB(app.DB)
|
||||
|
||||
err := Migrate(botConfig.Database.Connection, x.Version, x.Path)
|
||||
if err != nil && err.Error() == "no change" {
|
||||
fmt.Println("No changes detected. Skipping migration.")
|
||||
err = nil
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Migrate function
|
||||
func Migrate(database string, version string, path string) error {
|
||||
m, err := migrate.New("file://"+path, database)
|
||||
if err != nil {
|
||||
fmt.Printf("Migrations path %s does not exist or permission denied\n", path)
|
||||
if err := Migrate(x.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer m.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
func Migrate(version string) error {
|
||||
currentVersion := core.Migrations().Current()
|
||||
|
||||
defer core.Migrations().Close()
|
||||
|
||||
currentVersion, _, err := m.Version()
|
||||
if "up" == version {
|
||||
fmt.Printf("Migrating from %d to last\n", currentVersion)
|
||||
return m.Up()
|
||||
fmt.Printf("Migrating from %s to last\n", currentVersion)
|
||||
return core.Migrations().Migrate()
|
||||
}
|
||||
|
||||
if "down" == version {
|
||||
fmt.Printf("Migrating from %d to 0\n", currentVersion)
|
||||
return m.Down()
|
||||
fmt.Printf("Migrating from %s to 0\n", currentVersion)
|
||||
return core.Migrations().Rollback()
|
||||
}
|
||||
|
||||
if "next" == version {
|
||||
fmt.Printf("Migrating from %d to next\n", currentVersion)
|
||||
return m.Steps(1)
|
||||
fmt.Printf("Migrating from %s to next", currentVersion)
|
||||
return core.Migrations().MigrateNextTo(currentVersion)
|
||||
}
|
||||
|
||||
if "prev" == version {
|
||||
fmt.Printf("Migrating from %d to previous\n", currentVersion)
|
||||
return m.Steps(-1)
|
||||
fmt.Printf("Migration from %s to previous", currentVersion)
|
||||
return core.Migrations().MigratePreviousTo(currentVersion)
|
||||
}
|
||||
|
||||
ver, err := strconv.ParseUint(version, 10, 32)
|
||||
if err != nil {
|
||||
fmt.Printf("Invalid migration version %s\n", version)
|
||||
return err
|
||||
}
|
||||
|
||||
if ver != 0 {
|
||||
fmt.Printf("Migrating from %d to %d\n", currentVersion, ver)
|
||||
return m.Migrate(uint(ver))
|
||||
}
|
||||
|
||||
fmt.Printf("Migrations not found in path %s\n", path)
|
||||
|
||||
return errors.New("migrations not found")
|
||||
fmt.Printf("Migration from %s to %s", currentVersion, version)
|
||||
return core.Migrations().MigrateTo(version)
|
||||
}
|
||||
|
|
25
src/migrations/1577785798_app.go
Normal file
25
src/migrations/1577785798_app.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
"gopkg.in/gormigrate.v1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
core.Migrations().Add(&gormigrate.Migration{
|
||||
ID: "1577785798",
|
||||
Migrate: func(db *gorm.DB) error {
|
||||
if db.HasTable("schema_migrations") {
|
||||
return db.DropTable("schema_migrations").Error
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Rollback: func(db *gorm.DB) error {
|
||||
return errors.New("this migration cannot be rolled back")
|
||||
},
|
||||
})
|
||||
}
|
22
src/migrations/1577787226_app.go
Normal file
22
src/migrations/1577787226_app.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
"gopkg.in/gormigrate.v1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
core.Migrations().Add(&gormigrate.Migration{
|
||||
ID: "1577787226",
|
||||
Migrate: func(db *gorm.DB) error {
|
||||
return db.AutoMigrate(models.Connection{}).Error
|
||||
},
|
||||
Rollback: func(db *gorm.DB) error {
|
||||
return errors.New("this migration cannot be rolled back")
|
||||
},
|
||||
})
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
)
|
||||
|
||||
// Connection model
|
||||
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" 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
|
||||
UpdatedAt time.Time
|
||||
Active bool `json:"active,omitempty"`
|
||||
Commands postgres.Jsonb `gorm:"commands type:jsonb;" json:"commands,omitempty"`
|
||||
Lang string `gorm:"lang type:varchar(2)" json:"lang,omitempty"`
|
||||
Currency string `gorm:"currency type:varchar(12)" json:"currency,omitempty"`
|
||||
}
|
26
src/models/models.go
Normal file
26
src/models/models.go
Normal file
|
@ -0,0 +1,26 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/jinzhu/gorm/dialects/postgres"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
// Connection model
|
||||
type Connection struct {
|
||||
core.Connection
|
||||
Commands postgres.Jsonb `gorm:"commands type:jsonb;" json:"commands,omitempty"`
|
||||
Lang string `gorm:"lang type:varchar(2)" json:"lang,omitempty"`
|
||||
Currency string `gorm:"currency type:varchar(12)" json:"currency,omitempty"`
|
||||
}
|
||||
|
||||
// Config struct
|
||||
type Config struct {
|
||||
core.Config
|
||||
BotInfo BotInfo `yaml:"bot_info"`
|
||||
}
|
||||
|
||||
type BotInfo struct {
|
||||
Name string `yaml:"name"`
|
||||
Code string `yaml:"code"`
|
||||
LogoPath string `yaml:"logo_path"`
|
||||
}
|
70
src/models/repository.go
Normal file
70
src/models/repository.go
Normal file
|
@ -0,0 +1,70 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var app *core.Engine
|
||||
|
||||
// SetApp set app for all models
|
||||
func SetApp(engine *core.Engine) {
|
||||
if engine == nil {
|
||||
panic("engine shouldn't be nil")
|
||||
}
|
||||
|
||||
app = engine
|
||||
}
|
||||
|
||||
// GetConfig returns configuration
|
||||
func GetConfig() *Config {
|
||||
return app.Config.(*Config)
|
||||
}
|
||||
|
||||
// LoadConfig read & load configuration file
|
||||
func (c *Config) LoadConfig(path string) *Config {
|
||||
data := c.GetConfigData(path)
|
||||
c.Config.LoadConfigFromData(data)
|
||||
if err := yaml.Unmarshal(data, c); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func GetConnection(uid string) *Connection {
|
||||
var connection Connection
|
||||
app.DB.First(&connection, "client_id = ?", uid)
|
||||
|
||||
return &connection
|
||||
}
|
||||
|
||||
func GetConnectionByURL(urlCrm string) *Connection {
|
||||
var connection Connection
|
||||
app.DB.First(&connection, "api_url = ?", urlCrm)
|
||||
|
||||
return &connection
|
||||
}
|
||||
|
||||
func GetActiveConnection() []*Connection {
|
||||
var connection []*Connection
|
||||
app.DB.Find(&connection, "active = ?", true)
|
||||
|
||||
return connection
|
||||
}
|
||||
|
||||
func (c *Connection) SetConnectionActivity() error {
|
||||
return app.DB.Model(c).Where("client_id = ?", c.ClientID).Updates(map[string]interface{}{"active": c.Active, "api_url": c.URL}).Error
|
||||
}
|
||||
|
||||
func (c *Connection) SaveConnection() error {
|
||||
return app.DB.Model(c).Where("client_id = ?", c.ClientID).Update(c).Error
|
||||
}
|
||||
|
||||
func (c *Connection) CreateConnection() error {
|
||||
return app.DB.Create(c).Error
|
||||
}
|
||||
|
||||
func (c *Connection) NormalizeApiUrl() {
|
||||
c.URL = app.RemoveTrailingSlash(c.URL)
|
||||
}
|
37
src/orm.go
37
src/orm.go
|
@ -1,37 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
)
|
||||
|
||||
// Orm struct
|
||||
type Orm struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
// NewDb init new database connection
|
||||
func NewDb(config *BotConfig) *Orm {
|
||||
db, err := gorm.Open("postgres", config.Database.Connection)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
db.DB().SetConnMaxLifetime(time.Duration(config.Database.ConnectionLifetime) * time.Second)
|
||||
db.DB().SetMaxOpenConns(config.Database.MaxOpenConnections)
|
||||
db.DB().SetMaxIdleConns(config.Database.MaxIdleConnections)
|
||||
|
||||
db.SingularTable(true)
|
||||
db.LogMode(config.Database.Logging)
|
||||
|
||||
return &Orm{
|
||||
DB: db,
|
||||
}
|
||||
}
|
||||
|
||||
// Close connection
|
||||
func (orm *Orm) Close() {
|
||||
orm.DB.Close()
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
)
|
||||
|
||||
var rx = regexp.MustCompile(`/+$`)
|
||||
|
||||
func getConnection(uid string) *Connection {
|
||||
var connection Connection
|
||||
orm.DB.First(&connection, "client_id = ?", uid)
|
||||
|
||||
return &connection
|
||||
}
|
||||
|
||||
func getConnectionByURL(urlCrm string) *Connection {
|
||||
var connection Connection
|
||||
orm.DB.First(&connection, "api_url = ?", urlCrm)
|
||||
|
||||
return &connection
|
||||
}
|
||||
|
||||
func getActiveConnection() []*Connection {
|
||||
var connection []*Connection
|
||||
orm.DB.Find(&connection, "active = ?", true)
|
||||
|
||||
return connection
|
||||
}
|
||||
|
||||
func (c *Connection) setConnectionActivity() error {
|
||||
return orm.DB.Model(c).Where("client_id = ?", c.ClientID).Updates(map[string]interface{}{"active": c.Active, "api_url": c.APIURL}).Error
|
||||
}
|
||||
|
||||
func (c *Connection) createConnection() error {
|
||||
return orm.DB.Create(c).Error
|
||||
}
|
||||
|
||||
func (c *Connection) saveConnection() error {
|
||||
return orm.DB.Model(c).Where("client_id = ?", c.ClientID).Update(c).Error
|
||||
}
|
||||
|
||||
func (c *Connection) NormalizeApiUrl() {
|
||||
c.APIURL = rx.ReplaceAllString(c.APIURL, ``)
|
||||
}
|
144
src/routing.go
144
src/routing.go
|
@ -2,22 +2,64 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/retailcrm/api-client-go/v5"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
)
|
||||
|
||||
func initRouting(r *gin.Engine, enableCSRFValidation bool) {
|
||||
csrf := func(c *gin.Context) {}
|
||||
if enableCSRFValidation {
|
||||
csrf = app.VerifyCSRFMiddleware(core.DefaultIgnoredMethods)
|
||||
}
|
||||
|
||||
r.GET("/", checkAccountForRequest(), connectHandler)
|
||||
r.Any("/settings/:uid", settingsHandler)
|
||||
r.POST("/save/", csrf, checkConnectionForRequest(), saveHandler)
|
||||
r.POST("/create/", csrf, checkConnectionForRequest(), createHandler)
|
||||
r.POST("/bot-settings/", botSettingsHandler)
|
||||
r.POST("/actions/activity", activityHandler)
|
||||
}
|
||||
|
||||
func checkAccountForRequest() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ra := app.RemoveTrailingSlash(c.Query("account"))
|
||||
p := models.Connection{
|
||||
Connection: core.Connection{
|
||||
URL: ra,
|
||||
},
|
||||
}
|
||||
|
||||
c.Set("account", p)
|
||||
}
|
||||
}
|
||||
|
||||
func checkConnectionForRequest() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var conn models.Connection
|
||||
|
||||
if err := c.ShouldBindJSON(&conn); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": app.GetLocalizedMessage("incorrect_url_key")})
|
||||
return
|
||||
}
|
||||
conn.NormalizeApiUrl()
|
||||
|
||||
c.Set("connection", conn)
|
||||
}
|
||||
}
|
||||
|
||||
func connectHandler(c *gin.Context) {
|
||||
res := struct {
|
||||
Conn Connection
|
||||
Locale map[string]interface{}
|
||||
Year int
|
||||
Conn models.Connection
|
||||
TokenCSRF string
|
||||
Year int
|
||||
}{
|
||||
c.MustGet("account").(Connection),
|
||||
getLocale(),
|
||||
c.MustGet("account").(models.Connection),
|
||||
app.GetCSRFToken(c),
|
||||
time.Now().Year(),
|
||||
}
|
||||
|
||||
|
@ -32,11 +74,11 @@ func botSettingsHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
conn := getConnection(jm["client_id"])
|
||||
conn := models.GetConnection(jm["client_id"])
|
||||
conn.Lang = jm["lang"]
|
||||
conn.Currency = jm["currency"]
|
||||
|
||||
err := conn.saveConnection()
|
||||
err := conn.SaveConnection()
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
return
|
||||
|
@ -44,26 +86,26 @@ func botSettingsHandler(c *gin.Context) {
|
|||
|
||||
wm.setWorker(conn)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"msg": getLocalizedMessage("successful")})
|
||||
c.JSON(http.StatusOK, gin.H{"msg": app.GetLocalizedMessage("successful")})
|
||||
}
|
||||
|
||||
func settingsHandler(c *gin.Context) {
|
||||
uid := c.Param("uid")
|
||||
p := getConnection(uid)
|
||||
p := models.GetConnection(uid)
|
||||
if p.ID == 0 {
|
||||
c.Redirect(http.StatusFound, "/")
|
||||
return
|
||||
}
|
||||
|
||||
res := struct {
|
||||
Conn *Connection
|
||||
Locale map[string]interface{}
|
||||
Conn *models.Connection
|
||||
TokenCSRF string
|
||||
Year int
|
||||
LangCode []string
|
||||
CurrencyCode map[string]string
|
||||
}{
|
||||
p,
|
||||
getLocale(),
|
||||
app.GetCSRFToken(c),
|
||||
time.Now().Year(),
|
||||
[]string{"en", "ru", "es"},
|
||||
currency,
|
||||
|
@ -73,9 +115,9 @@ func settingsHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
func saveHandler(c *gin.Context) {
|
||||
conn := c.MustGet("connection").(Connection)
|
||||
conn := c.MustGet("connection").(models.Connection)
|
||||
|
||||
_, err, code := getAPIClient(conn.APIURL, conn.APIKEY)
|
||||
_, code, err := app.GetAPIClient(conn.URL, conn.Key)
|
||||
if err != nil {
|
||||
if code == http.StatusInternalServerError {
|
||||
c.Error(err)
|
||||
|
@ -85,7 +127,7 @@ func saveHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
err = conn.saveConnection()
|
||||
err = conn.SaveConnection()
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
return
|
||||
|
@ -93,19 +135,19 @@ func saveHandler(c *gin.Context) {
|
|||
|
||||
wm.setWorker(&conn)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"msg": getLocalizedMessage("successful")})
|
||||
c.JSON(http.StatusOK, gin.H{"msg": app.GetLocalizedMessage("successful")})
|
||||
}
|
||||
|
||||
func createHandler(c *gin.Context) {
|
||||
conn := c.MustGet("connection").(Connection)
|
||||
conn := c.MustGet("connection").(models.Connection)
|
||||
|
||||
cl := getConnectionByURL(conn.APIURL)
|
||||
cl := models.GetConnectionByURL(conn.URL)
|
||||
if cl.ID != 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("connection_already_created")})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": app.GetLocalizedMessage("connection_already_created")})
|
||||
return
|
||||
}
|
||||
|
||||
client, err, code := getAPIClient(conn.APIURL, conn.APIKEY)
|
||||
client, code, err := app.GetAPIClient(conn.URL, conn.Key)
|
||||
if err != nil {
|
||||
if code == http.StatusInternalServerError {
|
||||
c.Error(err)
|
||||
|
@ -115,22 +157,22 @@ func createHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
conn.ClientID = GenerateToken()
|
||||
conn.ClientID = app.GenerateToken()
|
||||
|
||||
data, status, e := client.IntegrationModuleEdit(getIntegrationModule(conn.ClientID))
|
||||
if e.RuntimeErr != nil {
|
||||
c.Error(e.RuntimeErr)
|
||||
if e != nil && e.Error() != "" {
|
||||
c.Error(e)
|
||||
return
|
||||
}
|
||||
|
||||
if status >= http.StatusBadRequest {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("error_activity_mg")})
|
||||
logger.Error(conn.APIURL, status, e.ApiErr, data)
|
||||
if status >= http.StatusBadRequest && e != nil {
|
||||
c.JSON(app.BadRequestLocalized("error_activity_mg"))
|
||||
app.Logger().Error(conn.URL, status, e.ApiError(), e.ApiErrors(), data)
|
||||
return
|
||||
}
|
||||
|
||||
conn.MGURL = data.Info.MgBotInfo.EndpointUrl
|
||||
conn.MGToken = data.Info.MgBotInfo.Token
|
||||
conn.GateURL = data.Info.MgBotInfo.EndpointUrl
|
||||
conn.GateToken = data.Info.MgBotInfo.Token
|
||||
conn.Active = true
|
||||
conn.Lang = "ru"
|
||||
conn.Currency = currency["Российский рубль"]
|
||||
|
@ -138,14 +180,14 @@ func createHandler(c *gin.Context) {
|
|||
bj, _ := json.Marshal(botCommands)
|
||||
conn.Commands.RawMessage = bj
|
||||
|
||||
code, err = SetBotCommand(conn.MGURL, conn.MGToken)
|
||||
code, err = SetBotCommand(conn.GateURL, conn.GateToken)
|
||||
if err != nil {
|
||||
c.JSON(code, gin.H{"error": getLocalizedMessage("error_activity_mg")})
|
||||
logger.Error(conn.APIURL, code, err)
|
||||
c.JSON(code, gin.H{"error": app.GetLocalizedMessage("error_activity_mg")})
|
||||
app.Logger().Error(conn.URL, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = conn.createConnection()
|
||||
err = conn.CreateConnection()
|
||||
if err != nil {
|
||||
c.Error(err)
|
||||
return
|
||||
|
@ -157,7 +199,7 @@ func createHandler(c *gin.Context) {
|
|||
http.StatusCreated,
|
||||
gin.H{
|
||||
"url": "/settings/" + conn.ClientID,
|
||||
"message": getLocalizedMessage("successful"),
|
||||
"message": app.GetLocalizedMessage("successful"),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -169,7 +211,7 @@ func activityHandler(c *gin.Context) {
|
|||
clientId = c.PostForm("clientId")
|
||||
)
|
||||
|
||||
conn := getConnection(clientId)
|
||||
conn := models.GetConnection(clientId)
|
||||
if conn.ID == 0 {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest,
|
||||
gin.H{
|
||||
|
@ -194,11 +236,11 @@ func activityHandler(c *gin.Context) {
|
|||
conn.Active = activity.Active && !activity.Freeze
|
||||
|
||||
if systemUrl != "" {
|
||||
conn.APIURL = systemUrl
|
||||
conn.URL = systemUrl
|
||||
}
|
||||
conn.NormalizeApiUrl()
|
||||
|
||||
if err := conn.setConnectionActivity(); err != nil {
|
||||
if err := conn.SetConnectionActivity(); err != nil {
|
||||
c.Error(err)
|
||||
return
|
||||
}
|
||||
|
@ -211,31 +253,3 @@ func activityHandler(c *gin.Context) {
|
|||
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
func getIntegrationModule(clientId string) v5.IntegrationModule {
|
||||
return v5.IntegrationModule{
|
||||
Code: config.BotInfo.Code,
|
||||
IntegrationCode: config.BotInfo.Code,
|
||||
Active: true,
|
||||
Name: config.BotInfo.Name,
|
||||
ClientID: clientId,
|
||||
Logo: fmt.Sprintf(
|
||||
"https://%s%s",
|
||||
config.HTTPServer.Host,
|
||||
config.BotInfo.LogoPath,
|
||||
),
|
||||
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{
|
||||
MgBot: &v5.MgBot{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,45 +8,58 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/h2non/gock"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
router *gin.Engine
|
||||
crmUrl = "https://test.retailcrm.ru"
|
||||
clientID = "09385039f039irf039fkj309fj30jf3"
|
||||
)
|
||||
|
||||
func init() {
|
||||
configPath := path.Clean("./../config_test.yml")
|
||||
info, err := os.Stat(configPath)
|
||||
if configPath == "/" || configPath == "." || err != nil || info.IsDir() {
|
||||
configPath = path.Clean("./config_test.yml")
|
||||
}
|
||||
|
||||
if configPath == "/" || configPath == "." {
|
||||
panic("config_test.yml not found")
|
||||
}
|
||||
|
||||
initVariables(configPath)
|
||||
app.Router().HTMLRender = nil
|
||||
|
||||
initialize(false)
|
||||
os.Chdir("../")
|
||||
config = LoadConfig("config_test.yml")
|
||||
orm = NewDb(config)
|
||||
logger = newLogger()
|
||||
router = setup()
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
c := Connection{
|
||||
ID: 1,
|
||||
ClientID: clientID,
|
||||
APIKEY: "ii32if32iuf23iufn2uifnr23inf",
|
||||
APIURL: crmUrl,
|
||||
MGURL: "https://test.retailcrm.pro",
|
||||
MGToken: "988730985u23r390rf8j3984jf32904fj",
|
||||
Active: true,
|
||||
c := models.Connection{
|
||||
Connection: core.Connection{
|
||||
ID: 1,
|
||||
ClientID: clientID,
|
||||
Key: "ii32if32iuf23iufn2uifnr23inf",
|
||||
URL: crmUrl,
|
||||
GateURL: "https://test.retailcrm.pro",
|
||||
GateToken: "988730985u23r390rf8j3984jf32904fj",
|
||||
Active: true,
|
||||
},
|
||||
}
|
||||
|
||||
orm.DB.Delete(Connection{}, "id > ?", 0)
|
||||
app.DB.Delete(models.Connection{}, "id > ?", 0)
|
||||
|
||||
c.createConnection()
|
||||
c.CreateConnection()
|
||||
retCode := m.Run()
|
||||
orm.DB.Delete(Connection{}, "id > ?", 0)
|
||||
app.DB.Delete(models.Connection{}, "id > ?", 0)
|
||||
os.Exit(retCode)
|
||||
}
|
||||
|
||||
|
@ -56,7 +69,7 @@ func TestRouting_connectHandler(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
router.ServeHTTP(rr, req)
|
||||
app.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))
|
||||
|
@ -69,7 +82,7 @@ func TestRouting_settingsHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
app.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))
|
||||
|
@ -97,7 +110,7 @@ func TestRouting_saveHandler(t *testing.T) {
|
|||
}
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
app.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))
|
||||
|
@ -138,7 +151,7 @@ func TestRouting_activityHandler(t *testing.T) {
|
|||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
router.ServeHTTP(rr, req)
|
||||
app.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))
|
||||
|
@ -155,7 +168,7 @@ func TestRouting_activityHandler(t *testing.T) {
|
|||
t.Error("worker don`t stop")
|
||||
}
|
||||
|
||||
if ok && w.connection.APIURL != v.Get("systemUrl") {
|
||||
if ok && w.connection.URL != v.Get("systemUrl") {
|
||||
t.Error("fail update systemUrl")
|
||||
}
|
||||
}
|
||||
|
|
165
src/run.go
165
src/run.go
|
@ -1,16 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"html/template"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/getsentry/raven-go"
|
||||
"github.com/gin-contrib/multitemplate"
|
||||
"github.com/gin-contrib/secure"
|
||||
"github.com/gin-gonic/gin"
|
||||
_ "github.com/golang-migrate/migrate/database/postgres"
|
||||
_ "github.com/golang-migrate/migrate/source/file"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
cors "github.com/rs/cors/wrapper/gin"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -21,20 +22,14 @@ func init() {
|
|||
)
|
||||
}
|
||||
|
||||
var (
|
||||
sentry *raven.Client
|
||||
wm = NewWorkersManager()
|
||||
)
|
||||
var sentry *raven.Client
|
||||
|
||||
// RunCommand struct
|
||||
type RunCommand struct{}
|
||||
|
||||
// Execute command
|
||||
func (x *RunCommand) Execute(args []string) error {
|
||||
config = LoadConfig(options.Config)
|
||||
orm = NewDb(config)
|
||||
logger = newLogger()
|
||||
|
||||
initialize(true)
|
||||
go start()
|
||||
|
||||
c := make(chan os.Signal, 1)
|
||||
|
@ -42,7 +37,7 @@ func (x *RunCommand) Execute(args []string) error {
|
|||
for sig := range c {
|
||||
switch sig {
|
||||
case os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM:
|
||||
orm.DB.Close()
|
||||
app.DB.Close()
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
|
@ -51,93 +46,79 @@ func (x *RunCommand) Execute(args []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func start() {
|
||||
router := setup()
|
||||
startWS()
|
||||
router.Run(config.HTTPServer.Listen)
|
||||
}
|
||||
|
||||
func setup() *gin.Engine {
|
||||
loadTranslateFile()
|
||||
setValidation()
|
||||
|
||||
if config.Debug == false {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
}
|
||||
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
if config.Debug {
|
||||
r.Use(gin.Logger())
|
||||
}
|
||||
|
||||
r.HTMLRender = createHTMLRender()
|
||||
|
||||
r.Static("/static", "./static")
|
||||
|
||||
r.Use(func(c *gin.Context) {
|
||||
setLocale(c.GetHeader("Accept-Language"))
|
||||
})
|
||||
|
||||
errorHandlers := []ErrorHandlerFunc{
|
||||
PanicLogger(),
|
||||
ErrorResponseHandler(),
|
||||
}
|
||||
|
||||
sentry, _ = raven.New(config.SentryDSN)
|
||||
if sentry != nil {
|
||||
errorHandlers = append(errorHandlers, ErrorCaptureHandler(sentry, true))
|
||||
}
|
||||
|
||||
r.Use(ErrorHandler(errorHandlers...))
|
||||
|
||||
r.GET("/", checkAccountForRequest(), connectHandler)
|
||||
r.Any("/settings/:uid", settingsHandler)
|
||||
r.POST("/save/", checkConnectionForRequest(), saveHandler)
|
||||
r.POST("/create/", checkConnectionForRequest(), createHandler)
|
||||
r.POST("/bot-settings/", botSettingsHandler)
|
||||
r.POST("/actions/activity", activityHandler)
|
||||
|
||||
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 checkAccountForRequest() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
ra := rx.ReplaceAllString(c.Query("account"), ``)
|
||||
p := Connection{
|
||||
APIURL: ra,
|
||||
}
|
||||
|
||||
c.Set("account", p)
|
||||
}
|
||||
}
|
||||
|
||||
func checkConnectionForRequest() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
var conn Connection
|
||||
|
||||
if err := c.ShouldBindJSON(&conn); err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": getLocalizedMessage("incorrect_url_key")})
|
||||
func initialize(enableCSRFVerification bool) {
|
||||
app.TaggedTypes = getSentryTaggedTypes()
|
||||
secret, _ := core.GetEntitySHA1(app.Config.GetTransportInfo())
|
||||
app.InitCSRF(
|
||||
secret,
|
||||
func(c *gin.Context, r core.CSRFErrorReason) {
|
||||
app.Logger().Errorf("[%s]: wrong csrf token: %s", c.Request.RemoteAddr, core.GetCSRFErrorMessage(r))
|
||||
c.AbortWithStatusJSON(app.BadRequestLocalized("incorrect_csrf_token"))
|
||||
return
|
||||
}
|
||||
conn.NormalizeApiUrl()
|
||||
},
|
||||
core.DefaultCSRFTokenGetter,
|
||||
)
|
||||
|
||||
c.Set("connection", conn)
|
||||
}
|
||||
app.ConfigureRouter(func(r *gin.Engine) {
|
||||
r.StaticFS("/static", static)
|
||||
|
||||
if r.HTMLRender == nil {
|
||||
r.HTMLRender = app.CreateRendererFS(templates, insertTemplates, template.FuncMap{})
|
||||
}
|
||||
|
||||
r.Use(cors.New(cors.Options{
|
||||
AllowedOrigins: []string{"https://" + app.Config.GetHTTPConfig().Host},
|
||||
AllowedMethods: []string{"HEAD", "GET", "POST", "PUT", "DELETE"},
|
||||
MaxAge: 60 * 5,
|
||||
AllowCredentials: true,
|
||||
OptionsPassthrough: false,
|
||||
Debug: false,
|
||||
}))
|
||||
r.Use(secure.New(secure.Config{
|
||||
STSSeconds: 315360000,
|
||||
IsDevelopment: false,
|
||||
STSIncludeSubdomains: true,
|
||||
FrameDeny: true,
|
||||
ContentTypeNosniff: true,
|
||||
BrowserXssFilter: true,
|
||||
IENoOpen: true,
|
||||
SSLProxyHeaders: map[string]string{"X-Forwarded-Proto": "https"},
|
||||
ContentSecurityPolicy: "default-src 'self' 'unsafe-eval' 'unsafe-inline' *.facebook.net *.facebook.com; object-src 'none'",
|
||||
}))
|
||||
|
||||
if enableCSRFVerification {
|
||||
r.Use(app.GenerateCSRFMiddleware())
|
||||
}
|
||||
|
||||
initRouting(r, enableCSRFVerification)
|
||||
})
|
||||
app.BuildHTTPClient(true)
|
||||
}
|
||||
|
||||
func start() {
|
||||
startWS()
|
||||
app.Run()
|
||||
}
|
||||
|
||||
func startWS() {
|
||||
res := getActiveConnection()
|
||||
res := models.GetActiveConnection()
|
||||
if len(res) > 0 {
|
||||
for _, v := range res {
|
||||
wm.setWorker(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getSentryTaggedTypes() core.SentryTaggedTypes {
|
||||
return core.SentryTaggedTypes{
|
||||
core.NewTaggedStruct(models.Connection{}, "connection", core.SentryTags{
|
||||
"crm": "URL",
|
||||
"clientID": "ClientID",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func insertTemplates(r *core.Renderer) {
|
||||
r.Push("home", "layout.html", "home.html")
|
||||
r.Push("form", "layout.html", "form.html")
|
||||
}
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
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()
|
||||
}
|
9
src/translations_extractor.go
Normal file
9
src/translations_extractor.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// +build ignore
|
||||
// This dummy file is used to run test case which will check translations correctness.
|
||||
// Actual TranslationsExtractor object lives in mg-transport-core.
|
||||
|
||||
package main
|
||||
|
||||
var _ = func() interface{} {
|
||||
return nil
|
||||
}
|
88
src/translations_extractor_test.go
Normal file
88
src/translations_extractor_test.go
Normal file
|
@ -0,0 +1,88 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
// TranslationsExtractorTest will compare correctness between translations. It uses TranslationExtractor.
|
||||
// TranslationExtractor will load translations data from files or from box, and then it will be used to
|
||||
// compare every translation file keys to keys from all other translation files. If there is any
|
||||
// difference - test will fail.
|
||||
type TranslationsExtractorTest struct {
|
||||
suite.Suite
|
||||
extractor *core.TranslationsExtractor
|
||||
locales []string
|
||||
}
|
||||
|
||||
// Test_Translations suite runner
|
||||
func Test_Translations(t *testing.T) {
|
||||
suite.Run(t, &TranslationsExtractorTest{
|
||||
extractor: core.NewTranslationsExtractor("translate.{}.yml"),
|
||||
locales: []string{"en", "es", "ru"},
|
||||
})
|
||||
}
|
||||
|
||||
// getError returns error message from text, or empty string if error is nil
|
||||
func (t *TranslationsExtractorTest) getError(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
func (t *TranslationsExtractorTest) SetupSuite() {
|
||||
configPath := path.Clean("./../config_test.yml")
|
||||
info, err := os.Stat(configPath)
|
||||
if configPath == "/" || configPath == "." || err != nil || info.IsDir() {
|
||||
configPath = path.Clean("./config_test.yml")
|
||||
}
|
||||
|
||||
require.False(t.T(), configPath == "/" || configPath == ".", "config_test.yml not found")
|
||||
|
||||
initVariables(configPath)
|
||||
|
||||
require.NotNil(t.T(), app, "app must be initialized to test translations")
|
||||
require.False(t.T(), app.TranslationsPath == "" && app.TranslationsBox == nil,
|
||||
"translations path or translations box must be initialized in app")
|
||||
|
||||
t.extractor.TranslationsPath = app.TranslationsPath
|
||||
t.extractor.TranslationsBox = app.TranslationsBox
|
||||
}
|
||||
|
||||
func (t *TranslationsExtractorTest) Test_Locales() {
|
||||
checked := map[string]string{}
|
||||
localeData := map[string][]string{}
|
||||
|
||||
for _, locale := range t.locales {
|
||||
data, err := t.extractor.LoadLocaleKeys(locale)
|
||||
require.NoError(t.T(), err, fmt.Sprintf("error while loading locale `%s`: %s", locale, t.getError(err)))
|
||||
localeData[locale] = data
|
||||
}
|
||||
|
||||
for _, comparableLocale := range t.locales {
|
||||
for _, innerLocale := range t.locales {
|
||||
if comparableLocale == innerLocale {
|
||||
continue
|
||||
}
|
||||
|
||||
if checkedLocale, ok := checked[comparableLocale]; ok && checkedLocale == innerLocale {
|
||||
continue
|
||||
}
|
||||
|
||||
diff := cmp.Diff(localeData[comparableLocale], localeData[innerLocale])
|
||||
assert.Empty(t.T(), diff,
|
||||
fmt.Sprintf("non-empty diff between `%s` and `%s`", comparableLocale, innerLocale))
|
||||
checked[innerLocale] = comparableLocale
|
||||
}
|
||||
}
|
||||
}
|
66
src/utils.go
66
src/utils.go
|
@ -1,66 +0,0 @@
|
|||
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"
|
||||
)
|
||||
|
||||
// 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, _, e := client.APICredentials()
|
||||
if e.RuntimeErr != nil {
|
||||
return nil, e.RuntimeErr, http.StatusInternalServerError
|
||||
}
|
||||
|
||||
if !cr.Success {
|
||||
return nil, errors.New(getLocalizedMessage("incorrect_url_key")), http.StatusBadRequest
|
||||
}
|
||||
|
||||
if res := checkCredentials(cr.Credentials); len(res) != 0 {
|
||||
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 := make([]string, len(botCredentials))
|
||||
copy(rc, botCredentials)
|
||||
|
||||
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
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"gopkg.in/go-playground/validator.v9"
|
||||
)
|
||||
|
||||
var regCommandName = regexp.MustCompile(`^https://?[\da-z.-]+\.(retailcrm\.(ru|pro|es)|ecomlogic\.com|simlachat\.(com|ru))/?$`)
|
||||
|
||||
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 {
|
||||
return regCommandName.Match([]byte(field.Field().Interface().(string)))
|
||||
}
|
167
src/worker.go
167
src/worker.go
|
@ -11,11 +11,12 @@ import (
|
|||
|
||||
"github.com/getsentry/raven-go"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"github.com/op/go-logging"
|
||||
"github.com/retailcrm/api-client-go/errs"
|
||||
v5 "github.com/retailcrm/api-client-go/v5"
|
||||
v1 "github.com/retailcrm/mg-bot-api-client-go/v1"
|
||||
"github.com/retailcrm/mg-bot-helper/src/models"
|
||||
"github.com/retailcrm/mg-transport-core/core"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
|
@ -26,26 +27,19 @@ const (
|
|||
)
|
||||
|
||||
var (
|
||||
events = []string{v1.WsEventMessageNew}
|
||||
msgLen = 2000
|
||||
emoji = []string{"0️⃣ ", "1️⃣ ", "2️⃣ ", "3️⃣ ", "4️⃣ ", "5️⃣ ", "6️⃣ ", "7️⃣ ", "8️⃣ ", "9️⃣ "}
|
||||
botCommands = []string{CommandPayment, CommandDelivery, CommandProduct}
|
||||
botCredentials = []string{
|
||||
"/api/integration-modules/{code}",
|
||||
"/api/integration-modules/{code}/edit",
|
||||
"/api/reference/payment-types",
|
||||
"/api/reference/delivery-types",
|
||||
"/api/store/products",
|
||||
}
|
||||
events = []string{v1.WsEventMessageNew}
|
||||
msgLen = 2000
|
||||
emoji = []string{"0️⃣ ", "1️⃣ ", "2️⃣ ", "3️⃣ ", "4️⃣ ", "5️⃣ ", "6️⃣ ", "7️⃣ ", "8️⃣ ", "9️⃣ "}
|
||||
botCommands = []string{CommandPayment, CommandDelivery, CommandProduct}
|
||||
)
|
||||
|
||||
type Worker struct {
|
||||
connection *Connection
|
||||
connection *models.Connection
|
||||
mutex sync.RWMutex
|
||||
localizer *i18n.Localizer
|
||||
localizer *core.Localizer
|
||||
|
||||
sentry *raven.Client
|
||||
logger *logging.Logger
|
||||
log chan LogMessage
|
||||
|
||||
mgClient *v1.MgClient
|
||||
crmClient *v5.Client
|
||||
|
@ -53,10 +47,10 @@ type Worker struct {
|
|||
close bool
|
||||
}
|
||||
|
||||
func NewWorker(conn *Connection, sentry *raven.Client, logger *logging.Logger) *Worker {
|
||||
crmClient := v5.New(conn.APIURL, conn.APIKEY)
|
||||
mgClient := v1.New(conn.MGURL, conn.MGToken)
|
||||
if config.Debug {
|
||||
func NewWorker(conn *models.Connection, sentry *raven.Client, logChannel chan LogMessage) *Worker {
|
||||
crmClient := v5.New(conn.URL, conn.Key)
|
||||
mgClient := v1.New(conn.GateURL, conn.GateToken)
|
||||
if app.Config.IsDebug() {
|
||||
crmClient.Debug = true
|
||||
mgClient.Debug = true
|
||||
}
|
||||
|
@ -64,47 +58,85 @@ func NewWorker(conn *Connection, sentry *raven.Client, logger *logging.Logger) *
|
|||
return &Worker{
|
||||
connection: conn,
|
||||
sentry: sentry,
|
||||
logger: logger,
|
||||
localizer: getLang(conn.Lang),
|
||||
log: logChannel,
|
||||
localizer: getLocalizer(conn.Lang),
|
||||
mgClient: mgClient,
|
||||
crmClient: crmClient,
|
||||
close: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Worker) UpdateWorker(conn *Connection) {
|
||||
func (w *Worker) UpdateWorker(conn *models.Connection) {
|
||||
w.mutex.RLock()
|
||||
defer w.mutex.RUnlock()
|
||||
|
||||
w.localizer = getLang(conn.Lang)
|
||||
w.localizer = getLocalizer(conn.Lang)
|
||||
w.connection = conn
|
||||
}
|
||||
|
||||
func (w *Worker) writeLog(text string, severity logging.Level) {
|
||||
w.log <- LogMessage{
|
||||
Text: text,
|
||||
Severity: severity,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Worker) sendSentry(err error) {
|
||||
tags := map[string]string{
|
||||
"crm": w.connection.APIURL,
|
||||
"crm": w.connection.URL,
|
||||
"active": strconv.FormatBool(w.connection.Active),
|
||||
"lang": w.connection.Lang,
|
||||
"currency": w.connection.Currency,
|
||||
"updated_at": w.connection.UpdatedAt.String(),
|
||||
}
|
||||
|
||||
w.logger.Errorf("ws url: %s\nmgClient: %v\nerr: %v", w.crmClient.URL, w.mgClient, err)
|
||||
w.writeLog(fmt.Sprintf("ws url: %s\nmgClient: %v\nerr: %v", w.crmClient.URL, w.mgClient, err), logging.ERROR)
|
||||
go w.sentry.CaptureError(err, tags)
|
||||
}
|
||||
|
||||
// LogMessage represents log message
|
||||
type LogMessage struct {
|
||||
Text string
|
||||
Severity logging.Level
|
||||
}
|
||||
|
||||
type WorkersManager struct {
|
||||
mutex sync.RWMutex
|
||||
log chan LogMessage
|
||||
workers map[string]*Worker
|
||||
}
|
||||
|
||||
func NewWorkersManager() *WorkersManager {
|
||||
return &WorkersManager{
|
||||
wm := &WorkersManager{
|
||||
workers: map[string]*Worker{},
|
||||
log: make(chan LogMessage),
|
||||
}
|
||||
|
||||
go wm.logCollector()
|
||||
|
||||
return wm
|
||||
}
|
||||
|
||||
func (wm *WorkersManager) logCollector() {
|
||||
for msg := range wm.log {
|
||||
switch msg.Severity {
|
||||
case logging.CRITICAL:
|
||||
app.Logger().Critical(msg.Text)
|
||||
case logging.ERROR:
|
||||
app.Logger().Error(msg.Text)
|
||||
case logging.WARNING:
|
||||
app.Logger().Warning(msg.Text)
|
||||
case logging.NOTICE:
|
||||
app.Logger().Notice(msg.Text)
|
||||
case logging.INFO:
|
||||
app.Logger().Info(msg.Text)
|
||||
case logging.DEBUG:
|
||||
app.Logger().Debug(msg.Text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *WorkersManager) setWorker(conn *Connection) {
|
||||
func (wm *WorkersManager) setWorker(conn *models.Connection) {
|
||||
wm.mutex.Lock()
|
||||
defer wm.mutex.Unlock()
|
||||
|
||||
|
@ -113,13 +145,13 @@ func (wm *WorkersManager) setWorker(conn *Connection) {
|
|||
if ok {
|
||||
worker.UpdateWorker(conn)
|
||||
} else {
|
||||
wm.workers[conn.ClientID] = NewWorker(conn, sentry, logger)
|
||||
wm.workers[conn.ClientID] = NewWorker(conn, sentry, wm.log)
|
||||
go wm.workers[conn.ClientID].UpWS()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *WorkersManager) stopWorker(conn *Connection) {
|
||||
func (wm *WorkersManager) stopWorker(conn *models.Connection) {
|
||||
wm.mutex.Lock()
|
||||
defer wm.mutex.Unlock()
|
||||
|
||||
|
@ -140,8 +172,8 @@ func (w *Worker) UpWS() {
|
|||
ROOT:
|
||||
for {
|
||||
if w.close {
|
||||
if config.Debug {
|
||||
w.logger.Debug("stop ws:", w.connection.APIURL)
|
||||
if app.Config.IsDebug() {
|
||||
w.writeLog(fmt.Sprint("stop ws:", w.connection.URL), logging.DEBUG)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -152,8 +184,8 @@ ROOT:
|
|||
continue ROOT
|
||||
}
|
||||
|
||||
if config.Debug {
|
||||
w.logger.Info("start ws: ", w.crmClient.URL)
|
||||
if app.Config.IsDebug() {
|
||||
w.writeLog(fmt.Sprint("start ws: ", w.crmClient.URL), logging.INFO)
|
||||
}
|
||||
|
||||
for {
|
||||
|
@ -168,8 +200,8 @@ ROOT:
|
|||
}
|
||||
|
||||
if w.close {
|
||||
if config.Debug {
|
||||
w.logger.Debug("stop ws:", w.connection.APIURL)
|
||||
if app.Config.IsDebug() {
|
||||
w.writeLog(fmt.Sprint("stop ws:", w.connection.URL), logging.DEBUG)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -188,7 +220,7 @@ ROOT:
|
|||
msg, msgProd, err := w.execCommand(eventData.Message.Content)
|
||||
if err != nil {
|
||||
w.sendSentry(err)
|
||||
msg = w.localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "incorrect_key"})
|
||||
msg = w.localizer.GetLocalizedMessage("incorrect_key")
|
||||
}
|
||||
|
||||
msgSend := v1.MessageSendRequest{
|
||||
|
@ -207,22 +239,32 @@ ROOT:
|
|||
if msgSend.Type != "" {
|
||||
d, status, err := w.mgClient.MessageSend(msgSend)
|
||||
if err != nil {
|
||||
w.logger.Warningf("MessageSend status: %d\nMessageSend err: %v\nMessageSend data: %v", status, err, d)
|
||||
w.writeLog(fmt.Sprintf("MessageSend status: %d\nMessageSend err: %v\nMessageSend data: %v", status, err, d), logging.WARNING)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func checkErrors(err errs.Failure) error {
|
||||
if err.RuntimeErr != nil {
|
||||
return err.RuntimeErr
|
||||
func checkErrors(err *errs.Failure) error {
|
||||
if err != nil && err.Error() != "" {
|
||||
return errors.New(err.Error())
|
||||
}
|
||||
|
||||
if err.ApiErr != "" {
|
||||
return errors.New(err.ApiErr)
|
||||
if err != nil && (err.ApiError() != "" || len(err.ApiErrors()) > 0) {
|
||||
if err.ApiError() != "" {
|
||||
return errors.New(err.ApiError())
|
||||
} else {
|
||||
var errStr []string
|
||||
for key, value := range err.ApiErrors() {
|
||||
errStr = append(errStr, key+": "+value)
|
||||
}
|
||||
|
||||
return errors.New(strings.Join(errStr, ", "))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -259,7 +301,7 @@ func (w *Worker) execCommand(message string) (resMes string, msgProd v1.MessageP
|
|||
res, _, er := w.crmClient.PaymentTypes()
|
||||
err = checkErrors(er)
|
||||
if err != nil {
|
||||
logger.Errorf("%s - Cannot retrieve payment types, error: %s", w.crmClient.URL, err.Error())
|
||||
w.writeLog(fmt.Sprintf("%s - Cannot retrieve payment types, error: %s", w.crmClient.URL, err.Error()), logging.ERROR)
|
||||
return
|
||||
}
|
||||
for _, v := range res.PaymentTypes {
|
||||
|
@ -268,13 +310,13 @@ func (w *Worker) execCommand(message string) (resMes string, msgProd v1.MessageP
|
|||
}
|
||||
}
|
||||
if len(s) > 0 {
|
||||
resMes = fmt.Sprintf("%s\n\n", w.localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "payment_options"}))
|
||||
resMes = fmt.Sprintf("%s\n\n", w.localizer.GetLocalizedMessage("payment_options"))
|
||||
}
|
||||
case CommandDelivery:
|
||||
res, _, er := w.crmClient.DeliveryTypes()
|
||||
err = checkErrors(er)
|
||||
if err != nil {
|
||||
logger.Errorf("%s - Cannot retrieve delivery types, error: %s", w.crmClient.URL, err.Error())
|
||||
w.writeLog(fmt.Sprintf("%s - Cannot retrieve delivery types, error: %s", w.crmClient.URL, err.Error()), logging.ERROR)
|
||||
return
|
||||
}
|
||||
for _, v := range res.DeliveryTypes {
|
||||
|
@ -283,18 +325,18 @@ func (w *Worker) execCommand(message string) (resMes string, msgProd v1.MessageP
|
|||
}
|
||||
}
|
||||
if len(s) > 0 {
|
||||
resMes = fmt.Sprintf("%s\n\n", w.localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "delivery_options"}))
|
||||
resMes = fmt.Sprintf("%s\n\n", w.localizer.GetLocalizedMessage("delivery_options"))
|
||||
}
|
||||
case CommandProduct:
|
||||
if params.Filter.Name == "" {
|
||||
resMes = w.localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "set_name_or_article"})
|
||||
resMes = w.localizer.GetLocalizedMessage("set_name_or_article")
|
||||
return
|
||||
}
|
||||
|
||||
res, _, er := w.crmClient.Products(params)
|
||||
err = checkErrors(er)
|
||||
if err != nil {
|
||||
logger.Errorf("%s - Cannot retrieve product, error: %s", w.crmClient.URL, err.Error())
|
||||
w.writeLog(fmt.Sprintf("%s - Cannot retrieve product, error: %s", w.crmClient.URL, err.Error()), logging.ERROR)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -335,7 +377,7 @@ func (w *Worker) execCommand(message string) (resMes string, msgProd v1.MessageP
|
|||
}
|
||||
|
||||
if len(s) == 0 {
|
||||
resMes = w.localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "not_found"})
|
||||
resMes = w.localizer.GetLocalizedMessage("not_found")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -387,17 +429,17 @@ func SetBotCommand(botURL, botToken string) (code int, err error) {
|
|||
|
||||
_, code, err = client.CommandEdit(v1.CommandEditRequest{
|
||||
Name: getTextCommand(CommandPayment),
|
||||
Description: getLocalizedMessage("get_payment"),
|
||||
Description: app.GetLocalizedMessage("get_payment"),
|
||||
})
|
||||
|
||||
_, code, err = client.CommandEdit(v1.CommandEditRequest{
|
||||
Name: getTextCommand(CommandDelivery),
|
||||
Description: getLocalizedMessage("get_delivery"),
|
||||
Description: app.GetLocalizedMessage("get_delivery"),
|
||||
})
|
||||
|
||||
_, code, err = client.CommandEdit(v1.CommandEditRequest{
|
||||
Name: getTextCommand(CommandProduct),
|
||||
Description: getLocalizedMessage("get_product"),
|
||||
Description: app.GetLocalizedMessage("get_product"),
|
||||
})
|
||||
|
||||
return
|
||||
|
@ -407,8 +449,23 @@ func getTextCommand(command string) string {
|
|||
return strings.Replace(command, "/", "", -1)
|
||||
}
|
||||
|
||||
func getLang(lang string) *i18n.Localizer {
|
||||
tag, _ := language.MatchStrings(matcher, lang)
|
||||
func getLocalizer(locale string) *core.Localizer {
|
||||
var localizer *core.Localizer
|
||||
|
||||
return i18n.NewLocalizer(bundle, tag.String())
|
||||
if app.TranslationsBox != nil {
|
||||
localizer = core.NewLocalizerFS(language.English, core.DefaultLocalizerBundle(),
|
||||
core.DefaultLocalizerMatcher(), app.TranslationsBox)
|
||||
}
|
||||
|
||||
if app.TranslationsPath != "" {
|
||||
localizer = core.NewLocalizer(language.English, core.DefaultLocalizerBundle(),
|
||||
core.DefaultLocalizerMatcher(), app.TranslationsPath)
|
||||
}
|
||||
|
||||
if localizer == nil {
|
||||
panic("cannot initialize localizer")
|
||||
}
|
||||
|
||||
localizer.SetLocale(locale)
|
||||
return localizer
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
$.ajaxSetup({
|
||||
headers: {
|
||||
"X-CSRF-Token": $('input[name=csrf_token]').val() || ""
|
||||
}
|
||||
});
|
||||
|
||||
$('#save-crm').on("submit", function(e) {
|
||||
e.preventDefault();
|
||||
let formData = formDataToObj($(this).serializeArray());
|
||||
|
|
|
@ -2,28 +2,29 @@
|
|||
<div class="row indent-top">
|
||||
<div class="col s12">
|
||||
<ul class="tabs" id="tab">
|
||||
<li class="tab col s6"><a class="active" href="#tab1">{{.Locale.TabSettings}}</a></li>
|
||||
<li class="tab col s6"><a class="" href="#tab2">{{.Locale.TabBots}}</a></li>
|
||||
<li class="tab col s6"><a class="active" href="#tab1">{{"tab_settings" | trans}}</a></li>
|
||||
<li class="tab col s6"><a class="" href="#tab2">{{"tab_bots" | trans}}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="tab1" class="col s12">
|
||||
<div class="row indent-top">
|
||||
<form id="save" class="tab-el-center" action="/save/" method="POST">
|
||||
<input name="csrf_token" type="hidden" value="{{.TokenCSRF}}">
|
||||
<input name="clientId" type="hidden" value="{{.Conn.ClientID}}">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<input placeholder="{{.Locale.ApiUrl}}" id="api_url" name="api_url" type="text" class="validate" value="{{.Conn.APIURL}}">
|
||||
<input placeholder="{{"api_url" | trans}}" id="api_url" name="api_url" type="text" class="validate" value="{{.Conn.URL}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<input placeholder="{{.Locale.ApiKey}}" id="api_key" name="api_key" type="text" class="validate" value="{{.Conn.APIKEY}}">
|
||||
<input placeholder="{{"api_key" | trans}}" id="api_key" name="api_key" type="text" class="validate" value="{{.Conn.Key}}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12 center-align">
|
||||
<button class="btn waves-effect waves-light red lighten-1" type="submit" name="action">
|
||||
{{.Locale.ButtonSave}}
|
||||
{{"button_save" | trans}}
|
||||
<i class="material-icons right">sync</i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -36,7 +37,7 @@
|
|||
<div class="lang-select">
|
||||
{{$LangCode := .LangCode}}
|
||||
{{$lang := .Conn.Lang}}
|
||||
<label>{{.Locale.Language}}</label>
|
||||
<label>{{"language" | trans}}</label>
|
||||
<select id="lang">
|
||||
{{range $key, $value := $LangCode}}
|
||||
<option value="{{$value}}" {{if eq $value $lang}}selected{{end}}>{{$value}}</option>
|
||||
|
@ -58,7 +59,7 @@
|
|||
<div class="input-field col s12 center-align">
|
||||
<button id="but-settings" class="btn waves-effect waves-light red lighten-1" type="submit" name="action"
|
||||
data-clientID="{{.Conn.ClientID}}" data-action="/bot-settings/">
|
||||
{{.Locale.ButtonSave}}
|
||||
{{"button_save" | trans}}
|
||||
<i class="material-icons right">sync</i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -2,21 +2,22 @@
|
|||
<div class="row indent-top home">
|
||||
<form class="tab-el-center" method="POST" id="save-crm" action="/create/">
|
||||
<div id="msg"></div>
|
||||
<input name="csrf_token" type="hidden" value="{{.TokenCSRF}}">
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<input placeholder="CRM Url" id="api_url" name="api_url" type="text" class="validate"
|
||||
{{if .Conn.APIURL}} value="{{.Conn.APIURL}}" {{end}}>
|
||||
{{if .Conn.URL}} value="{{.Conn.URL}}" {{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12">
|
||||
<input placeholder="{{.Locale.ApiKey}}" id="api_key" name="api_key" type="text" class="validate">
|
||||
<input placeholder="{{"api_key" | trans}}" id="api_key" name="api_key" type="text" class="validate">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="input-field col s12 center-align">
|
||||
<button class="btn waves-effect waves-light red lighten-1" type="submit" name="action">
|
||||
{{.Locale.ButtonSave}}
|
||||
{{"button_save" | trans}}
|
||||
<i class="material-icons right">sync</i>
|
||||
</button>
|
||||
</div>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -33,9 +33,12 @@ error_save: Error while saving, contact technical support
|
|||
error_delete: Error while deleting, contact technical support
|
||||
missing_credentials: "Required methods: {{.Credentials}}"
|
||||
error_activity_mg: Check if the integration with retailCRM Chat is enabled in CRM settings
|
||||
incorrect_csrf_token: Incorrect CSRF token, please update this page
|
||||
|
||||
crm_link: "<a href='//www.retailcrm.pro' title='retailCRM'>retailCRM</a>"
|
||||
doc_link: "<a href='//www.retailcrm.pro/docs' target='_blank'>documentation</a>"
|
||||
crm_link_url: "https://www.retailcrm.pro"
|
||||
crm_link_title: "retailCRM"
|
||||
doc_link_url: "https://help.retailcrm.pro/docs"
|
||||
doc_link_title: "documentation"
|
||||
|
||||
set_name_or_article: Enter product name or article number
|
||||
product_response: "Name: {{.Name}}\nStocks: {{.Quantity}} pcs\nPrice: {{.Price}} {{.Currency}}\nImage: {{.ImageURL}}"
|
||||
|
|
|
@ -33,9 +33,12 @@ error_save: Error al guardar, contacte con el soporte técnico
|
|||
error_delete: Error al eliminar, póngase en contacto con el soporte técnico
|
||||
missing_credentials: "Required methods: {{.Credentials}}"
|
||||
error_activity_mg: Compruebe que la integración con retailCRM Chat está habilitada en Ajustes de CRM
|
||||
incorrect_csrf_token: Token CSRF incorrecto, por favor actualice esta página
|
||||
|
||||
crm_link: "<a href='//www.retailcrm.pro' title='retailCRM'>retailCRM</a>"
|
||||
doc_link: "<a href='//www.retailcrm.pro/docs' target='_blank'>documentación</a>"
|
||||
crm_link_url: "https://www.retailcrm.pro"
|
||||
crm_link_title: "retailCRM"
|
||||
doc_link_url: "https://help.retailcrm.pro/docs"
|
||||
doc_link_title: "documentación"
|
||||
|
||||
set_name_or_article: Indique el nombre o el número de artículo
|
||||
product_response: "Nombre: {{.Name}}\nStock: {{.Quantity}} piezas\nPrecio: {{.Price}} {{.Currency}}\nImagen: {{.ImageURL}}"
|
||||
|
|
|
@ -33,9 +33,12 @@ error_save: Ошибка при сохранении, обратитесь в с
|
|||
error_delete: Ошибка при удалении, обратитесь в службу технической поддержки
|
||||
missing_credentials: "Необходимые методы: {{.Credentials}}"
|
||||
error_activity_mg: Проверьте активность интеграции с retailCRM Chat в настройках CRM
|
||||
incorrect_csrf_token: Неверный токен CSRF, пожалуйста, обновите страницу
|
||||
|
||||
crm_link: "<a href='//www.retailcrm.ru' title='retailCRM'>retailCRM</a>"
|
||||
doc_link: "<a href='//www.retailcrm.ru/docs' target='_blank'>документация</a>"
|
||||
crm_link_url: "https://www.retailcrm.ru"
|
||||
crm_link_title: "retailCRM"
|
||||
doc_link_url: "https://help.retailcrm.ru/docs"
|
||||
doc_link_title: "документация"
|
||||
|
||||
set_name_or_article: Укажите название или артикул товара
|
||||
product_response: "Название: {{.Name}}\nОстаток: {{.Quantity}} шт\nЦена: {{.Price}} {{.Currency}}\nИзображение: {{.ImageURL}}"
|
||||
|
|
Loading…
Add table
Reference in a new issue