| index 7cc800ae1..caf2d6517 100644 |
| --- a/mail_cli.go |
| +++ b/mail_cli.go |
| @@ -244,6 +244,23 @@ type apiEmailCounts struct { |
| Deliveries int `json:"deliveries"` |
| } |
| |
| +// apiEmailSender is one sender in the status reply (tenant-email senders |
| +// design §8). The reply's top-level fields keep describing the app sender |
| +// and the app's whole day, so an older console that sends no senders array |
| +// leaves this empty and the output is what it has always been. |
| +type apiEmailSender struct { |
| + ID string `json:"id"` |
| + Scope string `json:"scope"` |
| + Source string `json:"source"` |
| + Host string `json:"host,omitempty"` |
| + Address string `json:"address,omitempty"` |
| + Active bool `json:"active"` |
| + PauseKind string `json:"pause_kind,omitempty"` |
| + DailyCap int `json:"daily_cap"` |
| + KeyID string `json:"key_id,omitempty"` |
| + Counts apiEmailCounts `json:"counts"` |
| +} |
| + |
| // apiEmailReply is the shape every declare/status/enable/rotate/pause door |
| // answers with — emailReply's wire contract (internal/console/apimail.go). |
| type apiEmailReply struct { |
| @@ -259,6 +276,7 @@ type apiEmailReply struct { |
| DailyCap int `json:"daily_cap"` |
| Active bool `json:"active"` |
| Counts apiEmailCounts `json:"counts"` |
| + Senders []apiEmailSender `json:"senders"` |
| // DefaultHost is this app's own host — <app>.<sqid>.<platform-suffix> — |
| // spec §3's default sending identity, computed console-side (I4, |
| // review follow-up: the CLI itself never learns |
| @@ -1231,6 +1249,30 @@ func printEmailStatus(w io.Writer, res apiEmailReply) { |
| fmt.Fprintf(w, " today: %d/%d sends, %d delivered, %d bounces, %d complaints, %d rejects\n", |
| res.Counts.Sends, res.DailyCap, res.Counts.Deliveries, res.Counts.Bounces, res.Counts.Complaints, res.Counts.Rejects) |
| |
| + // One line per sender, and only when there is more than the app sender |
| + // to say something about: an app with one sender has already had its |
| + // whole story told by the lines above, and repeating it would be noise. |
| + if len(res.Senders) > 1 { |
| + fmt.Fprintln(w, " senders:") |
| + for _, sn := range res.Senders { |
| + what := sn.ID |
| + if sn.Host != "" { |
| + what = sn.Host |
| + } else if sn.Address != "" { |
| + what = sn.Address |
| + } |
| + state := c.ok("active") |
| + if !sn.Active { |
| + state = "paused" |
| + if sn.PauseKind != "" { |
| + state += " (" + sn.PauseKind + ")" |
| + } |
| + } |
| + fmt.Fprintf(w, " %s %s: %s, %d/%d sends, %d bounces, %d complaints\n", |
| + sn.Scope, what, state, sn.Counts.Sends, sn.DailyCap, sn.Counts.Bounces, sn.Counts.Complaints) |
| + } |
| + } |
| + |
| if len(res.MissingVars) > 0 { |
| warnf(w, " drift: %s not yet delivered to this app's config", strings.Join(res.MissingVars, ", ")) |
| } |