Files
GTI_GERENCIADOR_FISCAL/docs/GTI_GERENCIADOR_DISCOVERY_V1.md
T
GustavoandClaude Sonnet 5 34cc9c75a5 feat: include CNPJ (digits) in the internal Fiscal client payload
Needed by the Fiscal's searchable client combobox (search by name/code/CNPJ).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 08:08:06 -03:00

33 KiB

GTI Gerenciador — Discovery Técnico (v1)

Nota crítica de escopo, apurada nesta missão: "GTI Gerenciador" é o nome comercial/de produção do repositório painel-clientes-web. Não existe um projeto separado com esse nome — confirmado com o usuário após uma busca read-only que não encontrou nenhuma pasta *gerenciador* em D:\PROJETOS nem em 10.0.10.117. O sistema real é um painel de gestão de clientes/licenciamento (cadastro de clientes, funcionários, geração de chave de reativação, log de auditoria) — não um gerenciador de frota/RMM com Agents Windows, heartbeat, jobs e blockers, como o briefing da missão presumia. As seções abaixo refletem o sistema real; onde o briefing pedia algo que não existe (ex.: módulo de Agents), isso é dito explicitamente, não inventado.

1. Identidade do repositório

PROJECT_NAME=painel-clientes-web ("GTI Gerenciador" em produção)
REPOSITORY_PATH=D:\PROJETOS\BLOQUEIO SGV\painel-clientes-web
CURRENT_BRANCH=master
HEAD_COMMIT=ce718f7ac22a583fd8c5d3a23415a19dcfc31291 (2026-08-20)
WORKING_TREE_STATUS=limpo (só ../.claude/ não rastreado, fora do repo)
GIT_REMOTE=nenhum configurado — repositório só local, nunca teve push

Estrutura principal (app/):

  • routers/: auth, clientes, contatos, funcionarios, log, minha_conta, chave_manual
  • db/: clientes, contatos, funcionarios, log, chaves, connection — SQL cru via PyMySQL, sem ORM
  • templates/: Jinja2 server-rendered (base.html + uma página por rota)
  • static/: CSS puro + JS vanilla inline
  • auth.py, csrf.py, session.py, senha.py, rate_limit.py, n8n_client.py, gerador_chave.py, cnpj_lookup.py
  • docs/, docs/superpowers/specs|plans — specs e planos de cada feature (fluxo de desenvolvimento assistido por IA usado neste projeto)

Não há pasta frontend/, migrations/ (Alembic) ou deployment/ separada — é um monólito único, com deploy manual (não há migrations/; mudanças de schema são ALTER TABLE manuais documentadas em docstring + README).

2. Stack da aplicação

BACKEND_FRAMEWORK=FastAPI
BACKEND_LANGUAGE=Python
BACKEND_VERSION=3.12 (imagem Docker python:3.12-slim)

FRONTEND_TYPE=server-rendered
FRONTEND_FRAMEWORK=Jinja2Templates (FastAPI)
FRONTEND_BUILD_CHAIN=nenhuma (sem bundler/npm — CSS/JS estáticos escritos à mão)

DATABASE=MySQL 5.7
ORM=nenhum (PyMySQL cru, SQL parametrizado manualmente)
MIGRATION_TOOL=nenhum (ALTER TABLE manual, documentado em docstring do módulo + README.md)

CACHE=nenhum
QUEUE=nenhuma
BACKGROUND_JOBS=nenhum (tudo síncrono, request/response)

WEBSERVER=uvicorn (ASGI, standalone — sem gunicorn na frente)
APPLICATION_SERVER=uvicorn[standard]==0.30.6

Dependências (requirements.txt, todas pinadas por auditoria de segurança de 2026-08-15): fastapi==0.115.6, uvicorn[standard]==0.30.6, jinja2==3.1.6, python-multipart==0.0.20, PyMySQL==1.1.1, python-dotenv==1.0.1, httpx==0.27.2 (usado só para chamar a API do n8n).

Classificação: monolito único, server-rendered, sem frontend separado.

3. Funcionalidades atuais

