# Plano de backup e recuperação de desastres

## Metas (propostas — validar com a TI do hospital)

- **RPO (Recovery Point Objective):** até 24h de dados perdidos no pior
  caso (backup diário). Para um RPO menor, aumentar a frequência do backup
  (ver abaixo) ou habilitar WAL archiving contínuo do Postgres.
- **RTO (Recovery Time Objective):** até 2h para restaurar o serviço a
  partir de um backup, em uma VM nova (tempo dominado por: provisionar a
  VM, `git clone`/copiar o projeto, `./deploy/instalar-docker.sh`,
  `./deploy/subir-app.sh`, `./deploy/restaurar.sh <dump>`).

Estas metas assumem volume de dados de um hospital (não terabytes) — um
`pg_dump` completo deve levar segundos a poucos minutos.

## O que é coberto

- Todo o banco `chamados_unimed_nf`: quartos, usuários, dispositivos,
  chamados e a trilha de auditoria completa (`EventoAuditoria`).
- **Não coberto por este backup:** configuração do servidor (`.env`,
  certificados HTTPS do Caddy) — manter uma cópia segura separada dessas
  configurações (ex.: gerenciador de segredos da TI), já que elas contêm
  credenciais.

## Rotina de backup

```bash
./deploy/backup.sh
```

Gera um dump comprimido (`pg_dump -Fc`) em `./backups/`, com nome
`chamados_unimed_nf_<data>.dump`. Mantém automaticamente só os últimos 14
arquivos locais (ver o próprio script).

**Agendar via cron** (na VM de produção), por exemplo diariamente às 3h:

```
0 3 * * * /caminho/completo/Projeto_Chamados_Hospital/deploy/backup.sh >> /var/log/chamados-backup.log 2>&1
```

## Cópia fora do servidor (obrigatório em produção)

Um backup que só existe no mesmo disco/servidor do banco **não protege
contra falha de disco, do servidor inteiro, ou de um incidente de
segurança** que comprometa a VM inteira. Depois de `deploy/backup.sh`
gerar o arquivo, copie-o para um destino fora desta máquina — a escolha
concreta (S3/object storage, outro datacenter, backup gerenciado pela TI
central da Unimed) fica a critério da infraestrutura do hospital; este
projeto não prescreve um provedor específico. Um exemplo simples de
segunda etapa no cron, depois do backup local:

```bash
rclone copy ./backups/ remoto:chamados-backups/ --max-age 25h
```

(usando [rclone](https://rclone.org/) configurado para o destino
escolhido — S3, outro servidor via SFTP, etc.)

## Restauração

```bash
./deploy/restaurar.sh ./backups/chamados_unimed_nf_2026-01-01_030000.dump
```

**Destrutivo**: apaga o banco atual antes de restaurar. Pede confirmação
explícita (`SIM`) antes de prosseguir. Depois de restaurar:

```bash
docker compose restart app
```

## Teste de restauração (drill)

Um plano de backup só vale se a restauração já foi testada. Recomendado:
a cada trimestre, restaurar o backup mais recente em uma VM separada
(não a de produção) e confirmar:

1. `docker compose exec app npx prisma migrate deploy` roda sem erro
   (schema restaurado é compatível com o código atual).
2. Login com um usuário real funciona.
3. Os relatórios (`/dashboard/supervisor/relatorios`) mostram números
   condizentes com o esperado para a data do backup.
4. Uma consulta manual em `EventoAuditoria` confirma que o histórico de
   auditoria foi preservado (`SELECT count(*) FROM "EventoAuditoria";`
   antes e depois deve bater).

Documentar a data e o resultado de cada drill (mesmo que informalmente,
ex. em uma planilha da TI) — isso é parte do que auditores/compliance
tipicamente pedem como evidência de que o plano de backup funciona de
verdade, não só existe no papel.

## Cenários de desastre

| Cenário | Ação |
|---|---|
| Corrupção/perda do banco, VM continua íntegra | `./deploy/restaurar.sh <último backup>` |
| Perda total da VM | Provisionar VM nova (`deploy/instalar-docker.sh` → `deploy/subir-app.sh` → `deploy/restaurar.sh`) |
| Certificado HTTPS expirado/perdido | Caddy renova automaticamente via Let's Encrypt enquanto o domínio apontar para a VM; se precisar recriar do zero, basta reiniciar o container `caddy` com o domínio configurado |
| Credenciais comprometidas | Trocar `AUTH_SECRET`, senhas das roles do Postgres e chaves VAPID no `.env`; isso invalida todas as sessões ativas, forçando novo login |
