From 4065ff14ccb57cd736554a2de4453102d2537a7f Mon Sep 17 00:00:00 2001 From: Tue Date: Wed, 23 Nov 2022 09:50:34 +0700 Subject: [PATCH] build email --- client.go | 44 ++++++++++++++++++++++ email.go | 77 -------------------------------------- model/supplier_request.go | 11 ++++++ model/supplier_response.go | 5 +++ payload.go | 58 ---------------------------- supplier/supplier.go | 32 ++++++++++++++++ utils/bytes.go | 8 ++++ utils/passphrase.go | 59 +++++++++++++++++++++++++++++ 8 files changed, 159 insertions(+), 135 deletions(-) create mode 100644 client.go delete mode 100644 email.go create mode 100644 model/supplier_request.go create mode 100644 model/supplier_response.go delete mode 100644 payload.go create mode 100644 supplier/supplier.go create mode 100644 utils/bytes.go create mode 100644 utils/passphrase.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..20a9218 --- /dev/null +++ b/client.go @@ -0,0 +1,44 @@ +package email + +import ( + "errors" + "fmt" + "git.selly.red/Selly-Modules/natsio" +) + +var ( + c *Client + server natsio.Server + js natsio.JetStream +) + +// GetClient ... +func GetClient() *Client { + return c +} + +// Config ... +type Config struct { + Nats natsio.Config +} + +// Client ... +type Client struct { + Config Config +} + +// NewClient ... +func NewClient(cfg Config) (*Client, error) { + if cfg.Nats.URL == "" { + return nil, errors.New("nats url is required") + } + if err := natsio.Connect(cfg.Nats); err != nil { + return nil, fmt.Errorf("nats connect failed: %v", err) + } + + c = &Client{ + Config: cfg, + } + + return c, nil +} diff --git a/email.go b/email.go deleted file mode 100644 index ae234ed..0000000 --- a/email.go +++ /dev/null @@ -1,77 +0,0 @@ -package email - -import ( - "encoding/json" - "errors" - "fmt" - "git.selly.red/Selly-Modules/natsio" -) - -func getPrefixEmailService(subject string) string { - return fmt.Sprintf("selly.email.%s", subject) -} - -const ( - SubjectSendEmail = "send_email" -) - -const ( - KeyCreateSupplierUser = "create_supplier_user" -) - -var ( - c *Client -) - -// GetEmail ... -func GetEmail() *Client { - return c -} - -// Client ... -type Client struct { - Config Config - natsServer natsio.Server - natsJetStream natsio.JetStream -} - -// NewClient ... -func NewClient(cfg Config) (*Client, error) { - if cfg.Nats.URL == "" { - return nil, errors.New("nats url is required") - } - if err := natsio.Connect(cfg.Nats); err != nil { - return nil, fmt.Errorf("nats connect failed: %v", err) - } - - c = &Client{ - Config: cfg, - natsServer: natsio.GetServer(), - natsJetStream: natsio.GetJetStream(), - } - - return c, nil -} - -func (c *Client) Send(payload SendPayload) (requestID string, err error) { - msg, err := c.natsServer.Request(getPrefixEmailService(SubjectSendEmail), toBytes(payload)) - if err != nil { - return "", err - } - var res struct { - Data Response `json:"data"` - Error string `json:"error"` - } - if err = json.Unmarshal(msg.Data, &res); err != nil { - return "", err - } - if res.Error != "" { - return "", errors.New(res.Error) - } - return res.Data.RequestID, nil -} - -func toBytes(data interface{}) []byte { - b, _ := json.Marshal(data) - return b -} diff --git a/model/supplier_request.go b/model/supplier_request.go new file mode 100644 index 0000000..3b75546 --- /dev/null +++ b/model/supplier_request.go @@ -0,0 +1,11 @@ +package model + +type CreateSupplierUserEncrypt struct { + EncryptData string `json:"encryptData"` +} + +type CreateSupplierUserRequest struct { + Account string `json:"account"` + Email string `json:"email"` + Password string `json:"password"` +} diff --git a/model/supplier_response.go b/model/supplier_response.go new file mode 100644 index 0000000..f075e9e --- /dev/null +++ b/model/supplier_response.go @@ -0,0 +1,5 @@ +package model + +type CreateSupplierUserResponse struct { + RequestID string `json:"requestId"` +} diff --git a/payload.go b/payload.go deleted file mode 100644 index 9b6f5d0..0000000 --- a/payload.go +++ /dev/null @@ -1,58 +0,0 @@ -package email - -import ( - "git.selly.red/Selly-Modules/natsio" -) - -// Config ... -type Config struct { - Nats natsio.Config -} - -type To struct { - Name string `json:"name" bson:"name"` - Address string `json:"address" bson:"address"` -} - -type From struct { - Name string `json:"name" bson:"name"` - Address string `json:"address" bson:"address"` -} - -type Recipient struct { - To []To `json:"to" bson:"to"` - From From `json:"from" bson:"from"` - CC []string `json:"cc,omitempty" bson:"CC,omitempty"` - BCC []string `json:"bcc,omitempty" bson:"BCC,omitempty"` - Subject string `json:"subject,omitempty" bson:"subject,omitempty"` - Attachment []Attachment `json:"attachment,omitempty" bson:"attachment,omitempty"` -} - -type Attachment struct { - URL string `json:"url"` -} - -type Data struct { - Account string `json:"account" bson:"account,omitempty"` - Password string `json:"password" bson:"password,omitempty"` -} - -// SendPayload ... -type SendPayload struct { - Recipient *Recipient `json:"recipient,omitempty" bson:"recipient,omitempty"` - Subject string `json:"subject" bson:"subject"` - Data *Data `json:"data,omitempty" bson:"data,omitempty"` - Key string `json:"key" bson:"key"` - TemplateID string `json:"templateId" bson:"templateId"` - Content *Content `json:"content,omitempty" bson:"content,omitempty"` -} - -type Content struct { - Type string `json:"type" bson:"type"` - Value string `json:"value" bson:"value"` -} - -// Response ... -type Response struct { - RequestID string `json:"requestId"` -} diff --git a/supplier/supplier.go b/supplier/supplier.go new file mode 100644 index 0000000..63b513a --- /dev/null +++ b/supplier/supplier.go @@ -0,0 +1,32 @@ +package supplier + +import ( + "encoding/json" + "errors" + "git.selly.red/Selly-Modules/email/model" + "git.selly.red/Selly-Modules/email/utils" + "git.selly.red/Selly-Modules/natsio" +) + +const ( + SubjectCreateSupplierUser = "create_supplier_user" +) + +func CreateSupplierUser(payload model.CreateSupplierUserEncrypt) (requestID string, err error) { + msg, err := natsio.GetServer().Request(SubjectCreateSupplierUser, utils.ToBytes(payload)) + if err != nil { + return "", err + } + var res struct { + Data model.CreateSupplierUserResponse `json:"data"` + Error string `json:"error"` + } + + if err = json.Unmarshal(msg.Data, &res); err != nil { + return "", err + } + if res.Error != "" { + return "", errors.New(res.Error) + } + return res.Data.RequestID, nil +} diff --git a/utils/bytes.go b/utils/bytes.go new file mode 100644 index 0000000..f957d7f --- /dev/null +++ b/utils/bytes.go @@ -0,0 +1,8 @@ +package utils + +import "encoding/json" + +func ToBytes(data interface{}) []byte { + b, _ := json.Marshal(data) + return b +} diff --git a/utils/passphrase.go b/utils/passphrase.go new file mode 100644 index 0000000..6fc26c1 --- /dev/null +++ b/utils/passphrase.go @@ -0,0 +1,59 @@ +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/hmac" + "crypto/md5" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "io" +) + +func GeneratePassphrase(key string) string { + hash := md5.New() + hash.Write([]byte(key)) + return hex.EncodeToString(hash.Sum(nil)) +} + +func HashWithPassphrase(data []byte, key string) string { + h := hmac.New(sha256.New, []byte(key)) + h.Write([]byte(data)) + return hex.EncodeToString(h.Sum(nil)) +} + +func ASCEncrypt(data []byte, passphrase string) (string, error) { + block, _ := aes.NewCipher([]byte(GeneratePassphrase(passphrase))) + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + nonce := make([]byte, gcm.NonceSize()) + if _, err = io.ReadFull(rand.Reader, nonce); err != nil { + return "", err + } + ciphertext := gcm.Seal(nonce, nonce, data, nil) + return base64.StdEncoding.EncodeToString(ciphertext), nil +} + +func ASCDecrypt(msg, passphrase string) (string, error) { + data, err := base64.StdEncoding.DecodeString(msg) + key := []byte(GeneratePassphrase(passphrase)) + block, err := aes.NewCipher(key) + if err != nil { + return "", err + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } + nonceSize := gcm.NonceSize() + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return "", err + } + return string(plaintext), nil +}