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

115 lines
4.3 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```bash
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:
```bash
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.tech` → `api-scardua:8000`, TLS automático.
Caddyfile em `/srv/containers/stacks/caddy/Caddyfile`.
## Atualizar a app
```bash
# 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:
> ```bash
> 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.
```bash
cat configs.json | ssh vps-fls 'cat > ~/.config/api-scardua/configs.json && chmod 600 $_'
```
## Rollback
```bash
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)
```bash
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
```bash
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
```bash
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).