Skip to content

PetMatch ponta a ponta: contrato unificado, matches/chat, auth, mobile, uploads, CI e segurança - #4

Merged
LukeSantossz merged 22 commits into
mainfrom
claude/exciting-goodall-ycjonr
Jun 17, 2026
Merged

LukeSantossz merged 22 commits into
mainfrom
claude/exciting-goodall-ycjonr

Conversation

@LukeSantossz

Copy link
Copy Markdown
Collaborator

Contexto

Uma revisão da base apontou que o merge anterior (PR #3) deixou dois modelos de dados de Pet conflitantes (purpose/birthDate/Macho no NestJS vs. seeking/ageMonths/macho no seed/engine/web), o que quebrava o seed, o feed e a integração web↔API. Este PR corrige a causa-raiz e leva o produto a um MVP funcional ponta a ponta (web + mobile).

Principais mudanças

Correções de base

  • Unifica o modelo de Pet no contrato compartilhado (seeking, ageMonths, macho/femea, pequeno/medio/grande, location GeoJSON, active); toJSON expõe id.
  • Corrige o seed (validava 0/26 → 26/26) e adiciona npm run seed.
  • Alinha as rotas ao frontend: GET /pets, GET /pets/feed?petId= (delega ao motor, com fallback no Mongo), POST /pets (dono via X-Owner-Id, location derivada da cidade).
  • Corrige import duplicado em pets.module.ts que quebrava o build.

Novas funcionalidades

  • Match recíproco real + persistência do Match (índice pairKey no lugar do multikey petIds); lista de matches e chat (REST).
  • Autenticação JWT (/auth/register|login, /users/me, bcrypt) convivendo com a identidade anônima por X-Owner-Id.
  • Web: roteamento (react-router), telas de matches/chat/perfil/login, gestão de "meus pets", geolocalização e upload de fotos.
  • Mobile (Expo): sai do andaime para um app navegável (abas + stack) com login, swipe, matches, chat e perfil.
  • Upload de imagens (POST /uploads), Swagger em /docs e ConfigModule.

Segurança

  • Fecha IDOR: PATCH/DELETE /pets/:id agora exigem ser o dono (403 caso contrário).
  • POST /uploads exige login.

Qualidade

  • Testes: 41 (Jest) + 7 (Vitest) + 23 (Pytest). Lint (ESLint, Ruff/Black). CI por serviço (.github/workflows/ci.yml) e Dockerfiles + docker compose --profile full.

Como testar

docker compose --profile full up -d --build (ou npm run dev + npm run seed), abra http://localhost:5173. Detalhes em Como usar o app no README.

Ressalvas

  • Não houve execução ao vivo no ambiente de desenvolvimento (sem MongoDB/daemon Docker/dispositivo): fluxos com banco foram cobertos por testes com mock + validateSync + build/typecheck. O CI deste PR executa lint/build/testes dos quatro serviços.
  • Mobile validado por tsc --noEmit; rode expo start num device para o teste real.
  • npm audit: 1 item moderado transitivo (js-yaml via @nestjs/swagger), sem correção não-quebrante.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM


Generated by Claude Code

claude added 22 commits June 17, 2026 11:40
…artilhado

O schema/DTO do Pet divergiam do modelo usado pelo seed, pelo motor de
recomendação e pelo frontend (purpose/birthDate/energyLevel/Macho-Fêmea/isActive
vs. seeking/ageMonths/macho-femea/active), o que quebrava a integração
ponta a ponta.

- Schema e DTOs passam ao contrato compartilhado: gender (macho|femea),
  size (pequeno|medio|grande), seeking (socializacao|cruzamento|ambos),
  ageMonths, temperament, compatibility, city, active. toJSON expõe `id`
  (sem _id/__v). Índice composto passa a {location, seeking, status}.
- Rotas: adiciona GET /pets (lista) e GET /pets/feed?petId= (declarada antes
  de :id). O feed delega a ordenação ao recommendation-engine e cai para um
  modo degradado no Mongo se o motor estiver indisponível.
- POST /pets resolve o dono pelo header X-Owner-Id (@OwnerId) e deriva a
  location da cidade.
- Corrige imports duplicados em pets.module.ts (quebravam o build).
- Atualiza os specs ao novo modelo e comportamento.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
…pm run seed

O seed falhava a validação do Mongoose (faltavam birthDate/energyLevel/purpose e
gender/size estavam fora do enum) e não havia script para executá-lo. Agora os 26
pets de exemplo validam contra o schema unificado, com `city` no topo do
documento, e `npm run seed` executa src/seed.ts via ts-node.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
…no front

Ao registrar um like, o backend verifica se o alvo já havia curtido a origem e
devolve `matched` de verdade. O frontend passa a usar esse resultado em vez de
um match aleatório (Math.random). A persistência do documento Match e o chat
seguem como Etapa 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
Campos extras espelhados de Pet para o frontend montar o card sem uma segunda
consulta. Compatível com o contrato existente (campos opcionais, com default).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
…viços

README reflete o app funcionando ponta a ponta (passo de seed, variáveis,
status e próximos passos). Adiciona .github/workflows/ci.yml com build/testes do
backend, build do web e pytest do motor de recomendação.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- Match: corrige o índice (pairKey única no lugar do multikey {petIds} único) e
  expõe `id` no toJSON. MatchesService.ensureFromReciprocalLike faz upsert
  idempotente do par e monta o resumo a partir dos pets.
- SwipesService passa a criar o Match no like recíproco e devolve `matchId`.
- MatchesController: GET /matches (do dono, via X-Owner-Id).
- Chat: MessagesService + MessagesController (GET/POST /matches/:matchId/messages),
  com autorização pelo dono e atualização de lastMessageAt.
- Testes: matches, messages e swipes (32 no total).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- AuthModule: POST /auth/register e /auth/login devolvem { accessToken, user };
  senha com bcryptjs, JWT via @nestjs/jwt + passport-jwt.
- UsersModule: UsersService (create/findByEmail/findById) e GET /users/me
  protegido por JwtAuthGuard. toJSON do User esconde o passwordHash e expõe id.
- OptionalJwtAuthGuard global: popula req.user quando há JWT válido sem bloquear
  o fluxo anônimo. @OwnerId passa a preferir o usuário autenticado e cai para o
  X-Owner-Id/DEMO_OWNER_ID quando não há login.
- backend-api/.env.example com MONGO_URI/PORT/AI_ENGINE_URL/JWT_*.
- Testes de AuthService e UsersService (38 no total).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
Lista os pets do dono autenticado (ou identificado por X-Owner-Id), usado pela
tela de perfil do web. Declarado antes de :id para não colidir com a rota.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- react-router-dom: rotas para feed, /matches, /matches/:id (chat), /perfil,
  /login, /cadastro e /pets/novo.
- Auth: telas de login e criação de conta; sessão (JWT + conta) no localStorage;
  cliente HTTP envia Bearer quando logado (e X-Owner-Id no anônimo).
- Matches: lista por dono; o match no swipe agora abre o chat real (matchId).
- Chat: lista e envio de mensagens.
- Perfil: conta (entrar/sair) e "meus pets" (listar/excluir) via GET /pets/mine.
- SwipePage: navegação no header e onboarding preservado.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- ConfigModule.forRoot({ isGlobal: true }) carrega variáveis de .env.
- Swagger (plugin do nest-cli + DocumentBuilder) publica a doc interativa da API
  em /docs, com bearer auth.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
Botão "usar minha localização" captura coordenadas do dispositivo (GeoJSON
[lng, lat]) e as envia em `location`; sem isso, o backend deriva da cidade.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
…at, perfil)

