# Chat Privado Efêmero — Documentação do Sistema

## 0. Como rodar

```bash
npm install
npm start
```

Abre em http://localhost:3000 (ou `PORT=xxxx`). `npm test` roda a checagem da lógica de sala.

Arquivos: `server.js` (Express + Socket.io, salas em memória), `public/index.html` (home + sala, JS puro, sem build), `test.js`.

Diferenças em relação ao desenho abaixo: sem React (uma página HTML resolve as duas telas), `roomId` via `crypto` do Node em vez de `nanoid`, e sala vazia só é apagada após 2 min de tolerância (refresh ou queda de conexão não mata a sala na hora).

---

## 0.1 Deploy

**Não tem build.** Nada de webpack/vite: o `public/index.html` é servido como está e o servidor é o próprio `node server.js`. O que a hospedagem precisa é de um **processo Node que fica de pé** — Vercel/Netlify/GitHub Pages **não servem**, porque não mantêm WebSocket aberto.

Subir para o Git primeiro:

```bash
git init && git add . && git commit -m "chat efemero"
git remote add origin <url-do-seu-repo> && git push -u origin main
```

### Opção A — Railway / Render / Fly.io (mais simples)

Conecta o repositório e pronto. A plataforma detecta o `package.json`, roda `npm install` e `npm start` sozinha. Só confira:

| Item | Valor |
|---|---|
| Build command | *(vazio)* |
| Start command | `npm start` |
| Porta | não fixe nada — o código já lê `process.env.PORT` |
| HTTPS | automático nessas plataformas, e o `wss://` vem junto |

Cuidado com plano free que **hiberna** por inatividade: ao hibernar o processo morre e todas as salas somem. Para este produto isso é aceitável, mas uma sala aberta pode cair do nada.

### Opção B — VPS própria (Ubuntu + nginx)

```bash
# na VPS, dentro da pasta do projeto
npm ci --omit=dev
sudo npm i -g pm2
pm2 start server.js --name chat && pm2 save && pm2 startup
```

nginx como proxy reverso — **os cabeçalhos de Upgrade são obrigatórios**, sem eles o Socket.io cai em long-polling ou não conecta:

```nginx
server {
  listen 443 ssl;
  server_name chat.seudominio.com;

  ssl_certificate     /etc/letsencrypt/live/chat.seudominio.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/chat.seudominio.com/privkey.pem;

  location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_read_timeout 120s;
  }
}
```

Certificado com `sudo certbot --nginx -d chat.seudominio.com`, e redirecione a porta 80 para 443.

### Opção C — cPanel (Setup Node.js App)

O cPanel usa **Phusion Passenger** por baixo. Ele mesmo mantém o processo de pé, então não precisa de pm2 nem nginx.

**Antes: crie um subdomínio.** Em *Domínios → Criar um domínio*, algo como `chat.seudominio.com`. O app usa rotas absolutas (`/api`, `/sala`, `/socket.io`) e **quebra se ficar numa subpasta** tipo `seudominio.com/chat`.

**1. Suba os arquivos.** Pelo *Gerenciador de Arquivos*, crie `/home/usuario/chat` e mande `server.js`, `test.js`, `package.json`, `package-lock.json` e a pasta `public`. **Não suba `node_modules`** — o cPanel instala. Se preferir, suba um .zip e use *Extrair*.

**2. Setup Node.js App → Create Application:**

| Campo | Valor |
|---|---|
| Node.js version | a mais alta disponível (18+) |
| Application mode | Production |
| Application root | `chat` |
| Application URL | `chat.seudominio.com` |
| Application startup file | `server.js` |

**3. Run NPM Install** — botão na própria tela do app. Depois **Restart**.

**4. SSL** em *SSL/TLS Status* → *Run AutoSSL* no subdomínio. Sem HTTPS o navegador bloqueia o botão de copiar link, e o `wss://` não sobe.

#### Coisas que mordem nesse ambiente

