# Arquitetura

## Visão geral

```
┌────────────────┐     QR Code / HTTPS      ┌──────────────────────────┐
│  Paciente       │ ───────────────────────▶ │                          │
│  (navegador)    │                           │                          │
└────────────────┘                           │                          │
                                              │   Next.js 16 (app único) │
┌────────────────┐   POST /api/.../          │   - App Router (páginas  │
│  Botão IoT      │   emergencia (HTTPS,      │     + API routes)        │
│  (ESP32,        │──▶ deviceId+apiKey)  ────▶│   - server.ts: HTTP +     │
│  protótipo)     │                           │     Socket.IO custom     │
└────────────────┘                           │   - node-cron:            │
                                              │     escalonamento         │
┌────────────────┐   HTTPS + WebSocket        │   - web-push: VAPID       │
│  Dashboards     │◀─────────────────────────▶│                          │
│  (equipes,      │                           └────────────┬─────────────┘
│  navegador)     │                                        │ Prisma
└────────────────┘                                        ▼
                                              ┌──────────────────────────┐
                                              │  PostgreSQL              │
                                              │  - dados operacionais    │
                                              │  - EventoAuditoria       │
                                              │    (append-only,         │
                                              │    REVOKE aplicado)      │
                                              └──────────────────────────┘

                    Tudo atrás de: Caddy (proxy reverso, HTTPS automático)
```

Ver `docs/decisoes-arquiteturais.md` para o porquê de cada escolha acima
(monólito Next.js, Postgres único, Socket.IO, sem Grafana/Kubernetes).

## Componentes

### Aplicação (Next.js 16, App Router)

Um único processo Node.js (`server.ts`) que:
1. Serve as páginas (App Router — `src/app/`).
2. Serve a API REST (`src/app/api/`).
3. Roda um servidor Socket.IO no mesmo processo, no path `/socket.io`,
   autenticado via o JWT de sessão do Auth.js (nunca confia em identidade
   enviada pelo cliente no handshake).
4. Roda o job de escalonamento (`node-cron`, a cada
   `ESCALONAMENTO_INTERVALO_SEGUNDOS`, padrão 15s).

### Camada de dados (PostgreSQL + Prisma)

- **Duas roles de banco distintas** (ver `deploy/postgres-init/01-roles.sql`):
  - `chamados_migrate`: dona do schema, usada só por humanos/CI rodando
    `prisma migrate` — nunca pelo app em produção.
  - `chamados_app`: runtime da aplicação, privilégio reduzido — sem
    `UPDATE`/`DELETE` em `EventoAuditoria` desde a migração
    `*_lockdown_auditoria`.
- Modelo de dados completo em `prisma/schema.prisma`; referência de campos
  em `docs/documentacao-tecnica.md`.

### Tempo real (Socket.IO)

Salas por posto (qualquer um dos 3 setores — Enfermagem, Copa,
Rouparia), mais uma sala "sem posto" por setor, e uma sala `SUPERVISOR`
(`src/lib/realtime/events.ts#salaPosto/salaSemPosto`). `EMERGENCIA` é
roteado pra sala do posto de Enfermagem do quarto. Cada login de
Enfermagem/Copa/Rouparia entra na sala do próprio posto (+ a "sem
posto" do próprio setor); `SUPERVISOR` entra nas salas dos postos
escolhidos em "Meus setores"; `ADMINISTRADOR` entra só em `SUPERVISOR`
(acesso irrestrito, todo evento chega lá também). Eventos:
`chamado:novo`, `chamado:atualizado`, `chamado:emergencia`,
`chamado:escalado`, `chamado:escalado_critico`,
`chamado:finalizacao_escalada`, `chamado:desescalado`,
`chamado:cancelado`.

### Notificações

- **Visual/sonora:** sempre ativa nos dashboards (`useAlarmeSonoro`,
  gera o beep via Web Audio API — sem depender de arquivo de áudio).
- **Push (Web Push/VAPID):** opt-in por usuário (`NotificacaoPrompt`),
  via service worker (`public/sw.js`). Ver limitação de iOS Safari em
  `docs/decisoes-arquiteturais.md`.

### Dispositivos IoT

Autenticação por `deviceId` + `apiKey` (chave com hash bcrypt no banco),
não por sessão de usuário — o chamador é um gateway de hardware. Ver
`firmware/esp32-botao-emergencia/` para o firmware de referência.

## Disponibilidade e failover — desenhado, não provisionado nesta fase

O deploy padrão (`docker-compose.yml`) é **uma única VM**: um ponto único
de falha. O desenho abaixo é o caminho para alta disponibilidade real,
mas não foi provisionado como parte desta reconstrução (fora do escopo de
um MVP/primeira reconstrução — ver `docs/decisoes-arquiteturais.md` sobre
Kubernetes):

- **App:** múltiplas réplicas do container `app` atrás do Caddy
  (load balancing), todas apontando para o mesmo Postgres. O app é
  stateless exceto pelas conexões Socket.IO — com múltiplas réplicas,
  seria necessário um adaptador Socket.IO compartilhado (ex.: adaptador
  Redis) para eventos chegarem a clientes conectados em réplicas
  diferentes. **Não implementado ainda.**
- **Banco:** Postgres com replicação streaming (um primário + réplica(s)
  de leitura/standby), com failover automático (ex.: Patroni) ou manual
  documentado.
- **Proxy:** Caddy já suporta múltiplos upstreams nativamente; o gargalo
  de HA real é o Socket.IO multi-réplica acima.

## Monitoramento

- `GET /api/health` reporta conectividade com o banco e se o Socket.IO
  está de pé — usável por qualquer monitor externo (ex.: Uptime Kuma,
  healthcheck do orquestrador).
- Logs: `console.log`/`console.error` para stdout/stderr do container —
  capturáveis por `docker compose logs` ou qualquer coletor de logs do
  orquestrador (ex.: Loki, CloudWatch, journald). Não há um agregador de
  logs implantado nesta reconstrução.
- Métricas de negócio: `/dashboard/supervisor/relatorios` (não é
  monitoramento de infraestrutura, é indicador operacional).

## Escalabilidade

No volume esperado (chamados de um hospital), o gargalo mais provável é
o job de escalonamento fazendo um full scan de chamados abertos a cada
tick — aceitável na escala atual (índice em `[status, tipo]` mantém isso
rápido mesmo com milhares de chamados históricos, já que o filtro é só
sobre os `ABERTO`/`EM_ATENDIMENTO`, tipicamente uma fração pequena do
total). Se o volume crescer por ordem de grandeza, considerar mover essa
verificação para triggers/notificações do próprio Postgres em vez de
polling.
