177 lines
5.8 KiB
Go
177 lines
5.8 KiB
Go
package mailstore
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
|
|
"galaxy/mail/internal/domain/common"
|
|
"galaxy/mail/internal/service/acceptgenericdelivery"
|
|
)
|
|
|
|
// attachmentRow stores the on-disk JSONB encoding of one
|
|
// `common.AttachmentMetadata` entry. The encoding is intentionally explicit
|
|
// (named JSON keys) so the on-disk shape stays decoupled from accidental Go
|
|
// struct renames.
|
|
type attachmentRow struct {
|
|
Filename string `json:"filename"`
|
|
ContentType string `json:"content_type"`
|
|
SizeBytes int64 `json:"size_bytes"`
|
|
}
|
|
|
|
// marshalAttachments returns the JSONB bytes for the attachments column. A
|
|
// nil/empty slice round-trips as `[]` to keep the column NOT NULL across
|
|
// equality tests.
|
|
func marshalAttachments(attachments []common.AttachmentMetadata) ([]byte, error) {
|
|
rows := make([]attachmentRow, 0, len(attachments))
|
|
for _, attachment := range attachments {
|
|
rows = append(rows, attachmentRow{
|
|
Filename: attachment.Filename,
|
|
ContentType: attachment.ContentType,
|
|
SizeBytes: attachment.SizeBytes,
|
|
})
|
|
}
|
|
payload, err := json.Marshal(rows)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal attachments: %w", err)
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
// unmarshalAttachments decodes the attachments JSONB column into a
|
|
// domain-friendly slice. nil/empty payloads decode to a nil slice.
|
|
func unmarshalAttachments(payload []byte) ([]common.AttachmentMetadata, error) {
|
|
if len(payload) == 0 {
|
|
return nil, nil
|
|
}
|
|
var rows []attachmentRow
|
|
if err := json.Unmarshal(payload, &rows); err != nil {
|
|
return nil, fmt.Errorf("unmarshal attachments: %w", err)
|
|
}
|
|
if len(rows) == 0 {
|
|
return nil, nil
|
|
}
|
|
out := make([]common.AttachmentMetadata, 0, len(rows))
|
|
for _, row := range rows {
|
|
out = append(out, common.AttachmentMetadata{
|
|
Filename: row.Filename,
|
|
ContentType: row.ContentType,
|
|
SizeBytes: row.SizeBytes,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// marshalTemplateVariables returns the JSONB bytes for the template_variables
|
|
// column. nil maps round-trip as SQL NULL.
|
|
func marshalTemplateVariables(variables map[string]any) ([]byte, error) {
|
|
if variables == nil {
|
|
return nil, nil
|
|
}
|
|
payload, err := json.Marshal(variables)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal template variables: %w", err)
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
// unmarshalTemplateVariables decodes the template_variables JSONB column.
|
|
// SQL NULL payloads decode to a nil map.
|
|
func unmarshalTemplateVariables(payload []byte) (map[string]any, error) {
|
|
if len(payload) == 0 {
|
|
return nil, nil
|
|
}
|
|
var variables map[string]any
|
|
if err := json.Unmarshal(payload, &variables); err != nil {
|
|
return nil, fmt.Errorf("unmarshal template variables: %w", err)
|
|
}
|
|
return variables, nil
|
|
}
|
|
|
|
// payloadAttachmentRow stores the on-disk JSONB encoding of one
|
|
// `acceptgenericdelivery.AttachmentPayload`. The base64 body stays inline so
|
|
// the entire payload bundle round-trips as one JSONB value.
|
|
type payloadAttachmentRow struct {
|
|
Filename string `json:"filename"`
|
|
ContentType string `json:"content_type"`
|
|
ContentBase64 string `json:"content_base64"`
|
|
SizeBytes int64 `json:"size_bytes"`
|
|
}
|
|
|
|
// payloadRow stores the on-disk JSONB encoding of one
|
|
// `acceptgenericdelivery.DeliveryPayload`. delivery_id is intentionally
|
|
// excluded — the row is keyed by it via the `delivery_payloads` PRIMARY KEY.
|
|
type payloadRow struct {
|
|
Attachments []payloadAttachmentRow `json:"attachments"`
|
|
}
|
|
|
|
// marshalDeliveryPayload returns the JSONB bytes for the delivery_payloads
|
|
// row.
|
|
func marshalDeliveryPayload(payload acceptgenericdelivery.DeliveryPayload) ([]byte, error) {
|
|
rows := make([]payloadAttachmentRow, 0, len(payload.Attachments))
|
|
for _, attachment := range payload.Attachments {
|
|
rows = append(rows, payloadAttachmentRow{
|
|
Filename: attachment.Filename,
|
|
ContentType: attachment.ContentType,
|
|
ContentBase64: attachment.ContentBase64,
|
|
SizeBytes: attachment.SizeBytes,
|
|
})
|
|
}
|
|
encoded, err := json.Marshal(payloadRow{Attachments: rows})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal delivery payload: %w", err)
|
|
}
|
|
return encoded, nil
|
|
}
|
|
|
|
// unmarshalDeliveryPayload decodes the delivery_payloads row into a
|
|
// domain-friendly DeliveryPayload using deliveryID as the owning identifier.
|
|
func unmarshalDeliveryPayload(deliveryID common.DeliveryID, encoded []byte) (acceptgenericdelivery.DeliveryPayload, error) {
|
|
if len(encoded) == 0 {
|
|
return acceptgenericdelivery.DeliveryPayload{}, fmt.Errorf("unmarshal delivery payload: empty")
|
|
}
|
|
var row payloadRow
|
|
if err := json.Unmarshal(encoded, &row); err != nil {
|
|
return acceptgenericdelivery.DeliveryPayload{}, fmt.Errorf("unmarshal delivery payload: %w", err)
|
|
}
|
|
out := acceptgenericdelivery.DeliveryPayload{DeliveryID: deliveryID}
|
|
if len(row.Attachments) == 0 {
|
|
return out, nil
|
|
}
|
|
out.Attachments = make([]acceptgenericdelivery.AttachmentPayload, 0, len(row.Attachments))
|
|
for _, attachment := range row.Attachments {
|
|
out.Attachments = append(out.Attachments, acceptgenericdelivery.AttachmentPayload{
|
|
Filename: attachment.Filename,
|
|
ContentType: attachment.ContentType,
|
|
ContentBase64: attachment.ContentBase64,
|
|
SizeBytes: attachment.SizeBytes,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// marshalRawFields returns the JSONB bytes for the malformed_commands.raw_fields
|
|
// column. The map is serialised verbatim so future operator queries can match
|
|
// arbitrary keys.
|
|
func marshalRawFields(fields map[string]any) ([]byte, error) {
|
|
if fields == nil {
|
|
fields = map[string]any{}
|
|
}
|
|
payload, err := json.Marshal(fields)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal raw fields: %w", err)
|
|
}
|
|
return payload, nil
|
|
}
|
|
|
|
// unmarshalRawFields decodes the malformed_commands.raw_fields column.
|
|
func unmarshalRawFields(payload []byte) (map[string]any, error) {
|
|
out := map[string]any{}
|
|
if len(payload) == 0 {
|
|
return out, nil
|
|
}
|
|
if err := json.Unmarshal(payload, &out); err != nil {
|
|
return nil, fmt.Errorf("unmarshal raw fields: %w", err)
|
|
}
|
|
return out, nil
|
|
}
|