# painel-clientes-web Painel administrativo (FastAPI + Jinja2) para gestão de `CLIENTE`, `CLIENTE_CONTATO` e geração manual de chave de licenciamento no MySQL do sistema legado. Proteções: login por sessão em toda rota (cookie assinado — ver seção "Login e permissões" abaixo), CSRF via cookie de duplo envio (`app/csrf.py` — todo formulário que muda estado embute o mesmo token do cookie `csrf_token`, definido pelo middleware em `app/main.py`) e a lista de clientes é paginada (100 por página), com busca e ordenação por código/razão/fantasia/CNPJ. `CODIGO` de cliente não aceita duplicado (bloqueio duro, sem bypass) e o formulário de novo cliente já sugere o próximo código sequencial (editável). Só `CODIGO`, `CNPJ`, `RAZAO` e `FANTASIA` são obrigatórios — os campos de contrato (implantação, mensalidade, juros, datas) podem ficar em branco e recebem default (0,00 / data de hoje). `CLIENTE` tem uma coluna `ATIVO` (criada no banco de dev — **precisa ser criada manualmente em produção também**, `ALTER TABLE` completo no início de `app/db/clientes.py`) que controla o filtro de status da listagem (Ativos/Inativos/Todos, padrão Ativos). Sem essa coluna, a listagem quebra com erro 500. O `DEFAULT 1` do `ALTER TABLE` é intencional: todo cliente cadastrado antes da migração nasce como Ativo. A rota `/log` mostra o histórico de ações do painel (tabela `LOG_PAINEL`, criada no banco de dev — **precisa ser criada manualmente em produção também**, DDL no início de `app/db/log.py`) e, se `N8N_API_URL`/`N8N_API_KEY` estiverem configuradas (`.env.example`), os últimos envios automáticos de chave via WhatsApp (lidos da Data Table `pagamentos_processados` do n8n, somente leitura). ## Login e permissões O painel usa login por sessão (cookie assinado, `app/session.py`) — não é mais HTTP Basic Auth. Defina `SESSION_SECRET` no `.env` (`.env.example` tem o exemplo); sem isso, uma chave é gerada em memória a cada start do processo, o que derruba todas as sessões ativas a cada restart (funciona, só incomoda). Cada funcionário (`FUNCIONARIO_PAINEL`, criada no banco de dev — **precisa ser criada manualmente em produção também**, DDL no início de `app/db/funcionarios.py`) tem um perfil: **admin** (gerencia outros funcionários em `/funcionarios`: cadastra, edita, redefine senha, ativa/desativa, promove/rebaixa perfil) ou **padrão** (sem acesso a `/funcionarios`). Se `FUNCIONARIO_PAINEL` já existir de uma instalação anterior sem a coluna `PERFIL`, rode o `ALTER TABLE FUNCIONARIO_PAINEL ADD COLUMN PERFIL ...` (DDL completo em `app/db/funcionarios.py`) — **precisa ser rodado manualmente em produção também**; sem essa coluna, todo login falha com erro 500 (a query de autenticação seleciona `PERFIL`, que não existe na tabela antiga). O `DEFAULT 'ADMIN'` desse `ALTER TABLE` é intencional: todo funcionário cadastrado antes da migração vira admin automaticamente. O sistema nunca deixa ficar sem nenhum admin ativo — rebaixar ou desativar o último admin é bloqueado. Qualquer usuário logado troca a própria senha em **Minha conta** (`/minha-conta`), pedindo a senha atual antes de trocar. `ADMIN_USER`/`ADMIN_PASSWORD` (`.env.example`) continuam funcionando como conta de bootstrap, sempre com perfil admin — é com ela que você entra a primeira vez para cadastrar o primeiro funcionário, e serve de plano B se a tabela ficar vazia. Login checa primeiro o admin do `.env`, depois `FUNCIONARIO_PAINEL` (só contas com `ATIVO=1`). ## Rodando localmente (sem Docker) 1. Instale as dependências de desenvolvimento: ``` pip install -r requirements-dev.txt ``` 2. Defina as variáveis de ambiente necessárias (`ADMIN_USER`, `ADMIN_PASSWORD`, `DB_HOST`, `DB_PORT`, `DB_NAME`, `DB_USER`, `DB_PASSWORD`, `SESSION_SECRET` — veja `.env.example`) diretamente no shell antes de subir a aplicação. `SESSION_SECRET` é opcional em dev — sem ela, uma chave aleatória é gerada em memória a cada start (sessões caem a cada restart); defina um valor fixo em produção. **Importante:** apesar de `python-dotenv` estar nas dependências, nada no código chama `load_dotenv()` hoje — um arquivo `.env` sozinho NÃO é lido automaticamente fora do Docker. Exporte as variáveis manualmente (ex.: `set -a; source .env; set +a` no bash, ou `$env:...` no PowerShell) ou defina-as por outro meio. 3. Rode a aplicação: ``` uvicorn app.main:app --reload ``` 4. Rodando direto na máquina (fora de container), use `DB_HOST=127.0.0.1` (porta `3307`, a mesma do container de dev `licenciamento-mysql-dev`). ## Rodando os testes ``` pytest -v ``` Os testes usam defaults definidos em `tests/conftest.py` (equivalentes ao `.env.example`) e incluem testes de integração que precisam do container MySQL de dev (`licenciamento-mysql-dev`, `127.0.0.1:3307`) rodando. ## Rodando via Docker 1. Copie o arquivo de exemplo de variáveis de ambiente e edite `ADMIN_PASSWORD` (e demais valores, se necessário): ``` cp .env.example .env ``` 2. Suba o container: ``` docker compose up -d --build ``` O `docker-compose.yml` já usa `env_file: .env` e mapeia `host.docker.internal` para o host (via `extra_hosts`), então o valor padrão `DB_HOST=host.docker.internal` do `.env.example` funciona também em hosts Docker Linux, não só no Docker Desktop (Windows/Mac). 3. A aplicação fica disponível em `http://localhost:8010`.