MODULE_NAME PURPOSE MAIN_ROUTES MAIN_TABLES DEPENDENCIES
Clientes CRUD de clientes (CNPJ, contato, status ativo/inativo) /clientes, /clientes/{id}, /clientes/{id}/alternar-ativo CLIENTE cnpj_lookup.py (consulta CNPJ externa)
Contatos Contatos por cliente (nome/telefone/e-mail) rotas aninhadas sob /clientes/{id} CLIENTE_CONTATO —
Funcionários Usuários do painel (login/senha/perfil ADMIN|PADRAO) /funcionarios FUNCIONARIO_PAINEL senha.py (scrypt)
Gerador de chave Gera chave de reativação/liberação (cifra própria) /gerador-chave, /gerador-chave/gerar — (sem persistência própria) gerador_chave.py
Log Auditoria de ações do painel + espelho de envios automáticos do n8n /log, /log/limpar (admin) LOG_PAINEL n8n_client.py (lê Data Table do n8n via API, não escreve)
Autenticação Login humano (admin de ambiente + funcionários) /login, /logout FUNCIONARIO_PAINEL + ADMIN_USER/ADMIN_PASSWORD (env) session.py, csrf.py
Minha conta Trocar a própria senha /minha-conta FUNCIONARIO_PAINEL —

Não existe: módulo de servidores, agentes, backups, monitoramento, inventário ou "integrações" no sentido do briefing original — o sistema é puramente sobre clientes/licenciamento, não sobre gestão de infraestrutura de terceiros.

4. Autenticação e autorização

AUTH_MODEL=cookie de sessão assinado por HMAC-SHA256 (chave própria em session.py,
           não é JWT de biblioteca) + cookie CSRF de double-submit
USER_TABLE=FUNCIONARIO_PAINEL (login, hash de senha, PERFIL)
ROLE_MODEL=dois perfis fixos: ADMIN e PADRAO (sem granularidade por permissão)
PERMISSION_MODEL=checagem direta de perfil nas rotas (Depends(exigir_admin) vs
                  Depends(exigir_login)) — não é RBAC configurável
  • Existe também um login "raiz" fora da tabela: usuário/senha vindos de ADMIN_USER/ADMIN_PASSWORD (variáveis de ambiente), comparação direta — serve de acesso de emergência independente do banco.
  • Senha de funcionário: scrypt (stdlib do Python, sem bcrypt/argon2/passlib).
  • Sessão: HMAC-SHA256 sobre payload com login, nome, perfil, exp — validade de 12 horas, sem refresh automático (expira, força novo login).
  • CSRF: cookie csrf_token (HttpOnly) + campo hidden em todo formulário POST, comparados no servidor (double-submit).
  • Logout: limpa cookie de sessão.

Reuso por GTI Fiscal: o modelo de sessão (HMAC próprio) é simples e replicável, mas está fortemente amarrado a este processo Python específico (chave HMAC em memória do processo/env, sem endpoint de validação exposto para outro serviço consumir). Reuso real exigiria extrair isso para um serviço de auth compartilhado ou aceitar duplicar a lógica — não há hoje um "SSO" ou endpoint /auth/validar pronto para outro backend chamar.

5. Banco de dados

DB_ENGINE=MySQL
DB_VERSION=5.7 (imagem Docker mysql:5.7)
DB_HOST_MODEL=container único (gtisoftw-mysql), sem replicação/cluster
DB_NAME=gtisoftw_clientes_dev (nome de dev usado também em produção — dívida
        histórica de nomenclatura, não um ambiente de teste real)
MIGRATION_HEAD=n/a (sem Alembic/ferramenta — ALTER TABLE manual documentado)

Tabelas principais conhecidas: CLIENTE, CLIENTE_CONTATO, FUNCIONARIO_PAINEL, LOG_PAINEL (+ tabelas do sistema legado licenciamento-n8n que compartilham o mesmo banco, fora do escopo direto do painel). Total de ~9 tabelas segundo o dump de referência mais recente (Banco para Prod/README-DEPLOY-PROXMOX.md).

  • Volume de dados: pequeno — ~209 clientes, 127 contatos (dump de referência).
  • Pool/connections: sem pool — PyMySQL abre e fecha conexão por request (get_connection()/conn.close() a cada operação), aceitável no volume atual mas não escalaria bem sob concorrência alta.
  • Backup: não encontrei rotina de backup automatizada no servidor (sem cron de dump, sem script de backup do container) — só existe o procedimento manual documentado em Banco para Prod/README-DEPLOY-PROXMOX.md para popular/restaurar a partir de um dump.
  • Persistência: volume Docker nomeado (gtisoftw_mysql_data, implícito pelo compose de Banco para Prod/), não bind-mount.
CAN_GTI_FISCAL_SHARE_DATABASE=requires_analysis
Opção Vantagens Riscos
A) Mesmo PostgreSQL, schemas separados Simples de administrar (1 backup, 1 instância), sem novo processo de banco GTI Fiscal quer PostgreSQL, este ambiente só roda MySQL 5.7 — não é "mesmo Postgres", seria instalar um segundo motor de banco do zero
B) Bancos lógicos separados no mesmo motor N/A aqui pelo mesmo motivo acima —
C) PostgreSQL separado no mesmo host Cada stack usa o motor que já foi desenhado para ela, sem gambiarra de schema Mais um processo residente numa VM que já está com RAM apertada (ver §22)

