Pular para o conteúdo
Development

O webhook do Postiz que eu não consegui construir, e o polling em vez dele

Por Victor Da Luz
railsrubypostizdev-logblog-manager

Meu blog manager agenda posts sociais através do Postiz, um agendador self-hosted. Agendar é a metade fácil. A metade difícil é descobrir, depois, onde o post realmente foi parar. O Postiz publica de forma assíncrona, então a chamada de “agendar” retorna antes de qualquer coisa estar no ar e sem link nenhum para o post publicado. O plano no papel era simples: receber o webhook de publicação do Postiz e registrar a URL. Eu nunca cheguei a escrever esse receptor, e o motivo dele ter morrido é mais interessante do que o código que o substituiu.

Lendo antes de construir

O Postiz é open source, então em vez de adivinhar o payload do webhook eu fixei minha leitura na versão exata que minha máquina roda (v2.21.8) e rastreei o caminho de publicação. Duas descobertas, qualquer uma delas fatal.

Ele não aceita minha URL. A configuração do webhook valida o destino com uma função chamada IsSafeWebhookUrl. Ela faz uma consulta DNS de verdade e rejeita qualquer coisa que resolva para um endereço privado, 10.x, 192.168.x, e por aí vai. Meu blog manager vive num domínio do homelab que resolve para um endereço privado 192.168.x.x. O Postiz recusa registrá-lo. Não existe um endpoint público para apontar, então eu nem consigo criar o webhook.

E o payload está vazio de qualquer forma. Mesmo deixando de lado o problema da URL, o webhook da v2.21.8 não é autenticado (sem secret, sem assinatura, eu não teria como saber se um POST é realmente do Postiz), dispara só em caso de sucesso (então falhas ficam invisíveis) e, por causa de um id incompatível no código-fonte, faz POST de um array vazio. A função que monta o corpo busca o post pelo id errado, não encontra nada, e envia []. Rastreei linha por linha e ainda assim meio que esperava estar errado, mas o caminho é inequívoco.

Então a feature, como especificada, era impossível de construir. Isso é um resultado útil: um spike que termina em “não construa isso” me poupou de montar um ingress público e um receptor só para captar pings vazios e não verificáveis.

Polling, a resposta chata que funciona

A coisa sobre um sistema assíncrono com um webhook ruim é que, na maioria das vezes, você pode simplesmente perguntar a ele. O Postiz tem um endpoint de leitura, GET /posts sobre um intervalo de datas, e quando eu chamei com minha API key, todo campo que o webhook deveria entregar estava ali: por canal, o estado (QUEUE / PUBLISHED / ERROR), a URL publicada, o nome do provider. Autenticado, completo, e ainda me conta sobre falhas que o webhook nunca contaria.

Então eu faço polling. Quando um post é agendado, eu capturo o id de post por canal que o Postiz devolve, e um background job checa o calendário um minuto depois, casa meus ids contra ele, e registra onde cada canal foi parar, mudando o post para “posted” assim que todo canal está no ar, recuando e tentando de novo enquanto algum ainda está na fila. Menos elegante que um push, mas é o tipo de menos elegante que realmente funciona.

A pegadinha: posts de antes de eu capturar ids

Lancei a captura de id e testei, e um post que eu tinha publicado mais cedo naquele dia ainda aparecia como “scheduled.” Claro que sim, ele saiu antes de o código que captura o id de correlação existir, então o poller não tinha nada para casar. Em vez de adivinhar, conferi o que o calendário realmente retorna e achei a URL do post bem ali no conteúdo da linha. Então dei ao poller um fallback: se não há id capturado, casar com a linha do Postiz cujo conteúdo contém a URL do post. Posts novos casam por id; os antigos se autocorrigem por URL. Uma passada de reconciliação e o catálogo antigo acendeu com seus links reais.

O que eu levo dessa experiência

Duas coisas. Primeiro, leia o código-fonte daquilo com que você está integrando antes de desenhar sua solução em cima da documentação ou do issue tracker. O webhook “existe” no sentido de que há código, e “não funciona” no sentido de que o código envia um corpo vazio, e só o código-fonte diz qual dos dois é o caso. Segundo, quando uma push API falha com você, confira se o mesmo sistema tem uma pull API. Geralmente tem, e geralmente é a que de fato é mantida.

Leitura relacionada

Development

Add a feature, or move a responsibility?

Adding Postiz social cross-posting looked done until a blunt question exposed a double-post bug, and a full audit of every posting path in the app found two more like it.

Ler

Você também pode achar útil

AdGuard

AdGuard para iOS

Bloqueio de anúncios e rastreadores em todo o sistema no iOS, sem necessidade de um servidor DNS separado.

Como afiliado da AdGuard, ganho com compras qualificadas.

Saiba mais
Airalo

eSIM Airalo

eSIM de dados local para viagens - sem necessidade de trocar um SIM físico.

Este é meu link de indicação da Airalo. Você recebe um desconto no seu primeiro eSIM e eu ganho crédito da Airalo para o meu.

Saiba mais
Proton

Proton Mail

E-mail criptografado de ponta a ponta, com arquitetura de acesso zero.

Como parceiro da Proton, ganho com compras qualificadas dos serviços de privacidade e segurança da Proton (Pass, Mail, VPN, Drive).

Saiba mais