API-Holmes/docs/deploy.md
Ricardo 4d47887c00 Reestrutura para API multi-cliente com payload cifrado
A API deixa de ser especifica da Scardua e passa a atender varios
clientes pela rota /v1/holmes/{cliente}/...

Camada de clientes:
- app/clientes/base.py: ClienteBase (comportamento) + ClienteConfig (dado),
  separados de proposito — config sai do configs.json hoje e pode sair do
  banco amanha sem o resto mudar
- app/clientes/registry.py: resolve o {cliente} da rota pra instancia
- cada cliente traz a propria conta do Holmes; o cache de token virou um
  arquivo por cliente (antes era constante de modulo e dois clientes
  sobrescreveriam o token um do outro)

Holmes:
- funcoes de modulo viraram a classe HolmesAPI, amarrada a uma conta
- exposta pronta em cliente.holmes (cached_property)
- as funcoes de parsing puro (extrair_*) ficaram fora da classe

Config:
- app/schemas.py: modulo folha, so pydantic. Config e services usam os
  mesmos modelos sem um importar o outro
- app/config.py: so o carregamento do configs.json
- configs.json passa a ter "clientes": {"<nome>": {...}}

Compras:
- schemas aceitam o payload real do Holmes (PEDIDO_LINX vem como numero,
  NUMERO NF ENTRADA so existe depois da fase da nota)
- parcelas viram dict[int, Parcela], pareando parcelaN com o vencimento
  correspondente e pulando os slots vazios
- payload de saida carrega id_processo como chave de deduplicacao: o
  Holmes reentrega webhook e sem isso duplica linha no destino

Criptografia:
- app/services/cripto.py: envelope AES-256-GCM + RSA-OAEP-SHA256. RSA
  sozinho nao serve (cifra ~190 bytes; o payload passa disso)
- ciframos com a publica do conector e nao conseguimos desfazer

Deploy:
- Dockerfile e .dockerignore vieram da VPS pro repo
- docs/deploy.md: runbook, incluindo que --format docker e obrigatorio
  (em OCI o podman ignora o HEALTHCHECK silenciosamente)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 06:01:01 -03:00

4.3 KiB
Raw Permalink Blame History

Deploy — API (VPS da FLS)

Ambiente: VPS Hostinger (Debian 13), Podman rootless + Quadlet, Caddy (proxy + TLS), user admin. No ar: https://api-scardua.flstecnologia.tech

O nome api-scardua é legado — a API hoje atende múltiplos clientes (/v1/holmes/{cliente}/...). Renomear implica mexer junto em: nome do quadlet, Image=, ContainerName=, pasta do configs.json, build dir, bloco do Caddyfile e DNS. O reverse_proxy do Caddy resolve o container pelo ContainerName dentro da rede fls — se um mudar sem o outro, dá 502.

Acesso à VPS

ssh vps-fls          # alias -> 179.197.230.154, porta 3115, user admin
  • Porta 22 é fechada no firewall. Sempre -p 3115 (ou o alias).
  • systemctl --user sempre (rootless) — nunca sudo systemctl pros containers.
  • ⚠️ O ~/.ssh/config tem um BOM na primeira linha e o OpenSSH do Git Bash recusa (Bad configuration option: \357\273\277host). Contorno sem editar o arquivo:
    ssh -F none -i ~/.ssh/id_ed25519 -p 3115 admin@179.197.230.154
    

Arquitetura

  • Imagem: buildada local na VPS = localhost/api-scardua:latest (sem registry).
  • Container na rede fls — só o Caddy alcança; a porta 8000 não é publicada.
  • Config: configs.json montado read-only de ~/.config/api-scardua/configs.json.
  • Quadlet: ~/.config/containers/systemd/api-scardua.container.
  • Caddy: api-scardua.flstecnologia.techapi-scardua:8000, TLS automático. Caddyfile em /srv/containers/stacks/caddy/Caddyfile.

Atualizar a app

# 1. marcar rollback ANTES de qualquer coisa
ssh vps-fls 'podman tag localhost/api-scardua:latest localhost/api-scardua:rollback-$(date +%F)'

# 2. mandar o código (não tem rsync no Git Bash do Windows; tar resolve)
tar czf - --exclude='__pycache__' --exclude='*.pyc' --exclude='.ruff_cache' \
    main.py app requirements.txt Dockerfile .dockerignore pyproject.toml \
  | ssh vps-fls 'rm -rf ~/build/api-scardua && mkdir -p ~/build/api-scardua && tar xzf - -C ~/build/api-scardua'

# 3. buildar e reiniciar
ssh vps-fls 'cd ~/build/api-scardua && podman build --format docker -t api-scardua:latest . \
             && systemctl --user restart api-scardua'

⚠️ --format docker é obrigatório. No formato OCI (padrão do Podman) o HEALTHCHECK do Dockerfile é silenciosamente ignorado — e é ele que o podman auto-update usa pra decidir rollback. Conferir depois do build:

podman inspect localhost/api-scardua:latest --format '{{json .HealthCheck}}'

⚠️ Diretórios da build antiga podem estar sem bit de escrita (dr-x------) e travar o rm -rf no meio. Se acontecer: chmod -R u+rwX ~/build/api-scardua.

O configs.json da VPS é separado

Ele não vem do repo (gitignored). Vive em ~/.config/api-scardua/configs.json e tem que ser atualizado à mão quando o formato do Settings mudar — senão o container entra em crash-loop na validação do Pydantic, que roda no import.

cat configs.json | ssh vps-fls 'cat > ~/.config/api-scardua/configs.json && chmod 600 $_'

Rollback

ssh vps-fls '
  podman tag localhost/api-scardua:rollback-<data> localhost/api-scardua:latest
  systemctl --user restart api-scardua
'

Mexer no Caddy (com segurança)

cd /srv/containers/stacks/caddy
cp Caddyfile Caddyfile.bak
# editar...
podman exec caddy caddy validate --config /etc/caddy/Caddyfile   # valida ANTES
podman exec caddy caddy reload   --config /etc/caddy/Caddyfile   # sem downtime

Comandos úteis

systemctl --user status api-scardua
podman logs -f api-scardua
podman ps --filter name=api-scardua        # estado + health
podman healthcheck run api-scardua

Verificar depois de subir

curl -s -o /dev/null -w '%{http_code}\n' https://api-scardua.flstecnologia.tech/health
curl -X POST https://api-scardua.flstecnologia.tech/v1/holmes/scardua/compras/dados \
     -H 'Content-Type: application/json' -d @payload-do-holmes.json

Cliente desconhecido na rota deve dar 404 (cliente 'x' nao atendido).

Evolução planejada

  • Registry + CI (Forgejo Actions): publicar em git.flstecnologia.tech, trocar o Quadlet pro alvo com AutoUpdate=registry → deploy pull-based.
  • Backup do Postgres da VPS (pendência geral do servidor).