Changelog
v0.9.2
Setembro 2026
Rotas do frontend em inglês, edição do perfil concentrada num lugar só, e o esquema do banco consolidado.
As rotas do frontend acompanharam a API. /perfil virou /profile, /publicacao virou /post, /politica-de-privacidade virou /privacy-policy. A refatoração deixou duas classes de estrago que só apareceram depois: caminhos de importação atingidos pela substituição — a rota mudou de nome, a pasta components/perfil/ não —, e texto visível traduzido por engano. O segundo foi o pior: a política de privacidade acabou falando em "data de birthDate" e "contagem de followers", e o perfil exibia "No Blabry memberSince". A varredura por padrão de texto não pegou nada disso, porque texto de JSX não é string nem comentário. A verificação passou a ser feita sobre a árvore sintática — nós de texto e atributos visíveis, importações que resolvem para arquivo real, nomes importados que existem como exportação.
Toda a edição do perfil mudou de lugar. Os lápis espalhados pelo perfil saíram; nome, bio, e-mail, nascimento e nacionalidade passam a ser editados em "Conta e segurança" → "Informações pessoais", numa vista que desliza dentro da mesma janela em vez de abrir um segundo modal — modal sobre modal confunde o que o Esc fecha e, no celular, empilha duas caixas roláveis.
Os campos aparecem em dois grupos rotulados por quem enxerga: "Perfil", que qualquer pessoa vê, e "Dados pessoais", que só o dono vê. O agrupamento por visibilidade, e não por tipo de dado, responde à dúvida que de fato ocorre a quem está editando — sem o rótulo, é fácil escrever o telefone na bio achando que é campo privado. A foto continua sendo trocada pelo avatar no perfil: recorte de imagem não cabe num formulário de texto.
Nenhum endpoint mudou. É o mesmo PATCH /users/me de antes, chamado de outro lugar.
O esquema do banco voltou a ser um arquivo só. A pasta migrations/ foi removida e database.sql passou a ser o retrato do banco como ele está hoje. A separação entre "estado final" e "caminho até ele" existiu por um bom motivo — create table if not exists não adiciona coluna que falte, e uma divergência entre arquivo e banco vira falha em tempo de consulta sem causa evidente. Foi encerrada por outro: o git já é um registro numerado e imutável de cada alteração, com data, autor e diff, e a segunda cópia em SQL só criava a pergunta de qual das duas estava atualizada. Com um único banco em produção, migrações cobravam mais do que entregavam. No lugar delas ficou um aviso no topo do arquivo: ele cria, não atualiza.
Na limpeza, a tabela password_reset saiu do esquema. Ela guardava o código em texto puro e sem limite de tentativas, e verification_code já a havia substituído por completo. O DROP para bancos antigos ficou anotado no lugar dela. A carga de países virou INSERT IGNORE, para o arquivo poder ser reexecutado sem abortar no primeiro duplicado.
v0.9.1
Agosto 2026
Correções vindas de uma auditoria de segurança.
Não havia limitação de requisições em lugar nenhum. POST /auth/login aceitava tentativas infinitas, e a única defesa era a lentidão do bcrypt — que é também o vetor de ataque, porque cada tentativa consome cerca de 250ms de CPU e algumas dezenas em paralelo derrubam a instância. Agora há limite por janela fixa, e cada rota sensível tem o próprio balde: trinta tentativas de login a cada dez minutos, dez cadastros, trinta verificações de código, dez pedidos de e-mail, e um teto geral de seiscentas requisições.
Os baldes são separados por um motivo prático que só apareceu quando a suíte de testes começou a falhar com 429: um balde único significa que cada usuário novo gasta cadastro, verificação e login antes de fazer qualquer coisa. Atrás de uma rede compartilhada — universidade, escritório, operadora móvel —, onde centenas de pessoas saem pelo mesmo endereço, isso trancaria a quinta pessoa a se cadastrar.
O limitador foi escrito sem dependência porque a aplicação roda em uma instância, onde um contador em memória é exato. Com mais de uma instância nenhum contador local serve, e a resposta é um store compartilhado — não uma biblioteca diferente. Ele depende de trust proxy: atrás do proxy do Railway, sem essa configuração, todos os visitantes chegam com o mesmo endereço, dividem um único balde, e o primeiro a estourar o limite tranca os demais.
O middleware de erro mascarava toda mensagem em produção. A intenção era não vazar detalhe de exceção; o efeito foi que "É necessário aceitar a política de privacidade" chegava ao usuário como "Erro interno do servidor", e a interface caía no texto genérico por status. Passa a mascarar apenas os 5xx. A distinção é de origem, não de gravidade: os 4xx são frases escritas para o usuário ler, os 5xx carregam mensagem de exceção, que pode expor nome de tabela ou caminho de arquivo.
Excluir a conta não removia dado pessoal algum. A operação preenchia deleted_at e parava aí — e-mail, nome, data de nascimento, nacionalidade, bio, foto e hash de senha permaneciam no banco indefinidamente, contra o que a política de privacidade promete. E como email e alias são únicos, quem excluísse a conta nunca mais conseguiria se cadastrar com o mesmo endereço.
Agora o conteúdo publicado é apagado de fato, e o cadastro é anonimizado, liberando o e-mail e o @. Anonimizar em vez de apagar a linha é decisão deliberada: o DELETE dispararia ON DELETE CASCADE sobre like_post e removeria as curtidas dadas em publicações de terceiros, derrubando contadores alheios retroativamente. Encerrar a própria conta não pode alterar conteúdo de outras pessoas. O artigo 12 da LGPD coloca dado anonimizado fora do alcance da lei, de modo que a obrigação de eliminação é cumprida.
A migração 005 torna birth_date e nationality anuláveis — a obrigatoriedade no cadastro é regra do serviço, não da coluna.
Dois itens da auditoria seguem em aberto, por escolha: trocar a senha ainda não invalida sessões já abertas, e o tempo de resposta do login permite descobrir quais e-mails têm conta. Ambos mexem em autenticação e ficaram para depois deste ciclo de deploy.
v0.9.0
Agosto 2026
A API passou a ser inteiramente em inglês.
Antes ela misturava dois idiomas: caminhos de recurso em inglês, campos do JSON em português. A justificativa era que identificador de recurso e vocabulário de aplicação seriam coisas distintas — e ela não se sustentou. O custo apareceu em cada fronteira, e o exemplo que decidiu a questão foi encontrado durante a própria refatoração: paraPerfil devolvia alias enquanto toJSON devolvia apelido, dois nomes para o mesmo dado dependendo do endpoint, sem que ninguém notasse por meses. Um idioma só elimina a categoria inteira de erro.
A mudança atravessa as três camadas. Arquivos e importações; identificadores internos — deLinha/paraLinha viram fromRow/toRow, buscarPerfil vira findProfile; e o contrato público, que é o que quebra clientes: campos (nome → name, curtidas → likes, seguidores → followers), envelope de erro ({ erro } → { error }), campo do upload multipart (foto → photo) e caminhos de rota — /auth/cadastro virou /auth/signup, /auth/verificar-email virou /auth/verify-email, /users/me/exclusao/codigo virou /users/me/deletion/code.
Os apelidos de coluna no SQL acompanharam: seguindo_este virou is_following, te_segue virou follows_you. As colunas em si não mudaram — já estavam em inglês, e a tradução entre elas e o domínio continua sendo responsabilidade dos repositórios.
Duas coisas permanecem em português, por decisão e não por omissão: as mensagens de erro, que o usuário final lê, e os comentários do código, que quem mantém lê. Nenhum dos dois é interface de programação.
A variável de ambiente EMAIL_REMETENTE passou a EMAIL_SENDER.
O vocabulário completo virou documento — back_end/docs/VOCABULARIO.md —, e é a fonte única para os dois repositórios. Um nome que não estiver lá é um nome que ninguém decidiu.
v0.8.0
Agosto 2026
Privacidade e integridade de conta.
O perfil público deixou de expor dados pessoais. E-mail, data de nascimento e nacionalidade passam a existir apenas no próprio perfil, e a API os devolve como null para visitantes — a interface não esconde uma seção preenchida, ela simplesmente não a recebe. Uma caixa vazia com um travessão anunciaria que há algo ali; o perfil público termina na bio.
Política de privacidade em página própria e pública, com coleta e tratamento de dados, criptografias empregadas, direitos do titular e os serviços de infraestrutura no rodapé. O aceite passou a ser obrigatório no cadastro, bloqueado na interface e validado de novo no servidor — a interface é conveniência, a API é a fronteira real. A comparação do aceite é estrita com o booleano true, de modo que a string "true", que é o que chega quando um formulário serializa um booleano sem cuidado, seja recusada.
Publicações ganharam página própria em /post/:id, com os dados do autor, o conteúdo e os comentários já expandidos. O horário do cartão passou a ser o caminho até ela, seguindo a convenção da categoria e evitando transformar o cartão inteiro em link — o que quebraria os links do autor e os botões aninhados. O resultado da busca por publicações passou a levar à publicação, e não ao perfil de quem escreveu.
O perfil passou a listar as publicações do usuário, logo abaixo das informações, no próprio perfil e no de terceiros. É o mesmo componente nos dois casos: o que muda — poder editar ou excluir — já é decidido comparando o autor da publicação com o usuário do token.
Verificação por código de seis dígitos enviado ao e-mail, em três fluxos: confirmação da conta recém-criada, redefinição de senha e exclusão de conta. O cadastro deixou de devolver token; a conta nasce pendente e o login responde 403 até a confirmação — status distinto do 401 de credencial errada, e checado depois da senha, para que não se possa descobrir quais e-mails têm conta aqui.
Os códigos são gravados como hash, valem quinze minutos, servem uma única vez e toleram cinco tentativas erradas, com intervalo mínimo de sessenta segundos entre pedidos. O consumo acontece em um UPDATE ... WHERE used_at IS NULL, de modo que duas requisições simultâneas com o mesmo código não passem as duas. As rotas de reenvio respondem igual para e-mail cadastrado e não cadastrado, para não virarem um verificador de quem tem conta na plataforma.
Exclusão de conta implementada, em três etapas: entender o efeito, informar o código recebido por e-mail e digitar a palavra de confirmação. A exclusão é lógica — um DELETE físico arrastaria em cascata comentários de terceiros feitos em publicações do usuário, apagando conteúdo de quem não pediu nada.
Envio de e-mail transacional por Resend, escolhido no lugar de Nodemailer: o segundo é um transporte SMTP e ainda exigiria um servidor que entregasse de fato, com reputação de IP, SPF, DKIM e DMARC configurados. Em desenvolvimento, sem chave configurada, os e-mails são impressos no console.
Documentação ganhou a seção de Critérios de Aceitação, com os cenários escritos em BDD — Dado, Quando, Então.
v0.7.0
Agosto 2026
Feed implementado de ponta a ponta: publicação, listagem paginada, curtidas, comentários e exclusão. Curtidas e comentários são contados por agregação no banco a cada consulta, e não mantidos como colunas — o que elimina a possibilidade de o contador divergir dos registros que ele deveria refletir.
Busca dividida em duas abas, publicações e usuários, com o @ funcionando como atalho para a aba de usuários. A busca em publicações usa índice FULLTEXT; a de usuários usa comparação por prefixo. Ambas paginadas.
Edição de perfil e de comentários. Comentários podem ser editados apenas pelo autor e apenas nos primeiros quinze minutos, com o prazo medido pelo relógio do servidor; uma vez editados, passam a ser exibidos como tal, porque quem respondeu reagiu ao texto anterior. A troca de e-mail exige a senha atual, por ser o meio de recuperação da conta.
Foto de perfil pode ser trocada a qualquer momento, com recorte antes do envio.
Seletor de emojis com o conjunto Unicode completo, categorias e busca, carregado sob demanda para não pesar no bundle inicial.
Esquema de banco passou a ser versionado em migrações numeradas, separando o estado final desejado do caminho para alcançá-lo. Índices adicionados para as consultas de feed, comentários e busca.
v0.6.0
Agosto 2026
Interface autenticada reestruturada em torno de uma casca única — barra superior, menu lateral no desktop e navbar inferior no mobile —, com as rotas protegidas passando a compartilhar esse layout. Telas ainda não implementadas passam a sinalizar seu estado ao usuário.
Feed e perfil implementados a partir do protótipo: publicação com seletor de emojis, curtidas, comentários, busca de usuários e paginação em todas as listagens. A camada de serviços do frontend foi reorganizada, e o protocolo de paginação isolado em um hook reutilizável.
Especificação OpenAPI ampliada com os esquemas de publicação, comentário, perfil e paginação, e com os parâmetros de consulta comuns às listagens.
Seção de Arquitetura adicionada à documentação, registrando a decomposição em camadas e as decisões de projeto que a sustentam.
v0.5.0
Agosto 2026
Rota pública GET /countries implementada no backend, servindo a lista de nacionalidades diretamente da tabela countries. O formulário de cadastro passou a carregar as opções do banco em vez de mantê-las fixas no frontend, garantindo que só sejam oferecidos valores aceitos pela chave estrangeira de user.nationality.
Tabela countries remodelada: o id inteiro foi removido e o código ISO 3166-1 alpha-3 passou a ser a chave primária, acompanhado da coluna name com o nome do país em português.
Camada de persistência migrada para orientação a objetos — repositórios passaram a ser classes que recebem a conexão por injeção de dependência, e os models User e Country passaram a definir o formato exposto pela API. O hash de senha deixou de ser serializável, eliminando o risco de vazamento em respostas.
Documentação da API publicada em /docs via Swagger UI, gerada a partir de anotações nas próprias rotas.
:::note Sobre o salto de versão Até aqui o changelog acompanhava apenas o frontend. A partir desta entrada ele passa a registrar o projeto como um todo, adotando a versão do backend como referência única. :::
v0.2.1
Agosto 2026
Migração de IDs deixando o padrão int para dar lugar ao padrão de caracteres do UUID v7
v0.2.0
Agosto 2026
Integração do frontend com a API em produção. Login conectado ao backend com retorno de token JWT. Rotas protegidas implementadas com redirecionamento automático. Tela de Feed exibindo nome do usuário autenticado.
v0.1.0
Agosto 2026
Estrutura base do projeto React iniciada com Vite. Roteamento configurado com React Router DOM cobrindo as cinco rotas principais da aplicação. Componentes base de autenticação criados — AuthInput com campos controlados via useState e AuthButton com prop onClick. Tela de Login e tela de Cadastro implementadas com responsividade mobile.