Gerenciador de favoritos

O Markit é um app web para salvar e organizar links. Você cola um endereço, ele preenche sozinho título, descrição e imagem da página e guarda tudo em coleções, acessíveis de qualquer navegador. Desenhei a interface, construí o frontend em React e a API em FastAPI e coloquei tudo em produção.
Links salvos costumam acabar em abas esquecidas, notas soltas ou favoritos presos a um único navegador. A regra que guiou o projeto foi simples: salvar tem que custar um gesto, e achar depois tem que custar outro.
Como funciona:
1- Cole o link. Se faltar o https://, o app completa.

2- Revise o preview. Título, descrição, imagem e favicon chegam preenchidos, e dá para ajustar tudo antes de salvar.

3- Organize e encontre. Coleções, uma visão de recentes, busca por texto e filtros por ordenação, domínio e data em que o link foi salvo.

Cada bookmark abre num painel lateral, onde dá para editar título, descrição, URL e coleção, copiar o link ou apagar.

Salvar sem atrito. O campo aceita o link cru e completa o protocolo, então colar e salvar é um fluxo de duas ações. Com o preview já preenchido, confirmar é o caminho mais curto e editar é opcional.
Painel lateral no lugar de uma página de edição. A lista continua visível enquanto você edita, então não há troca de contexto nem perda da posição de scroll.
Filtros sob demanda. A barra de filtros pode ser escondida: quem só quer olhar os links vê uma tela limpa, quem precisa refinar abre ordenação, domínio e data.
O frontend é um app estático em React 19 e TypeScript, construído com Vite, com React Router, Tailwind, ícones da Iconoir e notificações com Sonner. Ele conversa com uma API FastAPI sobre SQLAlchemy e SQLite. O scraper de metadados usa httpx e BeautifulSoup para extrair título, descrição, imagem e favicon de cada página.
Como o app faz requisições a endereços digitados pelo usuário, tratei o scraper como a parte mais sensível do sistema.
Scraper: proteção contra SSRF, para que ninguém use o servidor para alcançar serviços internos.
Sessão: JWT em cookie HttpOnly, então o navegador não guarda token nem senha acessíveis ao JavaScript, e o logout revoga a sessão.
Contas: senhas com bcrypt, limite de tentativas no login, e alteração de perfil ou exclusão de conta exigem a senha atual.
Configuração: a chave de assinatura é obrigatória e precisa de entropia mínima, CORS é restrito e as mutações têm proteção CSRF por cabeçalho.
O frontend é estático e o backend roda com uvicorn atrás de um proxy Nginx. O app está hospedado 100% em uma VPS.