144 lines
3.7 KiB
Go
144 lines
3.7 KiB
Go
package communication
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"html/template"
|
|
"net/http"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/rs/zerolog"
|
|
|
|
"base/config"
|
|
"base/pkg/email"
|
|
)
|
|
|
|
type client struct {
|
|
logger zerolog.Logger
|
|
endpoint string
|
|
accessKey string
|
|
apiVersion string
|
|
senderAddress string
|
|
templates *template.Template
|
|
}
|
|
|
|
func New(logger zerolog.Logger, config *config.AppConfig) email.Email {
|
|
return &client{
|
|
logger: logger,
|
|
endpoint: config.AzureCommunicationConfig.Endpoint,
|
|
accessKey: config.AzureCommunicationConfig.AccessKey,
|
|
apiVersion: config.AzureCommunicationConfig.ApiVersion,
|
|
senderAddress: config.AzureCommunicationConfig.SenderAddress,
|
|
}
|
|
}
|
|
|
|
func (c client) Send(ctx context.Context, params email.Request) (*email.Response, error) {
|
|
var tpl bytes.Buffer
|
|
if err := c.templates.ExecuteTemplate(&tpl, generateTemplateName(params.Template.EmailTemplateName), params.Template.Data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
html := tpl.String()
|
|
|
|
request := &ApiRequest{
|
|
SenderAddress: c.senderAddress,
|
|
Content: ApiContentDto{
|
|
Subject: params.Subject,
|
|
Html: html,
|
|
},
|
|
Recipients: ApiRecipientDto{
|
|
To: []ApiRecipientDetailDto{
|
|
{
|
|
Address: params.RecipientAddress,
|
|
DisplayName: params.UserFullName,
|
|
},
|
|
},
|
|
CC: make([]ApiRecipientDetailDto, 0),
|
|
BCC: make([]ApiRecipientDetailDto, 0),
|
|
},
|
|
}
|
|
byteBody, err := json.Marshal(&request)
|
|
if err != nil {
|
|
return nil, errors.New("marshaling error")
|
|
}
|
|
|
|
method := "POST"
|
|
endpoint := c.endpoint
|
|
u, _ := url.Parse(endpoint)
|
|
snedPathAndQuery := fmt.Sprintf(
|
|
"/emails:send?api-version=%s",
|
|
c.apiVersion,
|
|
)
|
|
date := time.Now().In(time.FixedZone("GMT", 0)).Format("Mon, 02 Jan 2006 15:04:05 GMT")
|
|
host := u.Host
|
|
|
|
contentHash := computeContentHash(byteBody)
|
|
|
|
stringToSign := fmt.Sprintf("%s\n%s\n%s;%s;%s", method, snedPathAndQuery, date, host, contentHash)
|
|
signature, err := computeSignature(stringToSign, c.accessKey)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
authHeader := fmt.Sprintf("HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=%s", signature)
|
|
fullURL := endpoint + snedPathAndQuery
|
|
req, _ := http.NewRequest(method, fullURL, bytes.NewReader(byteBody))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("x-ms-date", date)
|
|
req.Header.Set("x-ms-content-sha256", contentHash)
|
|
req.Header.Set("Authorization", authHeader)
|
|
req.Header.Set("Host", host)
|
|
client := &http.Client{Timeout: 15 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusAccepted {
|
|
response := &ApiErrorResponse{}
|
|
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
|
return nil, err
|
|
}
|
|
c.logger.Info().Msgf("email sending failed. %v", response)
|
|
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
|
}
|
|
|
|
c.logger.Info().Msgf("email sending done. %v", resp.Body)
|
|
response := &email.Response{}
|
|
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return response, nil
|
|
}
|
|
|
|
func computeContentHash(body []byte) string {
|
|
sum := sha256.Sum256(body)
|
|
return base64.StdEncoding.EncodeToString(sum[:])
|
|
}
|
|
|
|
func computeSignature(stringToSign, base64AccessKey string) (string, error) {
|
|
key, err := base64.StdEncoding.DecodeString(base64AccessKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
mac := hmac.New(sha256.New, key)
|
|
_, err = mac.Write([]byte(stringToSign))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
sig := mac.Sum(nil)
|
|
return base64.StdEncoding.EncodeToString(sig), nil
|
|
}
|
|
|
|
func generateTemplateName(emailTemplateName email.Template) string {
|
|
return fmt.Sprintf("%s.html", emailTemplateName.String())
|
|
}
|