1. Frase de abertura ainda dizia 'login HTTP Basic em toda rota',
contradizendo a secao 'Login e permissoes' mais abaixo no mesmo
arquivo (ja correta, fala de login por sessao). Atualizada para
'login por sessao em toda rota'.
2. A lista de variaveis de ambiente para rodar localmente (secao
'Rodando localmente') nao mencionava SESSION_SECRET, mesmo a secao
'Login e permissoes' ja instruindo a defini-la. Adicionada a lista,
com nota de que e opcional em dev (chave aleatoria em memoria a cada
start, sessoes caem a cada restart) mas deve ser fixa em producao.
3. A mencao ao ALTER TABLE FUNCIONARIO_PAINEL ADD COLUMN PERFIL estava
em tom de fato passado. Ajustada para o mesmo padrao de aviso ja
usado para LOG_PAINEL/FUNCIONARIO_PAINEL nesse README ('precisa ser
rodado manualmente em producao tambem'), deixando explicito que sem
rodar isso todo login falha com 500 (a query de autenticacao
seleciona uma coluna PERFIL que nao existe na tabela antiga).
Sem teste automatizado - mudanca só de documentacao.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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).
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)
- Instale as dependências de desenvolvimento:
pip install -r requirements-dev.txt - 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 depython-dotenvestar nas dependências, nada no código chamaload_dotenv()hoje — um arquivo.envsozinho NÃO é lido automaticamente fora do Docker. Exporte as variáveis manualmente (ex.:set -a; source .env; set +ano bash, ou$env:...no PowerShell) ou defina-as por outro meio. - Rode a aplicação:
uvicorn app.main:app --reload - Rodando direto na máquina (fora de container), use
DB_HOST=127.0.0.1(porta3307, a mesma do container de devlicenciamento-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
- Copie o arquivo de exemplo de variáveis de ambiente e edite
ADMIN_PASSWORD(e demais valores, se necessário):cp .env.example .env - Suba o container:
O
docker compose up -d --builddocker-compose.ymljá usaenv_file: .enve mapeiahost.docker.internalpara o host (viaextra_hosts), então o valor padrãoDB_HOST=host.docker.internaldo.env.examplefunciona também em hosts Docker Linux, não só no Docker Desktop (Windows/Mac). - A aplicação fica disponível em
http://localhost:8010.