init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,185 @@
package queue
import (
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"strings"
"time"
)
var ErrInvalidEnvelope = errors.New("invalid queue envelope")
type JSONSerializer struct {
version string
enableLegacyFallback bool
queueType string
messageGroupID string
}
type LegacyJSONSerializer struct{}
func NewJSONSerializer(version string, enableLegacyFallback bool, queueType, messageGroupID string) *JSONSerializer {
version = strings.TrimSpace(version)
if version == "" {
version = "v1"
}
queueType = strings.ToLower(strings.TrimSpace(queueType))
if queueType == "" {
queueType = QueueTypeStandard
}
return &JSONSerializer{
version: version,
enableLegacyFallback: enableLegacyFallback,
queueType: queueType,
messageGroupID: strings.TrimSpace(messageGroupID),
}
}
func (s *JSONSerializer) Serialize(ctx context.Context, job EmailJob) (*OutboundMessage, error) {
envelope, err := NewEnvelope(ctx, s.version, job)
if err != nil {
return nil, err
}
body, err := json.Marshal(envelope)
if err != nil {
return nil, err
}
msg := &OutboundMessage{
ID: envelope.MessageID,
Body: body,
Attributes: map[string]string{
"message_kind": envelope.Kind,
"message_id": envelope.MessageID,
"correlation_id": envelope.CorrelationID,
"trace_id": envelope.TraceID,
"schema_version": envelope.Version,
"job_type": envelope.Payload.NormalizedType(),
},
}
if s.queueType == QueueTypeFIFO {
msg.MessageGroupID = s.messageGroupID
if msg.MessageGroupID == "" {
msg.MessageGroupID = "default"
}
msg.DeduplicationID = envelope.DeduplicationKey()
}
return msg, nil
}
func (s *JSONSerializer) Deserialize(body []byte, attributes map[string]string) (*Envelope, error) {
var envelope Envelope
if err := json.Unmarshal(body, &envelope); err == nil {
if err := validateEnvelope(&envelope); err == nil {
return &envelope, nil
}
}
if !s.enableLegacyFallback {
return nil, ErrInvalidEnvelope
}
var job EmailJob
if err := json.Unmarshal(body, &job); err != nil {
return nil, errors.Join(ErrInvalidEnvelope, err)
}
job = job.Canonical()
if err := job.Validate(); err != nil {
return nil, errors.Join(ErrInvalidEnvelope, err)
}
id := messageHash(body)
correlationID := strings.TrimSpace(attributes["correlation_id"])
if correlationID == "" {
correlationID = id
}
traceID := strings.TrimSpace(attributes["trace_id"])
if traceID == "" {
traceID = correlationID
}
return &Envelope{
Version: "legacy",
MessageID: id,
Kind: EmailKind,
OccurredAt: time.Now().UTC(),
CorrelationID: correlationID,
TraceID: traceID,
IdempotencyKey: id,
Payload: job,
}, nil
}
func (LegacyJSONSerializer) Serialize(ctx context.Context, job EmailJob) (*OutboundMessage, error) {
job = job.Canonical()
if err := job.Validate(); err != nil {
return nil, err
}
body, err := json.Marshal(job)
if err != nil {
return nil, err
}
id := messageHash(body)
correlationID := CorrelationIDFromContext(ctx)
if correlationID == "" {
correlationID = id
}
traceID := TraceIDFromContext(ctx)
if traceID == "" {
traceID = correlationID
}
return &OutboundMessage{
ID: id,
Body: body,
Attributes: map[string]string{
"message_kind": EmailKind,
"message_id": id,
"correlation_id": correlationID,
"trace_id": traceID,
"schema_version": "legacy",
"job_type": job.NormalizedType(),
},
}, nil
}
func (LegacyJSONSerializer) Deserialize(body []byte, attributes map[string]string) (*Envelope, error) {
return NewJSONSerializer("v1", true, QueueTypeStandard, "").Deserialize(body, attributes)
}
func validateEnvelope(envelope *Envelope) error {
if envelope == nil {
return ErrInvalidEnvelope
}
if strings.TrimSpace(envelope.Version) == "" {
return errors.Join(ErrInvalidEnvelope, errors.New("version is required"))
}
if strings.TrimSpace(envelope.MessageID) == "" {
return errors.Join(ErrInvalidEnvelope, errors.New("message id is required"))
}
if strings.TrimSpace(envelope.Kind) == "" {
return errors.Join(ErrInvalidEnvelope, errors.New("kind is required"))
}
if envelope.OccurredAt.IsZero() {
return errors.Join(ErrInvalidEnvelope, errors.New("occurred at is required"))
}
if err := envelope.Payload.Validate(); err != nil {
return errors.Join(ErrInvalidEnvelope, err)
}
envelope.Payload = envelope.Payload.Canonical()
if strings.TrimSpace(envelope.CorrelationID) == "" {
envelope.CorrelationID = envelope.MessageID
}
if strings.TrimSpace(envelope.TraceID) == "" {
envelope.TraceID = envelope.CorrelationID
}
if strings.TrimSpace(envelope.IdempotencyKey) == "" {
envelope.IdempotencyKey = envelope.MessageID
}
return nil
}
func messageHash(body []byte) string {
sum := sha256.Sum256(body)
return hex.EncodeToString(sum[:16])
}