Dado que GTI Fiscal foi desenhado para PostgreSQL e este ambiente só tem MySQL, a opção realista é C) PostgreSQL próprio no mesmo host (se houver recursos — ver §22) — não há cenário realista de "compartilhar" o banco atual entre os dois sistemas.

6. Ambiente de execução

HOSTNAME=srvbd
OS=Ubuntu 24.04.4 LTS
KERNEL=Linux 6.8.0-137-generic

VIRTUALIZATION_VISIBLE=kvm (systemd-detect-virt)
CONTAINER_OR_VM=VM (QEMU/KVM — "Standard PC (i440FX + PIIX, 1996)", vendor QEMU)

Confirmado como VM dentro de um host Proxmox: a pasta local Banco para Prod/README-DEPLOY-PROXMOX.md (deste mesmo repositório) documenta explicitamente o processo de deploy do banco "na VM/LXC do Proxmox" — este servidor (10.0.10.117) é essa VM.

CPU_CORES=2
RAM_TOTAL=1.9 GiB
RAM_AVAILABLE=~1.2 GiB (742 MiB em uso, 1.0 GiB em buff/cache)
DISK_TOTAL=8.1 GiB (partição raiz)
DISK_FREE=1.4 GiB (82% em uso)

LOAD=0.08, 0.02, 0.01 (ociosa)
UPTIME=36 dias

Só leitura — nenhum comando de escrita executado.

7. Docker / containers

DOCKER_VERSION=29.7.2
COMPOSE_VERSION=v5.4.0
NAME IMAGE STATUS PORTS NETWORKS
painel-clientes-web-painel-1 painel-clientes-web-painel (build local) Up 4 semanas 0.0.0.0:80->8000/tcp default + bancoparaprod_default (externa)
gtisoftw-mysql mysql:5.7 Up 5 semanas 0.0.0.0:3306->3306/tcp bancoparaprod_default (dedução — mesmo network externo usado pelo painel)

docker-compose.yml do painel (em /home/administrador/painel-clientes-web/):

services:
  painel:
    build: .
    ports: ["80:8000"]
    env_file: [.env]
    networks: [default, bancoparaprod_default]
    restart: unless-stopped
networks:
  bancoparaprod_default:
    external: true

O container do MySQL vem de um compose separado (Banco para Prod/, network bancoparaprod_default criada por ele, consumida como externa pelo painel) — os dois projetos/composes são independentes, unidos só pela network Docker compartilhada. Sem healthcheck explícito em nenhum dos dois serviços.

8. Recursos usados atualmente

painel-clientes-web-painel-1:  CPU 0.12%   RAM 78.8 MiB
gtisoftw-mysql:                CPU 0.07%   RAM 199.4 MiB
CURRENT_RESOURCE_PRESSURE=baixa em CPU (2 vCPU quase ociosos); MODERADA em RAM
                            (só a folga não-cache é ~200-400MiB reais depois
                            de reservar o que o SO/buffers já usam) e ALTA em
                            disco (82% da partição raiz de 8GB já ocupado)
HEADROOM_ESTIMATE=CPU: folgado. RAM: ~1.2GiB "available" reportado pelo
                    kernel, mas isso conta cache reclamável — para uma
                    segunda aplicação Python + segundo banco (Postgres) a
                    margem real é apertada, não confortável. Disco: crítico
                    — só 1.4GiB livres, qualquer novo serviço com imagem
                    Docker própria (Postgres + Hub FastAPI) provavelmente
                    não cabe sem aumentar o disco da VM antes.

9. Portas e rede

SERVICE LISTEN_ADDRESS PORT EXPOSED_EXTERNALLY PURPOSE
painel-clientes-web (uvicorn) 0.0.0.0 80 sim (direto, sem proxy na frente) HTTP da aplicação
MySQL 0.0.0.0 3306 sim Banco relacional
SSH 0.0.0.0 22 sim Administração
systemd-resolved 127.0.0.53/127.0.0.54 53 não DNS local

Nenhuma porta foi aberta ou alterada — apenas listadas via ss -tlnp.

10. Acesso web

