Banco de dadosTables
messages
Mensagens de chat — texto, áudio, vídeo, imagem, arquivo, calls.
Mensagens enviadas em conversas. Suporta vários formatos:
- Texto —
contentpreenchido, sem attachments - Áudio/Vídeo/Imagem/Arquivo — anexos em
message_attachmentscommime_typeindicando tipo - Call —
call_metadataJSONB com info da call (status, duração, iniciador) - Reply —
reply_to_idreferencia outra message do mesmo conversation
Mensagens podem ser deletadas (soft via deleted_at) ou escondidas per-user (hidden_for_user_ids).
Colunas
| Coluna | Tipo | Nullable | Default |
|---|---|---|---|
| id ● | uuid | NOT NULL | gen_random_uuid() |
| conversation_id | uuid | NOT NULL | — |
| user_id | uuid | NOT NULL | — |
| content | text | NULL ok | — |
| reply_to_id | uuid | NULL ok | — |
| call_metadata | jsonb | NULL ok | — |
| is_temporary | boolean | NOT NULL | false |
| temporary_event | jsonb | NULL ok | — |
| hidden_for_user_ids | jsonb | NOT NULL | [] |
| deleted_at | timestamp | NULL ok | — |
| created_at | timestamp | NOT NULL | now() |
| updated_at | timestamp | NOT NULL | now() |
● primary key ◆ unique
Funcionalidade dos campos
id— UUID, PKconversation_id— FK praconversations. Cascade delete.user_id— autor. Cascade delete.content— texto da mensagem. Pode serNULL(mensagens de só-mídia ou só-call).reply_to_id— FK auto-referencial pramessages.id. Usado pra "reply quoted" na UI. Sem cascade enforcement — apagar a msg referenciada deixa o reply órfão (frontend mostra "mensagem deletada").call_metadata— JSONB com{ status, durationSec, startedAt, endedAt, initiatorId }para mensagens de call. Status:completed | missed | rejected | cancelled | failed.is_temporary— flag set se a conversa estava em modo temporário no momento do enviotemporary_event— JSONB com{ enabled: bool, byUserId: string }. Marca o ponto de mudança de estado (ligar/desligar modo temp). Mensagem-evento mostra "Joe ativou mensagens temporárias".hidden_for_user_ids— JSONB array. Quando user X marca msg como "lida + escondida" no modo temp, seu id entra aqui. Quando todos os destinatários estão na lista, cron deleta a msg.deleted_at— soft delete. Frontend mostra "Mensagem apagada" no lugar do conteúdo.created_at/updated_at— managed.
Foreign keys
| Coluna(s) | Referência | ON DELETE | ON UPDATE |
|---|---|---|---|
| conversation_id | conversations.id | cascade | no action |
| user_id | users.id | cascade | no action |
Índices
| Nome | Único | Colunas | WHERE (parcial) |
|---|---|---|---|
| messages_conversation_created_at_idx | não | conversation_id, created_at | — |
messages_conversation_created_at_idx — query principal (paginar mensagens da conversa em ordem cronológica).
Constraints
PRIMARY KEY (id)
Soft delete vs hard delete
- Apagar pra mim — adiciona meu user_id em
hidden_for_user_ids. Outros continuam vendo. - Apagar pra todos — UPDATE
deleted_at = now(). Todos veem "Mensagem apagada". - Apagar pelo cron temp — UPDATE
deleted_atquandohidden_for_user_idscobre todos os membros (a mensagem some pra todos efetivamente).
Forward / share / download
Endpoints separados:
POST /chat/messages/:id/forward { conversationIds }— replica em N conversasGET /chat/messages/:id/share— gera link público temporário (raro, ainda é débito)GET /chat/messages/:id/download— para attachments, retorna URL S3 com signed URL
Padrões de uso
- Enviar texto —
POST /chat/messages { conversationId, content } - Enviar mídia — multipart upload, backend cria message + attachments numa transação
- Reply — campo
replyToIdno body - Marcar lida —
POST /chat/conversations/:id/read(atualizaconversation_members.last_read_at) - Reagir —
POST /chat/messages/:id/react { emoji }(insere emmessage_reactions)
Tabelas relacionadas
conversations— pai (cascade)users— autor (cascade)message_attachments— anexos (cascade)message_reactions— reações (cascade)conversation_members—last_read_atcomparado contracreated_at