Módulos
Notifications
Notificações persistidas + entrega real-time via socket + Web Push.
Visão geral
Sistema dual-channel de notificações:
- In-app — INSERT em
notifications+ emit socketnotification:new - Push — Web Push (VAPID) pra notificar quando app está fechada/minimizada
Mesmos eventos disparam ambos os canais. Cliente dedupe se necessário.
Notificações são flat — sem subtipos, polimorfismo via type (enum) + entity_id (UUID que aponta pra qualquer recurso dependendo do type). Frontend traduz type em copy + ícone + link.
Entidades principais
| Tabela | Papel |
|---|---|
notifications | Notificação persistida |
push_subscriptions | Subscriptions Web Push do user |
Endpoints
6 endpoints:
| Endpoint | Resumo |
|---|---|
GET /notifications | Listar (cursor pagination) |
GET /notifications/unread-count | Badge no sino |
POST /notifications/read-all | Marcar todas como lidas |
PATCH /notifications/:id/read | Marcar 1 como lida |
DELETE /notifications | Apagar todas |
DELETE /notifications/:id | Apagar 1 |
Push subscriptions são gerenciadas em POST/DELETE /chat/push-subscriptions (estão dentro do módulo Chat por proximidade).
Fluxos
Disparar notificação (padrão notifySafe)
Listagem com paginação
Marcar como lida (manual ou implícito)
Marca-lida pode acontecer:
- Manual — user clica "marcar lidas" ou clica numa notificação específica
- Implícito — alguns flows marcam automaticamente:
- Aceitar friendship → marca friend_request correspondente como lida
- Visualizar oferta → marca offer_received como lida (se implementado)
Ainda não há marcação implícita rica — flow primário é click do usuário.
Eventos de socket
Apenas um evento outbound:
| Evento | Sentido | Payload |
|---|---|---|
notification:new | server → user | { id, type, actorId, entityId, createdAt, actor: { id, displayName, avatarUrl } } |
Frontend escuta e:
- Adiciona ao topo da lista local
- Incrementa badge unread
- Mostra toast efêmero ("Joe comentou seu post")
Tipos de notificação
Lista completa em Tipos de notificação. Categorias:
- Sociais — friend_request, friend_accepted, dm_request_received, post_comment, post_reaction
- Vitrine — offer_received, offer_accepted, offer_rejected, offer_completed, offer_cancelled, offer_expired
- Vitrine — propostas — counter_proposal_received, proposal_rejected
- Vitrine — reputação — rating_received
- Steam — steam_import_done, steam_import_partial
Edge cases e regras especiais
actor_id == recipient_idpossível em casos comosteam_import_done(notify self). UsanotifySelfem vez denotify.- Sem retenção automática — tabela cresce indefinidamente. Pendência: cron pra deletar lidas > 90 dias.
- Duplicação possível — backend não dedupe. Reagir 2x rápido pode gerar 2 notifs (UPDATE também dispara hook). Hoje aceito.
entity_idpolimórfico — frontend resolve portype. Se entity foi deletada, link fica quebrado (mostra "conteúdo removido").- Push pode falhar — 410 (subscription expired) é tratado deletando; outros erros são logados mas silenciosos.
isMutedper-conversa suprime push de mensagens novas; mas notificações de outros tipos (offer, comment etc.) passam normais.
Dependências entre módulos
- Todos os outros módulos disparam notify para eventos relevantes
- Chat (PushService) — entrega Web Push; mesmo módulo cuida das push_subscriptions
- ChatGateway (socket) — entrega in-app real-time