CURRENT_URL=http://10.0.10.117/ (IP direto, porta 80)
DOMAIN=nenhum domínio público configurado
HTTPS=não (confirma pendência já registrada na auditoria de segurança de
      2026-08-15: "falta de TLS em produção é pendência aceita
      conscientemente")
TLS_PROVIDER=n/a

Fluxo real: Internet/rede interna → 10.0.10.117:80 → uvicorn (direto). Não há DNS/Cloudflare nem qualquer camada intermediária.

11. Reverse proxy

REVERSE_PROXY_TYPE=NENHUM instalado (nginx/caddy/traefik ausentes no host)
REVERSE_PROXY_LOCATION=n/a
TLS_TERMINATION=n/a
PROXY_TARGET=n/a

Isso é uma lacuna relevante para a integração: hoje não existe onde configurar /fiscal ou fiscal.dominio — seria preciso instalar um reverse proxy do zero (Caddy é o mais barato em RAM para esta VM apertada) antes de sequer cogitar rotear dois serviços por hostname/porta 80/443 únicos.

12. Firewall e exposição

PUBLICLY_EXPOSED_PORTS=22 (SSH), 80 (aplicação), 3306 (MySQL)

MySQL está exposto publicamente (0.0.0.0:3306), não só em loopback/rede interna — isso já era uma pendência independente desta missão, mas relevante para decidir se um segundo banco (Postgres do GTI Fiscal) repete o mesmo padrão ou é mantido só em rede interna Docker. Não alterei nenhuma regra.

13. Backup

BACKUP_STATUS=inexistente como rotina automatizada
RESTORE_PROCEDURE=manual, documentado (Banco para Prod/README-DEPLOY-PROXMOX.md)
                   — restaura a partir de dump SQL versionado no repo local,
                   não a partir de um backup do estado de produção atual
LAST_BACKUP_IF_VISIBLE=não há evidência de nenhum backup de produção
                        rodado — dumps encontrados localmente são snapshots
                        pontuais feitos manualmente (ex.:
                        dump-gtisoftw_clientes_dev-20260806_104136.sql)

14. Logs / observabilidade

LOGGING_MODEL=stdout do container (docker logs), mais LOG_PAINEL (tabela de
              auditoria de ações do painel, não logs técnicos)
MONITORING_MODEL=nenhum (sem Prometheus/Grafana/Loki/Zabbix — nada instalado
                  no host além do sysstat padrão do Ubuntu)

Eventos do GTI Fiscal (heartbeat, jobs, blockers) não têm onde se integrar hoje — não existe stack de observabilidade centralizada nesta VM para "encaixar" nele.

15. Serviços do sistema

  • systemd: só serviços padrão do Ubuntu + Docker daemon. Nenhum serviço systemd customizado para o painel (ele roda via restart: unless-stopped do próprio Docker, não via unit systemd).
  • cron: só as entradas padrão do Ubuntu (cron.hourly/daily/weekly/monthly via anacron) — nenhum job customizado do projeto.
  • Sem Windows services (host é Linux).

16. Deploy atual

DEPLOY_PROCEDURE=manual, via script Python ad-hoc (paramiko/SFTP) rodado pelo
                  desenvolvedor: empacota o repo local em .tar.gz (excluindo
                  docker-compose.yml e .env de produção), sobe por SFTP,
                  extrai por cima do diretório existente na VM, roda
                  "docker compose build" (com retry manual em caso de timeout
                  do registro Docker Hub) e "docker compose up -d". Migração
                  de schema (quando há) é um ALTER TABLE manual rodado à parte,
                  documentado no docstring do módulo Python afetado.
ROLLBACK_PROCEDURE=NÃO documentado/automatizado — não há tag de imagem
                    anterior preservada nem processo de reverter; reverter
                    exigiria checkout de um commit anterior local e repetir
                    o deploy manual.

Não há CI/CD, GitHub Actions, ou pipeline — é 100% manual, disparado por uma pessoa a cada mudança.

17. Estrutura do frontend

  • Layout: app/templates/base.html — sidebar fixa com navegação (ícones SVG inline), área de conteúdo à direita, tema claro/escuro via localStorage + atributo data-tema no <html>.
  • Templates: um arquivo Jinja2 por página, todos estendendo base.html via {% block conteudo %}. Sem componentização (sem includes/macros reutilizáveis além do layout base).
  • CSS: um único app/static/estilo.css, variáveis CSS para cores (tema claro/escuro), sem framework (sem Tailwind/Bootstrap).
  • Identidade visual: logo GTI (logo-gti-256x256.png), paleta escura por padrão com acento laranja — a UI já é rotulada "GTI" na sidebar ("Painel de Clientes").
  • Auth nos templates: request.state.usuario (setado por middleware) controla o que aparece na sidebar (ex.: "Funcionários" só para admin).
CAN_ADD_FISCAL_MENU=sim, tecnicamente simples — é só adicionar um <a> na
                     sidebar de base.html com a mesma checagem de
                     request.state.usuario.eh_admin, IF o Fiscal virar rota
                     deste mesmo processo FastAPI (Opção A da seção 20)
CAN_REUSE_LAYOUT=sim, mas exigiria portar o CSS de tema claro/escuro e a
                  sidebar para dentro do stack do GTI Fiscal (que hoje
                  também é Jinja2 — compatível em princípio) SE a integração
                  for Opção C (backend separado, UI incorporada)
CAN_REUSE_AUTH=parcialmente — ver §4: o modelo é simples de replicar/portar,
                mas não existe hoje um serviço de auth desacoplado para
                "importar" sem duplicar código

18. APIs e integrações

Inventário de rotas por módulo (ver §3). Não há uma "API" no sentido REST externo — todas as rotas retornam HTML (exceto /gerador-chave/gerar, que retorna JSON, chamado via fetch() do próprio frontend do painel). Não há autenticação por token/Bearer em lugar nenhum — só cookie de sessão.

Integração externa existente: app/n8n_client.py — chama a API REST de uma instância n8n (N8N_API_URL/N8N_API_KEY via env) para ler (não escrever) uma Data Table de envios automáticos de chave, exibida em /log. É a única integração "tipo API" que este sistema tem.

Não existe conceito de client/server/installation/agent no sentido do GTI Fiscal — o mais próximo é CLIENTE (empresa/licenciada), que é uma entidade de negócio/cobrança, não de infraestrutura técnica (não tem hostname, IP, SO, ou qualquer atributo de "servidor monitorado").

19. Modelo de cliente/servidor

CLIENTE   = empresa licenciada (CODIGO, RAZAO, FANTASIA, CNPJ, ATIVO,
            DIA_VENCIMENTO) — chave primária ID (int), CODIGO é o
            identificador de negócio usado no gerador de chave
CLIENTE_CONTATO = contatos humanos daquele cliente (nome/telefone/e-mail),
                  FK CLIENTE_ID

Não existem EMPRESA (é o mesmo que CLIENTE), SERVIDOR, INSTALAÇÃO, nem AGENT neste sistema.

Comparando com o GTI Fiscal (tenant/client, installation_id, agent_id, hostname, contexto de empresa): a única sobreposição conceitual real é cliente/empresa — CLIENTE.CNPJ poderia, em teoria, ser a chave de correlação entre "quem é o cliente" nos dois sistemas (mesmo CNPJ = mesma empresa), mas isso exigiria um design explícito de correlação (ex.: o Fiscal guardar o CLIENTE_ID ou CODIGO deste sistema como referência externa) — não existe isso hoje, seria trabalho novo de integração, não algo pronto para reaproveitar.

20. Possível integração do GTI Fiscal

Opção A — Mesmo processo/aplicação (GTI Fiscal como módulo interno do FastAPI existente): exigiria portar o Fiscal de FastAPI+PostgreSQL+Alembic próprios para dentro deste monolito Python+MySQL+SQL-cru — descartaria a stack já pronta do Fiscal (Postgres, migrations, auth de Agent via Bearer). Alto retrabalho, alto acoplamento, alto risco de regressão no painel de clientes hoje estável. Não recomendado.

Opção B — Serviço separado na mesma máquina (dois processos FastAPI independentes, dois bancos, roteados por porta ou subdomínio): mais fiel à arquitetura já pronta do Fiscal (Postgres, migrations Alembic, auth de Agent por Bearer token — nada disso precisa mudar). Exige: instalar PostgreSQL nesta VM, instalar um reverse proxy (inexistente hoje — §11), e — dado o disco em 82% de uso (§22) — provavelmente aumentar o disco da VM antes.

Opção C — Backend Fiscal separado + UI incorporada no Gerenciador: o Fiscal mantém seu backend/API de Agents isolado (bom para segurança — Agents externos batendo Bearer token não precisam tocar no mesmo processo do painel), mas a tela humana ("Resumo", "Agents/Servidores", "Blockers", "Jobs") é servida a partir do processo do painel, chamando o backend do Fiscal internamente (rede Docker) e renderizando com o mesmo layout Jinja2 já existente (§17). Combina isolamento de falha (Fiscal fora do ar não derruba o painel de clientes) com a experiência visual unificada que o usuário quer.

21. Recomendação

Opção B para o backend (times/dados isolados) combinada com a fachada de UI da Opção C, na prática: dois processos FastAPI + dois bancos (MySQL do painel, PostgreSQL do Fiscal) na mesma VM/rede Docker, sem misturar código Python nem schemas — e, quando fizer sentido visualmente, o painel ganha um item de menu "Fiscal" que ou (a) faz proxy/iframe para o serviço do Fiscal rodando noutra porta/subpath via reverse proxy, ou (b) mais elegante: o próprio Hub FastAPI do Fiscal serve suas páginas Jinja2 reaproveitando o CSS do painel (arquivo estático compartilhado ou copiado), ficando visualmente idêntico sem juntar os dois processos.

Critérios que pesaram:

  • Baixa manutenção / isolamento de falhas: times/schemas separados evitam que um bug do Fiscal (ex.: processamento de 500k produtos) afete o painel de clientes em produção, que hoje é estável.
  • Segurança: Agents Windows externos autenticam via Bearer contra o backend do Fiscal — não faz sentido esse tráfego externo passar pelo mesmo processo/sessão-cookie do painel administrativo interno.
  • Reaproveitamento real disponível: só a máquina (VM), a rede Docker e, superficialmente, o CSS/layout são reaproveitáveis hoje — banco, auth e modelo de dados não são, então forçar Opção A jogaria fora o trabalho já feito na stack do Fiscal sem ganho real.
  • Pré-requisito bloqueante: nada disso cabe sem antes resolver o disco (82% cheio) e decidir sobre reverse proxy — ver §22-23.

22. Capacidade da máquina

CAN_HOST_GTI_FISCAL_ON_SAME_MACHINE=false, no estado atual da VM (sem
                                     aumentar disco/RAM primeiro)

Considerando a carga alvo do briefing (Hub FastAPI + PostgreSQL + 100 Agents iniciais + 500k produtos + event logs + blockers + jobs) contra o que esta VM tem hoje:

  • CPU: 2 vCPU, carga atual quase zero — folga real, não é o gargalo.
  • RAM: 1.9GiB total, dos quais ~940MiB já em uso por SO+containers atuais. PostgreSQL sozinho, com um volume de 500k produtos + índices, tende a querer bem mais que a folga restante para não sofrer com cache pequeno (shared_buffers/effective_cache_size ficariam artificialmente baixos). 100 Agents com heartbeat periódico é tráfego leve por si, mas soma-se ao processo do Hub. Risco real de troca/swap sob carga.
  • Disco: 8.1GiB total, 1.4GiB livres (82% usado). Uma imagem PostgreSQL + o próprio volume de dados (500k produtos, mesmo que compactos, mais índices, mais WAL) muito provavelmente não cabe nesse espaço livre atual, ainda mais somado à imagem Docker do Hub FastAPI. Este é o gargalo mais crítico e imediato, antes mesmo de RAM.
NEEDS_PROXMOX_RESOURCE_INFO=true

O que falta, especificamente (só visível a partir do host Proxmox, não de dentro da VM):

  1. Quanto de CPU/RAM/disco físico está livre no host Proxmox para aumentar esta VM (ou criar uma segunda) — de dentro da VM não dá pra saber se o host tem folga para um pvesm resize/qm resize do disco de 8→20GB+, por exemplo.
  2. Política de backup/snapshot do Proxmox para esta VM (indiretamente relevante para §13 — se o Proxmox já faz snapshot no nível de hypervisor, isso muda a análise de "backup inexistente").
  3. Se há outras VMs/LXCs no mesmo host Proxmox concorrendo pelos mesmos recursos físicos (I/O de disco compartilhado principalmente).

23. Conflitos potenciais

PORT_CONFLICTS=nenhum imediato — Fiscal pode usar outra porta (8001, por
                exemplo); porta 80 já está tomada pelo painel
DATABASE_CONFLICTS=nenhum — motores diferentes (MySQL vs PostgreSQL) não
                    colidem, mas cada um consome memória/disco próprios
NETWORK_CONFLICTS=nenhum — network Docker externa (bancoparaprod_default)
                   pode ser reaproveitada ou uma nova criada, sem colisão
DOMAIN_CONFLICTS=n/a — não há domínio configurado hoje para nenhum dos dois
DEPENDENCY_CONFLICTS=nenhum a nível de processo (processos Python isolados
                      em containers separados não compartilham
                      dependências) — mas concorrem por RAM/disco da
                      mesma VM (ver §22)
AUTH_CONFLICTS=nenhum tecnicamente (mecanismos diferentes, cookie de sessão
               vs Bearer de Agent, não colidem) — mas também não se somam:
               um usuário logado no painel não fica "logado" automaticamente
               no Fiscal sem um esforço explícito de integração de auth

24. O que pode ser reutilizado

REUSABLE_COMPONENTS=
- Máquina/VM (10.0.10.117) — há folga de CPU, mas não de disco (ver §22)
- Rede Docker (bancoparaprod_default ou nova network dedicada)
- CSS/identidade visual (app/static/estilo.css + logo GTI) — copiável para
  o Fiscal se ele também for Jinja2
- Padrão de deploy manual via SFTP/docker compose (mesmo processo humano já
  usado, sem precisar inventar um novo fluxo de deploy)
- Conceito de correlação por CNPJ entre CLIENTE (aqui) e tenant/client (lá),
  SE for desenhado explicitamente — não existe pronto

25. O que deve ficar separado

SHOULD_REMAIN_ISOLATED=
- Banco de dados (MySQL do painel vs PostgreSQL do Fiscal — motores
  diferentes, sem cenário real de compartilhar)
- Autenticação de Agent (Bearer token) — não deve passar pelo mesmo
  middleware de sessão-cookie do painel administrativo
- Migrations (Alembic do Fiscal vs ALTER TABLE manual do painel — misturar
  processos de schema é fonte garantida de erro operacional)
- Processo do Hub FastAPI do Fiscal — mantê-lo como processo próprio
  (Opção B/C) preserva o isolamento de falha que o painel de clientes tem
  hoje (37 dias de uptime sem intervenção)

26. Segurança

Achados relevantes (todos já visíveis publicamente ou já documentados em auditoria anterior deste mesmo projeto — nada novo exposto aqui):

  • MySQL exposto publicamente na porta 3306 (0.0.0.0), não restrito a rede interna/loopback — ver §12.
  • Sem TLS/HTTPS em produção — tráfego HTTP puro na porta 80, incluindo login (pendência já registrada conscientemente em memória de projeto anterior — não uma novidade desta auditoria).
  • .env de produção existe e não foi lido (só variáveis específicas necessárias, via grep pontual, nunca o arquivo inteiro) — nenhuma senha, token ou secret aparece neste relatório.
  • Auth de funcionário usa scrypt (adequado); auth "raiz" via ADMIN_USER/ADMIN_PASSWORD em variável de ambiente, comparação direta (sem hashing) — aceitável para uma única conta de emergência, mas vale nota para quem for desenhar auth compartilhada com o Fiscal.
  • Nenhuma correção foi feita — só relatado, como pedido.

27. Artefato

Este arquivo: docs/GTI_GERENCIADOR_DISCOVERY_V1.md, dentro do repositório painel-clientes-web. Não commitado — a missão pediu regime read-only; o arquivo fica no working tree local para o usuário revisar e decidir se versiona.

28. Confirmação — nada foi alterado

Nenhum código, banco, container, proxy, firewall ou configuração foi modificado nesta missão. Todos os comandos no servidor foram de leitura (hostnamectl, uptime, free, df, docker ps, docker stats, ss, systemctl list-units, find, ls, cat de arquivos não sensíveis, crontab -l). A única tentativa que exigiria credencial adicional (contar tabelas via mysql -uroot) falhou por falta de acesso e não foi insistida.


Relatório final (resumo estruturado)

STATUS=discovery concluído, read-only, nenhuma alteração feita

PROJECT_NAME=painel-clientes-web ("GTI Gerenciador" é o nome comercial —
             confirmado com o usuário, não é um projeto separado)
REPOSITORY_PATH=D:\PROJETOS\BLOQUEIO SGV\painel-clientes-web
BRANCH=master
HEAD=ce718f7ac22a583fd8c5d3a23415a19dcfc31291
WORKTREE=limpo

BACKEND=FastAPI (Python 3.12) + PyMySQL, sem ORM
FRONTEND=Jinja2 server-rendered, sem build chain
DATABASE=MySQL 5.7 (gtisoftw_clientes_dev), sem migrations formais
AUTH_MODEL=cookie de sessão HMAC-SHA256 próprio (12h) + CSRF double-submit;
           login de funcionário (FUNCIONARIO_PAINEL, scrypt) + login raiz
           via env

HOSTNAME=srvbd
OS=Ubuntu 24.04.4 LTS
VIRTUALIZATION=KVM (VM dentro de Proxmox, confirmado por doc local do projeto)

CPU=2 vCPU (ocioso)
RAM=1.9GiB total (~940MiB em uso)
DISK_FREE=1.4GiB de 8.1GiB (82% usado — CRÍTICO)

DOCKER=29.7.2 / Compose v5.4.0
CONTAINERS=painel-clientes-web-painel-1 (up 4 semanas), gtisoftw-mysql (up 5 semanas)

CURRENT_URL=http://10.0.10.117/ (IP direto, sem domínio)
DOMAIN=nenhum
HTTPS=não
REVERSE_PROXY=nenhum instalado

PUBLIC_PORTS=22 (SSH), 80 (app), 3306 (MySQL — exposto publicamente)

BACKUP=inexistente como rotina automatizada; só restore manual documentado
MONITORING=inexistente (sem Prometheus/Grafana/Loki)

CLIENT_MODEL=CLIENTE (empresa licenciada, negócio/cobrança) — sem conceito
             de servidor/instalação/agente técnico
SERVER_MODEL=não existe
AGENT_MODEL=não existe

CAN_REUSE_AUTH=parcial (modelo simples, mas não desacoplado/exposto para
               outro serviço consumir sem duplicar código)
CAN_REUSE_CLIENT_MODEL=parcial (CNPJ como chave de correlação possível,
                        exigiria design novo de integração)
CAN_REUSE_SERVER_MODEL=n/a (não existe)
CAN_REUSE_LAYOUT=sim (CSS/Jinja2 portável se o Fiscal também for Jinja2)

CAN_HOST_GTI_FISCAL_ON_SAME_MACHINE=false no estado atual (disco em 82%,
                                     RAM apertada) — viável DEPOIS de
                                     aumentar disco (e provavelmente RAM)
                                     da VM
NEEDS_PROXMOX_RESOURCE_INFO=true (capacidade física do host Proxmox para
                            redimensionar esta VM; política de
                            backup/snapshot no nível de hypervisor; outras
                            VMs concorrendo pelo mesmo host)

RECOMMENDED_INTEGRATION_MODE=B (backend separado, mesma VM/rede Docker) +
                              fachada visual estilo C (menu "Fiscal" no
                              painel, reaproveitando layout/CSS)
RECOMMENDATION_REASON=isolamento de falha e de auth (Agents Bearer não devem
                       passar pelo processo/sessão do painel), motores de
                       banco incompatíveis (MySQL vs Postgres) inviabilizam
                       Opção A, e só máquina+rede+CSS são reaproveitáveis de
                       verdade hoje

PORT_CONFLICTS=nenhum
DATABASE_CONFLICTS=nenhum (motores diferentes)
AUTH_CONFLICTS=nenhum tecnicamente, mas também nenhuma integração pronta

REUSABLE_COMPONENTS=VM/rede Docker, CSS/identidade visual, processo de
                     deploy manual já estabelecido
SHOULD_REMAIN_ISOLATED=banco de dados, auth de Agent, migrations, processo
                        do Hub FastAPI do Fiscal

GTI_FISCAL_INTEGRATION_PREREQUISITES=
1. Aumentar disco da VM (crítico — 82% usado hoje, não cabe Postgres+dados)
2. Confirmar/aumentar RAM disponível junto ao Proxmox
3. Instalar reverse proxy (Caddy é o mais leve) — hoje não existe nenhum
4. Decidir domínio/subdomínio (nenhum configurado hoje)
5. Desenhar (não implementar ainda) a correlação CLIENTE.CNPJ ↔ tenant do
   Fiscal, se a intenção for cruzar dados de cliente entre os dois sistemas
6. Resolver a exposição pública do MySQL (3306) antes de repetir o padrão
   com um segundo banco

RISKS=
- Disco cheio (82%) é bloqueante técnico imediato, não é só "atenção"
- MySQL público na 3306 é uma exposição já existente, independente desta
  missão, mas relevante para não repetir o padrão
- Sem backup automatizado hoje — somar um segundo banco sem resolver isso
  primeiro dobra a superfície de risco de perda de dados
- Sem TLS — Agents do Fiscal batendo HTTPS externo precisarão de reverse
  proxy com certificado, que hoje não existe nesta VM

NEXT_STEP=levantar capacidade real do host Proxmox (item bloqueante
          NEEDS_PROXMOX_RESOURCE_INFO) e decidir se a VM atual cresce ou se
          uma segunda VM é criada especificamente para o Fiscal — só depois
          disso faz sentido detalhar a Opção B/C escolhida em um plano de
          implementação

VERDICT=Integração é viável e recomendável na forma B+C (backend separado,
        UI visualmente unificada), mas NÃO deve começar antes de resolver
        disco/RAM da VM e decidir sobre reverse proxy — do contrário, o
        Fiscal nasce competindo por recursos com um painel de clientes que
        hoje está estável e em produção real.