API backend do Mural de Fotos, construída com NestJS, Prisma e PostgreSQL. O sistema gerencia usuários, autenticação, posts com múltiplas mídias, curtidas, comentários, marcação de usuários, detecção/rotulagem de faces, stories retrospectivos e notificações por e-mail e push.
- Node.js 20
- NestJS 11
- TypeScript
- Prisma ORM
- PostgreSQL com extensão
pgvector - AWS S3 para armazenamento público de mídias
- Resend para envio de e-mails
- Expo Push Notifications
- Sharp e FFmpeg para otimização de imagens, vídeos e thumbnails
- Swagger/OpenAPI
- Zod via
nestjs-zodpara validação e serialização
- Cadastro, listagem, atualização e remoção de usuários.
- Login com JWT e proteção global de rotas autenticadas.
- Recuperação e redefinição de senha por código enviado por e-mail.
- Upload avulso de imagem para S3.
- Criação de posts com upload multipart de até 10 imagens/vídeos.
- Otimização de imagens e vídeos antes do upload.
- Geração de thumbnail para vídeos.
- Posts públicos e privados, com paginação, ordenação e busca.
- Marcação manual de usuários em posts.
- Curtidas e comentários com notificações.
- Integração com serviço externo de detecção facial.
- Agrupamento de faces por embeddings usando
pgvector. - Rotulagem de clusters/faces com usuário ou nome.
- Stories retrospectivos trimestrais/anuais e retrospectiva global.
- Lembretes automáticos de memória por cron.
src/
app.module.ts # módulos globais, guards, pipes e interceptors
main.ts # bootstrap, CORS, prefixo global, Swagger e porta 4000
auths/ # login, JWT, local strategy e guards
users/ # usuários, recuperação de senha e tokens push
posts/ # posts, mídia, busca, thumbnails e startup reprocessing
likes/ # curtidas em posts
comments/ # comentários em posts
aws/ # upload para S3
labeling/ # detecção facial, clusters e rotulagem
stories/ # geração e consulta de stories retrospectivos
notification/ # e-mail, push e listeners de eventos
common/ # pipes, filtros, interceptors e decorators
databases/prisma/ # PrismaService
prisma/
schema.prisma # modelos e relações do banco
migrations/ # migrations versionadas
- Node.js 20 ou compatível
- Yarn 1.x
- Docker e Docker Compose, recomendado para banco local
- FFmpeg instalado no ambiente local se rodar fora do Docker
- Bucket S3 com permissão de escrita e leitura pública dos objetos enviados
- Chave do Resend para fluxos de e-mail
- Serviço externo compatível com
POST /detect-facesse usar detecção facial
Crie um arquivo .env na raiz do projeto. Exemplo para desenvolvimento:
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/nestdb"
JWT_SECRET="troque-este-segredo"
ROUTE=""
AWS_REGION="us-east-1"
AWS_ACCESS_KEY="sua-access-key"
AWS_SECRET_KEY="sua-secret-key"
AWS_PUBLIC_BUCKET_NAME="seu-bucket-publico"
RESEND_API_KEY="re_xxxxxxxxx"
POST_CREATED_WEBHOOK_URL="http://localhost:8000"Notas:
ROUTEé opcional e define um prefixo global para a API. SeROUTE=/api, a documentação fica em/api/docs.JWT_SECRETtem fallback parasecretno código, mas deve ser definido em qualquer ambiente real.POST_CREATED_WEBHOOK_URLé usado comobaseURLdo cliente HTTP que chama/detect-faces.- O
docker-compose.ymlsobrescreveDATABASE_URLdentro do container da API para apontar para o serviçodatabase.
yarn installSuba o banco local:
docker compose up -d databaseGere o Prisma Client e aplique as migrations:
yarn prisma generate
yarn prisma migrate deployInicie em modo desenvolvimento:
yarn start:devA API escuta em:
http://localhost:4000
Swagger:
http://localhost:4000/docs
OpenAPI JSON:
http://localhost:4000/swagger/json
Para subir API e banco:
docker compose up --buildServiços:
- API:
http://localhost:4000 - PostgreSQL/pgvector:
localhost:5432
O container da API executa:
yarn prisma migrate deploy && yarn start:prodyarn build # compila a aplicação Nest
yarn start # inicia a aplicação
yarn start:dev # inicia com watch mode
yarn start:prod # executa dist/src/main
yarn start:prod:migrate
yarn lint # eslint com --fix
yarn format # prettier em src e test
yarn test # testes unitários
yarn test:e2e # testes e2e
yarn test:cov # coberturaO Prisma modela as principais entidades:
User: usuário, perfil, senha, tokens push e relações sociais.PushToken: tokens Expo por usuário e plataforma.Post: publicação com legenda, visibilidade, thumbnail, autor e usuários marcados.Media: imagens/vídeos de um post, com ordenação e status de processamento.Entity: face detectada, bounding box, embedding vetorial e vínculo com mídia/cluster/usuário.EntityCluster: agrupamento de faces por similaridade.CommenteLike: interações em posts.StoryeStoryItem: retrospectivas temporárias com mídias ordenadas.
O banco precisa suportar vector, usado nos campos embedding e centroidEmbedding.
A API usa JWT Bearer. O JwtAuthGuard é global, então as rotas são privadas por padrão. Rotas públicas usam o decorator @Public().
Fluxo básico:
- Crie um usuário em
POST /users. - Faça login em
POST /auths/login. - Envie o token retornado no header:
Authorization: Bearer <accessToken>Observação: o DTO aceita identifier como e-mail, CPF ou CNPJ, mas a implementação atual consulta usuário por e-mail.
POST /auths/login: autentica usuário e retornaaccessToken.
POST /users: cria usuário. Público.GET /users: lista usuários com paginação e filtro por nome. Público.GET /users/:id: busca usuário por ID. Público.PATCH /users/me: atualiza usuário autenticado e pode registrar token Expo.DELETE /users/me: remove usuário autenticado.POST /users/recover-password: gera código de recuperação e envia e-mail. Público.POST /users/reset-password: redefine senha com código. Público.
POST /posts: cria post autenticado viamultipart/form-data.GET /posts: lista posts com paginação, filtros e busca. Público.GET /posts/:id: busca post por ID. Público.PATCH /posts/:id: atualiza post.DELETE /posts/:id: remove post.
Campos aceitos na criação:
caption: legenda.public: boolean.taggedUserIds: array de UUIDs dos usuários marcados.media: arquivos de imagem/vídeo, até 10 arquivos.
A busca de posts considera legenda, nome do autor, usuários marcados e informações de entidades/faces reconhecidas.
POST /posts/:id/like: curte um post.DELETE /posts/:id/like: remove curtida.GET /posts/:postId/liked: verifica se o usuário autenticado curtiu o post.
POST /posts/:id/comments: cria comentário.GET /posts/:id/comments: lista comentários do post.
POST /upload: envia uma imagem avulsa para S3 usando campoimage.
Campo opcional:
folder: pasta/chave lógica dentro do bucket, comoavatarsouposts.
GET /labeling: lista clusters/entities com paginação e filtros.POST /labeling/label: rotula um cluster comuserIdouname.POST /labeling/entity/:entityId/cluster/:clusterId: adiciona entity a um cluster.DELETE /labeling/entity/:entityId/cluster: remove entity do cluster.
GET /stories: lista stories ativos visíveis para o usuário autenticado.GET /stories/:id: retorna story com mídias ordenadas.
A aplicação usa @nestjs/event-emitter para desacoplar fluxos internos:
post.created: dispara processamento facial e notificação de nova publicação.comment.created: envia e-mail e push ao autor do post.post.liked: envia push ao autor do post.post.users_tagged: envia push aos usuários marcados.face.detected: envia e-mail e push quando uma face reconhecida aparece em uma mídia.password.reset: envia e-mail de recuperação de senha.post.memory_reminder: envia push de lembrança.
Jobs agendados:
- Stories: geração e limpeza diária às 11:00 em
America/Porto_Velho. - Lembretes de memória: execução diária às 11:00 em
America/Porto_Velho.
Serviços de startup:
PostsStartupService: reprocessa posts antigos com thumbnail ausente ou pendente.StoriesService: tenta gerar/limpar stories na inicialização.LabelStartup: inicializa processamento relacionado à rotulagem quando aplicável.
Ao criar um post:
- O backend valida se os arquivos são imagens ou vídeos.
- Imagens são convertidas/otimizadas com
sharp. - Vídeos são reencodados com
ffmpeg. - A primeira mídia define a thumbnail do post.
- Os arquivos são enviados para S3.
- O evento
post.createdaciona o processamento das imagens. - Para imagens, a API baixa a mídia e envia para
POST /detect-facesno serviço externo. - As faces retornadas são persistidas como
Entitye agrupadas emEntityClustervia similaridade vetorial. - Se o cluster já estiver vinculado a um usuário, a aplicação notifica esse usuário.
E-mail:
- comentários em posts;
- recuperação de senha;
- face detectada em nova foto.
Push Expo:
- novo comentário;
- novo like;
- nova publicação;
- usuário marcado em post;
- face detectada;
- lembrete de memória;
- stories retrospectivos.
Tokens Expo são registrados em PATCH /users/me usando os campos token e platform, onde platform deve ser IOS ou ANDROID.
Rodar todos os testes:
yarn testRodar teste específico de posts:
yarn test posts.service.spec.tsRodar build:
yarn build- A aplicação sempre escuta na porta
4000. - O diretório
images/é criado no bootstrap e montado no Docker Compose, embora os uploads principais sejam enviados para S3. - As rotas públicas dependem do decorator
@Public(). Novas rotas são privadas por padrão. - Migrations devem ser versionadas em
prisma/migrations. - Após alterações no Prisma, rode
yarn prisma generate. - Em produção, prefira
yarn prisma migrate deployem vez demigrate dev.