mg-transport-core/core/util/utils.go

299 lines
6.9 KiB
Go
Raw Permalink Normal View History

package util
2019-09-04 15:22:27 +03:00
import (
2023-10-18 14:41:37 +03:00
"bytes"
2019-12-12 09:35:05 +03:00
// nolint:gosec
2019-09-04 15:22:27 +03:00
"crypto/sha1"
"crypto/sha256"
"encoding/json"
"fmt"
2023-10-18 14:41:37 +03:00
"io"
2019-09-04 15:22:27 +03:00
"net/http"
"regexp"
"strings"
"sync/atomic"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
2023-10-18 14:41:37 +03:00
"github.com/gin-gonic/gin"
2021-11-03 17:27:56 +03:00
retailcrm "github.com/retailcrm/api-client-go/v2"
"github.com/retailcrm/mg-transport-core/v2/core/config"
2024-06-14 18:10:48 +03:00
v1 "github.com/retailcrm/mg-transport-api-client-go/v1"
"github.com/retailcrm/mg-transport-core/v2/core/logger"
"github.com/retailcrm/mg-transport-core/v2/core/util/errorutil"
)
var (
markdownSymbols = []string{"*", "_", "`", "["}
slashRegex = regexp.MustCompile(`/+$`)
2019-09-04 15:22:27 +03:00
)
2021-12-13 16:49:42 +03:00
var (
DefaultScopes = []string{
"integration_read",
"integration_write",
}
DefaultCredentials = []string{
"/api/integration-modules/{code}",
"/api/integration-modules/{code}/edit",
}
)
2021-11-03 17:27:56 +03:00
var defaultCurrencies = map[string]string{
"rub": "₽",
"uah": "₴",
"byn": "Br",
"kzt": "₸",
"usd": "$",
"eur": "€",
"prb": "PRB",
"mdl": "L",
"kgs": "с",
"pln": "zł",
"azn": "₼",
"amd": "֏",
"thb": "฿",
"aed": "AED",
"nok": "kr",
"cad": "C$",
"czk": "Kč",
"sek": "kr",
"dkk": "kr",
"ron": "lei",
"uzs": "So'm",
"aud": "$",
"chf": "₣",
"inr": "₹",
"bgn": "лв",
"ngn": "₦",
"huf": "ƒ",
"ils": "₪",
"try": "₺",
"stn": "₡",
"ars": "$",
"bob": "Bs",
"ves": "Bs",
"gtq": "Q",
"hnl": "L",
"dop": "RD$",
"cop": "COL$",
"crc": "₡",
"cup": "$MN",
"nio": "C$",
"pab": "B/",
"pyg": "₲",
"pen": "S/",
"svc": "₡",
"uyu": "$U",
"gel": "₾",
"gbp": "£",
}
// Utils service object.
2019-09-04 15:22:27 +03:00
type Utils struct {
Logger logger.Logger
2019-09-04 15:22:27 +03:00
slashRegex *regexp.Regexp
AWS config.AWS
TokenCounter uint32
IsDebug bool
2019-09-04 15:22:27 +03:00
}
// NewUtils will create new Utils instance.
func NewUtils(awsConfig config.AWS, logger logger.Logger, debug bool) *Utils {
2019-09-04 15:22:27 +03:00
return &Utils{
IsDebug: debug,
AWS: awsConfig,
2019-09-04 15:22:27 +03:00
Logger: logger,
TokenCounter: 0,
slashRegex: slashRegex,
2019-09-04 15:22:27 +03:00
}
}
// ResetUtils resets the utils inner state.
func (u *Utils) ResetUtils(awsConfig config.AWS, debug bool, tokenCounter uint32) {
2019-09-04 15:22:27 +03:00
u.TokenCounter = tokenCounter
u.AWS = awsConfig
2019-09-04 15:22:27 +03:00
u.IsDebug = debug
u.slashRegex = slashRegex
2019-09-04 15:22:27 +03:00
}
// GenerateToken will generate long pseudo-random string.
func (u *Utils) GenerateToken() string {
c := atomic.AddUint32(&u.TokenCounter, 1)
return fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%d%d", time.Now().UnixNano(), c))))
}
// GetAPIClient will initialize RetailCRM api client from url and key.
2021-12-13 16:49:42 +03:00
// Scopes will be used to determine if client is valid. If there are no scopes - credentials will be used instead.
func (u *Utils) GetAPIClient(
url, key string, scopes []string, credentials ...[]string) (*retailcrm.Client, int, error) {
2021-11-03 17:27:56 +03:00
client := retailcrm.New(url, key).
2023-10-18 14:27:23 +03:00
WithLogger(logger.APIClientAdapter(u.Logger))
2019-09-04 15:22:27 +03:00
client.Debug = u.IsDebug
2021-11-03 17:27:56 +03:00
cr, status, err := client.APICredentials()
if err != nil {
return nil, status, err
2019-09-04 15:22:27 +03:00
}
2021-11-03 17:27:56 +03:00
if res := u.checkScopes(cr.Scopes, scopes); len(res) != 0 {
2021-12-13 16:49:42 +03:00
if len(credentials) == 0 || len(cr.Scopes) > 0 {
2023-11-24 15:23:07 +03:00
u.Logger.Error(url, logger.HTTPStatusCode(status), logger.Body(res))
2021-12-13 16:49:42 +03:00
return nil, http.StatusBadRequest, errorutil.NewInsufficientScopesErr(res)
}
if res := u.checkScopes(cr.Credentials, credentials[0]); len(res) != 0 {
2023-11-24 15:23:07 +03:00
u.Logger.Error(url, logger.HTTPStatusCode(status), logger.Body(res))
2021-12-13 16:49:42 +03:00
return nil, http.StatusBadRequest, errorutil.NewInsufficientScopesErr(res)
}
2019-09-04 15:22:27 +03:00
}
return client, 0, nil
}
2021-11-03 17:27:56 +03:00
func (u *Utils) checkScopes(scopes []string, scopesRequired []string) []string {
rs := make([]string, len(scopesRequired))
copy(rs, scopesRequired)
2019-09-04 15:22:27 +03:00
2021-11-03 17:27:56 +03:00
for _, vs := range scopes {
for kn, vn := range rs {
if vn == vs {
if len(rs) == 1 {
rs = rs[:0]
2019-09-04 15:22:27 +03:00
break
}
2021-11-03 17:27:56 +03:00
rs = append(rs[:kn], rs[kn+1:]...)
2019-09-04 15:22:27 +03:00
}
}
}
2021-11-03 17:27:56 +03:00
return rs
2019-09-04 15:22:27 +03:00
}
// UploadUserAvatar will upload avatar for user.
2019-09-04 15:22:27 +03:00
func (u *Utils) UploadUserAvatar(url string) (picURLs3 string, err error) {
s3Config := &aws.Config{
Credentials: credentials.NewStaticCredentials(
u.AWS.AccessKeyID,
u.AWS.SecretAccessKey,
2019-09-04 15:22:27 +03:00
""),
Region: aws.String(u.AWS.Region),
2019-09-04 15:22:27 +03:00
}
if u.AWS.Endpoint != "" {
s3Config.Endpoint = aws.String(u.AWS.Endpoint)
}
2019-09-04 15:22:27 +03:00
s := session.Must(session.NewSession(s3Config))
uploader := s3manager.NewUploader(s)
2019-12-12 09:35:05 +03:00
// nolint:gosec
2019-09-04 15:22:27 +03:00
resp, err := http.Get(url)
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode >= http.StatusBadRequest {
return "", fmt.Errorf("get: %v code: %v", url, resp.StatusCode)
}
result, err := uploader.Upload(&s3manager.UploadInput{
Bucket: aws.String(u.AWS.Bucket),
Key: aws.String(fmt.Sprintf("%v/%v.jpg", u.AWS.FolderName, u.GenerateToken())),
2019-09-04 15:22:27 +03:00
Body: resp.Body,
ContentType: aws.String(u.AWS.ContentType),
2019-09-04 15:22:27 +03:00
ACL: aws.String("public-read"),
})
if err != nil {
return
}
picURLs3 = result.Location
return
}
// RemoveTrailingSlash will remove slash at the end of any string.
2019-09-18 18:02:40 +03:00
func (u *Utils) RemoveTrailingSlash(crmURL string) string {
return u.slashRegex.ReplaceAllString(crmURL, ``)
}
// GetMGItemData will upload file to MG by URL and return information about attachable item.
2019-09-04 15:22:27 +03:00
func GetMGItemData(client *v1.MgClient, url string, caption string) (v1.Item, int, error) {
item := v1.Item{}
data, st, err := client.UploadFileByURL(
v1.UploadFileByUrlRequest{
Url: url,
},
)
if err != nil {
return item, st, err
}
item.ID = data.ID
item.Caption = caption
return item, st, err
}
// GetEntitySHA1 will serialize any value to JSON and return SHA1 hash of this JSON.
2019-09-04 15:22:27 +03:00
func GetEntitySHA1(v interface{}) (hash string, err error) {
res, _ := json.Marshal(v)
2019-12-12 09:35:05 +03:00
// nolint:gosec
2019-09-04 15:22:27 +03:00
h := sha1.New()
_, err = h.Write(res)
hash = fmt.Sprintf("%x", h.Sum(nil))
return
}
// ReplaceMarkdownSymbols will remove markdown symbols from text.
2019-09-04 15:22:27 +03:00
func ReplaceMarkdownSymbols(s string) string {
for _, v := range markdownSymbols {
s = strings.ReplaceAll(s, v, "\\"+v)
2019-09-04 15:22:27 +03:00
}
return s
}
// DefaultCurrencies will return default currencies list for all bots.
2019-09-04 15:22:27 +03:00
func DefaultCurrencies() map[string]string {
return defaultCurrencies
}
// GetCurrencySymbol returns currency symbol by it's ISO 4127 code.
// It returns provided currency code in uppercase if currency symbol cannot be found.
func GetCurrencySymbol(code string) string {
if i, ok := DefaultCurrencies()[strings.ToLower(code)]; ok {
return i
2019-09-04 15:22:27 +03:00
}
return strings.ToUpper(code)
2019-09-04 15:22:27 +03:00
}
func FormatCurrencyValue(value float32) string {
return fmt.Sprintf("%.2f", value)
}
2023-10-18 14:41:37 +03:00
// BindJSONWithRaw will perform usual ShouldBindJSON and will return the original body data.
func BindJSONWithRaw(c *gin.Context, obj any) ([]byte, error) {
closer := c.Request.Body
defer func() { _ = closer.Close() }()
data, err := io.ReadAll(closer)
if err != nil {
2023-10-18 15:40:46 +03:00
return []byte{}, err
2023-10-18 14:41:37 +03:00
}
c.Request.Body = io.NopCloser(bytes.NewReader(data))
return data, c.ShouldBindJSON(obj)
}