O último publisher nativo: fechando a consolidação do Postiz
O blog-manager nasceu com uma integração nativa por plataforma: um cliente Medium, um cliente Dev.to, um cliente Hashnode, um anunciador de LinkedIn, cada um com seu próprio fluxo de rascunho, job de sincronização e pilha de colunas de status. Nas últimas semanas venho apagando essas integrações uma por uma e roteando tudo através do Postiz, o agendador auto-hospedado que já cuida dos meus posts em redes sociais. Essa semana a última caiu. O Medium se foi, o código nativo, pelo menos, e o blog-manager agora conversa com exatamente um sistema externo.
Por que o Medium ficou por último
O Dev.to foi o modo fácil: ele tem uma API funcional, o Postiz tem um provedor Dev.to funcional, e migrar foi basicamente trabalho de payload. O Medium é o oposto. O Medium parou de emitir tokens novos de API e eu nunca tive um de antes desse corte, o que significa que minha “integração nativa com o Medium” eram quatrocentas linhas de código que não conseguiam publicar nada. Não era uma integração. Era uma peça de museu.
Não existe mais caminho de API para o Medium. O caminho que existe, construído num projeto separado do homelab, é um pequeno sidecar ao lado do Postiz: um navegador headless com patch furtivo mantendo uma sessão logada no Medium, acionando o recurso oficial do próprio Medium de importar uma história. O provedor Medium do Postiz recebeu um patch para chamar esse sidecar em vez de api.medium.com. Do ponto de vista do blog-manager, nada dessa maquinaria é visível, ele simplesmente agenda um post para o canal Medium como qualquer outro.
Um artigo cujo corpo é uma URL
A dobra de design interessante: para o Dev.to, o blog-manager manda o corpo completo em markdown, mais título, tags, capa e URL canônica. Para o Medium ele manda quase nada, a URL canônica pública do post como o próprio conteúdo. A ponte cola essa URL na caixa de importação do Medium, e o próprio Medium busca a página, extrai o artigo, e define rel=canonical de volta para o meu site. Quanto menos eu mando, menos pode divergir. O próprio importador do Medium é o motor de renderização, então a fidelidade da formatação é problema do Medium, não meu.
Isso deixou a mudança de código pequena. O serviço ArticlePoster existente, vindo da migração do Dev.to, ficou ciente do provedor: um branch monta o payload do Dev.to (corpo + configurações), o outro manda a URL. O rastreamento de status não precisou de nenhuma mudança, as entradas dos dois provedores caem no mesmo mapa de publicação por canal, e o mesmo job de reconciliação recorrente fica de olho para ver quando elas mudam de rascunho para publicado.
Mordido duas vezes, mas mais rápido na segunda
Mandei o primeiro post do Medium com configurações vazias, no raciocínio de que, já que o Medium extrai tudo da URL, não havia nada para configurar. O Postiz rejeitou: settings.title should not be null… settings.subtitle should not be null.
Esse é exatamente o mesmo modo de falha que me pegou durante a migração do Dev.to: o Postiz valida a requisição contra um DTO de configurações antes mesmo do provedor rodar, e o DTO exige campos que o runtime mal usa. Da última vez perdi uma rodada de teste ao vivo com isso. Dessa vez reconheci o erro de cara, puxei o MediumSettingsDto do código-fonte do Postiz, vi que title e subtitle são obrigatórios com um mínimo de dois caracteres, e mandei o fix em minutos. Tem algo satisfatório num bug que custa uma hora na primeira vez e cinco minutos na segunda, significa que aquela primeira hora de fato comprou alguma coisa. (Também ajudou eu ter escrito essa pegadinha na minha base de conhecimento depois da primeira rodada, incluindo a instrução “leia tanto o provider QUANTO o DTO de configurações.” Meu eu do passado deixou um bilhete exatamente onde meu eu do futuro ia tropeçar.)
Um detalhe sutil: o título não é só para agradar o DTO. O importador do Medium descarta o título silenciosamente, e a ponte reaplica ele depois da importação a partir desse campo de configuração. Então o campo obrigatório que parecia burocracia é, na verdade, essencial para o funcionamento.
A fogueira
Depois que um post real completou o ciclo, o blog-manager preparou, o Postiz entregou para a ponte, a ponte importou e publicou, o artigo saiu com o título certo, corpo, link canônico e lista de referências corretos, apaguei a integração nativa. Seis classes de serviço, dois jobs, um endpoint de webhook, um poller de RSS, sete colunas em posts e duas em blogs, a interface de credenciais, um card de dashboard, e uma sincronização recorrente. Líquido: menos 1.603 linhas.
Contando o arco inteiro (Hashnode, LinkedIn, Dev.to, Medium), o blog-manager se livrou de todo cliente de plataforma que já teve. O que sobra é um cliente Postiz, um article poster com dois pequenos branches por provedor, e um loop de reconciliação. Três mecanismos diferentes de sincronização de status viraram um só.
O que eu diria para o meu eu do passado
Apague código que não consegue rodar. A integração nativa do Medium ficou lá por meses parecendo uma funcionalidade que funcionava. O token dela nunca poderia ser reemitido. Código que não consegue ter sucesso é pior do que nenhum código, ele faz a interface mentir.
Quando você bater numa falha estranha de terceiro, escreva onde você vai tropeçar de novo na próxima vez. A nota sobre o DTO ser mais rígido que o runtime se pagou sozinha numa única issue.
Consolidação se acumula. Cada migração deixou a próxima menor. O Dev.to exigiu construir o caminho do artigo, o sweep de reconciliação, e a linha na interface. O Medium reaproveitou tudo isso e foi basicamente um branch de payload e um diff de remoção.
A dependência nova é real: toda a publicação agora passa pela minha instância Postiz do homelab, e o Medium especificamente depende de uma sessão de navegador que vai expirar em algum momento. Essa é uma troca que fiz sabendo o que estava fazendo, um motor só para manter em vez de quatro integrações para vigiar, com um botão de importação manual como plano B se a ponte quebrar no meio da semana.
Leitura relacionada
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.
O webhook do Postiz que eu não consegui construir, e o polling em vez dele
Um spike que terminou em "não construa isso": rejeição de IP privado, um payload vazio sem autenticação, e a pull API que tinha tudo que a push API não tinha.
Agendamento e compartilhamento de link mais inteligente no meu cross-poster do Postiz
Agendamento para o futuro, blurbs mais ricos com thumbnails, um seletor de qual URL usar, além do bug de timezone no horário de parede e a importação que copiou a coluna errada.
Você também pode achar útil
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 maiseSIM 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 maisNordPass
Gerenciador de senhas da equipe por trás da NordVPN, com um plano gratuito.
Como afiliado da NordPass, ganho com compras qualificadas.
Saiba mais