- **Passenger ignora a porta.** O `process.env.PORT` não é usado, quem escuta é o Passenger. O código funciona assim mesmo, não mexa.
- **O app hiberna sem tráfego** (alguns minutos). Ao hibernar, o processo morre e **todas as salas somem**. É o comportamento mais chato em hospedagem compartilhada.
- **Toda alteração de arquivo exige Restart** na tela do app.
- **WebSocket pode estar bloqueado** em plano compartilhado. Se estiver, o Socket.io cai sozinho para long-polling e o chat continua funcionando, só mais pesado. Confira no DevTools → Network → WS: `101 Switching Protocols` é o ideal.
- O limite de processos do seu plano (100, pelo painel) é bem folgado para isso.

### Checklist pós-deploy

1. Abrir o site, criar sala, copiar o link
2. Abrir o mesmo link em outro navegador ou no celular
3. Mandar mensagem dos dois lados e ver se chega na hora
4. No DevTools, aba Network, filtro WS: a conexão deve ficar **101 Switching Protocols**. Se aparecer um monte de requisição `polling`, faltou o Upgrade no nginx.

## 1. Visão Geral

Sistema de chat em tempo real, sem cadastro, sem login e sem persistência permanente de mensagens. O usuário cria uma sala, recebe um link único e compartilha esse link com a pessoa que deseja conversar. Quem recebe o link entra direto na sala e já pode conversar — sem etapas intermediárias.

O diferencial do produto é o **histórico curto**: cada participante vê apenas as últimas 2 mensagens de cada pessoa na conversa. Mensagens mais antigas somem naturalmente da tela conforme novas chegam, e nada é salvo em banco de dados — tudo vive na memória (RAM) do servidor enquanto a sala existe.

**Objetivo do produto:** oferecer um espaço de conversa rápida e privada, sem deixar rastro permanente do tipo que um app como WhatsApp deixaria (histórico infinito, backup na nuvem, etc.).

---

## 2. Stack Tecnológica

| Camada | Tecnologia | Motivo |
|---|---|---|
| Frontend | React | Componentização simples da tela de chat |
| Backend | Node.js + Express | Serve o frontend e expõe rotas básicas |
| Comunicação em tempo real | Socket.io | Rooms nativas, reconexão automática, broadcast fácil |
| Armazenamento | Memória (RAM), sem banco de dados | Reforça o conceito de "não fica registro" |
| Deploy | VPS / Railway / Fly.io / Render | Precisa de processo Node persistente (não serverless) |

---

## 3. Fluxo do Usuário (visão de produto)

1. Usuário acessa o site → vê um botão único: **"Criar sala"**
2. Ao clicar, o backend gera um ID de sala aleatório e curto (ex: `x7f2-plm9`)
3. O usuário é redirecionado para `/sala/x7f2-plm9`
4. No topo da tela aparece o link completo da sala com um botão **"Copiar link"**
5. O usuário compartilha esse link (por qualquer meio) com a outra pessoa
6. Quem recebe o link, ao abrir, **entra direto na sala** — sem digitar nome, sem senha, sem clicar em "aceitar" nada
7. Os dois começam a trocar mensagens em tempo real
8. Cada pessoa só vê as **últimas 2 mensagens de cada participante** no histórico visível
9. Quando todos saem da sala (fecham a aba / desconectam), a sala e tudo que estava nela é apagado da memória do servidor

---

## 4. Arquitetura Geral

```
[ Navegador A ]                [ Navegador B ]
      |                              |
      |  WebSocket (Socket.io)       |  WebSocket (Socket.io)
      |                              |
      +---------------+--------------+
                       |
                [ Servidor Node.js ]
                       |
              rooms = { } (em memória)
```

Não existe banco de dados. A estrutura `rooms` é um objeto (ou `Map`) que vive na memória do processo Node enquanto o servidor está rodando. Se o servidor reiniciar, todas as salas somem — isso é esperado e faz parte do design.

---

## 5. Lógica do Backend

### 5.1 Estrutura de dados da sala

Cada sala guardada em memória tem este formato conceitual:

```js
rooms = {
  "x7f2-plm9": {
    createdAt: 1732000000000,
    participants: {
      "socketId_A": {
        messages: ["oi, tudo bem?", "vamos marcar amanhã?"] // só as 2 últimas
      },
      "socketId_B": {
        messages: ["opa", "bora sim"]
      }
    }
  }
}
```

