65 lines
2.7 KiB
Go
65 lines
2.7 KiB
Go
package main
|
|
|
|
// Hearing mesh-mail's added events (novox/hq ADR 0459 §3, §12, issue 1518): mesh-mail-reader consumes
|
|
// mesh-mail.mail.*.message.added under its own runs-as account, exactly as mesh-mail-notifier does (main.go
|
|
// AddedPattern, watchWith), but records each one as a waiting mail (store.go, pgstore.go) BEFORE the handler
|
|
// returns success — so a mail heard is never lost, even across a restart: the controller composes a durable
|
|
// consumer (MaxDeliver 5, AckWait 30s) that redelivers an event five times before it is dead-lettered, and a
|
|
// store failure here must make that redelivery happen, never swallow the event.
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"time"
|
|
)
|
|
|
|
// EventBody is what mesh-mail's added event says of a message (metadata only; mailevent.go names where it is
|
|
// said). Unknown fields are ignored: the reader reads only what it needs, never a body.
|
|
type EventBody struct {
|
|
Account string `json:"account"`
|
|
Folder string `json:"folder"`
|
|
UID uint32 `json:"uid"`
|
|
UIDValidity uint32 `json:"uidvalidity"`
|
|
MessageID string `json:"message-id"`
|
|
Date string `json:"date"`
|
|
From []Person `json:"from"`
|
|
Subject string `json:"subject"`
|
|
Size int64 `json:"size"`
|
|
Flags []string `json:"flags"`
|
|
}
|
|
|
|
// Hear records one added event as a waiting mail (1518/AC2): the store is written to before this returns
|
|
// nil, so the caller (the bus subscription's handler) only then acknowledges the event. A store failure is
|
|
// returned unchanged, so the event is redelivered rather than lost (1518/AC2). The same event heard twice
|
|
// (1518/AC3) is recorded once: AddWaiting itself is idempotent on the mail's own identity.
|
|
func Hear(ctx context.Context, store Store, raw json.RawMessage, now func() time.Time) error {
|
|
var body EventBody
|
|
if err := json.Unmarshal(raw, &body); err != nil {
|
|
// An event this reader cannot even read is taken and dropped, exactly as mesh-mail-notifier does
|
|
// (main.go watchWith): a malformed event would otherwise be redelivered forever, never succeeding.
|
|
return nil
|
|
}
|
|
date, err := time.Parse(time.RFC3339, body.Date)
|
|
if err != nil {
|
|
date = now()
|
|
}
|
|
w := WaitingMail{
|
|
Account: body.Account,
|
|
Folder: body.Folder,
|
|
MessageID: body.MessageID,
|
|
UIDValidity: body.UIDValidity,
|
|
UID: body.UID,
|
|
Date: date,
|
|
From: body.From,
|
|
Subject: body.Subject,
|
|
Flags: body.Flags,
|
|
Size: body.Size,
|
|
Heard: now(),
|
|
}
|
|
return store.AddWaiting(ctx, w)
|
|
}
|
|
|
|
// AddedPattern is the mesh-mail stream's event this module consumes (its own `consumes` entry, novox/hq ADR
|
|
// 0459 §3): a new message's `added` only.
|
|
const AddedPattern = "mesh-mail.mail.*.message.added"
|