Skip to main content

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.