diff options
| author | Chia <Chia@93.nz> | 2026-08-05 22:01:29 +1200 |
|---|---|---|
| committer | Chia <Chia@93.nz> | 2026-08-05 22:07:50 +1200 |
| commit | eadb2ffe85c43cf6fc741c9823cd28eedb4a844c (patch) | |
| tree | 1aba2536d57360da403aa35c9ced58b615c7064e /internal/controlplane/outbox.go | |
| parent | cd0dd91ab93653631904f2ea0e574ccde6d60339 (diff) | |
feat: harden prepaid billing and commercial operations
Diffstat (limited to '')
| -rw-r--r-- | internal/controlplane/outbox.go | 19 |
1 files changed, 16 insertions, 3 deletions
diff --git a/internal/controlplane/outbox.go b/internal/controlplane/outbox.go index b54a064..b708a90 100644 --- a/internal/controlplane/outbox.go +++ b/internal/controlplane/outbox.go @@ -109,8 +109,9 @@ func (s *Store) ClaimMail(ctx context.Context) (mailer.Message, bool, error) { var ciphertext []byte err = tx.QueryRow(ctx, `WITH candidate AS ( SELECT id FROM console_mail_outbox - WHERE ((status IN ('pending','failed') AND available_at <= now()) + WHERE ((status IN ('pending','retry') AND available_at <= now()) OR (status='sending' AND claimed_at < now()-interval '5 minutes')) + AND NOT EXISTS (SELECT 1 FROM mail_suppressions s WHERE lower(s.recipient)=lower(console_mail_outbox.recipient)) ORDER BY available_at,created_at FOR UPDATE SKIP LOCKED LIMIT 1 ) UPDATE console_mail_outbox o SET status='sending',claimed_at=now(),attempts=attempts+1,last_error='' FROM candidate WHERE o.id=candidate.id @@ -146,7 +147,19 @@ func (s *Store) MarkMailFailed(ctx context.Context, id string, deliveryErr error if len(message) > 1000 { message = message[:1000] } - _, err := s.db.Exec(ctx, `UPDATE console_mail_outbox SET status='failed',claimed_at=NULL,last_error=$2, - available_at=now()+make_interval(secs => LEAST(300, 5 * attempts)) WHERE id=$1 AND status='sending'`, id, message) + _, err := s.db.Exec(ctx, `UPDATE console_mail_outbox SET + status=CASE WHEN attempts>=10 THEN 'dead' ELSE 'retry' END,claimed_at=NULL,last_error=$2, + available_at=now()+make_interval(secs => LEAST(3600, 5 * power(2,LEAST(attempts,9))::int)) + WHERE id=$1 AND status='sending'`, id, message) return err } + +func (s *Store) MailQueueStatus(ctx context.Context) (MailQueueStatus, error) { + var result MailQueueStatus + err := s.db.QueryRow(ctx, `SELECT + count(*) FILTER (WHERE status IN ('pending','sending','retry')), + count(*) FILTER (WHERE status='dead'), + min(created_at) FILTER (WHERE status IN ('pending','sending','retry')) + FROM console_mail_outbox`).Scan(&result.Backlog, &result.Failed, &result.OldestPending) + return result, err +} |