Pontos importantes:
- A chave da sala é o `roomId` (gerado com `nanoid` ou similar, curto e não sequencial, pra não ser adivinhável).
- Cada participante é identificado pelo `socketId` da conexão (não tem login, então a identidade da pessoa dura só enquanto a conexão está aberta).
- O array `messages` de cada participante nunca passa de 2 itens — isso é garantido no momento de inserir uma nova mensagem.

### 5.2 Criação da sala

Rota simples (pode ser HTTP ou até via evento de socket):

```js
app.post('/api/create-room', (req, res) => {
  const roomId = generateShortId(); // ex: nanoid(8)
  rooms[roomId] = {
    createdAt: Date.now(),
    participants: {}
  };
  res.json({ roomId });
});
```

O frontend recebe o `roomId` e redireciona o usuário para `/sala/:roomId`.

### 5.3 Entrar na sala (sem fricção)

Quando o navegador abre `/sala/x7f2-plm9`, o frontend conecta via Socket.io e emite:

```js
socket.emit('join_room', { roomId });
```

No backend:

```js
socket.on('join_room', ({ roomId }) => {
  if (!rooms[roomId]) {
    // sala não existe (expirou ou link errado)
    socket.emit('room_not_found');
    return;
  }

  socket.join(roomId);
  rooms[roomId].participants[socket.id] = { messages: [] };

  // avisa a sala que alguém entrou (opcional, pra UX)
  io.to(roomId).emit('user_joined');
});
```

Não há nenhuma validação de identidade, senha ou aprovação — é intencional, conforme pedido: "não precisa pedir nada, perguntar nada, só entra e fala".

### 5.4 Enviar mensagem

```js
socket.on('send_message', ({ roomId, text }) => {
  const room = rooms[roomId];
  if (!room) return;

  const participant = room.participants[socket.id];
  if (!participant) return;

  // adiciona a nova mensagem e mantém só as 2 últimas
  participant.messages.push(text);
  if (participant.messages.length > 2) {
    participant.messages.shift(); // remove a mais antiga
  }

  // envia pra todo mundo na sala (incluindo quem enviou, se quiser)
  io.to(roomId).emit('new_message', {
    senderId: socket.id,
    text,
    timestamp: Date.now()
  });
});
```

**Importante:** o corte para "últimas 2 mensagens" pode ser feito tanto no backend (controlando o que fica guardado em memória) quanto no frontend (controlando o que é renderizado). A abordagem mais consistente é fazer nos dois lugares:
- Backend só **guarda** as últimas 2 por participante (evita acumular memória à toa).
- Frontend só **renderiza** as últimas 2 por participante (é o que garante a experiência visual do "some da tela").

### 5.5 Saída e expiração da sala

Dois gatilhos de limpeza:

**a) Quando todo mundo desconecta:**
```js
socket.on('disconnect', () => {
  for (const roomId in rooms) {
    const room = rooms[roomId];
    if (room.participants[socket.id]) {
      delete room.participants[socket.id];

      // se a sala ficou vazia, apaga tudo
      if (Object.keys(room.participants).length === 0) {
        delete rooms[roomId];
      }
    }
  }
});
```

**b) Expiração por tempo (rotina de limpeza periódica):**
```js
setInterval(() => {
  const now = Date.now();
  const LIMITE = 1000 * 60 * 60 * 24; // 24h, ajustável

  for (const roomId in rooms) {
    if (now - rooms[roomId].createdAt > LIMITE) {
      delete rooms[roomId];
    }
  }
}, 1000 * 60 * 10); // roda a cada 10 minutos
```

Isso evita salas "fantasma" ficarem ocupando memória do servidor indefinidamente, mesmo que ninguém tenha desconectado corretamente (ex: queda de conexão sem o evento `disconnect` disparar).

---

## 6. Lógica do Frontend (React)

### 6.1 Estrutura de telas

Só duas telas, bem enxutas:

1. **Home (`/`)** — botão único "Criar sala"
2. **Sala (`/sala/:roomId`)** — tela de chat

### 6.2 Tela da sala — componentes

