# ARCHITECTURE ## Visão geral ``` ┌─────────────────────────┐ ┌──────────────────────────────────┐ │ APP (celular/web) │ │ SHIVAO CLOUD (VPS Coolify) │ │ │ │ │ │ - HTML standalone │ ─HTTPS─→│ Express + SQLite │ │ - localStorage (state) │ Bearer │ - /api/data (sync) │ │ - IndexedDB (mídia) │ Token │ - /api/media (upload/serve) │ │ - Leaflet + OSM │ │ - /api/anchor/* (vigia) │ │ - Web Audio (alarme) │ │ - /share/:token (público) │ │ - Geolocation API │ │ │ │ │←HTTPS───│ Notificações (fan-out): │ │ │ updates │ - Telegram │ └─────────────────────────┘ │ - ntfy.sh │ │ - SMTP │ ↓ webhooks diretos │ - Twilio (SMS/WhatsApp) │ (do app sem servidor) │ - Webhook genérico │ ↓ │ │ ┌──────────────────┐ │ Dead-man switch: │ │ Telegram bot │ │ - heartbeat a cada 30s │ │ Discord webhook │ │ - timeout 5min → alarme │ │ Webhook genérico │ │ │ └──────────────────┘ │ Compartilhamento público: │ │ - tokens aleatórios │ ↓ Windy API │ - posições rotativas (500 max) │ (do app, com chave do dono) │ - cleanup diário │ ↓ └──────────────────────────────────┘ ┌──────────────────┐ ↓ │ api.windy.com │ ┌──────────────────────────────────┐ │ Point Forecast │ │ Volume persistente /data │ │ - GFS │ │ - shivao.db (SQLite) │ │ - GFS Wave │ │ - media/ (uploads) │ └──────────────────┘ └──────────────────────────────────┘ ``` ## Decisões técnicas ### Frontend: por que single-file HTML? **Prós:** - Distribuição trivial (compartilhar por e-mail, drive, etc) - Funciona offline (após primeiro carregamento) - Pode ser "instalado" no celular sem app store (apenas Chrome → Adicionar à tela inicial) - Zero build step para customizações simples **Contras:** - Arquivo monolítico (~3500 linhas, 200 KB) - Difícil de revisar em PRs - Sem code splitting **Alternativas consideradas:** - React/Vue PWA: maior overhead, build step, mas componentização melhor - Capacitor/Cordova: app nativo "real", mas complexidade de publicação ### Frontend: armazenamento **Estado estruturado (`localStorage`)**: travessias, manutenções, pendências, configurações. Vantagem: sincronização trivial (1 JSON blob). Desvantagem: limite de ~5MB total — se viagens viram milhares com track points longos, pode estourar. **Mídias (`IndexedDB`)**: blobs de fotos, áudios, vídeos. Cada arquivo guardado como Blob, separado do estado. URLs criadas com `URL.createObjectURL()` sob demanda. Limites do navegador: ~50% do disco no Android, varia no iOS. ### Backend: por que SQLite + filesystem? Para single-tenant pessoal: - **Simplicidade**: zero configuração, sem servidor de DB separado - **Backup trivial**: copiar o arquivo `.db` + pasta `media/` - **Performance**: WAL mode + synchronous=NORMAL é mais que rápido o suficiente - **Coolify-friendly**: 1 container, 1 volume Se evoluir para multi-tenant: Postgres + S3 (ou MinIO) seria a escolha óbvia. ### Backend: ESM modules Node 20+ com `"type": "module"`. Imports nativos `import x from 'y'`. Decisão para ficar moderno e por preferência. Trade-off: algumas libs antigas precisam de gambiarras de import. ### Auth: Bearer Token simples Um único `BOAT_TOKEN` configurado via env var. Vai em todo request. **Por quê não OAuth/JWT/cookies?** - Single-tenant pessoal — não há "usuários" - Token simples roda em qualquer dispositivo do dono - Sem expiração — para revogar, troca o env var **Limitações:** - Se o token vazar, intruso tem acesso completo - Sem audit trail granular (não tem quem fez o quê) Mitigação: token longo aleatório (32 bytes hex). HTTPS obrigatório (garantido pelo Coolify). ### Mapas: Leaflet + OSM **Leaflet**: biblioteca leve (~40 KB), API estável, plugin ecosystem. **OSM tiles** (`tile.openstreetmap.org`): gratuitos, sem chave, qualidade boa para uso recreativo. Política de uso permite apps pessoais. **Limitações:** - Tiles náuticas específicas não disponíveis (sem batimetria, sem profundidade) - Para ECDIS-grade, considerar Mapbox + nautical layer ou OpenSeaMap overlay ### Áudio do alarme: Web Audio API Em vez de tocar mp3 estático: **Pros:** - Volume real-time controlável - Padrão de tons alternados (klaxon 900↔1200 Hz) impossível de gerar com `