Pular para o conteúdo
Development

Adicionando o Unsplash como um segundo provedor de imagem de destaque

Por Victor Da Luz•
railsrubyimagesdev-logblog-manager

O blog-manager já tinha uma fonte de imagem pras imagens de destaque: o Pexels. Uma caixa de busca bate na API do Pexels, eu escolho uma foto, e a URL é hotlinkada no frontmatter do post. Um issue anterior transformou isso numa camada de verdade agnóstica de provedor (um registro indexado por nome de provedor, resultados intercalados em round-robin, um formato de hash normalizado compartilhado). Este aqui era sobre de fato plugar um segundo provedor nesse registro: o Unsplash.

A parte interessante não foi a integração de busca em si. As diretrizes de API do Unsplash têm dois requisitos que o Pexels não tem: você precisa hotlinkar (nada de baixar e re-hospedar, o que convenientemente combina com o jeito que esse app já trata o Pexels), e você precisa dar um ping num endpoint de “rastreamento de download” por foto sempre que alguém de fato escolhe uma foto, só pra que o Unsplash possa contar isso como um download nas próprias métricas deles.

O que eu construí

Unsplash::Client espelha Pexels::Client quase linha por linha: Net::HTTP feito à mão, um proc connection: como costura de teste em vez de puxar webmock ou VCR, a mesma hierarquia Error/AuthError sob ImageSearch::Error. A única diferença de verdade é o formato de resposta do Unsplash (results em vez do photos do Pexels, urls.regular/urls.small em vez de src.large/src.medium) e um novo método track_download pro ping de rastreamento.

O próprio ping virou seu próprio job pequeno, UnsplashDownloadPingJob, disparado a partir de select_image no momento em que alguém escolhe uma foto do Unsplash. Sem retries, sem discard_on, só um rescue-and-log. Se o endpoint de rastreamento do Unsplash estiver fora do ar, isso não é motivo pra bloquear alguém de escolher uma imagem de destaque.

Uma decisão que quase pulei

A URL de rastreamento de download (links.download_location) vem de volta na resposta de busca do Unsplash e viaja como um campo de formulário escondido através do botão “Select” já existente em cada resultado de busca. O que significa, por construção, que é um valor que o navegador controla. Minha primeira versão simplesmente pegava o que o client mandava e disparava um GET autenticado nele com a chave do Unsplash do app anexada. Um campo de formulário adulterado vazaria alegremente essa chave pra qualquer host que um atacante colocasse nesse campo.

O Brakeman não pegou isso, porque não é um padrão que ele checa, e é fácil de perder porque url_full (a URL real da imagem) tem exatamente a mesma forma de “confiar no cliente” mas é inofensivo, já que é o navegador que busca essa, não o servidor. O fix foi uma checagem de host de uma linha em track_download antes de anexar o header Client-ID. Pequeno, mas do tipo que é óbvio em retrospecto e invisível até você ir procurar.

O que me surpreendeu

Verificar isso manualmente sem uma chave real do Unsplash (registrar uma é uma viagem ao painel pra depois) virou um pequeno desvio. Acabei conduzindo o app através de ActionDispatch::Integration::Session dentro de um script rails runner contra o banco de dados de desenvolvimento real, já que o app está atrás de autenticação OIDC de verdade e não tem bypass de modo dev. Isso funcionou, até eu misturar um dispatch completo de request/response com uma query pura do ActiveRecord no mesmo processo e bater num erro genuinamente confuso de ActiveSupport::ExecutionContext nas profundezas do Rails. Nada a ver com meu código. Separar cada checagem na sua própria invocação de runner fez o erro sumir. Eu não tinha esbarrado nessa interação específica antes.

A única coisa que funcionou de forma limpa: o app já tinha uma chave real do Pexels configurada em desenvolvimento, então consegui confirmar que a mudança pro registro de dois provedores não regrediu a busca só-Pexels já existente. Ela retornou resultados reais do Pexels e o rodapé “Photos provided by Pexels,” exatamente como antes.

O que vem a seguir

Isso vai ao ar com o Unsplash conectado à busca, seleção, atribuição e página de configurações, mas não fica ativo até que uma Unsplash Access Key seja registrada e colada em Settings. O modo demo é 50 requisições por hora, o que o cache de busca de 12 horas já existente torna tranquilo pra uma pessoa só. A aprovação de produção (5.000 por hora) precisa de screenshots de atribuição enviados pro Unsplash depois que isso tiver sido de fato usado por um tempo.

Leitura relacionada

Você também pode achar útil

RackNerd

RackNerd VPS

Hospedagem VPS econômica para serviços leves que funcionam continuamente.

Como afiliado da RackNerd, ganho com compras qualificadas.

Saiba mais
Proton

Proton VPN

VPN comercial com filtragem NetShield e interruptor de desligamento automático.

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

Saiba mais
Proton

Proton Drive

Armazenamento em nuvem criptografado, da equipe por trás do Proton Mail.

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

Saiba mais