Pular para o conteúdo
Development

Construindo o blog-manager: um model Blog com Active Record Encryption

Por Victor Da Luz
railsblog-managerdev-logactive-record-encryption

Estou construindo um app em Rails para gerenciar meus blogs. O objetivo: um lugar só para configurar o repositório GitHub, o token do Medium e as credenciais do LinkedIn de cada blog, e depois automatizar o cross-posting mais adiante. Hoje tenho dois blogs e o fluxo é uma bagunça de abas salvas e tokens que sempre esqueço. Isso resolve isso.

Esta é a parte 1: o model Blog e a UI.

O model Blog: as credenciais, não o CRUD

O CRUD é a parte chata. A parte interessante são as credenciais. Cada blog tem seu próprio personal access token do GitHub, um token de integração do Medium e um token OAuth do LinkedIn. Nenhum deles pode ficar em texto puro no SQLite.

Meu primeiro instinto foi o attr_encrypted, que é o que a maioria dos tutoriais recomenda. Aí lembrei que o Rails 7 trouxe o Active Record Encryption e eu ainda não tinha usado. A API é tão enxuta quanto pode ser:

class Blog < ApplicationRecord
  encrypts :github_token, :medium_token, :linkedin_access_token

  validates :name, presence: true
  validates :github_repo_url, presence: true
end

Isso é tudo. Sem gem extra, sem colunas de ciphertext pareadas, sem coluna de IV pra gerenciar na mão. O Rails cuida de tudo, e as chaves ficam em config/credentials.yml.enc, dentro de active_record_encryption, que é onde deveriam estar.

A pegadinha das fixtures que quebrou todos os testes

Adicionei as colunas criptografadas em blogs.yml e a suíte inteira começou a estourar ActiveRecord::Encryption::Errors::Decryption em todo teste. O carregador de fixtures ignora os callbacks do model, então escreve strings cruas em colunas onde o Rails espera ciphertext, e tudo quebra no primeiro teste que tenta ler de volta.

A solução foi tirar as colunas criptografadas do arquivo de fixtures e usar Blog.create! nos testes que realmente precisam de um token. Deixei um comentário explicando o motivo, pra não desfazer isso sem querer daqui a seis meses.

Uma coluna para uma funcionalidade que ainda não existe

O token do LinkedIn expira em 60 dias, então já estou adicionando uma coluna expires_at mesmo sem o fluxo OAuth existir ainda. Sai mais barato agora do que uma migration depois. A view de listagem usa essa coluna para mostrar “expirado” em vermelho e “expira em breve” em âmbar quando um token está a menos de uma semana de vencer.

Uma UI que eu realmente usaria

O model levou uma tarde. A UI levou mais tempo, porque os scaffolds do Rails são funcionais mas parecem Bootstrap de 2012, e eu queria algo que eu gostasse de abrir todo dia.

Cheguei numa sidebar escura (#111110), uma área principal em creme quente (#f5f3ee), mais ou menos a cor de papel envelhecido, e um acento terracota (#c85e32) na marca. Para tipografia, usei Bricolage Grotesque nos títulos, que tem um caráter editorial levemente áspero e passa intencionalidade, DM Sans no corpo do texto, e JetBrains Mono nos campos de token. Tudo do Google Fonts, nada instalado localmente.

Cada integração tem seu próprio card no formulário com um ícone pequeno: um campo mono para o token do GitHub, um aviso amarelo na seção do LinkedIn sobre aquela expiração de 60 dias. Na tabela, o status da credencial é um único ponto colorido: verde para configurado, vermelho para expirado, âmbar para prestes a expirar, um traço para ausente. A tela de login é toda escura, com um card creme flutuando no meio e um botão de entrar em terracota. Se eu sou o único usuário, ainda assim pode ser bonito.

Duas coisas pequenas que me custaram tempo

O button_to no Rails 8 envolve o botão no seu próprio <form>. Então uma classe CSS no botão não alcança o form que o envolve, e o display padrão do form quietamente quebrou o alinhamento das células da minha tabela. A correção é form: { style: "display:inline;" } (ou form_class:). Óbvio depois que você sabe, chato na primeira vez.

A outra foi o bin/dev. O shebang dele é #!/usr/bin/env sh, e esse shell não carrega o ~/.zshrc, então meus shims do asdf não entram no $PATH. O bin/dev antigo tentava dar gem install foreman contra o Ruby 2.6 do sistema e falhava. Corrigi o sintoma adicionando o foreman ao Gemfile e chamando bundle exec foreman, e a causa raiz exportando o caminho dos shims diretamente no ~/.zshrc, já que o asdf 0.19 abandonou o antigo asdf.sh.

O que vem depois

A parte 1 é um model e uma UI. O ponto real do app é a próxima peça: puxar os posts do repositório GitHub e exibi-los, que é onde a lógica de sindicação começa. Isso é a parte 2.

Leitura relacionada

Você também pode achar útil

Proton

Proton Pass

Gerenciador de senhas focado em privacidade, 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
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 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