Add html_body field for multipart email notifications

Callers can now supply a plain-text Body alongside an HTML html_body;
the SMTP sender emits multipart/alternative using both verbatim instead
of auto-stripping HTML to derive the plain-text fallback. The legacy
content_type=HTML path is preserved (deprecated) for existing callers.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-06 23:59:12 -07:00
parent a35b3e6283
commit ac3ba35736
5 changed files with 42 additions and 25 deletions
+2
View File
@@ -66,6 +66,7 @@ func (h *NotifierHandler) SendNotification(ctx context.Context, req *pb.SendNoti
Priority: domain.Priority(req.Priority),
Subject: req.Subject,
Body: req.Body,
HTMLBody: req.HtmlBody,
ContentType: contentType,
Recipients: req.Recipients,
CC: req.Cc,
@@ -368,6 +369,7 @@ func convertDomainToProtoNotification(notif *domain.Notification) *pb.Notificati
Status: convertDomainToProtoStatus(notif.Status),
Subject: notif.Subject,
Body: notif.Body,
HtmlBody: notif.HTMLBody,
Recipients: notif.Recipients,
Metadata: convertInterfaceMapToString(notif.Metadata),
CreatedAt: timestamppb.New(notif.CreatedAt),
+4 -2
View File
@@ -81,7 +81,8 @@ message Notification {
NotificationStatus status = 5;
string subject = 6;
string body = 7;
ContentType content_type = 18; // Format of the body (text or html)
ContentType content_type = 18 [deprecated = true]; // Deprecated: use html_body instead. Format of the body (text or html).
string html_body = 19; // Optional HTML body for email; if set, sends multipart/alternative with body as text/plain and html_body as text/html. Ignored for non-email types.
repeated string recipients = 8;
repeated string cc = 16; // Carbon copy recipients (email only)
repeated string bcc = 17; // Blind carbon copy recipients (email only)
@@ -111,13 +112,14 @@ message SendNotificationRequest {
Priority priority = 3;
string subject = 4;
string body = 5;
ContentType content_type = 12; // Format of the body (text or html) - auto-detected if not specified
ContentType content_type = 12 [deprecated = true]; // Deprecated: use html_body instead. Format of the body (text or html) - auto-detected if not specified.
repeated string recipients = 6;
repeated string cc = 10; // Carbon copy recipients (email only)
repeated string bcc = 11; // Blind carbon copy recipients (email only)
map<string, string> metadata = 7;
google.protobuf.Timestamp scheduled_for = 8;
int32 max_retries = 9;
string html_body = 13; // Optional HTML body for email; if set, sends multipart/alternative with body as text/plain and html_body as text/html. Ignored for non-email types.
}
// SendNotificationResponse returns the result of sending a notification
+5 -1
View File
@@ -16,7 +16,8 @@ type SendNotificationRequest struct {
Priority int `json:"priority,omitempty"`
Subject string `json:"subject"`
Body string `json:"body"`
ContentType string `json:"content_type,omitempty"` // "text" or "html" - auto-detected if not specified
HTMLBody string `json:"html_body,omitempty"` // Optional HTML body for email; if set, sends multipart/alternative.
ContentType string `json:"content_type,omitempty"` // Deprecated: prefer html_body. "text" or "html".
Recipients []string `json:"recipients"`
CC []string `json:"cc,omitempty"` // Carbon copy recipients (email only)
BCC []string `json:"bcc,omitempty"` // Blind carbon copy recipients (email only)
@@ -75,6 +76,7 @@ func (r *SendNotificationRequest) ToNotification() *domain.Notification {
Status: domain.StatusPending,
Subject: r.Subject,
Body: r.Body,
HTMLBody: r.HTMLBody,
ContentType: contentType,
Recipients: r.Recipients,
CC: r.CC,
@@ -111,6 +113,7 @@ type Notification struct {
Status string `json:"status"`
Subject string `json:"subject"`
Body string `json:"body"`
HTMLBody string `json:"html_body,omitempty"`
ContentType string `json:"content_type,omitempty"`
Recipients []string `json:"recipients"`
CC []string `json:"cc,omitempty"`
@@ -134,6 +137,7 @@ func NotificationFromDomain(n *domain.Notification) Notification {
Status: string(n.Status),
Subject: n.Subject,
Body: n.Body,
HTMLBody: n.HTMLBody,
ContentType: string(n.ContentType),
Recipients: n.Recipients,
CC: n.CC,