diff --git a/api/compose/spec.json b/api/compose/spec.json index 080427024..c0f4947c9 100644 --- a/api/compose/spec.json +++ b/api/compose/spec.json @@ -1085,6 +1085,12 @@ "type": "sqlxTypes.JSONText", "required": true, "title": "Message content" + }, + { + "name": "remoteAttachments", + "type": "[]string", + "required": false, + "title": "Remote files to attach to the email" } ] } diff --git a/api/compose/spec/notification.json b/api/compose/spec/notification.json index 663b29aaa..9be4afc06 100644 --- a/api/compose/spec/notification.json +++ b/api/compose/spec/notification.json @@ -50,6 +50,12 @@ "required": true, "title": "Message content", "type": "sqlxTypes.JSONText" + }, + { + "name": "remoteAttachments", + "required": false, + "title": "Remote files to attach to the email", + "type": "[]string" } ] } diff --git a/compose/rest/notification.go b/compose/rest/notification.go index c6983ed6f..82341bf1b 100644 --- a/compose/rest/notification.go +++ b/compose/rest/notification.go @@ -26,6 +26,7 @@ type ( notificationService interface { SendEmail(*gomail.Message) error AttachEmailRecipients(*gomail.Message, string, ...string) error + AttachRemoteFiles(context.Context, *gomail.Message, ...string) error } ) @@ -64,6 +65,13 @@ func (ctrl *Notification) EmailSend(ctx context.Context, r *request.Notification msg.SetHeader("Subject", r.Subject) + if len(r.RemoteAttachments) > 0 { + err := ntf.AttachRemoteFiles(ctx, msg, r.RemoteAttachments...) + if err != nil { + return nil, err + } + } + if err := ntf.SendEmail(msg); err != nil { return false, err } else { diff --git a/compose/rest/request/notification.go b/compose/rest/request/notification.go index 3c3f848e7..31b16f6fc 100644 --- a/compose/rest/request/notification.go +++ b/compose/rest/request/notification.go @@ -34,11 +34,12 @@ var _ = multipart.FileHeader{} // Notification email/send request parameters type NotificationEmailSend struct { - To []string - Cc []string - ReplyTo string - Subject string - Content sqlxTypes.JSONText + To []string + Cc []string + ReplyTo string + Subject string + Content sqlxTypes.JSONText + RemoteAttachments []string } func NewNotificationEmailSend() *NotificationEmailSend { @@ -53,6 +54,7 @@ func (r NotificationEmailSend) Auditable() map[string]interface{} { out["replyTo"] = r.ReplyTo out["subject "] = r.Subject out["content"] = r.Content + out["remoteAttachments"] = r.RemoteAttachments return out } @@ -105,6 +107,10 @@ func (r *NotificationEmailSend) Fill(req *http.Request) (err error) { } } + if val, ok := req.Form["remoteAttachments"]; ok { + r.RemoteAttachments = parseStrings(val) + } + return err } diff --git a/compose/service/notification.go b/compose/service/notification.go index f813c711b..ff74233f4 100644 --- a/compose/service/notification.go +++ b/compose/service/notification.go @@ -1,12 +1,17 @@ package service import ( + "context" + "net/http" + "path" "strings" + "sync" "github.com/pkg/errors" "go.uber.org/zap" gomail "gopkg.in/mail.v2" + httpClient "github.com/cortezaproject/corteza-server/internal/http" "github.com/cortezaproject/corteza-server/internal/mail" ) @@ -70,3 +75,61 @@ func (svc notification) AttachEmailRecipients(message *gomail.Message, field str message.SetHeader(field, recipients...) return } + +func (svc notification) AttachRemoteFiles(ctx context.Context, message *gomail.Message, rr ...string) error { + var ( + wg = &sync.WaitGroup{} + l = &sync.Mutex{} + + client, err = httpClient.New(&httpClient.Config{ + Timeout: 10, + }) + + log = svc.logger + ) + + log.Debug("attaching files to mail notification", zap.Strings("urls", rr)) + + if err != nil { + return errors.WithStack(err) + } + + get := func(log *zap.Logger, req *http.Request) { + defer wg.Done() + + resp, err := client.Do(req) + if err != nil { + log.Error("could not send request to download remote attachment", zap.Error(err)) + return + } + + if resp.StatusCode != http.StatusOK { + log.Error("could not download remote attachment", zap.String("status", resp.Status)) + return + } + + log.Info("download successful", + zap.Int64("content-length", resp.ContentLength), + zap.String("content-type", resp.Header.Get("Content-Type")), + ) + + l.Lock() + defer l.Unlock() + message.AttachReader(path.Base(req.URL.Path), resp.Body) + } + + for _, url := range rr { + log := log.With(zap.String("remote-file", url)) + + req, err := client.Get(url) + if err != nil { + return errors.WithStack(err) + } + + wg.Add(1) + go get(log, req) + } + + wg.Wait() + return nil +} diff --git a/docs/compose/README.md b/docs/compose/README.md index cf42f1aed..3d3ef98a9 100644 --- a/docs/compose/README.md +++ b/docs/compose/README.md @@ -702,6 +702,7 @@ Compose Notifications | replyTo | string | POST | Email address in reply-to field | N/A | NO | | subject | string | POST | Email subject | N/A | NO | | content | sqlxTypes.JSONText | POST | Message content | N/A | YES | +| remoteAttachments | []string | POST | Remote files to attach to the email | N/A | NO | ---