```
<SalaChat>
  <TopoComLink roomId={roomId} />   // mostra o link + botão copiar
  <Historico mensagens={mensagens} />  // últimas 2 por pessoa
  <CaixaDeTexto onEnviar={enviarMensagem} />
</SalaChat>
```

### 6.3 Estado local (React)

```js
const [mensagens, setMensagens] = useState({}); 
// formato: { socketId: [msg1, msg2] }

const [textoAtual, setTextoAtual] = useState('');
```

### 6.4 Conectando e ouvindo eventos

```js
useEffect(() => {
  const socket = io(SERVER_URL);
  socket.emit('join_room', { roomId });

  socket.on('new_message', ({ senderId, text }) => {
    setMensagens(prev => {
      const anteriores = prev[senderId] || [];
      const atualizadas = [...anteriores, text].slice(-2); // garante só 2
      return { ...prev, [senderId]: atualizadas };
    });
  });

  socket.on('room_not_found', () => {
    // redireciona pra home ou mostra aviso "sala não existe/expirou"
  });

  return () => socket.disconnect();
}, [roomId]);
```

### 6.5 Enviando mensagem

```js
function enviarMensagem() {
  if (!textoAtual.trim()) return;
  socket.emit('send_message', { roomId, text: textoAtual });
  setTextoAtual('');
}
```

### 6.6 Renderização do histórico

Como o estado `mensagens` já guarda só as últimas 2 por pessoa, o componente de histórico é direto:

```jsx
function Historico({ mensagens }) {
  return (
    <div className="historico">
      {Object.entries(mensagens).map(([senderId, msgs]) => (
        <div key={senderId} className="bloco-participante">
          {msgs.map((msg, i) => (
            <p key={i} className="mensagem">{msg}</p>
          ))}
        </div>
      ))}
    </div>
  );
}
```

### 6.7 Layout visual

Conforme pedido — sem frescura, estilo simples parecido com a tela de conversa do Claude:

- Topo: link da sala + botão de copiar
- Meio: área de histórico (scroll se necessário), mensagens organizadas por participante
- Base: caixa de texto grande + botão enviar (ou tecla Enter)

Não há necessidade de nomes de usuário, avatares ou distinção visual elaborada entre participantes — dá pra diferenciar só pela posição (esquerda/direita) ou uma cor sutil por `socketId`.

---

## 7. Segurança e Boas Práticas

| Item | Motivo |
|---|---|
| HTTPS + WSS obrigatório em produção | Sem isso, mensagens trafegam sem criptografia |
| Sanitizar texto antes de renderizar | Evita XSS (alguém enviar `<script>` na mensagem) |
| Rate limiting por socket/IP | Evita flood/spam na sala |
| `roomId` não sequencial e difícil de adivinhar | Evita gente entrando em salas aleatórias por força bruta |
| Não logar conteúdo das mensagens no servidor (nem em arquivo de log) | Reforça a promessa de privacidade — se você loga em disco, deixou de ser efêmero |

---

## 8. Considerações sobre Escala (se precisar crescer depois)

Enquanto for um servidor Node único, tudo funciona em memória sem problema. Se um dia precisar rodar em múltiplas instâncias (load balancer, autoscaling), aí entra a necessidade de um **Redis adapter para Socket.io** — porque cada instância teria sua própria memória isolada, e duas pessoas na mesma sala poderiam cair em servidores diferentes. Não é necessário pensar nisso agora, mas fica registrado como próximo passo natural caso o produto cresça.

---

## 9. Resumo do Ciclo de Vida de uma Sala

1. Criada → existe só em memória, com `roomId` único
2. Ativa → participantes entram, mandam mensagens, cada um vê só as últimas 2 de cada pessoa
3. Encerrada → quando todos desconectam OU quando passa do tempo limite de expiração
4. Apagada → objeto removido da memória, sem deixar rastro em disco ou banco

---

## 10. Próximos Passos Sugeridos

1. Montar o esqueleto do backend (Express + Socket.io + lógica de sala descrita acima)
2. Montar os dois componentes React (Home e SalaChat)
3. Testar localmente com duas abas do navegador simulando dois participantes
4. Configurar HTTPS/WSS e subir num serviço com processo persistente (Railway, Fly.io, Render ou VPS)