208 lines
11 KiB
Markdown
208 lines
11 KiB
Markdown
# Guia de Desenvolvimento — 18/12/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.10` 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**: PostgreSQL local (Docker recomendado). Defina `DATABASE_URL` apontando para seu PostgreSQL.
|
||
- **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 + PostgreSQL)
|
||
|
||
1. Suba um PostgreSQL local (Docker recomendado):
|
||
|
||
```bash
|
||
docker run -d --name postgres-dev -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18
|
||
```
|
||
|
||
2. Configure a `DATABASE_URL` no seu `.env`:
|
||
|
||
```
|
||
DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados
|
||
```
|
||
|
||
3. Gere/atualize o schema local:
|
||
|
||
```bash
|
||
bun install
|
||
bunx prisma db push
|
||
bun run prisma:generate
|
||
bun run auth:seed
|
||
```
|
||
|
||
4. Rode o app Next.js:
|
||
|
||
```bash
|
||
bun run dev:bun
|
||
```
|
||
> Alternativas: `bun run dev` (Node) ou `bun run dev:webpack` se precisar do fallback oficial.
|
||
|
||
5. Credenciais padrao (seed): `admin@sistema.dev / admin123`.
|
||
6. Herdou dados antigos? Execute `node scripts/remove-legacy-demo-users.mjs` para limpar contas demo legadas.
|
||
|
||
## Next.js 16 (estável)
|
||
|
||
- Mantemos o projeto em `next@16.0.10`, 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`](https://nextjs.org/docs/messages/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
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
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 256 px, 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:
|
||
|
||
```bash
|
||
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 | Banco nao foi inicializado | Rodar `bunx prisma db push` e `bun run auth:seed`. |
|
||
| 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 ±1 s 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/OPERATIONS.md`.
|
||
- **Plano do agente desktop / heartbeat**: `docs/archive/plano-app-desktop-dispositivos.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.
|