mirror of
https://github.com/retailcrm/mg-transport-core.git
synced 2024-11-30 08:56:06 +03:00
299 lines
6.9 KiB
Go
299 lines
6.9 KiB
Go
package util
|
||
|
||
import (
|
||
"bytes"
|
||
// nolint:gosec
|
||
"crypto/sha1"
|
||
"crypto/sha256"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"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"
|
||
"github.com/gin-gonic/gin"
|
||
retailcrm "github.com/retailcrm/api-client-go/v2"
|
||
"github.com/retailcrm/mg-transport-core/v2/core/config"
|
||
|
||
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(`/+$`)
|
||
)
|
||
|
||
var (
|
||
DefaultScopes = []string{
|
||
"integration_read",
|
||
"integration_write",
|
||
}
|
||
DefaultCredentials = []string{
|
||
"/api/integration-modules/{code}",
|
||
"/api/integration-modules/{code}/edit",
|
||
}
|
||
)
|
||
|
||
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.
|
||
type Utils struct {
|
||
Logger logger.Logger
|
||
slashRegex *regexp.Regexp
|
||
AWS config.AWS
|
||
TokenCounter uint32
|
||
IsDebug bool
|
||
}
|
||
|
||
// NewUtils will create new Utils instance.
|
||
func NewUtils(awsConfig config.AWS, logger logger.Logger, debug bool) *Utils {
|
||
return &Utils{
|
||
IsDebug: debug,
|
||
AWS: awsConfig,
|
||
Logger: logger,
|
||
TokenCounter: 0,
|
||
slashRegex: slashRegex,
|
||
}
|
||
}
|
||
|
||
// ResetUtils resets the utils inner state.
|
||
func (u *Utils) ResetUtils(awsConfig config.AWS, debug bool, tokenCounter uint32) {
|
||
u.TokenCounter = tokenCounter
|
||
u.AWS = awsConfig
|
||
u.IsDebug = debug
|
||
u.slashRegex = slashRegex
|
||
}
|
||
|
||
// 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.
|
||
// 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) {
|
||
client := retailcrm.New(url, key).
|
||
WithLogger(logger.APIClientAdapter(u.Logger))
|
||
client.Debug = u.IsDebug
|
||
|
||
cr, status, err := client.APICredentials()
|
||
if err != nil {
|
||
return nil, status, err
|
||
}
|
||
|
||
if res := u.checkScopes(cr.Scopes, scopes); len(res) != 0 {
|
||
if len(credentials) == 0 || len(cr.Scopes) > 0 {
|
||
u.Logger.Error(url, logger.HTTPStatusCode(status), logger.Body(res))
|
||
return nil, http.StatusBadRequest, errorutil.NewInsufficientScopesErr(res)
|
||
}
|
||
|
||
if res := u.checkScopes(cr.Credentials, credentials[0]); len(res) != 0 {
|
||
u.Logger.Error(url, logger.HTTPStatusCode(status), logger.Body(res))
|
||
return nil, http.StatusBadRequest, errorutil.NewInsufficientScopesErr(res)
|
||
}
|
||
}
|
||
|
||
return client, 0, nil
|
||
}
|
||
|
||
func (u *Utils) checkScopes(scopes []string, scopesRequired []string) []string {
|
||
rs := make([]string, len(scopesRequired))
|
||
copy(rs, scopesRequired)
|
||
|
||
for _, vs := range scopes {
|
||
for kn, vn := range rs {
|
||
if vn == vs {
|
||
if len(rs) == 1 {
|
||
rs = rs[:0]
|
||
break
|
||
}
|
||
rs = append(rs[:kn], rs[kn+1:]...)
|
||
}
|
||
}
|
||
}
|
||
|
||
return rs
|
||
}
|
||
|
||
// UploadUserAvatar will upload avatar for user.
|
||
func (u *Utils) UploadUserAvatar(url string) (picURLs3 string, err error) {
|
||
s3Config := &aws.Config{
|
||
Credentials: credentials.NewStaticCredentials(
|
||
u.AWS.AccessKeyID,
|
||
u.AWS.SecretAccessKey,
|
||
""),
|
||
Region: aws.String(u.AWS.Region),
|
||
}
|
||
|
||
if u.AWS.Endpoint != "" {
|
||
s3Config.Endpoint = aws.String(u.AWS.Endpoint)
|
||
}
|
||
|
||
s := session.Must(session.NewSession(s3Config))
|
||
uploader := s3manager.NewUploader(s)
|
||
|
||
// nolint:gosec
|
||
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())),
|
||
Body: resp.Body,
|
||
ContentType: aws.String(u.AWS.ContentType),
|
||
ACL: aws.String("public-read"),
|
||
})
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
picURLs3 = result.Location
|
||
|
||
return
|
||
}
|
||
|
||
// RemoveTrailingSlash will remove slash at the end of any string.
|
||
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.
|
||
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.
|
||
func GetEntitySHA1(v interface{}) (hash string, err error) {
|
||
res, _ := json.Marshal(v)
|
||
|
||
// nolint:gosec
|
||
h := sha1.New()
|
||
_, err = h.Write(res)
|
||
hash = fmt.Sprintf("%x", h.Sum(nil))
|
||
|
||
return
|
||
}
|
||
|
||
// ReplaceMarkdownSymbols will remove markdown symbols from text.
|
||
func ReplaceMarkdownSymbols(s string) string {
|
||
for _, v := range markdownSymbols {
|
||
s = strings.ReplaceAll(s, v, "\\"+v)
|
||
}
|
||
|
||
return s
|
||
}
|
||
|
||
// DefaultCurrencies will return default currencies list for all bots.
|
||
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
|
||
}
|
||
|
||
return strings.ToUpper(code)
|
||
}
|
||
|
||
func FormatCurrencyValue(value float32) string {
|
||
return fmt.Sprintf("%.2f", value)
|
||
}
|
||
|
||
// 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 {
|
||
return []byte{}, err
|
||
}
|
||
c.Request.Body = io.NopCloser(bytes.NewReader(data))
|
||
return data, c.ShouldBindJSON(obj)
|
||
}
|