sistema-de-chamados/docs/DEV.md
2025-12-10 17:10:52 -03:00

11 KiB
Raw Blame History

Guia de Desenvolvimento — 18/10/2025

Este documento consolida o estado atual do ambiente de desenvolvimento, descreve como rodar lint/test/build localmente (e no CI) e registra erros recorrentes com as respectivas soluções.

Resumo rápido

  • Bun (runtime padrão): 1.3+ já instalado no runner e VPS (bun --version). Após instalar localmente, exporte PATH="$HOME/.bun/bin:$PATH" para tornar o binário disponível. Use bun install, bun run dev:bun, bun run convex:dev:bun, bun run build:bun e bun test como fluxo principal (scripts Node continuam disponíveis como fallback).
  • Node.js: mantenha a versão 20.9+ instalada para ferramentas auxiliares (Prisma CLI, scripts legados em Node) quando não estiver usando o runtime do Bun.
  • Next.js 16: Projeto roda em next@16.0.8 com Turbopack como bundler padrão (dev e build); webpack continua disponível como fallback.
  • Lint/Test/Build: bun run lint, bun test, bun run build:bun. O test runner do Bun já roda em modo não interativo; utilize bunx vitest --watch apenas quando precisar do modo watch manualmente.
  • Banco DEV: SQLite em prisma/prisma/db.dev.sqlite. Defina DATABASE_URL="file:./prisma/db.dev.sqlite" ao chamar CLI do Prisma.
  • Desktop (Tauri): fonte em apps/desktop. Usa Radix tabs + componentes shadcn-like, integra com os endpoints /api/machines/* e suporta atualização automática via GitHub Releases.
  • CI: Workflow Quality Checks roda lint/test/build para pushes e PRs na main, além do pipeline de deploy existente.

Banco de dados (Prisma)

  1. Gere/atualize o schema local:

    bun install
    DATABASE_URL="file:./prisma/db.dev.sqlite" bunx prisma db push
    DATABASE_URL="file:./prisma/db.dev.sqlite" bun run prisma:generate
    DATABASE_URL="file:./prisma/db.dev.sqlite" bun run auth:seed
    
  2. Rode o app Next.js:

    bun run dev:bun
    

    Alternativas: bun run dev (Node) ou bun run dev:webpack se precisar do fallback oficial.

  3. Credenciais padrão (seed): admin@sistema.dev / admin123.

  4. Herdou dados antigos? Execute node scripts/remove-legacy-demo-users.mjs para limpar contas demo legadas.

Por quê inline? Evitamos declarar DATABASE_URL em prisma/.env porque o Prisma lê também o .env da raiz (produção). O override inline garante isolamento do banco DEV.

Next.js 16 (estável)

  • Mantemos o projeto em next@16.0.8, com React 19 e o App Router completo.
  • Bundlers: Turbopack permanece habilitado no next dev/bun run dev:bun e agora também no next build --turbopack. Use next build --webpack somente para reproduzir bugs ou comparar saídas.
  • Whitelist de hosts: o release estável continua sem aceitar server.allowedHosts (vide invalid-next-config), portanto bloqueamos domínios exclusivamente via middleware.ts.

Editor rich text (TipTap) — menções de ticket

  • Menções (ticketMention) agora têm prioridade maior (priority: 1000) e um Link seguro (SafeLinkExtension) foi introduzido para ignorar <a data-ticket-mention="true">. Isso evita que o Link do StarterKit capture as âncoras na hidratação, garantindo que as menções continuem como nodes (não como marks) durante a edição.
  • O mesmo helper normalizeTicketMentionHtml é aplicado ao carregar/atualizar conteúdo no editor, dentro dos fluxos de comentários e no Convex. Esse helper reescreve qualquer HTML legado (#123•Assunto) no formato de chip completo (datasets, spans, dot).
  • Resultado: o chip mantém layout e comportamento ao editar (Backspace/Delete removem o node inteiro, node view continua ativo) sem exigir reload.
  • Se precisar adicionar novos comportamentos, importe SafeLinkExtension e mantenha a ordem [TicketMentionExtension, StarterKit (link:false), SafeLinkExtension, Placeholder] para que o parser continue estável.

Comandos de qualidade

  • bun run lint: executa ESLint (flat config) sobre os arquivos do projeto.
  • bun test: roda a suíte de testes utilizando o runner nativo do Bun. Para modo watch, use bunx vitest --watch manualmente.
  • bun run build:bun: next build --turbopack usando o runtime Bun (Turbopack).
  • Scripts com Bun (padrão atual): bun run dev:bun, bun run convex:dev:bun, bun run build:bun, bun run start:bun. Eles mantêm os scripts existentes, apenas forçando o runtime do Bun via bun run --bun. O cross-env garante NODE_ENV consistente (development/production).
  • bun run build:webpack: build com o bundler oficial do Next (fallback).
  • bun run dev:webpack: fallback do Next em dev quando o Turbopack apresentar problemas.
  • bun run prisma:generate: necessário antes do build quando o client Prisma muda. Para migrações use bunx prisma migrate deploy.

Automação no CI

Arquivo: .github/workflows/quality-checks.yml

Etapas:

  1. Instala dependências (bun install --frozen-lockfile).
  2. bun run prisma:generate.
  3. bun run lint.
  4. bun test.
  5. bun run build:bun.

O workflow dispara em todo push/pull_request para main e fornece feedback imediato sem depender do pipeline de deploy.

Testes rápidos via curl (Convites & acessos)

  1. Rode bun run dev:bun (ou bun run dev se preferir Node) e autentique-se em http://localhost:3000/login usando admin@sistema.dev / admin123.
  2. Copie o valor do cookie BETTER_AUTH_SESSION e exporte no shell: export COOKIE="BETTER_AUTH_SESSION=<valor>".

Usuários

# Listar usuários com acesso web
curl -s http://localhost:3000/api/admin/users \
  -H "Cookie: $COOKIE" \
  -H "Accept: application/json" | jq '.users | map({ id, email, role })' # remova o pipe se não tiver jq

# Criar usuário gestor (ajuste o e-mail se necessário)
NEW_EMAIL="api.teste.$(date +%s)@sistema.dev"
curl -s -X POST http://localhost:3000/api/admin/users \
  -H "Cookie: $COOKIE" \
  -H "Content-Type: application/json" \
  -d "{\"name\":\"Usuário via curl\",\"email\":\"$NEW_EMAIL\",\"role\":\"manager\",\"tenantId\":\"tenant-atlas\"}" \
  | tee /tmp/user-created.json

# Remover o usuário recém-criado
USER_ID=$(jq -r '.user.id' /tmp/user-created.json)
curl -i -X DELETE http://localhost:3000/api/admin/users/$USER_ID \
  -H "Cookie: $COOKIE"

Os exemplos acima utilizam jq para facilitar a leitura. Se não estiver disponível, remova os pipes e leia o JSON bruto.

Convites

# Criar convite válido por 7 dias
INVITE_EMAIL="convite.$(date +%s)@sistema.dev"
curl -s -X POST http://localhost:3000/api/admin/invites \
  -H "Cookie: $COOKIE" \
  -H "Content-Type: application/json" \
  -d "{\"email\":\"$INVITE_EMAIL\",\"name\":\"Convite via curl\",\"role\":\"collaborator\",\"tenantId\":\"tenant-atlas\",\"expiresInDays\":7}" \
  | tee /tmp/invite-created.json

# Revogar convite pendente
INVITE_ID=$(jq -r '.invite.id' /tmp/invite-created.json)
curl -i -X PATCH http://localhost:3000/api/admin/invites/$INVITE_ID \
  -H "Cookie: $COOKIE" \
  -H "Content-Type: application/json" \
  -d '{"reason":"Revogado via curl"}'

# Reativar (até 7 dias após a revogação)
curl -i -X PATCH http://localhost:3000/api/admin/invites/$INVITE_ID \
  -H "Cookie: $COOKIE" \
  -H "Content-Type: application/json" \
  -d '{"action":"reactivate"}'

Dica: ao receber 409 na criação de convite, há outro convite pendente/aceito para o mesmo e-mail. Revogue ou remova o usuário antes.

Desktop (Tauri)

  • Tabs Radix + estilos shadcn: apps/desktop/src/components/ui/tabs.tsx.
  • Painel principal: apps/desktop/src/main.tsx — abas Resumo/Inventário/Diagnóstico/Configurações, envio manual de inventário, seleção de persona (colaborador/gestor) e vínculo com usuário.
  • Coleta/hardware: apps/desktop/src-tauri/src/agent.rs.
  • Variáveis de build:
    • VITE_APP_URL (URL Web).
    • VITE_API_BASE_URL (API).

Build local

bun install
bun install --cwd apps/desktop
VITE_APP_URL=http://localhost:3000 \
VITE_API_BASE_URL=http://localhost:3000 \
bun run --cwd apps/desktop tauri build

Artefatos: apps/desktop/src-tauri/target/release/bundle/.

Ícone do instalador (NSIS)

  • O Windows espera que apps/desktop/src-tauri/icons/icon.ico contenha sprites em 16, 24, 32, 48, 64, 128 e 256px, todos com fundo transparente. Sem esses tamanhos o Explorer gera uma miniatura reduzida com bordas acinzentadas.

  • Para atualizar o ícone a partir do icon-512.png, execute:

    cd apps/desktop/src-tauri
    python3 - <<'PY'
    

from PIL import Image img = Image.open("icons/icon-512.png") img.save("icons/icon.ico", sizes=[(16,16),(24,24),(32,32),(48,48),(64,64),(128,128),(256,256)]) PY


- Depois de regerar `icon.ico`, faça o commit e rode novamente `bun run --cwd apps/desktop tauri build` para empacotar o instalador com o ícone correto.

### Atualizações OTA

1. Gere chaves (`bun run --cwd apps/desktop tauri signer generate`).
2. Defina `TAURI_SIGNING_PRIVATE_KEY` (+ password) no ambiente de build.
3. Publique os pacotes e um `latest.json` em release GitHub.
4. O app verifica ao iniciar e pelo botão “Verificar atualizações”.

## Erros recorrentes e soluções

| Sintoma | Causa | Correção |
| --- | --- | --- |
| `ERR_BUN_LOCKFILE_OUTDATED` no pipeline | Dependências do desktop alteradas sem atualizar o `bun.lock` | Rodar `bun install` (raiz e `apps/desktop`) e commitar o lockfile. |
| Prisma falha com `P2021` / tabelas Better Auth inexistentes | CLI leu `.env` da raiz (produção) | Usar `DATABASE_URL="file:./prisma/db.dev.sqlite"` nos comandos. |
| Vitest trava em modo watch | Script `bun test` sem `--run` e CI detecta TTY | Ajustado para `vitest --run --passWithNoTests`. Localmente, use `bun test -- --watch` se quiser. |
| Desktop não encontra updater | Falta `latest.json` ou assinatura inválida | Publicar release com `*.sig` e `latest.json` apontando para os pacotes corretos. |

## Cronômetro dos tickets

- A UI e o backend agora compartilham um relógio real alinhado via `serverNow`. Toda resposta de `tickets.workSummary`, listagens de tickets e mutations `startWork/pauseWork` envia `serverNow` (epoch UTC).
- O frontend (`ticket-summary-header` e tabela) calcula um deslocamento (`offset = Date.now() - serverNow`) e projeta `Date.now()` para a linha do tempo do servidor usando `toServerTimestamp`. Isso elimina drift quando o navegador está adiantado/atrasado.
- Durante o `startWork`, se a mutation retornar `status = already_started` sem `startedAt`, a UI usa `getServerNow()` como fallback, e assim que o Convex reenviar a sessão ativa, o reconciliador (`reconcileLocalSessionStart`) substitui o valor local. 
- O tempo em execução exibido “ao vivo” torna-se idêntico ao acumulado após pausar, com tolerância de ±1s por conta do tick do cronômetro. Ao depurar inconsistências, verifique se `serverNow` está chegando (painel de rede) e se o offset em `ticket-timer.utils.ts` está sendo calibrado.

## Referências úteis

- **Deploy (Swarm)**: veja `docs/DEPLOY-RUNBOOK.md`.
- **Plano do agente desktop / heartbeat**: `docs/plano-app-desktop-maquinas.md`.
- **Histórico de incidentes**: `docs/historico-agente-desktop-2025-10-10.md`.

> Última revisão: 18/10/2025. Atualize este guia sempre que o fluxo de DEV ou automações mudarem.
- **Next.js 16 (beta)**: comportamento sujeito a mudanças. Antes de subir para stable, acompanhe o changelog oficial (quebra: `revalidateTag` com segundo argumento, params assíncronos, etc.). Já estamos compatíveis com os breaking changes atuais.