coda/docs/blueprints/10-ntfy-notifications.md

4.2 KiB

Blueprint 10 — Ntfy Notifications

  • ID: BP-10
  • Feature: Notificações via ntfy sobre sucesso/falha de jobs
  • Requisitos: RF-017
  • Depende de: BP-05 (Worker Pool)
  • Status: concluído

Objetivo

Implementar um cliente HTTP que envia notificações para um servidor ntfy (self-hosted ou público) ao final de cada job, informando se a conversão foi concluída com sucesso ou falhou. Respeitar DRY_RUN.

Contexto

  • BP-05 definiu o worker pool com um fluxo de process que termina chamando finishJob (status done ou error). O notificador será chamado antes do finish.
  • config.Service fornecerá NtfyEnabled, NtfyURL, NtfyTopic, NtfyToken.
  • O padrão segue a mesma abordagem do Rescanner: interface injetada no worker.Pool, substituível para testes.

Restrições e Convenções

  • Sem comentários; erros com %w; logs com slog.With("job_id", id).
  • Resiliência: falha ao enviar notificação não deve interromper o pipeline. Logar o erro e seguir.
  • DRY_RUN=true: não enviar notificação; logar a intenção.
  • Se NTFY_ENABLED=false ou NtfyURL/NtfyTopic vazios: pular (log debug).
  • Token (NTFY_TOKEN) é opcional; se presente, enviar como Authorization: Bearer <token>.
  • Timeout de 10s no HTTP client.
  • Mensagens em inglês.

Design

internal/ntfy/client.go

type Client struct {
    cfg *config.Service
    hc  *http.Client
}

func New(cfg *config.Service) *Client

// Notify envia uma notificação com o resultado do job.
// Implementa worker.Notifier.
func (c *Client) Notify(ctx context.Context, job *models.Job) error
  • Endpoint: POST {NtfyURL}/{NtfyTopic}
  • Prioridade: 4 (high) para erro, 3 (default) para sucesso.
  • Tags: white_check_mark (done), x (error).
  • Corpo: json.Marshal(ntfyMessage{...}) com title, message, priority, tags.
  • Header Authorization: Bearer <NtfyToken> apenas se token != "".

internal/worker/notifier.go

type Notifier interface {
    Notify(ctx context.Context, job *models.Job) error
}

type NoopNotifier struct{}

func (NoopNotifier) Notify(ctx context.Context, job *models.Job) error {
    return nil
}

Adicionar campo Notifier ao worker.Pool e chamar p.Notifier.Notify(ctx, job) antes de finishJob.

Wiring em cmd/server/main.go

  • notifier := ntfy.New(cfgSvc) (ou NoopNotifier{} se NtfyEnabled=false).
  • Passar notifier ao worker.New(..., notifier).

Config (novas chaves editáveis)

Key Default Descrição
NTFY_ENABLED false Habilita notificações ntfy
NTFY_URL URL base do servidor ntfy
NTFY_TOPIC Tópico para publicar
NTFY_TOKEN Token de autenticação (opcional)

Tarefas

  • Adicionar Notifier interface em internal/worker/.
  • Criar internal/ntfy/client.go (New, Notify).
  • Adicionar keys ntfy ao config (Key + validação + editableKeys + Config struct + buildConfig).
  • Chamar Notifier.Notify no worker.process antes de finishJob.
  • Wiring no main.go: ntfy.New(cfgSvc)worker.New(...).
  • Atualizar documentos: README.md, AGENTS.md, requirements.md, architecture.md.

Testes

internal/ntfy/client_test.go (httptest.Server):

  • Notify envia POST /<topic> com prioridade/título/tags corretos para job concluído.
  • Notify envia prioridade high e tag x para job com erro.
  • DRY_RUN=true → nenhum request é feito.
  • NtfyEnabled=false → nenhum request, retorna nil.
  • Token presente → header Authorization: Bearer <token>.
  • Timeout/erro de rede → erro logado, não interrompe.

internal/worker/worker_test.go (atualizar):

  • Verificar que Notifier fake é chamado antes do finishJob tanto em sucesso quanto em erro.
  • Verificar que NoopNotifier não causa efeitos colaterais.

Definição de Pronto

  • DoD comum atendido.
  • go test ./internal/ntfy/... ./internal/worker/... verde.
  • make run-prod: notificações enviadas ao final de cada job (se habilitado).

Arquivos

  • Criar: internal/ntfy/client.go (+ _test.go), internal/worker/notifier.go.
  • Modificar: internal/config/config.go, internal/worker/worker.go, cmd/server/main.go.