feat: backend service
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
package mail
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// AdminListDeliveriesPage bundles the pagination metadata returned to
|
||||
// the admin API. The same shape is reused by AdminListDeadLettersPage
|
||||
// — keeping it explicit clarifies the wire contract for handlers.
|
||||
type AdminListDeliveriesPage struct {
|
||||
Items []Delivery
|
||||
Page int
|
||||
PageSize int
|
||||
Total int64
|
||||
}
|
||||
|
||||
// AdminListDeadLettersPage mirrors AdminListDeliveriesPage for the
|
||||
// dead-letter listing.
|
||||
type AdminListDeadLettersPage struct {
|
||||
Items []DeadLetter
|
||||
Page int
|
||||
PageSize int
|
||||
Total int64
|
||||
}
|
||||
|
||||
// AdminListDeliveries returns the requested delivery page. page is
|
||||
// 1-indexed; pageSize is bounded by the caller (handler defaults).
|
||||
func (s *Service) AdminListDeliveries(ctx context.Context, page, pageSize int) (AdminListDeliveriesPage, error) {
|
||||
page, pageSize = normalisePaging(page, pageSize)
|
||||
offset := (page - 1) * pageSize
|
||||
items, total, err := s.deps.Store.ListDeliveries(ctx, offset, pageSize)
|
||||
if err != nil {
|
||||
return AdminListDeliveriesPage{}, err
|
||||
}
|
||||
return AdminListDeliveriesPage{
|
||||
Items: items,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// AdminGetDelivery returns the delivery row by id, or
|
||||
// ErrDeliveryNotFound when the row does not exist.
|
||||
func (s *Service) AdminGetDelivery(ctx context.Context, deliveryID uuid.UUID) (Delivery, error) {
|
||||
return s.deps.Store.GetDelivery(ctx, deliveryID)
|
||||
}
|
||||
|
||||
// AdminListAttempts returns every attempt for the delivery in
|
||||
// attempt_no order. ErrDeliveryNotFound is returned when the delivery
|
||||
// row itself does not exist; an empty list (no rows yet) returns nil
|
||||
// without error.
|
||||
func (s *Service) AdminListAttempts(ctx context.Context, deliveryID uuid.UUID) ([]Attempt, error) {
|
||||
if _, err := s.deps.Store.GetDelivery(ctx, deliveryID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.deps.Store.ListAttempts(ctx, deliveryID)
|
||||
}
|
||||
|
||||
// AdminResendDelivery re-arms the targeted row for another delivery
|
||||
// cycle. The contract: ErrDeliveryNotFound when the row is missing,
|
||||
// ErrResendOnSent when the row is in the terminal `sent` state.
|
||||
// Otherwise the row is reset to status='pending' with attempts=0 and
|
||||
// next_attempt_at=now(); the worker picks it up on the next tick.
|
||||
func (s *Service) AdminResendDelivery(ctx context.Context, deliveryID uuid.UUID) (Delivery, error) {
|
||||
return s.deps.Store.ResendNonSent(ctx, deliveryID, s.deps.Now())
|
||||
}
|
||||
|
||||
// AdminListDeadLetters returns the dead-letter page newest-first.
|
||||
func (s *Service) AdminListDeadLetters(ctx context.Context, page, pageSize int) (AdminListDeadLettersPage, error) {
|
||||
page, pageSize = normalisePaging(page, pageSize)
|
||||
offset := (page - 1) * pageSize
|
||||
items, total, err := s.deps.Store.ListDeadLetters(ctx, offset, pageSize)
|
||||
if err != nil {
|
||||
return AdminListDeadLettersPage{}, err
|
||||
}
|
||||
return AdminListDeadLettersPage{
|
||||
Items: items,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// normalisePaging clamps page and pageSize to the values handlers can
|
||||
// safely pass through to the store. The defaults match what the
|
||||
// existing admin endpoints use elsewhere in `internal/server`.
|
||||
func normalisePaging(page, pageSize int) (int, int) {
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize <= 0 {
|
||||
pageSize = 25
|
||||
}
|
||||
if pageSize > 200 {
|
||||
pageSize = 200
|
||||
}
|
||||
return page, pageSize
|
||||
}
|
||||
Reference in New Issue
Block a user