diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f8e8b47 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,144 @@ +# CLAUDE.md + +Guidance for Claude Code when working in this repository. + +> User-facing docs are in Portuguese (BR): [README.md](README.md) and [docs/](docs/). +> This file is in English and is the technical source of truth for how the code works. + +## What this is + +A terminal UI (TUI) **installer** for the "App do Dono" **client middleware**. The +middleware runs on a tenant's infrastructure and brokers communication between the +tenant's servers and davinTI's **central server**. + +The installer is a guided wizard that: checks Docker → logs into the private registry +→ pulls images → (optionally) sets up a WireGuard tunnel (`vproxy`) when there is no +public IP → collects configuration via terminal forms → generates config files +(`config.toml`, `envs`) → runs the Docker containers. + +It is **not** the middleware itself — it only provisions and launches it via `docker`. + +## Tech stack + +- **Go 1.25** +- **Bubble Tea v2** (`charm.land/bubbletea/v2`) — Elm-architecture TUI runtime. +- **Bubbles v2** — `textinput`, `spinner` components. +- **Lip Gloss v2** — styling. +- **BurntSushi/toml** — read existing `config.toml` for form defaults. +- Module path: `git.davinti.com.br/davinTI/app-dono/tui`. + +## Layout + +``` +cmd/main.go Entry point; starts the Bubble Tea program. +internal/tui/ + model.go Model struct, ConfigValues, loadConfig (defaults), InitialModel, Init. + steps.go step enum — the ordered wizard stages. + update.go Update: global keys + per-step dispatch (handlers). + view.go View: header + per-step body + help footer (AltScreen). + form.go Generic FormStep/FormField component (text/password/number/select). + cmds.go Msg types and async tea.Cmd wrappers around docker/file ops. + docker.go os/exec wrappers around the `docker` binary. + config.go Generates & writes config.toml and envs (+ numeric validation). + styles.go Lip Gloss palette/styles. +Makefile Multi-arch build + S3 publish. +``` + +## Architecture (Model–Update–View) + +- **Model** ([model.go](internal/tui/model.go)) holds all state: `currentStep`, + progress/error flags, one `FormStep` per form, and `configValues` (`ConfigValues`) + which accumulates each form's `map[string]string` output by section. +- **Update** ([update.go](internal/tui/update.go)) handles global messages + (`Ctrl+C`, window size, spinner tick) then dispatches on `currentStep` to per-step + handlers. Step transitions happen when a form returns `done == true` or when an + async completion message arrives. +- **View** ([view.go](internal/tui/view.go)) renders per-step. +- **Commands** ([cmds.go](internal/tui/cmds.go)) wrap every blocking op (docker calls, + file writes) as a `tea.Cmd` returning a `Msg`: `DockerCheckedMsg`, + `ImageDownloadFinishedMsg`, `ConfigFileMsg`, `DockerRunMsg`, `TickMsg`. + +## Wizard flow + +`steps.go` defines the order. See [docs/fluxo.md](docs/fluxo.md) for full detail. + +1. `StepCheckDocker` — `exec.LookPath("docker")`. Missing → `StepDockerInstall` + (instructs manual install and exits; **never auto-installs Docker**). +2. `StepDockerLogin` → `StepDownloadImage` — `docker login` + `pull` of the app image. + These registry credentials are **reused** later for the vproxy image. +3. `StepIPQuestion` — "public IP available?" + - **Yes** → jump to `StepAppConfig`. + - **No** → vproxy block: `StepWireguardConfig` → `StepGenerateWireguardFile` + (writes `envs`) → `StepDownloadWireguard` → `StepRunWireguard`. +4. Config forms: `StepAppConfig` → `StepServerConfig` → `StepDatabaseConfig` → + `StepCertConfig`. +5. `StepGenerateFile` — writes `config.toml` (validates numeric fields first). +6. `StepRunDocker` — runs `app-dono-cliente` container. +7. `StepDone`. + +## Key constants + +In [update.go](internal/tui/update.go): + +- `imageName = hub.davinti.com.br:443/app-dono/app-cliente:latest` +- `wireguardImageName = hub.davinti.com.br:443/davinti-vproxy:latest` +- `configPath = config.toml`, `wireguardConfigPath = envs` + +In [docker.go](internal/tui/docker.go): `networkName = app-dono_app` (all containers +join this network; created on demand). + +## Generated files + +- **`config.toml`** — app config, bind-mounted at `/app/config.toml`. If present at + startup it seeds form defaults via `loadConfig`. Generated by `GenerateConfigTOML`. +- **`envs`** — vproxy/WireGuard env vars, passed via `--env-file`. Generated only when + there is no public IP. By `GenerateWireguardConfig`. + +Both are gitignored. + +## Containers + +| Container | Image | Notes | +| ------------------ | -------------------- | -------------------------------------------------------- | +| `app-dono-cliente` | app-cliente | `:8080`, mounts config.toml + cert dir, `--restart unless-stopped`, runs as host uid:gid. | +| `vproxy` | davinti-vproxy | `--cap-add=NET_ADMIN`, `/dev/net/tun`, `--env-file envs`, only without public IP. | + +`seccomp=unconfined` is added to either container when the "Modo Compatibilidade" +(`seccomp_unconfined`) select is `"Sim"` — for old machines. + +## Gotchas (read before editing) + +- **Port:** `config.toml` always writes `port = 8080` (hardcoded in + `GenerateConfigTOML`). The form's "Porta" value only sets the **host-side** port in + the `docker run` mapping (`:8080`); inside the container the app always listens + on 8080. +- **Network name** is the `networkName` constant (`app-dono_app`) in + [docker.go](internal/tui/docker.go); use it rather than re-typing the literal. +- **Registry credentials** are collected once (login form) and reused for the vproxy + pull — no second prompt. +- Containers run detached and are verified with `docker inspect` for `running`; on + failure the last 20 log lines are surfaced. + +## Build & run + +```bash +go run ./cmd # run from source +go build -o installer ./cmd # local binary + +make build # multi-arch (linux/darwin/windows, amd64/arm64) → ./dist +make build VERSION=2.0.0 +make clean +make push S3_BUCKET= VERSION= # sync dist/ to S3 (version + latest) +make release S3_BUCKET= VERSION= # per-binary copy with OS/arch naming +``` + +There is no test suite. Verify changes by running `go build ./...` and exercising the +TUI manually. Requires a working Docker daemon and registry access to fully run. + +## Conventions + +- User-facing strings in the TUI are **Portuguese (BR)**; keep new ones consistent. +- Keep code comments and identifiers matching the surrounding style (mixed + English identifiers, Portuguese user messages). +- When adding a wizard step: add it to `steps.go`, a handler in `update.go`, a render + branch in `view.go`, and any new `Msg`/`Cmd` in `cmds.go`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f45d6b --- /dev/null +++ b/README.md @@ -0,0 +1,200 @@ +# App do Dono — Instalador Cliente (TUI) + +Instalador de terminal (TUI) que faz, em poucos passos guiados, a configuração e o +provisionamento do **middleware cliente** do "App do Dono". Esse middleware roda na +infraestrutura do **tenant** (cliente) e é responsável por intermediar a comunicação +entre os servidores do tenant e o **servidor central** da davinTI. + +A ferramenta cuida de tudo de ponta a ponta: valida o Docker, autentica no registry +privado, baixa as imagens, coleta as configurações via formulários no terminal, gera +os arquivos de configuração (`config.toml` e `envs`) e sobe os containers necessários. + +> Construído com [Bubble Tea](https://github.com/charmbracelet/bubbletea), +> [Bubbles](https://github.com/charmbracelet/bubbles) e +> [Lip Gloss](https://github.com/charmbracelet/lipgloss) (linha Charm v2). + +--- + +## Sumário + +- [O que ele faz](#o-que-ele-faz) +- [Pré-requisitos](#pré-requisitos) +- [Como usar](#como-usar) +- [Fluxo de instalação](#fluxo-de-instalação) +- [Conectividade: IP público vs. vproxy](#conectividade-ip-público-vs-vproxy) +- [Arquivos gerados](#arquivos-gerados) +- [Containers e rede Docker](#containers-e-rede-docker) +- [Build a partir do código](#build-a-partir-do-código) +- [Documentação adicional](#documentação-adicional) + +--- + +## O que ele faz + +O instalador conduz o operador por um assistente (wizard) no terminal que: + +1. **Verifica o Docker** na máquina (encerra com instruções se não houver). +2. **Autentica** no registry Docker privado (`hub.davinti.com.br`). +3. **Baixa a imagem** do app cliente (`app-dono/app-cliente`). +4. Pergunta se a máquina possui **IP público**: + - **Sim** → segue direto para a configuração da aplicação. + - **Não** → configura o túnel **vproxy** (WireGuard) e sobe esse container antes. +5. Coleta, via formulários, as configurações de **aplicação, servidor, banco de dados + e certificados**. +6. **Gera o `config.toml`** e sobe o container `app-dono-cliente`. +7. Exibe a confirmação de sucesso. + +## Pré-requisitos + +- **Docker** instalado e em execução na máquina de destino. + - O instalador **não** instala o Docker automaticamente — se não encontrar, ele + orienta a instalação manual e encerra. +- **Credenciais** do registry privado `hub.davinti.com.br`. +- **Token de inscrição** (enrollment token) gerado no painel web do App do Dono. +- Quando **não** houver IP público: dados do túnel **vproxy** (chave privada, IP + virtual, pre-shared key e mapeamento de proxy). +- Diretório local com os **certificados** mTLS do cliente (`client.crt`, `client.key`, + `ca.crt`). + +## Como usar + +Baixe o binário pré-compilado correspondente ao seu sistema operacional (distribuído +via S3 — veja o time de infraestrutura) e execute: + +```bash +chmod +x installer-linux-amd64 +./installer-linux-amd64 +``` + +Ou rode direto a partir do código-fonte: + +```bash +go run ./cmd +``` + +### Navegação na interface + +| Tecla | Ação | +| ------------------ | ------------------------------------- | +| `Tab` / `↓` | Próximo campo | +| `Shift+Tab` / `↑` | Campo anterior | +| `←` / `→` | Alternar opção (campos de seleção) | +| `Enter` | Confirmar campo / avançar etapa | +| Qualquer tecla | Avançar em telas de status | +| `Ctrl+C` | Sair a qualquer momento | + +## Fluxo de instalação + +``` +┌──────────────────┐ +│ Verifica Docker │ ──── não instalado ──► orienta instalação e encerra +└────────┬─────────┘ + │ instalado + ▼ +┌──────────────────┐ +│ Login Registry │ +└────────┬─────────┘ + ▼ +┌──────────────────┐ +│ Baixa imagem │ (app-cliente) +│ app-cliente │ +└────────┬─────────┘ + ▼ +┌──────────────────┐ +│ Tem IP público? │ +└───┬──────────┬───┘ + │ Sim │ Não + │ ▼ + │ ┌──────────────────┐ + │ │ Config. vproxy │ → gera "envs" → baixa imagem vproxy → sobe container vproxy + │ └────────┬─────────┘ + │ │ + ▼ ▼ +┌─────────────────────────────────────────┐ +│ Config. Aplicação → Servidor → │ +│ Banco de Dados → Certificados │ +└────────────────────┬────────────────────┘ + ▼ + ┌──────────────────┐ + │ Gera config.toml │ + └────────┬─────────┘ + ▼ + ┌──────────────────┐ + │ Sobe container │ (app-dono-cliente) + │ app-dono-cliente │ + └────────┬─────────┘ + ▼ + ✅ Concluído +``` + +## Conectividade: IP público vs. vproxy + +O middleware cliente precisa se comunicar com o servidor central. A forma de +conectividade depende da infraestrutura do tenant: + +- **Com IP público:** a comunicação é direta; o passo do vproxy é pulado. +- **Sem IP público:** sobe-se o container **vproxy** (túnel WireGuard, imagem + `davinti-vproxy`), que estabelece o túnel de saída e expõe os serviços necessários + através do `PROXY_EDPS`. O protocolo padrão é **UDP** (melhor desempenho); caso + firewalls restritivos bloqueiem UDP, é possível selecionar **TCP**. + +## Arquivos gerados + +O instalador gera dois arquivos no diretório de execução: + +- **`config.toml`** — configuração do app cliente (servidor, banco, certificados, + aplicação, log). É montado dentro do container em `/app/config.toml`. +- **`envs`** — variáveis de ambiente do vproxy/WireGuard (gerado somente quando não há + IP público). É passado ao container via `--env-file`. + +Ambos os arquivos são reaproveitados como **valores padrão** caso já existam ao reabrir +o instalador (no caso do `config.toml`). + +## Containers e rede Docker + +Todos os containers são conectados à rede Docker **`app-dono_app`** (criada +automaticamente se não existir). + +| Container | Imagem | Quando sobe | +| ------------------ | ------------------------------------------------- | -------------------- | +| `app-dono-cliente` | `hub.davinti.com.br:443/app-dono/app-cliente` | Sempre | +| `vproxy` | `hub.davinti.com.br:443/davinti-vproxy` | Quando não há IP púb.| + +Características: + +- Ambos sobem com `--restart unless-stopped`. +- O container do app expõe a porta configurada no host, mapeando para a `8080` interna, + e monta o `config.toml` e o diretório de certificados como volumes. +- O `vproxy` roda com `--cap-add=NET_ADMIN` e acesso a `/dev/net/tun`. +- O **modo compatibilidade** (`seccomp=unconfined`) pode ser ativado para máquinas + antigas onde o seccomp padrão causa problemas. + +## Build a partir do código + +Requer **Go 1.25+**. O `Makefile` gera binários estáticos para múltiplas plataformas: + +```bash +make build # compila para linux/darwin/windows (amd64/arm64) em ./dist +make build VERSION=2.0.0 # define a versão +make clean # remove ./dist + +# Publicação no S3 (requer S3_BUCKET): +make push S3_BUCKET=meu-bucket VERSION=1.0.0 +make release S3_BUCKET=meu-bucket VERSION=2.0.0 +make help # lista variáveis e alvos +``` + +Para um build local rápido: + +```bash +go build -o installer ./cmd +./installer +``` + +## Documentação adicional + +- [docs/fluxo.md](docs/fluxo.md) — detalhamento de cada etapa do assistente. +- [docs/configuracao.md](docs/configuracao.md) — referência de todos os campos de + configuração e dos arquivos gerados. +- [docs/arquitetura.md](docs/arquitetura.md) — visão da arquitetura interna do código + (modelo Bubble Tea, comandos, etapas). diff --git a/docs/arquitetura.md b/docs/arquitetura.md new file mode 100644 index 0000000..73323e2 --- /dev/null +++ b/docs/arquitetura.md @@ -0,0 +1,122 @@ +# Arquitetura Interna + +Visão de como o código está organizado. A aplicação segue o padrão +[The Elm Architecture](https://github.com/charmbracelet/bubbletea) (Model–Update–View) +da biblioteca Bubble Tea. + +## Estrutura de pastas + +``` +. +├── cmd/ +│ └── main.go # Ponto de entrada; inicia o programa Bubble Tea +├── internal/tui/ +│ ├── model.go # Struct Model, ConfigValues, carga de defaults, InitialModel/Init +│ ├── steps.go # Enum das etapas do assistente +│ ├── update.go # Lógica de transição (Update) por etapa +│ ├── view.go # Renderização (View) por etapa +│ ├── form.go # Componente genérico de formulário (FormStep/FormField) +│ ├── cmds.go # Mensagens (Msg) e comandos assíncronos (tea.Cmd) +│ ├── docker.go # Wrappers de chamadas ao binário docker +│ ├── config.go # Geração e gravação de config.toml e envs +│ └── styles.go # Paleta de cores e estilos Lip Gloss +├── config.toml # (gerado em runtime; ignorado no git) +├── envs # (gerado em runtime, vproxy) +└── Makefile # Build multiplataforma e publicação no S3 +``` + +## Componentes principais + +### `Model` ([model.go](../internal/tui/model.go)) + +Estado único da aplicação. Campos relevantes: + +- `currentStep` — etapa atual do assistente. +- Estado de progresso/flags: `dockerInstalled`, `downloadDone`, `finishedFile`, + `finishedDockerRun`, e os respectivos erros. +- Um `FormStep` por etapa de formulário (`loginForm`, `wireguardForm`, `appForm`, + `serverForm`, `dbForm`, `certForm`). +- `configValues` (`ConfigValues`) — acumula os valores de todos os formulários, + agrupados por seção. + +`loadConfig()` define os **valores padrão** e, se houver um `config.toml` no diretório, +sobrescreve com o conteúdo dele (usando `BurntSushi/toml`). `InitialModel()` constrói +todos os formulários com esses defaults. + +### Etapas ([steps.go](../internal/tui/steps.go)) + +`step` é um enum (`iota`) que define a ordem do assistente. O `Update` faz o dispatch +com base em `currentStep`. Ver [fluxo.md](fluxo.md) para o detalhamento. + +### Update ([update.go](../internal/tui/update.go)) + +`Update` trata primeiro mensagens globais (`Ctrl+C`, `WindowSizeMsg`, tick do spinner) +e depois delega para um handler por etapa (ex.: `updateCheckDocker`, +`updateDownloadImage`, `updateRunDocker`). As transições de etapa acontecem ao +concluir formulários (`done == true`) ou ao receber mensagens de conclusão dos +comandos assíncronos. + +Constantes importantes ficam no topo do arquivo: + +- `imageName` / `wireguardImageName` — imagens no registry privado. +- `configPath` (`config.toml`) / `wireguardConfigPath` (`envs`). + +### View ([view.go](../internal/tui/view.go)) + +`View` monta a tela com cabeçalho fixo, corpo dependente da etapa e rodapé de ajuda. +Usa `AltScreen`. Cada etapa tem um método `viewXxx` ou reutiliza o `View()` do +formulário. + +### Formulário genérico ([form.go](../internal/tui/form.go)) + +`FormStep` agrupa vários `FormField`. Suporta os tipos texto, senha, número e seleção. +`Update` cuida da navegação entre campos (`Tab`/setas) e retorna `done = true` quando +o `Enter` é pressionado no último campo. `Values()` devolve um `map[string]string` +indexado pelo `Id` de cada campo — é isso que alimenta o `ConfigValues`. + +### Comandos e mensagens ([cmds.go](../internal/tui/cmds.go)) + +Toda operação bloqueante (chamar `docker`, escrever arquivo) é encapsulada em um +`tea.Cmd` que roda em goroutine e devolve uma `Msg` ao loop do Bubble Tea: + +| Comando | Ação | Mensagem de retorno | +| ----------------------------- | ------------------------------------- | --------------------------- | +| `CheckDockerCmd` | `exec.LookPath("docker")` | `DockerCheckedMsg` | +| `DownloadImageCmd` | `docker login` + `pull` (app) | `ImageDownloadFinishedMsg` | +| `DownloadWireguardImageCmd` | `docker login` + `pull` (vproxy) | `ImageDownloadFinishedMsg` | +| `GenerateConfigFile` | grava `config.toml` | `ConfigFileMsg` | +| `GenerateWireguardConfigFile` | grava `envs` | `ConfigFileMsg` | +| `RunAppContainer` | `docker run` (app) | `DockerRunMsg` | +| `RunWireguardContainer` | `docker run` (vproxy) | `DockerRunMsg` | +| `TickCmd` | tick da barra de progresso | `TickMsg` | + +### Camada Docker ([docker.go](../internal/tui/docker.go)) + +Funções utilitárias que invocam o binário `docker` via `os/exec`: + +- `EnsureNetwork` — cria a rede `app-dono_app` se necessário. +- `RunAppClienteContainer` / `RunWireguardDockerContainer` — montam os argumentos do + `docker run`, removem container homônimo prévio e verificam o status `running`. +- `verifyContainerRunning` — inspeciona o status e, em falha, anexa os últimos logs. +- `removeExistingContainer`, `PullImage`, `ImageExists`, `PushFileToContainer`. + +### Estilos ([styles.go](../internal/tui/styles.go)) + +Paleta de cores e estilos Lip Gloss reutilizados na View (títulos, cursor, ajuda, +erros, barra de progresso, seleção). + +## Dependências + +- `charm.land/bubbletea/v2` — runtime TUI (loop Model/Update/View). +- `charm.land/bubbles/v2` — componentes (`textinput`, `spinner`). +- `charm.land/lipgloss/v2` — estilização. +- `github.com/BurntSushi/toml` — leitura do `config.toml` para defaults. + +## Observações para manutenção + +- **Porta:** o `config.toml` grava `port = 8080` fixo; a porta do formulário só afeta o + mapeamento de host no `docker run`. Ver [configuracao.md](configuracao.md). +- **Credenciais:** o login do registry é coletado uma vez e reutilizado para baixar a + imagem do vproxy. +- **Rede:** o nome da rede é a constante `networkName` (`app-dono_app`) em `docker.go`; + usar sempre a constante ao referenciá-la. diff --git a/docs/configuracao.md b/docs/configuracao.md new file mode 100644 index 0000000..cd7ddfe --- /dev/null +++ b/docs/configuracao.md @@ -0,0 +1,96 @@ +# Referência de Configuração + +Este documento descreve todos os campos coletados pelo instalador e os arquivos que +ele gera. + +## Arquivo `config.toml` + +Gerado por `GenerateConfigTOML` em +[`internal/tui/config.go`](../internal/tui/config.go) e montado no container do app em +`/app/config.toml`. Se um `config.toml` já existir no diretório ao iniciar o +instalador, seus valores são usados como **padrão** nos formulários. + +### `[server]` + +| Campo | Origem (formulário) | Observações | +| ----------------- | ----------------------------- | ------------------------------------------------------------ | +| `port` | **fixo `8080`** no arquivo | A porta do formulário é usada apenas no **mapeamento do host** (`:8080`). Dentro do container o app sempre escuta na `8080`. | +| `timeout_seconds` | Servidor → Timeout | Em segundos. Padrão `30`. | +| `environment` | Servidor → Ambiente | `development` ou `production`. | + +> **Atenção:** o campo "Porta" do formulário define a porta exposta no host, não a +> porta interna. O `config.toml` sempre grava `port = 8080`. + +### `[database]` + +| Campo | Origem | Observações | +| ----------- | -------------------- | ------------------------------------ | +| `type` | Banco → Tipo do Banco| `postgres` ou `oracle`. | +| `url` | Banco → URL de acesso| String de conexão completa. | +| `max_conns` | Banco → Conexões máx.| Validado como número. | +| `min_conns` | Banco → Conexões mín.| Validado como número. | + +### `[certificate]` + +| Campo | Valor | Observações | +| ------------ | ------------------------------------ | -------------------------------------------- | +| `mapped_dir` | Certificado → Diretório | Diretório local montado em `/app/certs`. | +| `cert_path` | `/app/certs/client.crt` (fixo) | Caminho **dentro** do container. | +| `key_path` | `/app/certs/client.key` (fixo) | Caminho **dentro** do container. | +| `ca_path` | `/app/certs/ca.crt` (fixo) | Caminho **dentro** do container. | + +O diretório indicado deve conter os arquivos `client.crt`, `client.key` e `ca.crt` +(comunicação mTLS com o servidor central). + +### `[application]` + +| Campo | Origem | Observações | +| -------------------- | ------------------------------- | ------------------------------------ | +| `erp` | `TOTVS` (fixo) | ERP integrado. | +| `central_server_url` | Aplicação → URL Servidor Central| Ex.: `https://app-dono-api.vitruvio.com.br:8443`. | +| `enrollment_token` | Aplicação → Token de Inscrição | Gerado no painel web. | + +### `[log]` + +| Campo | Valor | +| -------- | ----------------- | +| `level` | `debug` (fixo) | +| `format` | `json` (fixo) | + +## Arquivo `envs` (vproxy / WireGuard) + +Gerado por `GenerateWireguardConfig` apenas quando **não há IP público**. Passado ao +container do vproxy via `--env-file`. + +| Variável | Origem | Observações | +| ------------ | ----------------------- | ------------------------------------------------------------ | +| `PRIVKEY` | vproxy → Chave Privada | Chave privada WireGuard. | +| `VIP` | vproxy → IP Virtual | Padrão `127.0.0.1`. | +| `PSK` | vproxy → Pre-Shared Key | Chave pré-compartilhada. | +| `PROXY_EDPS` | vproxy → Proxy EDPS | Mapeamento de portas, ex.: `22:127.0.0.1:22`. | +| `MTU` | vproxy → MTU | Opcional. Padrão `1380`. Validado como número. | +| `PROTO` | vproxy → Protocolo | `UDP` (padrão) ou `TCP`. | + +### Sobre o protocolo + +- **UDP** (padrão): melhor desempenho e estabilidade. Manter sempre que possível. +- **TCP**: usar apenas quando firewalls restritivos bloqueiam o tráfego UDP e não for + possível negociar a liberação com o cliente. + +## Campo "Modo Compatibilidade" + +Presente no formulário de Servidor (`seccomp_unconfined`). Quando definido como +**`Sim`**, os containers sobem com `--security-opt seccomp=unconfined`. Útil em +máquinas antigas onde o perfil seccomp padrão do Docker causa falhas. Aplica-se tanto +ao container do app quanto ao do vproxy. + +## Tipos de campo dos formulários + +Definidos em [`internal/tui/form.go`](../internal/tui/form.go): + +| Tipo | Comportamento | +| ------------------ | ---------------------------------------------- | +| `FieldTypeText` | Texto livre. | +| `FieldTypePassword`| Texto mascarado. | +| `FieldTypeNumber` | Texto (validação numérica na gravação). | +| `FieldTypeSelect` | Opções alternadas com `←`/`→`. | diff --git a/docs/fluxo.md b/docs/fluxo.md new file mode 100644 index 0000000..7e22e5f --- /dev/null +++ b/docs/fluxo.md @@ -0,0 +1,113 @@ +# Fluxo do Instalador + +Este documento detalha cada etapa do assistente, a ordem em que ocorrem e as ações +executadas em segundo plano. As etapas são definidas em +[`internal/tui/steps.go`](../internal/tui/steps.go). + +## Visão geral das etapas + +| # | Etapa (`step`) | O que acontece | +| -- | ---------------------------- | ------------------------------------------------------------------- | +| 1 | `StepCheckDocker` | Verifica se o binário `docker` está no `PATH`. | +| 2 | `StepDockerInstall` | Tela final exibida quando o Docker não é encontrado. | +| 3 | `StepDockerLogin` | Formulário de login no registry privado. | +| 4 | `StepDownloadImage` | `docker login` + `docker pull` da imagem do app cliente. | +| 5 | `StepIPQuestion` | Pergunta se há IP público disponível. | +| 6 | `StepWireguardConfig` | Formulário de configuração do vproxy (apenas sem IP público). | +| 7 | `StepGenerateWireguardFile` | Gera o arquivo `envs`. | +| 8 | `StepDownloadWireguard` | `docker login` + `docker pull` da imagem do vproxy. | +| 9 | `StepRunWireguard` | Sobe o container `vproxy`. | +| 10 | `StepAppConfig` | Formulário da aplicação (URL central, token). | +| 11 | `StepServerConfig` | Formulário do servidor (porta, timeout, ambiente, compatibilidade). | +| 12 | `StepDatabaseConfig` | Formulário do banco de dados. | +| 13 | `StepCertConfig` | Formulário do diretório de certificados. | +| 14 | `StepGenerateFile` | Gera o `config.toml`. | +| 15 | `StepRunDocker` | Sobe o container `app-dono-cliente`. | +| 16 | `StepDone` | Mensagem de sucesso. | + +## Detalhamento + +### 1. Verificação do Docker (`StepCheckDocker`) + +Ao iniciar, o `Init()` dispara três comandos em paralelo: `CheckDockerCmd`, +`TickCmd` (anima a barra de progresso) e o tick do spinner. O `CheckDockerCmd` +executa `exec.LookPath("docker")`. + +- **Encontrado:** ao pressionar qualquer tecla, segue para o login. +- **Não encontrado:** vai para `StepDockerInstall`, que orienta a instalação manual + e encerra. O instalador **não** instala o Docker. + +### 3–4. Login e download da imagem do app + +O formulário coleta usuário e senha do registry. Em seguida, `DownloadImageCmd` +executa: + +``` +docker login hub.davinti.com.br:443/app-dono/app-cliente:latest -u -p +docker pull hub.davinti.com.br:443/app-dono/app-cliente:latest +``` + +As **mesmas credenciais** são reaproveitadas mais adiante para baixar a imagem do +vproxy. Em caso de erro de login ou pull, a mensagem do Docker é exibida e o +instalador encerra ao pressionar qualquer tecla. + +### 5. Pergunta de IP público (`StepIPQuestion`) + +- **Sim** → pula o bloco do vproxy e vai direto para `StepAppConfig`. +- **Não** → vai para `StepWireguardConfig`. + +### 6–9. Bloco vproxy (somente sem IP público) + +1. **`StepWireguardConfig`** — coleta `PRIVKEY`, `VIP`, `PSK`, `PROXY_EDPS`, `MTU` + e `PROTO` (UDP/TCP). +2. **`StepGenerateWireguardFile`** — grava o arquivo `envs` + (ver [`config.go`](../internal/tui/config.go), `GenerateWireguardConfig`). +3. **`StepDownloadWireguard`** — faz login e pull da imagem `davinti-vproxy`. +4. **`StepRunWireguard`** — sobe o container `vproxy` com `--cap-add=NET_ADMIN`, + `--device /dev/net/tun`, `--env-file envs` e o conecta à rede `app-dono_app`. + Após subir, espera 2s e verifica se o status é `running`; se não, mostra os + últimos logs do container. + +Ao final do bloco, segue para `StepAppConfig`. + +### 10–13. Configuração da aplicação + +Quatro formulários sequenciais preenchem o `ConfigValues`: + +- **Aplicação:** URL do servidor central e token de inscrição. +- **Servidor:** porta, timeout, ambiente (`development`/`production`) e modo + compatibilidade (`seccomp=unconfined`). +- **Banco de dados:** tipo (`postgres`/`oracle`), URL de conexão, conexões máx./mín. +- **Certificado:** diretório local com os certificados mTLS. + +### 14. Geração do `config.toml` (`StepGenerateFile`) + +`WriteConfigFile` valida os campos numéricos (`port`, `timeout`, `max_conns`, +`min_conns`) e grava o `config.toml`. Em caso de valor inválido, exibe o erro e +permite tentar novamente. + +### 15. Subida do container do app (`StepRunDocker`) + +`RunAppClienteContainer` remove um container homônimo existente, garante a rede +`app-dono_app`, e executa `docker run` com: + +- usuário/grupo do host (`-u uid:gid`); +- mapeamento de porta `:8080`; +- volume do `config.toml` em `/app/config.toml`; +- volume do diretório de certificados em `/app/certs`; +- `--restart unless-stopped`. + +Verifica o status `running` da mesma forma que o vproxy. + +### 16. Conclusão (`StepDone`) + +Exibe "Instalação realizada com sucesso!". Qualquer tecla encerra. + +## Tratamento de erros + +Em etapas de download, geração de arquivo e subida de container, qualquer falha: + +- interrompe o avanço automático; +- mostra a mensagem/erro do Docker (ou do sistema de arquivos) com estilo de erro; +- permite **sair** (em downloads) ou **tentar novamente** (em geração/run) conforme + a etapa.