Diego
← Voltar para projetos

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