financas-app — correção e extensão
App de controle financeiro (Node 20 + Express, EJS SSR + JS vanilla, MongoDB/Mongoose,
Zod, JWT). Três módulos independentes (financas/investimentos/veiculos) ligados por flags
em config/env.js. Arquitetura de camadas rígida: rota → controller → service → model.
Seguir AGENTS.md (Zod obrigatório, erros com AppError, sem JS inline — todo script em
/js/, CSP proíbe unsafe-inline).
Arquitetura de deploy (PORTA ÚNICA 4460)
:4460 (nginx, única porta exposta)
/demo/* ──► app-demo (autologin, mongo-demo isolado, JWT_SECRET_DEMO)
/* ──► app (principal; mongo volume fa_mongo_data)
app-demo monta rotas TAMBÉM sob /demo (páginas /demo/* e APIs /demo/api/*); o
frontend prefixa chamadas de API com data-api-prefix do <html> (lido em common.js).
demoAutologin emite JWT de verdade a cada request (só com DEMO_AUTOLOGIN=true).
PITFALL RECORRENTE (já custou horas)
A página /demo/app carrega o CSS de /css/main.css (app principal), NÃO de
/demo/css/main.css (app-demo). Por isso, para validar qualquer correção de CSS, é
obrigatório rebuildar AMBOS os containers (app e app-demo) e subir com a mesma
ASSET_VERSION. Se mexer só no app-demo, o CSS novo não aparece no browser.
- Bump
ASSET_VERSION via env no shell (não editar .env): ASSET_VERSION=N docker compose -p fa build --no-cache app app-demo && docker compose -p fa up -d app app-demo.
- Se o CSS servido não mudar, o Docker pode ter cacheado a camada de COPY: usar
--no-cache.
- Checar o CSS servido:
curl -s http://127.0.0.1:4460/css/main.css | grep ... (app principal)
e /demo/css/main.css (app-demo).
Validação no browser (headless)
- O browser da ferramenta alcança o docker no host 192.168.80.1:4460 (NÃO 127.0.0.1:4460).
browser_navigate + browser_snapshot para inspecionar; browser_vision para checagem
visual (tema claro/escuro). O usuário aprova UI só vendo screenshot real.
browser_click nem sempre dispara handlers JS (overlay/datepicker interceptando); se o
modal não abrir, disparar via browser_console: document.querySelector('button[data-acao=...]').click().
- Evitar cache do browser de validação: ao trocar ASSET_VERSION, navegar primeiro na URL
exata do CSS (
/css/main.css?v=N sem /demo) para popular aquele cache, depois na página.
Verificação ad-hoc (exigida pelo system reminder)
Após editar, criar script temporário em /tmp/hermes-verify-*.js que inspeciona artifacts
servidos (CSS/JS/HTML via http) + fontes no repo, e resume explicitamente como verificação
ad-hoc (não suíte jest). Rodar com node e remover o script. Não confiar só em "jest verde"
para trabalho de UI. Cuidado: curl anônimo de rota autenticada cai em /login (falso
negativo) — validar HTML autenticado pelo browser ou checar o arquivo dentro do container
(docker compose exec app-demo grep ...).
Padrão Editar/Excluir (tudo que é registrável)
public/js/modal.js exporta abrirModal(titulo, campos, onSalvar) (sem JS inline, CSP ok).
- Ativos, Corretoras e Lançamentos já usam: botão "Editar" na linha/card abre o modal
pré-preenchido; PATCH no backend já existe (schema Zod + controller + service).
- Ao adicionar edição a outro recurso, seguir o mesmo padrão (não criar novo modal).
Segurança — NUNCA expor token da demo
O demoAutologin emite um JWT real do usuário demo. NÃO extrair, logar nem reutilizar esse
cookie/token. Se um comando de verificação capturá-lo por engano, descartar com rm
imediatamente e não reusá-lo. Validar logout/reset via browser autenticado ou checando
artifacts servidos, nunca copiando o token.
BUG root-cause recorrente: tokenValidAfter invalida JWT
user.model.js tinha tokenValidAfter: { type: Date, default: () => new Date() }.
Todo usuário novo NASCE com sessão inválida: o iat (segundos TRUNCADOS) do JWT emitido
logo após fica MENOR que validAfterSec → resolveUser retorna null → "Token invalido
ou expirado" em logout (/api/auth/logout) e reset (/api/reset-demo).
- Sintoma reportado pelo usuário: "não consigo fazer logout, token inválido".
- Fix aplicado:
default: null (resolveUser pula a checagem quando null). As ações de
invalidação reais (troca/reset de senha, desativação em userService) continuam setando
new Date() explicitamente — isso é correto e deve continuar.
- Middleware
authOptional (não barra token inválido, só popula req.user se válido)
aplicado em /api/auth/logout e /api/reset-demo para o logout/reset SEMPRE concluírem
(limpam cookie / resetam banco) mesmo com token vencido.
- Ao mexer em auth, lembre: logout é a operação que INVALIDA a sessão — não pode exigir
token válido no middleware
auth.
UI: remover > reestilizar (preferência do usuário)
- O usuário rejeitou a paginação "Primeiro/Anterior/1/Próximo/Último" MESMO após
reestilização cuidadosa (bordas, hover, página ativa azul). Aceitou a REMOÇÃO:
paging: false no DataTable (busca client-side + rolagem natural da página continuam).
- Lição: quando o usuário chamar um elemento de UI de "horrível", REMOVA-o (ou simplifique
radicalmente), não apenas estilize. Valide por screenshot antes de commitar.
- Atenção após remover paginação: a tabela fica larga em mobile → precisa de
.table-responsive (overflow-x:auto). Issue #21 do repo cobre isso.
Gateway Telegram (Planck bot) — já existe, use
- Não é necessário configurar: o Hermes já tem
hermes-gateway.service (systemd, ativo)
com o Telegram pareado no canal "Pedro Rocha" (DM, id 8869378956) — esse é o "Planck bot"
(hostname da máquina = planck.local).
channel_directory.json em ~/.hermes lista o canal. hermes gateway status mostra o
service ativo.
- Para continuar a conversa por lá, o USUÁRIO abre o DM do bot e manda msg; o agente CLI não
migra o chat em andamento sozinho. Pode haver instabilidade de rede ao
api.telegram.org
no ambiente — mas o canal existe.
Versionamento / Issues (quando o usuário pedir "levantamento")
- Quer SemVer + CHANGELOG + tag de release;
app.locals.assetVersion deveria vir de
package.json#version.
- Criar issues em lote com
gh issue create usando os LABELS VÁLIDOS do repo
(ver references/github-issues.md): bug, documentation, enhancement, modulo:financas,
modulo:investimentos, modulo:moto, infra, seguranca, testes, ux,
good first issue, help wanted. NÃO usar labels inventados (ex.: melhoria não existe).
- Ao pedir levantamento, o usuário quer que eu CRIE as issues E COMECE a implementar uma —
não apenas planeje.
Testes
cd app && npx jest --forceExit (Mongo em memória). Alvo: 230+ verde.
- REGRA DO AGENTS.md: não rodar linters/testes em trabalho criativo de UI até o usuário
gostar ou antes de commitar — MAS rodar jest antes do commit para garantir verde.
Referências
references/demo-architecture.md — detalhes de rotas /demo, autologin, reset-demo.
references/ui-fix-workflow.md — passo a passo de correção de UI + validação.
references/github-issues.md — labels válidos e script de criar issues em lote.
1---2name: financas-app3description: Corrigir, estender e validar o app de finanças pessoais (Node/Express + EJS + MongoDB + Docker). Cobre a arquitetura de porta única 4460 com demo via /demo, o fluxo de correção de UI (rebuild de AMBOS os containers + ASSET_VERSION), validação por browser headless no IP docker, verificação ad-hoc e o padrão Editar/Excluir via modal. Use quando o usuário pedir ajustes de UI, correção de bugs, edição de registros, ou versionamento/issues no repo pedroiff0/financas-app.4---56# financas-app — correção e extensão78App de controle financeiro (Node 20 + Express, EJS SSR + JS vanilla, MongoDB/Mongoose,9Zod, JWT). Três módulos independentes (financas/investimentos/veiculos) ligados por flags10em `config/env.js`. Arquitetura de camadas rígida: rota → controller → service → model.11Seguir `AGENTS.md` (Zod obrigatório, erros com `AppError`, sem JS inline — todo script em12`/js/`, CSP proíbe `unsafe-inline`).1314## Arquitetura de deploy (PORTA ÚNICA 4460)1516```17:4460 (nginx, única porta exposta)18 /demo/* ──► app-demo (autologin, mongo-demo isolado, JWT_SECRET_DEMO)19 /* ──► app (principal; mongo volume fa_mongo_data)20```21- `app-demo` monta rotas TAMBÉM sob `/demo` (páginas `/demo/*` e APIs `/demo/api/*`); o22 frontend prefixa chamadas de API com `data-api-prefix` do `<html>` (lido em `common.js`).23- `demoAutologin` emite JWT de verdade a cada request (só com `DEMO_AUTOLOGIN=true`).2425### PITFALL RECORRENTE (já custou horas)26A página `/demo/app` carrega o CSS de **`/css/main.css` (app principal)**, NÃO de27`/demo/css/main.css` (app-demo). Por isso, para validar qualquer correção de CSS, é28obrigatório **rebuildar AMBOS os containers** (`app` e `app-demo`) e subir com a mesma29`ASSET_VERSION`. Se mexer só no app-demo, o CSS novo não aparece no browser.30- Bump `ASSET_VERSION` via env no shell (não editar `.env`): `ASSET_VERSION=N docker compose -p fa build --no-cache app app-demo && docker compose -p fa up -d app app-demo`.31- Se o CSS servido não mudar, o Docker pode ter cacheado a camada de COPY: usar `--no-cache`.32- Checar o CSS servido: `curl -s http://127.0.0.1:4460/css/main.css | grep ...` (app principal)33 e `/demo/css/main.css` (app-demo).3435## Validação no browser (headless)36- O browser da ferramenta alcança o docker no host **192.168.80.1:4460** (NÃO 127.0.0.1:4460).37- `browser_navigate` + `browser_snapshot` para inspecionar; `browser_vision` para checagem38 visual (tema claro/escuro). O usuário aprova UI só vendo screenshot real.39- `browser_click` nem sempre dispara handlers JS (overlay/datepicker interceptando); se o40 modal não abrir, disparar via `browser_console`: `document.querySelector('button[data-acao=...]').click()`.41- Evitar cache do browser de validação: ao trocar ASSET_VERSION, navegar primeiro na URL42 exata do CSS (`/css/main.css?v=N` sem /demo) para popular aquele cache, depois na página.4344## Verificação ad-hoc (exigida pelo system reminder)45Após editar, criar script temporário em `/tmp/hermes-verify-*.js` que inspeciona artifacts46servidos (CSS/JS/HTML via http) + fontes no repo, e resume explicitamente como verificação47ad-hoc (não suíte jest). Rodar com `node` e remover o script. Não confiar só em "jest verde"48para trabalho de UI. Cuidado: `curl` anônimo de rota autenticada cai em `/login` (falso49negativo) — validar HTML autenticado pelo browser ou checar o arquivo dentro do container50(`docker compose exec app-demo grep ...`).5152## Padrão Editar/Excluir (tudo que é registrável)53- `public/js/modal.js` exporta `abrirModal(titulo, campos, onSalvar)` (sem JS inline, CSP ok).54- Ativos, Corretoras e Lançamentos já usam: botão "Editar" na linha/card abre o modal55 pré-preenchido; PATCH no backend já existe (schema Zod + controller + service).56- Ao adicionar edição a outro recurso, seguir o mesmo padrão (não criar novo modal).5758## Segurança — NUNCA expor token da demo59O `demoAutologin` emite um JWT real do usuário demo. NÃO extrair, logar nem reutilizar esse60cookie/token. Se um comando de verificação capturá-lo por engano, descartar com `rm`61imediatamente e não reusá-lo. Validar logout/reset via browser autenticado ou checando62artifacts servidos, nunca copiando o token.6364## BUG root-cause recorrente: tokenValidAfter invalida JWT65- `user.model.js` tinha `tokenValidAfter: { type: Date, default: () => new Date() }`.66 Todo usuário novo NASCE com sessão inválida: o `iat` (segundos TRUNCADOS) do JWT emitido67 logo após fica MENOR que `validAfterSec` → `resolveUser` retorna null → "Token invalido68 ou expirado" em logout (`/api/auth/logout`) e reset (`/api/reset-demo`).69- Sintoma reportado pelo usuário: "não consigo fazer logout, token inválido".70- Fix aplicado: `default: null` (resolveUser pula a checagem quando null). As ações de71 invalidação reais (troca/reset de senha, desativação em userService) continuam setando72 `new Date()` explicitamente — isso é correto e deve continuar.73- Middleware `authOptional` (não barra token inválido, só popula `req.user` se válido)74 aplicado em `/api/auth/logout` e `/api/reset-demo` para o logout/reset SEMPRE concluírem75 (limpam cookie / resetam banco) mesmo com token vencido.76- Ao mexer em auth, lembre: logout é a operação que INVALIDA a sessão — não pode exigir77 token válido no middleware `auth`.7879## UI: remover > reestilizar (preferência do usuário)80- O usuário rejeitou a paginação "Primeiro/Anterior/1/Próximo/Último" MESMO após81 reestilização cuidadosa (bordas, hover, página ativa azul). Aceitou a REMOÇÃO:82 `paging: false` no DataTable (busca client-side + rolagem natural da página continuam).83- Lição: quando o usuário chamar um elemento de UI de "horrível", REMOVA-o (ou simplifique84 radicalmente), não apenas estilize. Valide por screenshot antes de commitar.85- Atenção após remover paginação: a tabela fica larga em mobile → precisa de86 `.table-responsive` (overflow-x:auto). Issue #21 do repo cobre isso.8788## Gateway Telegram (Planck bot) — já existe, use89- Não é necessário configurar: o Hermes já tem `hermes-gateway.service` (systemd, ativo)90 com o Telegram pareado no canal "Pedro Rocha" (DM, id 8869378956) — esse é o "Planck bot"91 (hostname da máquina = `planck.local`).92- `channel_directory.json` em `~/.hermes` lista o canal. `hermes gateway status` mostra o93 service ativo.94- Para continuar a conversa por lá, o USUÁRIO abre o DM do bot e manda msg; o agente CLI não95 migra o chat em andamento sozinho. Pode haver instabilidade de rede ao `api.telegram.org`96 no ambiente — mas o canal existe.9798## Versionamento / Issues (quando o usuário pedir "levantamento")99- Quer SemVer + CHANGELOG + tag de release; `app.locals.assetVersion` deveria vir de100 `package.json#version`.101- Criar issues em lote com `gh issue create` usando os LABELS VÁLIDOS do repo102 (ver references/github-issues.md): `bug`, `documentation`, `enhancement`, `modulo:financas`,103 `modulo:investimentos`, `modulo:moto`, `infra`, `seguranca`, `testes`, `ux`,104 `good first issue`, `help wanted`. NÃO usar labels inventados (ex.: `melhoria` não existe).105- Ao pedir levantamento, o usuário quer que eu CRIE as issues E COMECE a implementar uma —106 não apenas planeje.107108## Testes109- `cd app && npx jest --forceExit` (Mongo em memória). Alvo: 230+ verde.110- REGRA DO AGENTS.md: não rodar linters/testes em trabalho criativo de UI até o usuário111 gostar ou antes de commitar — MAS rodar jest antes do commit para garantir verde.112113## Referências114- `references/demo-architecture.md` — detalhes de rotas /demo, autologin, reset-demo.115- `references/ui-fix-workflow.md` — passo a passo de correção de UI + validação.116- `references/github-issues.md` — labels válidos e script de criar issues em lote.