Importação inicial do projeto Shivão (Diário de Bordo do veleiro) em estado pronto pra produção, já incluindo as 5 mudanças de hardening implementadas pela squad shivao-melhoria em 2026-04-27. Mudanças de hardening (HANDOFF.md seção "✅ Pronto"): 1. **Rate limiting nos 3 endpoints públicos de share** - express-rate-limit ^8.4.1, 60 req/min/IP - server/src/index.js linhas 38, 262, 271, 279 2. **Tamanho de upload reduzido pra 50MB** (era 200MB) - multer linha 84 3. **Validação Zod nos endpoints autenticados** (POST /api/data e /zones) - novo arquivo server/src/schemas/index.js - middleware validate() retorna 400 com top 5 issues 4. **Audit log de ações sensíveis** - nova tabela audit_log + funções db.audit() e db.recentAudit() - 6 endpoints instrumentados (state_set, media_insert/delete, share_create/revoke/zones_update) - novo endpoint GET /api/audit (autenticado) 5. **Catch silencioso de webhook em zona PROIBIDA tratado** - app/diario-bordo.html + server/public/index.html linha 3280 - agora loga erro + exibe toast ao usuário Status final do HANDOFF: - 🔴 Críticos restantes: 1 (CORS — decisão consciente single-tenant, não-acionável) - 🟡 Importantes: 4 itens (testes, vigia reconnect, refator frontend, demais catches) - 🟢 Bom-ter: 5 itens Stack: Node 20 ESM + Express + better-sqlite3 + Docker (Coolify) Single-tenant pessoal · single-file frontend HTML · offline-first Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6.2 KiB
DEPLOY — Coolify (Hetzner VPS)
Guia passo-a-passo para colocar o shivao-cloud em produção no Coolify.
Pré-requisitos
- VPS Hetzner com Coolify instalado e acessível
- Domínio (ou subdomínio) apontando para o IP do servidor
- Ex:
shivao.exemplo.com→ IP da VPS
- Ex:
- DNS propagado (verifique com
dig shivao.exemplo.com)
Passo 1 — Subir o código para Git
Recomendado: criar repo privado no GitHub/GitLab.
cd shivao-projeto/server
git init
git add .
git commit -m "Initial commit"
git remote add origin git@github.com:OWNER/shivao-cloud.git
git push -u origin main
Alternativa: usar a opção "Deploy from Docker Image" do Coolify e fazer build local + push para um registry. Mas Git é mais simples.
Passo 2 — No Coolify
2.1. Criar a aplicação
- + New Resource → Application
- Selecione Public/Private Repository (with GitHub App) ou Public/Private Repository
- Cole a URL do repo
- Branch:
main - Build Pack:
Dockerfile - Base Directory: deixe em branco (o Dockerfile está na raiz)
- Dockerfile location:
/Dockerfile - Port:
3000 - Salvar
2.2. Configurar domínio
- Aba Domains
- Adicione:
https://shivao.exemplo.com - Generate Let's Encrypt (automático)
- Salvar
2.3. Variáveis de ambiente
Aba Environment Variables → adicionar:
Mínimo obrigatório
BOAT_TOKEN=<gere com: openssl rand -hex 32>
Pelo menos um canal de notificação
Telegram (recomendado, grátis):
TELEGRAM_BOT_TOKEN=<token do BotFather>
TELEGRAM_CHAT_IDS=<seus chat IDs separados por vírgula>
E-mail (Gmail com app password):
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=seu-email@gmail.com
SMTP_PASS=<senha de app de 16 chars>
SMTP_FROM=Shivao <seu-email@gmail.com>
SMTP_TO=destinatario@exemplo.com,outro@exemplo.com
ntfy.sh (push grátis sem cadastro):
NTFY_TOPIC=<algo aleatório, ex: shivao-alertas-x7k9p2>
Veja server/.env.example para todas as opções (Twilio, webhook, etc).
Opcionais
HEARTBEAT_TIMEOUT_SEC=300 # 5 min — tempo até dead-man disparar
NODE_ENV=production
2.4. Volume persistente
Crítico — sem isso, ao reiniciar o container você perde tudo.
- Aba Storages
- + Add
- Tipo: Persistent Storage ou Volume
- Name:
shivao-data - Mount Path:
/data - Salvar
2.5. Deploy
- Aba principal da aplicação
- Deploy (canto superior direito)
- Acompanhar logs do build (~2-3 minutos primeira vez)
Se tudo der certo, ao final:
Shivao Cloud rodando em :3000
Canais configurados: telegram, email, ...
Dead-man switch: 300s
Passo 3 — Verificar
3.1. Health check público
curl https://shivao.exemplo.com/api/health
# {"ok":true,"ts":1730000000000}
3.2. Auth funcionando
curl -H "Authorization: Bearer SEU_TOKEN" https://shivao.exemplo.com/api/info
# {"channels":["telegram","email"],"heartbeatTimeoutSec":300,"version":"1.0"}
3.3. Acessar o app
Abra https://shivao.exemplo.com/ no navegador. O frontend deve carregar.
3.4. Testar notificação
No app: Arquivo → preencha servidor + token → Disparar mensagem de teste
Você deve receber a mensagem em todos os canais configurados.
Passo 4 — Configurar Telegram (5 minutos)
Se ainda não fez:
- No Telegram, fale com @BotFather
/newbot→ escolha nome e username → ele dá um token- Copie o token (tipo
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11) - Inicie conversa com o bot recém-criado (envie qualquer coisa)
- Acesse
https://api.telegram.org/bot<SEU_TOKEN>/getUpdates - No JSON retornado, procure
"chat":{"id":123456789,...}→ esse é seuCHAT_ID - Cole
TELEGRAM_BOT_TOKENeTELEGRAM_CHAT_IDSno Coolify - Redeploy
- Teste no app
Para grupo: adicione o bot ao grupo, mande uma mensagem lá, repita o passo 5
(o chat_id do grupo é negativo).
Passo 5 — Conectar o app ao servidor
No celular:
- Abrir
https://shivao.exemplo.com/no Chrome - Menu (⋮) → Adicionar à tela inicial (vira "app")
- Abrir o app, ir em Arquivo
- Seção ☁️ Sincronização na nuvem:
- Servidor:
https://shivao.exemplo.com - Token: o
BOAT_TOKENdefinido no Coolify
- Servidor:
- Testar conexão → deve receber notificação de teste
- ↑ Enviar tudo para nuvem (primeira vez)
A partir daí, o app sincroniza automaticamente. Se trocar de celular, basta colar a URL + token e tocar em ↓ Baixar da nuvem.
Atualizando o código
Sempre que houver mudanças:
git push origin main
No Coolify:
- Deploy → ele puxa, builda, e troca o container
- A migração de schema do SQLite é automática (CREATE TABLE IF NOT EXISTS)
Backup
O volume /data contém:
shivao.db(SQLite com todos os dados)media/(fotos, áudios, vídeos)
Recomendado: configurar backup periódico do volume no Coolify ou
sincronizar /data para um S3 com restic ou borgbackup via cronjob.
Troubleshooting
"BOAT_TOKEN não configurado"
Variável de ambiente faltando ou < 16 chars. Veja Passo 2.3.
"Failed to fetch" no app
- DNS não propagou ainda
- Token errado
- Coolify não está expondo SSL (verificar Domains)
"Webhooks falham mas Telegram funciona"
Discord/genérico podem ter rate limits. O servidor faz fan-out independente, então um falhar não afeta os outros.
"GPS não funciona no app"
Geolocation API só funciona em HTTPS. Coolify configura HTTPS
automaticamente — mas verifique se está acessando https://... e não http://.
"Dead-man switch não dispara"
- Verificar logs do servidor:
docker logs <container> - Dead-man só dispara se houve
/api/anchor/startantes - Default é 5 min sem heartbeat — pode ajustar em
HEARTBEAT_TIMEOUT_SEC
Container reinicia em loop
Verificar logs. Causa comum: BOAT_TOKEN muito curto (mínimo 16 chars).
Custos esperados
- Hetzner CX22 (2 vCPU, 4 GB RAM): ~6€/mês — sobra recurso
- Domínio: ~10€/ano
- Telegram, ntfy, Open-Meteo: grátis
- Twilio (se ativar SMS): ~$1/mês + ~$0.05 por SMS no Brasil
- Windy Point Forecast: o dono já tem premium pessoal
Total mínimo: ~7€/mês.