Sai do andaime para um app navegável (react-navigation: tabs + stack):
- Sessão em AsyncStorage (token + conta + ownerId anônimo) hidratada no boot.
- Cliente HTTP espelhando o web (Bearer/X-Owner-Id).
- Telas: descoberta (curtir/passar + match abre o chat), matches, chat,
  perfil (entrar/sair, meus pets, excluir), login, criar conta e cadastrar pet.
Validado com tsc --noEmit (build/execução exigem Expo + dispositivo).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- backend-api: build multi-stage (node) -> roda dist/main.js.
- recommendation-engine: imagem python + uvicorn.
- web-app: build Vite -> nginx (com fallback SPA para o react-router).
- docker-compose: serviços de app sob o profile "full" (o padrão segue só com
  o Mongo, para combinar com `npm run dev`). `docker compose --profile full up`
  sobe tudo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- ESLint flat config no backend e no web (typescript-eslint + regras de hooks no
  web); scripts `lint`. Remove diretivas eslint-disable obsoletas.
- Engine: aplica o formato do Black (test_ranking). Ruff e Black já configurados
  no pyproject e nas deps de dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- Vitest (jsdom) com testes dos helpers de display e do MatchModal.
- CI: lint no backend/web, build, testes (jest/vitest), typecheck do mobile e
  ruff/black/pytest no motor de recomendação.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- Backend: POST /uploads (multipart, campo "file") grava a imagem em disco e
  devolve { url }; arquivos servidos estaticamente em /uploads. Valida tipo
  (imagens) e tamanho (5 MB).
- Web: campo de arquivo no cadastro de pet que envia a foto e usa a URL retornada
  (a URL manual continua funcionando).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
…s/uploads)

O padrão `uploads/` ignorava também `src/modules/uploads`, deixando o módulo de
fora do commit anterior (o build quebraria). Padrão corrigido para `/uploads/`
(apenas a pasta de runtime na raiz do serviço) e os fontes adicionados.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
Reflete matches/chat, auth JWT, uploads, Swagger, geolocalização, app mobile,
testes/lint/CI e a stack via docker compose --profile full. Próximos passos
revisados (tempo real, storage externo, etc.).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
- PATCH/DELETE /pets/:id agora exigem que o solicitante seja o dono (@OwnerId +
  verificação no service), fechando o IDOR que permitia editar/excluir o pet de
  qualquer um. Lança 403 quando não é o dono.
- POST /uploads passa a exigir JWT (JwtAuthGuard); evita upload anônimo. No web,
  o seletor de arquivo só aparece logado — anônimo continua podendo colar URL.
- Testes cobrindo os casos de "Forbidden" (41 no backend).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
Passo a passo de uso (onboarding, swipe, match, chat, conta/perfil), nota sobre
reciprocidade para gerar match, instruções do mobile e rotas principais da API
(Swagger em /docs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
O main trouxe `fix: importacao correta do module`, que removeu as linhas de
import duplicadas em pets.module.ts de forma diferente da deste branch. O merge
3-way juntou as duas remoções e apagou TODOS os imports de PetsController/
PetsService (TS2304 no CI do PR). Imports restaurados; backend volta a compilar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VcgjQctU9a6AkRXuXKh2CM
@LukeSantossz
LukeSantossz merged commit 38c5f38 into main Jun 17, 2026
8 checks passed
@LukeSantossz
LukeSantossz deleted the claude/exciting-goodall-ycjonr branch June 17, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants