Linkiess
Plataforma link-in-bio — API NestJS + front React, com customização de perfil e upload para S3.
Back-end (API)
- NestJS 9
- TypeScript
- Prisma 4.5
- PostgreSQL
- Passport.js (Local + JWT)
- bcrypt
- Multer
- AWS S3 SDK
- class-validator
Front-end
- React 18
- React Router DOM 6
- Redux Toolkit
- Axios
- Tailwind CSS 3
- jwt-decode
- React Icons
Full-screen para maior qualidade.
Contexto
Linkiess é uma plataforma link-in-bio onde o usuário concentra todos os seus links em uma página pública personalizada. Após criar conta, cada usuário tem um perfil em /{username} e uma área privada de edição. O perfil é customizável: imagem de fundo, foto de perfil, descrição, cor do texto e cor das barras. Imagens são armazenadas na AWS S3.
O back-end (NestJS 9 + Prisma 4.5 + PostgreSQL) implementa autenticação via Passport.js (Local + JWT), upload de imagem em dois estágios (disco → S3), proteção global com opt-out por decorator, e verificação de ownership por token.
O front-end (React 18 + Redux Toolkit + Tailwind) gerencia auth e mensagens globais via Redux, verifica ownership do perfil decodificando o JWT no cliente, e usa um boolean trigger no estado local como mecanismo de re-fetch pós-mutação.
Decisões técnicas
Front · Redux Toolkit para auth + feedback global
Estado de autenticação (isLogged, username) vive no Redux persistido no localStorage. O mesmo store gerencia mensagens de sucesso/erro via messageSlice — sem lib de toast externa.
Front · JWT decodificado no cliente antes de cada operação sensível
Antes de renderizar a tela de customização, o token é decodificado com jwt-decode e o username do payload é comparado com o da URL e com o do localStorage. Qualquer divergência loga o usuário e redireciona.
Front · Boolean trigger como mecanismo de re-fetch pós-mutação
Ações de escrita (upload de imagem, editar link, mudar cor) invertem um booleano trigger no estado local. O useEffect da página depende desse valor — quando flipa, rebusca os dados do perfil.
API · Upload em dois estágios: disco → S3
Multer salva o arquivo em ./uploads (disco local). Após a escrita, S3Storage.saveFile() lê o buffer, faz putObject na bucket com ACL public-read e apaga o temp com fs/promises unlink. Multer exige destino síncrono no parse do multipart — o estágio local funciona como área de staging antes de persistir no object store.
API · Guard global + @IsPublic() opt-out
JwtAuthGuard é aplicado globalmente no módulo raiz — todas as rotas são protegidas por padrão. Rotas públicas (criação de conta, leitura de perfil) recebem o decorator @IsPublic(), que seta metadata isPublic: true. O guard lê essa metadata antes de validar o token; se presente, deixa passar.
API · Verificação de ownership via jwt_decode no service
Rotas de edição recebem o header Authorization e passam para authorizationMiddleware() no service, que usa jwt_decode para extrair o username do payload e compara com o id_user do recurso. Se divergir, lança 401. Guard valida assinatura + expiração via Passport; middleware valida se o dono do token é o dono do recurso.
Rodar localmente
Necessário: Docker e Git instalados.
mkdir linkiess-local && cd linkiess-local
git clone https://github.com/odgiedev/linkiess
git clone https://github.com/odgiedev/linkiess-api
cp linkiess/.env.example linkiess/.env
cp linkiess-api/.env.example linkiess-api/.env
# Terminal 1 — API
cd linkiess-api && docker compose up
# Terminal 2 — Front
cd linkiess && docker compose up