Compare commits

...

961 commits

Author SHA1 Message Date
rever-tecnologia
a43151f84f Ajusta tamanho do logo colapsado
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m46s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m59s
2025-12-19 08:07:11 -03:00
rever-tecnologia
0dd9f10984 Reduz logo colapsado na sidebar 2025-12-19 07:59:12 -03:00
rever-tecnologia
2334e9a9ec Ajusta texto do portal e logo da sidebar 2025-12-19 07:55:45 -03:00
rever-tecnologia
badcb0f502 Ajusta marca na sidebar
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m40s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m2s
2025-12-19 00:17:11 -03:00
rever-tecnologia
0a0f722bd8 Melhora UX do chat no desktop
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m41s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m5s
2025-12-18 23:53:24 -03:00
rever-tecnologia
9142446f06 Corrige inicio do chat no desktop
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m21s
Quality Checks / Lint, Test and Build (push) Successful in 3m28s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
2025-12-18 22:50:24 -03:00
rever-tecnologia
9c6e724128 chore(prod): ajustar stack convex/traefik e registrar alteracoes
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 10s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m37s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m55s
2025-12-18 21:22:06 -03:00
rever-tecnologia
c030a3ac09 fix: tratar tokens de maquinas e alinhar stack/docs
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m41s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m0s
2025-12-18 18:20:35 -03:00
rever-tecnologia
b7e2c4cc98 Exibe status de reprovisionamento quando token é revogado
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m53s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m13s
2025-12-18 16:12:48 -03:00
rever-tecnologia
89f756e088 Corrige redirect do portal após reset do dispositivo
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m54s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m31s
2025-12-18 15:29:08 -03:00
rever-tecnologia
84117e6821 feat(ui): implementa sidebar colapsavel com icones e tooltips
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 9s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m42s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m20s
- Muda collapsible de offcanvas para icon na sidebar
- Adiciona tooltips aos itens de menu quando colapsado
- Itens com submenu mostram mini-menu no tooltip
- Logo mostra apenas icone quando colapsado
- NavUser mostra apenas avatar quando colapsado
- Adiciona separadores entre secoes quando colapsado
- Centraliza icones horizontalmente no modo colapsado
- Persiste estado da sidebar via cookie entre navegacoes
- Corrige hydration mismatch com sincronizacao pos-hidratacao
- Desabilita transicoes durante sincronizacao inicial
- Remove bolinha do tooltip e ajusta espacamento
- Corrige redirecionamento ao resetar dispositivo no Tauri

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 14:38:35 -03:00
rever-tecnologia
826b376dd3 fix(desktop): corrige navegacao ao resetar dispositivo
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m1s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m53s
Usa window.location.href com URL do Tauri em vez de API inexistente

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 14:08:56 -03:00
rever-tecnologia
d6531e2a4c fix(desktop): navega para URL do app Tauri ao resetar dispositivo
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
Usa getCurrentWebviewWindow().navigate() para voltar para a pagina
inicial do app Tauri em vez de ir para o servidor web

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 14:06:53 -03:00
rever-tecnologia
649a270416 fix(desktop): remove parametro nao utilizado companyName
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
Quality Checks / Lint, Test and Build (push) Successful in 4m10s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m28s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:57:15 -03:00
rever-tecnologia
af37f0b30d style(desktop): padroniza header da tela de registro
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
Usa mesmo layout da sidebar: Raven + badge Plataforma de chamados

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:56:16 -03:00
rever-tecnologia
026772e2f4 fix(desktop): corrige comportamento ao resetar dispositivo
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
Quality Checks / Lint, Test and Build (push) Has been cancelled
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
- Limpa todos os campos de input ao resetar (codigo, email, nome)
- Forca recarregar pagina inicial para sair de pagina web carregada
- Evita redirect para login web quando dispositivo e resetado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:54:27 -03:00
rever-tecnologia
06c16ab2a9 fix(desktop): impede scroll na tela de desativacao
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m37s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Usa fixed inset-0 com overflow-hidden para bloquear scroll

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:50:19 -03:00
rever-tecnologia
73c14e2be3 fix(desktop): ajusta layout da tela de desativacao
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- Remove exibicao do nome da empresa
- Coloca botoes lado a lado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:48:59 -03:00
rever-tecnologia
f4a3b22aab fix(desktop): corrige tela de desativacao duplicada no app Tauri
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 10s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m38s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m11s
- Adiciona verificacao de isMachineActive antes de redirecionar para handshake
- Remove mensagens de erro antigas com erro gramatical
- Corrige texto do template HTML de desativacao no servidor
- Corrige mensagem de erro na API de sessions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 13:32:07 -03:00
rever-tecnologia
1a0574e7f4 style(login): melhora layout da pagina de login
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m7s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m24s
- Adiciona bordas arredondadas e efeito suspenso no painel direito
- Aumenta proporcionalmente o header (Raven, Helpdesk, Por Rever Tecnologia)
- Ajusta posicionamento do header com margem superior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:52:51 -03:00
rever-tecnologia
26b1a65ec4 refactor(devices): substitui badge arquitetura por status ativacao Windows
- Remove identificador machine-xxx do header dos cards
- Substitui badge de arquitetura (X86_64) por status de ativacao do Windows
- Remove badge redundante de tipo de dispositivo
- Ajusta altura da badge na sidebar para nao cortar texto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:48:12 -03:00
rever-tecnologia
d95af184be style(sidebar): transforma subtitulo em badge pill
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m33s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m32s
- Subtitulo agora aparece como badge com fundo escuro
- Alterado texto para "Plataforma de chamados"
- Estilo consistente com badges da plataforma

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:37:45 -03:00
rever-tecnologia
08ae1bd969 style(desktop): transforma titulo em badge estilizada
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- "Sistema de chamados" agora aparece como badge pill
- Estilo consistente com badges usadas na plataforma web

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:34:49 -03:00
rever-tecnologia
eb284a7f50 fix(desktop): corrige logo na tela de registro
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- Adiciona logo-raven.png local no public do desktop
- Usa logo local como padrao em vez de buscar do servidor
- Fallback continua buscando do servidor se local falhar

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:33:55 -03:00
rever-tecnologia
993bd3890a style(devices): substitui spinner por skeleton cards no loading
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Has been cancelled
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
- Cria DeviceCardSkeleton que simula a estrutura do DeviceCard
- Exibe grid de 6 skeletons durante carregamento
- Mantém mesmo layout responsivo dos cards reais

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:31:17 -03:00
rever-tecnologia
a9feea9b78 style(companies): centraliza itens na tabela de empresas
- Adiciona text-center em todos os TableCell
- Centraliza conteudo interno com justify-center
- Mantem texto a esquerda dentro dos cards de contrato/contato

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:29:29 -03:00
rever-tecnologia
d38d5d39eb fix(companies): corrige modal de edicao de empresas
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m37s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m4s
- Corrige erro NaN nos campos numericos (reopenWindowDays, contractedHoursPerMonth)
  usando z.preprocess para converter NaN para null
- Ajusta espacamento nos AccordionContent para melhor legibilidade
- Define tamanho consistente nos botoes Cancelar/Salvar (size="sm")
- Accordions agora iniciam fechados por padrao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:26:02 -03:00
rever-tecnologia
a5bab2cc33 fix(desktop): corrige tela de desativacao e adiciona botao Verificar novamente
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m30s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m56s
- Corrige erro gramatical: "Dispositivo desativada" -> "Dispositivo desativado"
- Adiciona botao "Verificar novamente" na tela de desativacao
- Adiciona callback onReactivated no MachineStateMonitor
- Corrige fundo escuro para cobrir toda a tela quando desativado
- Corrige acentuacoes faltantes no historico de automacoes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 11:09:17 -03:00
rever-tecnologia
70cba99424 feat(automations): historico expandivel com detalhes das acoes
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m15s
Quality Checks / Lint, Test and Build (push) Successful in 3m35s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 1m20s
- Adiciona linhas expandiveis no historico de execucoes
- Mostra detalhes completos de cada acao (destinatarios, assunto, etc.)
- Salva mais informacoes no backend para acoes de e-mail
- Remove log de progresso do dashboard de reports

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 10:51:37 -03:00
rever-tecnologia
c2802b1a4d feat(export): adiciona colunas bootInfo e remove Fleet do inventario
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 8s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m19s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m45s
- Adiciona colunas lastBootTime, uptimeFormatted, bootCount30d
- Remove colunas fleetId, fleetTeam, fleetUpdatedAt (nao utilizadas)
- Adiciona funcao extractBootInfo() para extrair dados de extended.windows.bootInfo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 10:39:39 -03:00
rever-tecnologia
1a75a69d4a refactor(devices): remove secoes de alertas de postura e historico
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m45s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m45s
2025-12-18 10:08:56 -03:00
rever-tecnologia
ad5e26f211 chore: remove debug logs do heartbeat
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
2025-12-18 10:06:14 -03:00
rever-tecnologia
dc740cd89a fix: remove declaracao duplicada de rawInventory
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
Quality Checks / Lint, Test and Build (push) Successful in 3m12s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m18s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 1m41s
2025-12-18 09:56:26 -03:00
rever-tecnologia
db23ea1901 fix(heartbeat): extrai inventory de metadata quando args.inventory vazio
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 2m54s
CI/CD Web + Desktop / Deploy Convex functions (push) Failing after 41s
Quality Checks / Lint, Test and Build (push) Has been cancelled
O agente envia o inventory dentro de metadata, não diretamente em args.inventory.
Agora o backend verifica ambos os lugares.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 09:49:08 -03:00
rever-tecnologia
f39bd46c2b feat: adiciona informacoes de reinicio e melhora SLA global
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m0s
Quality Checks / Lint, Test and Build (push) Successful in 3m29s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 1m24s
- Agente Rust: captura LastBootTime, uptime e contagem de boots
- Backend: extrai campos do extended (bootInfo, discos, RAM, etc) antes de salvar
- Frontend /devices: exibe secao de ultimo reinicio
- SLA global: adiciona campos de modo, threshold de alerta e status de pausa
- Corrige acento em "destinatario" -> "destinatario" em automations

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 09:38:58 -03:00
rever-tecnologia
d32b94c22d fix(sla): corrige acentuacao no SLA por empresa
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m38s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m5s
- Corrige acentos: Critico -> Crítico, Media -> Média
- Corrige: Horas uteis -> Horas úteis
- Corrige: solucao -> solução, resolucao -> resolução
- Corrige: nao -> não, Nao foi possivel -> Não foi possível
- Corrige: acao ira -> ação irá, passarao -> passarão

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 09:15:11 -03:00
rever-tecnologia
d6188fd384 fix(checklists): adiciona skeleton de carregamento na lista de templates
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m6s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m22s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 09:10:47 -03:00
rever-tecnologia
ce52a4393b fix(close-ticket): adiciona segundos na formatacao e ajuste de tempo
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m15s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m24s
- Corrige formatacao de tempo para exibir segundos (ex: 2m 04s)
- Adiciona campo de segundos nos inputs de ajuste de tempo
- Melhora espacamento entre secoes de tempo interno e externo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:59:43 -03:00
rever-tecnologia
f9deb408dc fix: melhora UX de softwares e campos personalizados
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m4s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m20s
Softwares instalados:
- Corrige contador de paginação (mostra 1-30, 31-60, etc.)
- Remove tooltip do botão de limpar pesquisa

Campos personalizados:
- Adiciona mensagem indicando tipo de dispositivo (desktop/celular)
- Melhora mensagem quando não há campos disponíveis
- Adiciona botão X para limpar valor de campos texto/número

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:44:57 -03:00
rever-tecnologia
73de65bbaf fix: corrige acentuacao e melhora UX de softwares
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m19s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 3m49s
- Adiciona botao de limpar pesquisa em softwares
- Corrige paginacao com historico de cursores
- Corrige acentuacao em politicas de SLA (politica -> política)
- Corrige acentuacao em varios textos do frontend

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:23:16 -03:00
rever-tecnologia
cfb72358bc fix: corrige acentuacao em mensagens do usuario
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
Quality Checks / Lint, Test and Build (push) Successful in 4m20s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m23s
- versao -> versão
- nao -> não
- Atualizacao -> Atualização

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:15:45 -03:00
rever-tecnologia
3e63589055 feat(devices): exibe bateria, sensores termicos, rede, monitores e chassis
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- Adiciona tipos TypeScript para novos dados do Windows
- Exibe informacoes de bateria com status traduzido
- Exibe sensores termicos em tabela
- Exibe adaptadores de rede com velocidade e status
- Exibe monitores conectados com fabricante e serial
- Exibe info do chassis/gabinete com tipo traduzido

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:13:26 -03:00
rever-tecnologia
f0c2bdc283 feat(agent): adiciona captura de bateria, sensores termicos, rede e monitores
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m11s
Quality Checks / Lint, Test and Build (push) Successful in 4m22s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 1m28s
- Captura info de bateria (Win32_Battery) com status traduzido
- Captura sensores termicos via WMI ThermalZone e OpenHardwareMonitor
- Captura adaptadores de rede fisicos com status de conexao
- Captura monitores conectados (fabricante, serial, modelo)
- Captura info de chassis/gabinete com tipo traduzido

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:04:43 -03:00
rever-tecnologia
23fe67e7d3 feat(devices): implementa tabela separada para softwares instalados
- Cria tabela machineSoftware no schema com indices otimizados
- Adiciona mutations para sincronizar softwares do heartbeat
- Atualiza heartbeat para processar e salvar softwares
- Cria componente DeviceSoftwareList com pesquisa e paginacao
- Integra lista de softwares no drawer de detalhes do dispositivo

feat(sla): transforma formulario em modal completo

- Substitui formulario inline por modal guiado
- Adiciona badge "Global" para indicar escopo da politica
- Adiciona seletor de unidade de tempo (minutos, horas, dias)
- Melhora textos e adiciona dica sobre hierarquia de SLAs

fix(reports): ajusta altura do SearchableCombobox

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 08:00:40 -03:00
rever-tecnologia
ef2545221d ui(sla): move SLA por empresa para acima de SLA por categoria
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
Quality Checks / Lint, Test and Build (push) Successful in 3m35s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m34s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:46:15 -03:00
rever-tecnologia
a2fa5d046c fix(sla): adiciona verificacao segura de company.id
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Has been cancelled
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:43:18 -03:00
rever-tecnologia
a55f889689 fix(sla): adiciona viewerId na query companies.list
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m45s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m41s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:35:51 -03:00
rever-tecnologia
4be622c838 fix(sla): adiciona import de Id no companySlas
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
Quality Checks / Lint, Test and Build (push) Successful in 4m2s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m15s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 2m23s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:22:47 -03:00
rever-tecnologia
5db31ba365 fix(sla): corrige tipo de categoryId na insercao de regra
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m0s
Quality Checks / Lint, Test and Build (push) Successful in 4m12s
CI/CD Web + Desktop / Deploy Convex functions (push) Failing after 1m20s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:15:34 -03:00
rever-tecnologia
158fb32b8a fix(email): adiciona tratamento de erros no envio de e-mails de automacao
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m55s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m58s
CI/CD Web + Desktop / Deploy Convex functions (push) Failing after 1m22s
- Adiciona try-catch para cada envio individual
- Registra logs detalhados de sucesso e falha por destinatario
- Retorna informacao sobre quantos e-mails falharam
- Imprime resumo de envios no console para debug

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:05:24 -03:00
rever-tecnologia
8a237a820d feat(reports): adiciona opcao Todas as empresas no relatorio por empresa
- Frontend: usa usePersistentCompanyFilter para persistir selecao
- Frontend: adiciona opcao "Todas as empresas" como primeira opcao
- Backend: torna companyId opcional na query companyOverview
- Backend: usa resolveScopedCompanyId para scoping de gestores

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 19:01:56 -03:00
rever-tecnologia
14480df9f3 feat(chat): vincula timer automaticamente com inicio/fim do chat ao vivo
- Ao iniciar chat: inicia timer EXTERNAL automaticamente se nao houver sessao ativa
- Ao encerrar chat: pausa timer automaticamente se houver sessao ativa
- Adiciona razao de pausa END_LIVE_CHAT para identificar pausas automaticas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 18:57:58 -03:00
rever-tecnologia
47ccdc51a7 feat(sla): adiciona interface de administração de SLA por empresa
- Adiciona CompanySlaManager para gerenciar empresas com SLA customizado
- Adiciona CompanySlaDrawer para configurar regras de SLA por empresa
- Integra componentes no SlasManager existente

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 18:53:32 -03:00
rever-tecnologia
33f0cc2e13 feat: adiciona SLA por empresa e modal de exclusao de automacoes
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 8s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m45s
Quality Checks / Lint, Test and Build (push) Successful in 3m58s
CI/CD Web + Desktop / Deploy Convex functions (push) Failing after 1m17s
## SLA por Empresa
- Adiciona tabela companySlaSettings no schema
- Cria convex/companySlas.ts com queries e mutations
- Modifica resolveTicketSlaSnapshot para verificar SLA da empresa primeiro
- Fallback: empresa > categoria > padrao

## Modal de Exclusao de Automacoes
- Substitui confirm() nativo por Dialog gracioso
- Segue padrao do delete-ticket-dialog

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 18:44:05 -03:00
rever-tecnologia
b3fcbcc682 fix: adiciona import useCallback no auth-client
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m45s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m53s
2025-12-17 18:03:20 -03:00
rever-tecnologia
ae4fd7f890 feat(portal): adiciona monitoramento em tempo real de desativação de máquina
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Has been cancelled
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
- Cria hook useMachineStateMonitor com subscription Convex
- Cria MachineDeactivationOverlay para bloquear acesso
- Integra no PortalShell para exibir overlay quando máquina é desativada
- Adiciona refreshMachineContext ao AuthProvider para retry manual
- Funciona tanto via Raven quanto via navegador web

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 18:02:23 -03:00
rever-tecnologia
413749d999 fix(desktop): corrige detecção de desativação em tempo real
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m46s
Quality Checks / Lint, Test and Build (push) Successful in 3m54s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
- Renderiza MachineStateMonitor mesmo durante tela de loading
- Adiciona verificação de isMachineActive na condição de early return
- Detecta estado de desativação/reset já na carga inicial
- Adiciona logs para facilitar debugging

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 17:47:47 -03:00
rever-tecnologia
0bfe4edc6c feat(devices): adiciona modais de confirmacao e deteccao em tempo real
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 10s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m31s
Quality Checks / Lint, Test and Build (push) Successful in 4m46s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 1m39s
- Adiciona modais de confirmacao para resetar e desativar dispositivos
- Cria query getMachineState no Convex para monitoramento em tempo real
- Implementa MachineStateMonitor no desktop para detectar mudancas
- Desktop redireciona para tela de registro apos reset
- Desktop mostra tela de desativacao imediatamente apos bloqueio

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 17:13:37 -03:00
rever-tecnologia
cd3305f1e3 feat(tickets): exclui visitas da listagem principal de tickets
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m49s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m47s
- Adiciona filtro excludeVisits no estado de filtros
- Aplica excludeVisits: true em /tickets e /tickets/resolved
- Visitas agora aparecem apenas em /tickets/visits

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 16:27:22 -03:00
rever-tecnologia
0a36ed049f feat(tickets): adiciona menu dedicado para Visitas na sidebar
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
Quality Checks / Lint, Test and Build (push) Successful in 4m2s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m56s
- Adiciona item "Visitas" no submenu de Tickets com icone MapPin
- Cria pagina /tickets/visits que filtra apenas tickets da fila Visitas
- Corrige teste de automacao para usar emailProps ao inves de html

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 16:20:09 -03:00
rever-tecnologia
b8170d0225 fix(automations): corrige erro de import dinamico no envio de email
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
Quality Checks / Lint, Test and Build (push) Failing after 2m45s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m41s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 1m44s
- Move renderizacao do React Email para a action Node.js
- Passa props do email em vez do HTML ja renderizado
- Resolve erro "dynamic module import unsupported"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 15:23:39 -03:00
rever-tecnologia
bddce33217 fix(dashboard): "Em andamento" conta apenas tickets com play ativo
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
Quality Checks / Lint, Test and Build (push) Successful in 3m46s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m39s
CI/CD Web + Desktop / Deploy Convex functions (push) Successful in 2m13s
- Tickets com status AWAITING_ATTENDANCE mas sem play ativo
  agora contam como "Em aberto"
- "Em andamento" mostra apenas tickets onde working === true

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 14:39:39 -03:00
rever-tecnologia
6f9cdc8670 style(dashboard): renomeia "Pendentes" para "Em aberto" nas filas
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 10s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m20s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m39s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 14:27:42 -03:00
rever-tecnologia
b5ff8034d2 fix(email): usa TicketCardLegacy no automation-email
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m5s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m16s
Corrige erro de TypeScript ao usar o componente legado que
aceita o formato { ticket: TicketCardData }

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 14:00:20 -03:00
rever-tecnologia
034f6f47ff feat(company): define prazo de reabertura por empresa
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
Quality Checks / Lint, Test and Build (push) Successful in 3m22s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m25s
CI/CD Web + Desktop / Deploy Convex functions (push) Failing after 1m23s
- Adiciona campo reopenWindowDays no cadastro de empresa (padrao 7 dias)
- Ticket usa automaticamente o prazo da empresa ao ser resolvido
- Remove selecao de prazo do modal de encerramento de ticket
- Valor e gravado no ticket no momento da resolucao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 13:53:14 -03:00
rever-tecnologia
d12dcf9512 fix(tickets): filtra templates de checklist por empresa selecionada
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 9s
Quality Checks / Lint, Test and Build (push) Successful in 4m3s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m55s
- Remove templates incompativeis quando empresa do ticket muda
- Limpa selecao de template ao mudar empresa
- Remove texto "(opcional)" do titulo da secao de checklist

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 13:42:19 -03:00
rever-tecnologia
fffc3f553c style(admin): centraliza celulas e melhora modal de exclusao
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m17s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m47s
- Centraliza dados das celulas em todas as tabelas (text-center)
- Melhora layout do modal de exclusao com card destacado
- Exibe nome e email do usuario no card de confirmacao
- Usa cores rose para indicar acao destrutiva

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 11:56:58 -03:00
rever-tecnologia
30768ea090 style(admin): centraliza dados das celulas nas tabelas
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- Adiciona text-center em todas as celulas de dados
- Centraliza botoes de acoes com inline-flex
- Mantem consistencia visual entre headers e dados

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 11:55:31 -03:00
rever-tecnologia
3e923d5a53 style(admin): ajusta tabelas para consistencia com /admin/companies
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- Remove text-[11px] e tracking-wide dos headers
- Remove table-fixed e larguras fixas das colunas
- Usa mesmo estilo de /admin/companies (tamanho padrao)
- Remove bordas laterais desnecessarias dos headers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 11:52:46 -03:00
rever-tecnologia
ec7dc4ce12 style(admin): ajusta titulos das tabelas para sentence case
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 8s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
- Remove uppercase dos headers das tabelas
- Corrige acentuacao em "Acoes" para "Ações"
- Ajusta tamanho da fonte para text-[11px] (consistente com outras tabelas)
- Remove min-width das tabelas para evitar scroll horizontal

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 11:50:20 -03:00
rever-tecnologia
498b9789b5 feat(email): adiciona templates React Email e melhora UI admin
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m33s
Quality Checks / Lint, Test and Build (push) Successful in 3m41s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been cancelled
- Cria 10 novos templates React Email (invite, password-reset, new-login,
  sla-warning, sla-breached, ticket-created, ticket-resolved,
  ticket-assigned, ticket-status, ticket-comment)
- Adiciona envio de email ao criar convite de usuario
- Adiciona security_invite em COLLABORATOR_VISIBLE_TYPES
- Melhora tabela de equipe com badges de papel e colunas fixas
- Atualiza TicketCard com nova interface de props
- Remove botao de limpeza de dados antigos do admin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 11:46:02 -03:00
rever-tecnologia
8546a1feb1 fix(avatar): sincroniza avatar apos atualizar
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m51s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m14s
- Propaga Set-Cookie do Better Auth no endpoint de avatar\n- Forca refresh da sessao apos upload/remocao\n- Adiciona teste de propagacao e defaults de env para testes
2025-12-17 10:38:07 -03:00
rever-tecnologia
74c06ffa33 fix(auth): inicia machineContextLoading como true para evitar flash
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 6s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m52s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m18s
2025-12-17 10:35:34 -03:00
rever-tecnologia
1e674d5006 style(tickets): aumenta largura do botao Criar
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m0s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m17s
2025-12-17 10:25:42 -03:00
rever-tecnologia
965672e0fa fix(portal): corrige encoding e textos do formulario de ticket
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 9s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m10s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m36s
- Remove texto com encoding quebrado ("informações da máquina")
- Corrige gênero de "dispositivo" (masculino, não feminino)
- Remove lógica redundante - skeleton já cobre o estado de carregamento

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 10:18:00 -03:00
rever-tecnologia
385a8ee3df feat(ui): melhora UX do formulario de tickets
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m8s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m35s
- Adiciona skeleton loading no formulario de novo chamado do portal
- Remove texto confuso do tipo de solicitacao padrao
- Padroniza estilo dos labels Categoria/Subcategoria com os demais campos
- Move botao "Criar" do header para parte inferior do modal na web

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 10:12:02 -03:00
rever-tecnologia
811ad0641a feat(portal): adiciona skeletons para melhor UX durante carregamento
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m16s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m48s
- Adiciona skeleton no header quando dados do usuario estao carregando
- Remove mensagem "Sem e-mail definido" durante loading
- Substitui spinner por skeleton cards na lista de tickets
- Cria componente PortalTicketCardSkeleton para estado de loading

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 09:52:56 -03:00
rever-tecnologia
aa9c09c30e refactor(tickets): corrige avatar desincronizado e otimiza performance
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m26s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 5m1s
- Corrige avatar desincronizado na listagem de tickets usando sessao do usuario logado
- Otimiza timer da tickets-table de 1s para 15s (reduz re-renders)
- Remove useEffect desnecessario em status-select (usa prop diretamente)
- Remove useEffects desnecessarios em ticket-custom-fields (usa ticket.customFields diretamente)
- Adiciona React.memo no AssigneeCell para evitar re-renders

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 09:35:51 -03:00
rever-tecnologia
f617916fe7 perf(fonts): migra para next/font/google e elimina FOUT
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m13s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m45s
- Usa next/font/google para carregar Inter e JetBrains Mono
- Remove @font-face manuais do globals.css
- Corrige variaveis CSS confusas (--font-geist-* com 4 hifens)
- Remove duplicacao de @layer base
- Fontes agora sao otimizadas automaticamente (WOFF2, subset, preload)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 09:00:22 -03:00
rever-tecnologia
4669be0107 style(portal): usa tamanho padrao da badge de status no detalhe do ticket
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 3m39s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m12s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 08:50:01 -03:00
rever-tecnologia
028154a7bc style(sidebar): reduz espacamento entre titulo e subtitulo da marca
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m4s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m10s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 08:41:14 -03:00
rever-tecnologia
67433ed5e4 style(portal): aumenta tamanho da badge de status no card de ticket
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 10s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 4m26s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 5m3s
- Altera altura para h-9 (36px) igual ao componente web
- Aumenta fonte de text-xs para text-sm

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 08:33:44 -03:00
esdrasrenan
3f9461a18f fix(desktop-chat): estabiliza janelas e melhora multi-conversas
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 7s
Quality Checks / Lint, Test and Build (push) Successful in 4m41s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 5m30s
2025-12-17 01:44:28 -03:00
esdrasrenan
380b2e44e9 fix(ci): deploy atomico no Forgejo (symlink) 2025-12-17 01:44:00 -03:00
esdrasrenan
2bdc5ae882 chore: atualiza Bun para versao 1.3.4
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m1s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 23:55:57 -03:00
esdrasrenan
52452f3023 ci: trigger redeploy after permission fix issue
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m15s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
O fix de permissao anterior limpou o diretorio mas o rsync nao
copiou os arquivos corretamente. Este commit dispara um novo deploy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 23:27:23 -03:00
esdrasrenan
9c258b43f1 docs: adiciona instrucoes para repositorio privado
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m40s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Atualiza docs/SETUP.md e scripts/setup-dev.sh com:
- Instrucoes para configurar chave SSH
- Opcao de usar Personal Access Token (PAT)
- Comandos para clonar/configurar via SSH ou HTTPS
- Script setup-dev.sh agora aceita --ssh para repo privado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 23:18:31 -03:00
esdrasrenan
9e385b664d fix(ci): corrige backup/restore do .env usando Docker
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m29s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 7m0s
O problema anterior: apos limpar o conteudo do diretorio, o proprio
diretorio ainda tinha permissoes de root, fazendo o cp do .env falhar.

Nova abordagem:
- Salvar .env usando Docker (monta origem e /tmp)
- Remover o diretorio COMPLETAMENTE usando Docker (monta diretorio pai)
- Recriar o diretorio com mkdir -p (permissoes do usuario runner)
- Restaurar o .env (agora o diretorio tem permissoes corretas)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 23:03:53 -03:00
esdrasrenan
6943a88e66 fix(ci): solucao definitiva para erro de permissao no rsync
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 4s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Failing after 3m0s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m2s
O problema: Docker cria arquivos como root durante o build, e em
deploys subsequentes o rsync falha com "Permission denied" porque
o usuario runner nao consegue sobrescrever arquivos de root.

A solucao anterior (chown do destino) falhava silenciosamente porque
alguns arquivos ja tinham permissoes de root de deploys anteriores.

Nova abordagem:
- Antes do rsync, limpar completamente o destino usando Docker Alpine
- Docker Alpine roda como root e consegue remover qualquer arquivo
- O .env eh preservado (backup/restore)
- rsync copia para diretorio limpo, sem conflitos de permissao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:57:57 -03:00
esdrasrenan
12a809805e fix(ci): correcao definitiva de permissoes Docker
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Failing after 3m19s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
Quality Checks / Lint, Test and Build (push) Successful in 4m8s
Adiciona step dedicado para corrigir permissoes apos build Docker:
- Usa container Alpine para fazer chown -R 1000:1000 no build
- Tambem corrige permissoes do destino antes do rsync

Isso resolve o erro "Permission denied" do rsync causado por
arquivos criados pelo Docker como root.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:49:42 -03:00
esdrasrenan
f0a4b9b782 chore: altera subtitulo da sidebar para "Chamados"
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Has been cancelled
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:48:18 -03:00
esdrasrenan
2c95834598 chore: desativa GitHub Actions e adiciona docs de setup
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m34s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Failing after 4m37s
- Move .github/workflows para .github/workflows.disabled
- Adiciona docs/SETUP.md com guia de setup em novo computador
- Adiciona scripts/setup-dev.sh para setup automatizado
- Remove GitHub Actions runner da VPS (agora usa apenas Forgejo)

CI/CD agora e feito exclusivamente via Forgejo Actions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:43:14 -03:00
esdrasrenan
454c3d5c3b fix(ci): corrige permissoes de arquivos gerados por Docker
All checks were successful
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 3m56s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Successful in 6m34s
Arquivos em src/generated/prisma/ criados por containers Docker
(como root) impediam o rsync de sobrescrever no proximo deploy.

Adiciona correcao de permissoes antes do rsync para garantir que
o usuario runner consiga sobrescrever os arquivos.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:32:47 -03:00
esdrasrenan
04226c16cc docs: adiciona troubleshooting para CI/CD Forgejo
Some checks failed
CI/CD Web + Desktop / Detect changes (push) Successful in 5s
Quality Checks / Lint, Test and Build (push) Successful in 4m30s
CI/CD Web + Desktop / Deploy Convex functions (push) Has been skipped
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Failing after 3m56s
Adiciona solucoes para problemas comuns:
- Regeneracao de hooks quando workflows nao disparam
- Correcao de erro de LevelDB lock
- Reinicio do runner apos reinicio do Forgejo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:26:44 -03:00
esdrasrenan
d067bda610 test: verificar CI apos regenerar hooks
Some checks failed
CI/CD Web + Desktop / Deploy Convex functions (push) Blocked by required conditions
CI/CD Web + Desktop / Detect changes (push) Successful in 8s
CI/CD Web + Desktop / Deploy (VPS Linux) (push) Has been cancelled
Quality Checks / Lint, Test and Build (push) Has been cancelled
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:24:43 -03:00
esdrasrenan
174b42eaab test: verificar CI apos reinicio do Forgejo
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:22:40 -03:00
esdrasrenan
f748be1931 test: verificar se CI dispara apos habilitar Actions no repo
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:20:49 -03:00
esdrasrenan
415d1c33f2 fix(ci): usa queue channel em vez de leveldb para evitar lock
O Forgejo estava falhando ao iniciar as queues devido ao LevelDB
nao funcionar bem em containers Docker. A mudanca para queue
type=channel resolve o problema de lock.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 22:16:27 -03:00
esdrasrenan
11dd44b54f test: trigger CI after queue cleanup 2025-12-16 22:11:36 -03:00
esdrasrenan
b38d6689ae test: trigger CI after Forgejo restart 2025-12-16 22:04:23 -03:00
esdrasrenan
707306ddf8 refactor(ci): otimiza workflows Forgejo removendo steps desnecessarios
- Remove setup pnpm e Node.js (bun ja inclui Node.js)
- Remove steps Verify Bun e Permissions diagnostic
- Remove Prune workspace (criava pnpm-workspace.yaml)
- Remove smoke test (register + heartbeat) - nao mais necessario
- Atualiza chaves de cache para usar apenas bun.lock
- Atualiza docs para indicar desktop_release comentado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 21:56:12 -03:00
esdrasrenan
acb2c35eeb ci: comenta job desktop_release (sem runner Windows)
O job ficava 'aguardando' eternamente porque nao existe runner
com labels [self-hosted, windows, desktop]. Comentado ate que
um runner Windows seja configurado.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 21:46:46 -03:00
esdrasrenan
c83cbd7e48 docs: atualiza documentacao do Forgejo para push direto
Remove referencias ao mirror (desabilitado) e documenta o novo fluxo
de push para ambos os remotes (GitHub + Forgejo).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 21:44:46 -03:00
esdrasrenan
a48d98f6c4 fix(ci): usa --no-owner --no-group no rsync
O rsync estava preservando o UID 1000 dos arquivos criados pelo
container Docker, causando erros de permissao para o runner (UID 999).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 21:21:56 -03:00
esdrasrenan
9a65679ca4 fix(ci): corrige permissoes do rsync no deploy VPS
O runner do Forgejo roda com UID 999, mas o workflow estava
fazendo chown para UID 1000, causando erros de permissao no rsync.

- Usa id -u e id -g para pegar o UID/GID correto do runner
- Remove --chown do rsync para usar permissoes do runner

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 21:00:39 -03:00
esdrasrenan
1f88880dbd test: trigger CI/CD via webhook 2025-12-16 20:34:45 -03:00
esdrasrenan
98a64f6166 fix(ci): corrige tipo dos inputs do workflow_dispatch
Adiciona type: boolean aos inputs para compatibilidade com Forgejo Actions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 20:27:40 -03:00
esdrasrenan
e5bf783432 docs: atualiza documentacao do Forgejo para refletir pull mirror
O Forgejo agora esta configurado como pull mirror do GitHub:
- Sincronizacao automatica a cada 10 minutos
- Usuario continua usando apenas git push origin main
- CI/CD dispara automaticamente apos sincronizacao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 20:23:13 -03:00
esdrasrenan
4a369ac783 fix(ci): corrige runs-on dos workflows Forgejo para self-hosted
O runner Forgejo esta configurado como self-hosted, entao todos os jobs
precisam usar as labels corretas em vez de ubuntu-latest.

Alteracoes:
- ci-cd-web-desktop.yml: job changes agora usa [self-hosted, linux, vps]
- quality-checks.yml: job lint-test-build agora usa [self-hosted, linux, vps]
- docs/FORGEJO-CI-CD.md: documentacao atualizada com essa diferenca

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 20:17:19 -03:00
esdrasrenan
aaa64e339c feat(ci): adiciona Forgejo Actions como alternativa ao GitHub Actions
Configura o Forgejo como plataforma de CI/CD self-hosted para evitar
custos futuros do GitHub Actions (a partir de marco/2026).

Arquivos adicionados:
- .forgejo/workflows/ci-cd-web-desktop.yml: workflow principal de deploy
- .forgejo/workflows/quality-checks.yml: lint, test e build
- forgejo/stack.yml: stack Docker do Forgejo para Swarm
- forgejo/setup-runner.sh: script de configuracao do runner
- docs/FORGEJO-CI-CD.md: documentacao completa

Forgejo rodando em: https://git.esdrasrenan.com.br

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 20:13:29 -03:00
esdrasrenan
771e25798d fix(checklist): corrige exibicao da descricao do template no ticket
O campo templateDescription nao estava sendo exibido porque o schema
Zod em src/lib/mappers/ticket.ts nao incluia esse campo, fazendo com
que ele fosse removido durante a validacao dos dados do servidor.

- Adiciona templateDescription ao schema Zod do checklist
- Remove logs de debug dos arquivos de backend e frontend

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 19:34:25 -03:00
rever-tecnologia
8e5eccfd8e debug: log resultado da funcao applyChecklistTemplateToItems 2025-12-16 16:38:38 -03:00
rever-tecnologia
c0713875b1 debug(checklist): adiciona logs no backend para investigar
Logs adicionados:
- Na criacao do template para ver se description esta sendo salva
- Na aplicacao do template para ver se description existe

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 16:28:05 -03:00
rever-tecnologia
db73e87cdc debug(checklist): adiciona logs para investigar templateDescription
Adiciona:
- Query debugTemplateAndTicketChecklist para verificar dados no backend
- Console.log no frontend para verificar dados recebidos

Esses logs serao removidos apos identificar o problema.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 16:19:37 -03:00
rever-tecnologia
58cda4f6ea fix(checklist): inclui templateDescription no retorno do ticket
O campo templateDescription estava sendo salvo no checklist mas nao
era incluido no mapeamento quando o ticket era retornado pela query.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 10:02:59 -03:00
rever-tecnologia
e844f16b7f fix(lint): ignora arquivos gerados do Convex no desktop
Adiciona apps/desktop/src/convex/_generated/** ao ignore do ESLint
para evitar warnings de diretivas eslint-disable nao utilizadas em
arquivos gerados automaticamente.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 09:49:40 -03:00
rever-tecnologia
6e8a6fe890 feat(checklist): exibe descricao do template e do item no ticket
- Adiciona campo templateDescription ao schema do checklist
- Copia descricao do template ao aplicar checklist no ticket
- Exibe ambas descricoes na visualizacao do ticket (template em italico)
- Adiciona documentacao de desenvolvimento local (docs/LOCAL-DEV.md)
- Corrige prisma-client.mjs para usar PostgreSQL em vez de SQLite

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 09:45:09 -03:00
esdrasrenan
6430d33c7c fix(desktop): adiciona permissao start-dragging e debug logging no Hub
- Adiciona core🪟allow-start-dragging para corrigir erro ACL do drag-region
- Adiciona logging detalhado no ChatHubWidget para debug de clicks
- Adiciona logging no comando open_chat_window para diagnostico
- Ajusta ordem size/position e set_ignore_cursor_events no Hub
- Remove set_hub_minimized apos build para evitar conflitos de timing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 01:17:42 -03:00
esdrasrenan
0cdbc082ab fix(desktop): evita sobreposicao entre janelas de chat e hub
- Fecha janelas individuais de chat ao abrir o Hub (2+ sessoes)
- Fecha o Hub ao voltar para apenas 1 sessao
- Evita problemas de clique causados por sobreposicao de janelas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:21:43 -03:00
esdrasrenan
2ba5f71580 fix(convex): permite sessoes sem lastAgentMessageAt em listMachineSessions
O filtro anterior excluia sessoes novas onde o agente ainda nao
enviou mensagem, impedindo que o segundo chat aparecesse no desktop.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:13:57 -03:00
esdrasrenan
5c5bf0385e fix(desktop): traz janela de chat para frente com novas mensagens
- Adiciona set_focus() apos show() para trazer janela para frente
- Adiciona unminimize() para garantir visibilidade
- Aplica mesma logica para Hub e janelas individuais de chat

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 00:02:12 -03:00
esdrasrenan
d9d5b495a1 fix(desktop): corrige nomes de parametros nos comandos Tauri
- Usa camelCase (ticketId, ticketRef) em vez de snake_case
- Tauri converte automaticamente snake_case do Rust para camelCase no JS

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 23:55:21 -03:00
esdrasrenan
1986bf286a fix(desktop): corrige inicializacao do estado minimizado do chat
- Inicializa isMinimized baseado na altura real da janela
- Usa h-full em vez de h-screen para layout correto
- Evita inconsistencia entre estado React e tamanho da janela

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 23:48:12 -03:00
esdrasrenan
c51b08f127 feat(desktop): implementa Convex React subscriptions para chat em tempo real
- Adiciona ConvexMachineProvider para autenticacao via machine token
- Cria hooks customizados (useMachineSessions, useMachineMessages, etc)
- Refatora ChatWidget e ChatHubWidget para usar useQuery/useMutation
- Remove polling e dependencia de Tauri events para mensagens
- Adiciona copia local dos arquivos _generated do Convex
- Remove componentes obsoletos (ChatSessionItem, ChatSessionList)

Beneficios:
- Tempo real verdadeiro via WebSocket (sem polling)
- Melhor escalabilidade e performance
- Codigo mais simples e maintivel
- Consistencia de estado entre multiplas janelas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 23:40:34 -03:00
esdrasrenan
a6af4aa580 fix(profile): corrige persistencia da remocao de avatar
O Better Auth usa cookie cache de 5 minutos para a sessao.
Quando o avatar era removido via Prisma, o cache ainda tinha
o avatar antigo. Agora usamos auth.api.updateUser para
atualizar o usuario e invalidar o cache da sessao.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 22:40:06 -03:00
esdrasrenan
92954b45c7 feat(automations): agrupa subcategorias por categoria no select
Melhora UX do select de subcategorias nas condicoes de automacao,
agrupando visualmente as subcategorias dentro de suas respectivas
categorias usando SelectGroup e SelectLabel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 22:32:39 -03:00
esdrasrenan
022e1f63ba feat: melhorias de UX e redesign de comentários
- Corrige sincronização do avatar no perfil após upload
- Reduz tamanho dos ícones de câmera/lixeira no avatar
- Remove atributos title (tooltips nativos) de toda aplicação
- Adiciona regra no AGENTS.md sobre uso de tooltips
- Permite desmarcar resposta no checklist (toggle)
- Torna campo answer opcional na mutation setChecklistItemAnswer
- Adiciona edição inline dos campos de resumo no painel de detalhes
- Redesenha comentários com layout mais limpo e consistente
- Cria tratamento especial para comentários automáticos de sistema
- Aplica fundo ciano semi-transparente em comentários públicos
- Corrige import do Loader2 no notification-preferences-form

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 22:05:27 -03:00
esdrasrenan
23ea426c68 style(checklist): padroniza botao Tornar opcional
Remove classes customizadas para usar mesmo estilo do Somente pendentes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:40:46 -03:00
esdrasrenan
95c50d9d62 style(checklist): ajusta icone ? e badges
- Icone ? com fundo preto semitransparente e texto branco
- Badges maiores (text-[11px], px-2.5) e mais espacadas (gap-2)
- Badge Pergunta com estilo slate ao inves de cyan

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:39:29 -03:00
esdrasrenan
17fe70ad71 style(checklist): melhora layout do item de pergunta
- Opcoes de resposta como botoes com estilo pill
- Opcao selecionada em preto com texto branco
- Badges menores e mais compactos
- Icone ? com estilo cyan
- Melhor espacamento geral

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:37:51 -03:00
esdrasrenan
358e1256b9 style(checklist): centraliza botoes verticalmente no item
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:35:34 -03:00
esdrasrenan
bf8975df2d style(checklist): ajusta estilo do botao Tornar opcional
Altera para variant outline com estilo similar ao botao Acessar remoto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:34:23 -03:00
esdrasrenan
ab2bcdc755 fix(checklist): inclui description, type, options e answer na query getById
Os campos estavam sendo salvos corretamente no banco, mas a query
getById nao os incluia no mapeamento de resposta, fazendo com que
as opcoes de resposta e descricao nao aparecessem no frontend.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:31:34 -03:00
esdrasrenan
d990450698 fix(email): corrige acentuacoes e adiciona dependencia radio-group
- Corrige todas as acentuacoes em portugues nos templates de e-mail
- Adiciona @radix-ui/react-radio-group como dependencia

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 20:05:53 -03:00
esdrasrenan
61c36dbb7c feat(email): redesign completo dos templates de e-mail
- Sincroniza cores com globals.css e status-badge.tsx
- Adiciona botoes grandes e destacados (cyan primary, preto secondary)
- Implementa badges pill com border-radius arredondado
- Importa fonte Inter do Google Fonts
- Adiciona icones em circulos grandes (64px) para templates de status
- Cria cards de informacao do ticket bem estruturados
- Aumenta espacamentos e padding para layout mais limpo
- Centraliza estrelas de avaliacao
- Melhora tipografia com pesos bem definidos

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 19:56:03 -03:00
esdrasrenan
eedd446b36 fix: corrige hydration, notificacoes e melhora visual
- Corrige hydration mismatch no Toaster (sonner) e ChatWidgetProvider
- Corrige API de preferencias de notificacao (typePreferences como string)
- Melhora visual do Switch (estado ativo em preto)
- Adiciona icones em circulos na pagina de notificacoes
- Corrige acentuacao em "Obrigatorio"
- Corrige centralizacao das estrelas de avaliacao nos e-mails
- Aplica Sentence case nos titulos dos templates de e-mail
- Adiciona script de teste de e-mail

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 19:52:46 -03:00
esdrasrenan
7a3791117b fix: corrige cliques nao responsivos e erros silenciosos
- Remove coluna de acoes sem implementacao em data-table
- Corrige loading states travados em new-ticket-dialog, close-ticket-dialog
- Adiciona finally blocks em forgot-password e reset-password
- Adiciona tratamento de erros em invokes do Tauri (ChatWidget, ChatHubWidget)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 19:15:51 -03:00
esdrasrenan
a285e6f252 fix: corrige indices na mutation updateAvatar
Usa by_tenant_requester e by_tenant_assignee com tenantId

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 18:59:22 -03:00
esdrasrenan
9d1908a5aa fix: remove duplicacao de comentario na troca de responsavel e corrige avatar
- Remove criacao automatica de comentario ao trocar responsavel (ja aparece na timeline)
- Adiciona migration removeAssigneeChangeComments para limpar comentarios antigos
- Adiciona campos description, type, options, answer ao schema de checklist no mapper
- Cria mutation updateAvatar no Convex para sincronizar avatar com snapshots
- Atualiza rota /api/profile/avatar para sincronizar com Convex ao adicionar/remover foto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 18:53:49 -03:00
rever-tecnologia
59e9298d61 fix(chat): filtra sessões problemáticas nas queries
Adiciona filtro para ignorar sessões sem lastAgentMessageAt
(sessões legadas que causam erro de shape no Convex)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 17:05:53 -03:00
rever-tecnologia
f451ca2e3b fix(chat): corrige sessao problematica diretamente pelo ID
- Usa patch direto sem buscar sessao (evita erro de shape)
- Encerra sessao pd71bvfbxx7th3npdj519hcf3s7xbe2j que estava causando erros

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:48:11 -03:00
rever-tecnologia
129ae70930 feat(api): adiciona endpoint para corrigir sessões de chat antigas
- POST /api/admin/fix-chat-sessions
- Chama mutation fixLegacySessions do Convex
- Requer autenticação de admin

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:43:34 -03:00
rever-tecnologia
10078c7aa7 fix(checklist): corrige acentuação e adiciona modal de exclusão
- Corrige acentuações: Opções, Não, Descrição, Obrigatório, máx
- Adiciona modal de confirmação para exclusão de itens do checklist
- Remove uso de confirm() nativo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:35:58 -03:00
rever-tecnologia
f1833be1ea fix(chat): melhora fixLegacySessions para buscar todas sessoes
- Busca todas as sessoes, nao apenas por index
- Corrige sessoes encerradas que tambem nao tem o campo
- Adiciona log das sessoes problematicas encontradas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:30:45 -03:00
rever-tecnologia
0f3ba07a5e feat(checklist): adiciona tipo pergunta e descricao nos itens
- Adiciona campo `type` (checkbox/question) nos itens do checklist
- Adiciona campo `description` para descricao do item
- Adiciona campo `options` para opcoes de resposta em perguntas
- Adiciona campo `answer` para resposta selecionada
- Atualiza UI para mostrar descricao e opcoes de pergunta
- Cria componente radio-group para selecao de respostas
- Adiciona mutation setChecklistItemAnswer para salvar respostas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:27:23 -03:00
rever-tecnologia
98b23af4b2 feat(tickets): Play e Chat atribuem responsavel automaticamente
- Botao Play habilitado mesmo sem responsavel
- Ao clicar Play sem responsavel, atribui usuario logado automaticamente
- Ao iniciar chat ao vivo sem responsavel, atribui usuario logado
- Adiciona mutation fixLegacySessions para corrigir sessoes antigas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 16:10:13 -03:00
rever-tecnologia
3bfc5793f1 fix(auth): sincroniza User e AuthUser com mesmo ID
- Aceitar convite: cria User com mesmo ID do AuthUser
- Criar usuario admin: usa ID do AuthUser no upsert do User
- Garante sincronismo entre tabelas de auth e dominio

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 15:52:28 -03:00
rever-tecnologia
7f63120336 fix(desktop,smtp): corrige chat polling e documenta SMTP
Desktop:
- Adiciona chamada para start_chat_polling no frontend
- Chat agora inicia corretamente quando usuario faz login

SMTP:
- Atualiza documentacao com nomes corretos das variaveis
- Variaveis configuradas no container da VPS

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 15:41:00 -03:00
rever-tecnologia
c776499403 fix(desktop): ajusta tamanho da janela hub expandida
- Reduz tamanho de 380x480 para 340x400 (cabe melhor na tela)
- Reposiciona antes de redimensionar para evitar corte
- Melhora logging de debug

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 15:06:37 -03:00
rever-tecnologia
8863fffc37 fix(desktop): corrige reinstalacao do RavenService e uso via IPC
- NSIS: remove servico antigo antes de instalar novo (corrige reinstalacoes)
- NSIS: adiciona retry ao iniciar servico
- agent.rs: usa service_client para aplicar politica USB via IPC primeiro
- Fallback para aplicacao direta se servico indisponivel

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 15:01:09 -03:00
rever-tecnologia
f6efc0d678 fix(settings): reverte centralização do Label Alterar senha no formulário
Label no formulário de perfil deve ficar alinhado à esquerda.
O botão na seção Segurança está centralizado corretamente.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:48:32 -03:00
rever-tecnologia
c0e0421369 style(settings): centraliza botão Alterar senha
Remove justify-start para centralizar conteúdo como o botão Encerrar sessão.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:47:22 -03:00
rever-tecnologia
c7b6d78ec2 fix(desktop): corrige travamento com múltiplos chats
Problemas corrigidos:
- Removido always_on_top de todas as janelas (causava competição de Z-order)
- Removido inner_size() síncrono que bloqueava a UI thread
- Simplificado process_chat_update para não fazer múltiplas operações de janela
- Removidos logs de debug

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:46:39 -03:00
rever-tecnologia
4ad0dc5c1e style(settings): centraliza label "Alterar senha"
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:38:58 -03:00
rever-tecnologia
424927573c fix(settings): posiciona background no topo absoluto do card
Background agora usa position absolute para cobrir desde o
topo do card, eliminando a faixa branca acima.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:34:41 -03:00
rever-tecnologia
5f0c9b68c3 fix(settings): aumenta altura do background animado do perfil
Aumenta de h-20 para h-28 e ajusta margin-top do CardHeader
para cobrir toda a área superior do card.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:24:08 -03:00
rever-tecnologia
3b6b9dfeac fix(desktop): corrige parâmetros invoke para snake_case no Tauri 2
Tauri 2 espera parâmetros em snake_case nos comandos Rust.
Corrigido: ticketId -> ticket_id, ticketRef -> ticket_ref, etc.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:24:01 -03:00
rever-tecnologia
915ca6d8ff fix(settings): corrige background animado do perfil preenchendo cantos
Remove rounded-t-2xl redundante que criava gap branco nos cantos
superiores. O card pai já possui overflow-hidden com rounded-2xl.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:13:28 -03:00
rever-tecnologia
05bc1cb7b4 fix(chat-widget): corrige referência a chatData não definida
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:11:14 -03:00
rever-tecnologia
d97e692756 debug(chat): adiciona logs detalhados no invoke do hub
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 14:03:08 -03:00
rever-tecnologia
c36e18117b fix(desktop): corrige problemas do chat (redimensionamento e cliques)
Correções implementadas:
1. Adiciona .resizable(false) nas janelas de chat e hub para impedir redimensionamento manual
2. Corrige área clicável invisível quando minimizado (janela agora tem tamanho correto)
3. Corrige clique na lista de sessões para expandir janela quando clicado
4. Diferencia abertura automática (minimizada) de abertura manual (expandida)

- Chat agora abre expandido quando clicado na lista do hub
- Chat abre minimizado quando nova mensagem chega (menos intrusivo)
- Janelas não permitem mais redimensionamento manual
- Área clicável agora corresponde ao tamanho visual da janela

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:53:53 -03:00
rever-tecnologia
6b137434fe fix(desktop): corrige permissoes e redimensionamento do chat
- Adiciona chat-hub explicitamente nas capabilities do Tauri
- Adiciona .resizable(false) nas janelas de chat e hub
- Corrige problema de comandos invoke nao funcionando no hub

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:51:34 -03:00
rever-tecnologia
a3b46e5222 debug(chat): adiciona mais logs para debugar clique na lista
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:46:05 -03:00
rever-tecnologia
ca59b6ed92 debug(chat): adiciona logs no clique da lista de sessoes
Logs para debugar problema de clique não funcionando
na lista de sessões do desktop.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:22:47 -03:00
rever-tecnologia
973e3496e2 fix(chat): corrige acentuação em "não lida"
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:19:56 -03:00
rever-tecnologia
6c6d53034f style(chat): remove icone do header para mais espaco
Remove o icone circular preto do header do chat
quando esta dentro de uma conversa, mantendo mais
espaco para informacoes do ticket.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:19:08 -03:00
rever-tecnologia
86f818c6f3 feat(chat): adiciona encerramento automatico por inatividade (12h)
- Sessoes de chat sao encerradas apos 12 horas sem atividade
- Criterios de encerramento automatico:
  1. Maquina offline (5 min sem heartbeat)
  2. Chat inativo (12 horas sem atividade) - NOVO
  3. Ticket orfao (sem maquina vinculada)
- Log detalhado com contagem por motivo de encerramento
- Evento no timeline com reason "inatividade_chat"

Isso evita acumular sessoes abertas indefinidamente
quando usuario esquece de encerrar o chat.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 13:06:24 -03:00
rever-tecnologia
29fbbfaa26 feat(desktop): adiciona hub de chats para multiplas sessoes
- Cria ChatSessionList, ChatSessionItem e ChatHubWidget no desktop
- Adiciona comandos Rust para gerenciar hub window
- Quando ha multiplas sessoes, abre hub ao inves de janela individual
- Hub lista todas as sessoes ativas com badge de nao lidos
- Clicar em sessao abre/foca janela de chat especifica
- Menu do tray abre hub quando ha multiplas sessoes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 12:13:47 -03:00
rever-tecnologia
95ab1b5f0c feat(chat): adiciona interface de lista de chats estilo WhatsApp
- Cria ChatSessionList e ChatSessionItem para listar sessões ativas
- Refatora ChatWidget para usar viewMode (list/chat)
- Ordena por não lidos primeiro, depois por última atividade
- Adiciona botão de voltar quando há múltiplos chats
- Persiste viewMode no localStorage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 12:03:40 -03:00
rever-tecnologia
bc5ba0c73a feat(checklist): adiciona opção de desmarcar todos os itens
O botão "Concluir todos" agora alterna para "Desmarcar todos"
quando todos os itens do checklist estão marcados como concluídos.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:46:13 -03:00
rever-tecnologia
2c21daee79 fix(profile): corrige persistência do avatar e melhora fluxo de salvamento
- Corrige campo de avatar na API (avatarUrl ao invés de image)
- Altera fluxo para salvar foto apenas ao clicar em "Salvar alterações"
- Adiciona preview local antes do upload definitivo
- Ajusta shader para preencher bordas arredondadas do card

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:25:25 -03:00
rever-tecnologia
4e2dd7f77e feat(settings): adiciona opção de remover foto de perfil
- Adiciona endpoint DELETE em /api/profile/avatar
- Mostra dois botões ao hover: câmera (upload) e lixeira (remover)
- Lixeira só aparece quando há uma foto definida

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:17:22 -03:00
rever-tecnologia
4bbd3fda24 feat(settings): adiciona shader animado no header do perfil
Substitui o degradê estático pelo shader animado usado nas páginas de login.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:14:45 -03:00
rever-tecnologia
b614fcd7dc style: melhora layout de login e settings
- Badge Helpdesk preta com texto branco
- Texto maior no painel direito das páginas de auth
- Badge de papel preta em settings
- Adiciona descrição na seção Segurança
- Espaçamento entre título e campos no formulário de login
- Autocomplete nos inputs de senha
- Link de notificações funcional no menu do usuário
- Fallback do avatar com fundo cinza e texto preto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:13:00 -03:00
rever-tecnologia
ab7dfa81ca feat: melhora página de perfil e integra preferências de notificação
- Atualiza cores das badges para padrão cyan do projeto
- Adiciona degradê no header do card de perfil
- Implementa upload de foto de perfil via API Convex
- Integra notificações do Convex com preferências do usuário
- Cria API /api/notifications/send para verificar preferências
- Melhora layout das páginas de login/recuperação com degradê
- Adiciona badge "Helpdesk" e título "Raven" consistente

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 11:00:02 -03:00
rever-tecnologia
1bc08d3a5f feat: adiciona fluxo de redefinição de senha e melhora página de configurações
- Adiciona página /recuperar para solicitar redefinição de senha
- Adiciona página /redefinir-senha para definir nova senha com token
- Cria APIs /api/auth/forgot-password e /api/auth/reset-password
- Adiciona notificação por e-mail quando ticket é criado
- Repagina página de configurações removendo informações técnicas
- Adiciona script de teste para todos os tipos de e-mail
- Corrige acentuações em templates de e-mail

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 10:42:08 -03:00
rever-tecnologia
300179279a feat(chat): adiciona separador de dias e melhora layout do header
- Adiciona separador de data entre mensagens de dias diferentes (estilo WhatsApp)
- Mostra "Hoje", "Ontem" ou data completa (ex: "segunda-feira, 15 de dezembro")
- Separa hostname da maquina em linha propria no header
- Hostname com truncate e tooltip para nomes longos

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 10:10:04 -03:00
rever-tecnologia
2293a0275a fix(chat): melhora confiabilidade da deteccao de novas mensagens
- Implementa deteccao dual: timestamp (lastActivityAt) + contador
- Adiciona persistencia de estado em ~/.local/share/Raven/chat-state.json
- Corrige race condition no servidor com refetch antes do patch
- Adiciona campo lastAgentMessageAt no schema do Convex
- Adiciona logs de diagnostico detalhados

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 09:44:03 -03:00
esdrasrenan
c4664ab1c7 feat(desktop): adiciona Raven Service e corrige UAC
- Implementa Windows Service (raven-service) para operacoes privilegiadas
- Comunicacao via Named Pipes sem necessidade de UAC adicional
- Adiciona single-instance para evitar multiplos icones na bandeja
- Corrige todos os warnings do clippy (rustdesk, lib, usb_control, agent)
- Remove fallback de elevacao para evitar UAC desnecessario
- USB Policy e RustDesk provisioning agora usam o servico quando disponivel

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-15 02:30:43 -03:00
esdrasrenan
caa6c53b2b style(sidebar): adiciona padding no topo do header
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 15:11:26 -03:00
esdrasrenan
117fbba175 refactor(sidebar): remove busca de tickets e aumenta brand
- Remove campo de busca "Buscar tickets" do header da sidebar
- Aumenta logo de 40x40 para 48x48 pixels
- Aumenta titulo e subtitulo da marca para melhor destaque

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 14:52:46 -03:00
esdrasrenan
2ab6ed595c fix(email-action): corrige cores e acentuação, adiciona drag and drop
- Badges de variáveis agora com fundo preto e texto branco
- Corrige todos os textos sem acentuação (Referência, Título, etc.)
- Adiciona suporte a drag and drop das badges de variáveis

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-14 00:00:36 -03:00
esdrasrenan
81e4b528d3 style(email-action): ajusta cores para cyan consistente com o projeto
- Substitui cores sky por cyan para manter consistencia visual
- Usa transparencia (cyan-50/60, cyan-200/60) conforme padrao do projeto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:56:55 -03:00
esdrasrenan
a4144dd39e feat(automations): redesign da acao de enviar e-mail com UX melhorada
- Cria componente EmailActionConfig dedicado para configuracao de e-mail
- Layout expandido (full-width) para melhor aproveitamento do espaco
- Variaveis como badges clicaveis que inserem no campo ativo
- Editor TipTap para mensagem com suporte a variaveis inline
- Autocomplete de variaveis ao digitar {{
- Organizacao visual melhorada com secoes claras

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:49:38 -03:00
esdrasrenan
e401053667 fix: remove overflow-hidden que bloqueava scroll do combobox
O overflow-hidden no PopoverContent impedia o scroll da
ScrollArea filha. Removendo, o scroll funciona corretamente.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:26:40 -03:00
esdrasrenan
10918d0893 fix: corrige scroll do dropdown e ajusta botão Adicionar
- Adiciona collisionPadding e overflow-hidden no SearchableCombobox
- Reduz tamanho do botão Adicionar no checklist (size=sm)
- Adiciona espaçamento antes do botão Adicionar

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:19:26 -03:00
esdrasrenan
3f5062c9b6 ui: ajusta espaçamento dos botões no modal de exclusão
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:10:33 -03:00
esdrasrenan
3c939f9ce4 ui: substitui confirm() por Dialog na exclusão de template
Melhora UX da exclusão de templates de checklist usando
modal estilizado ao invés do alert nativo do navegador.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:05:04 -03:00
esdrasrenan
cf7ff01e34 fix(chart): corrige overflow no gráfico Abertos x Resolvidos
- Adiciona YAxis com domain=[0, dataMax+1] para forçar mínimo em 0
- Muda type="monotone" para type="linear" para evitar curvas que
  ultrapassam os pontos de dados

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 23:01:31 -03:00
esdrasrenan
a04618afc0 fix(dashboard): corrige saída do modo apresentação
Ao clicar em "Encerrar" ou "Sair da tela cheia" no modo apresentação,
agora desativa corretamente o modo TV (remove param ?tv=1), evitando
que o fullscreen seja reativado automaticamente pelo useEffect.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 22:51:05 -03:00
esdrasrenan
245d5dc15b ui: melhorias de UX em várias telas
- Truncate com ellipsis na coluna Empresa (tickets-table)
- Botão excluir em templates de checklist + mutation remove no backend
- Botões Editar/Arquivar com size="sm" em checklist templates
- Hover com borda no botão "Tornar opcional" do checklist
- Botão Resetar em devices com estilo padrão (remove amarelo)
- Botão "Encerrar" no modo apresentação do dashboard
- Sidebar abre automaticamente ao sair do fullscreen

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 22:42:37 -03:00
esdrasrenan
06388b3688 ui: ajustes de layout em automações e checklist
- Reorganiza header da página de automações (título/descrição em cima, filtros embaixo)
- Aumenta espaçamento da badge "quando" na tabela
- Ajusta largura do input de checklist nos tickets para melhor distribuição

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 22:22:42 -03:00
esdrasrenan
e0fcd5bf7c feat(ui): checklist com rolagem e progresso 2025-12-13 22:11:13 -03:00
esdrasrenan
7c82ef18b3 fix(ui): ajustes em automações e checklist 2025-12-13 22:03:35 -03:00
esdrasrenan
aa3c1855b2 fix(desktop): remove instalacao WebView2 para evitar UAC duplo
Windows 10/11 ja possuem WebView2 pre-instalado. Usar "skip" elimina
o segundo prompt de UAC durante a instalacao.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 21:36:44 -03:00
esdrasrenan
74e64b2dba Merge remote-tracking branch 'origin/feat/checklists-e-automacoes' 2025-12-13 21:26:53 -03:00
esdrasrenan
b01818cf5f ui: dashboard filas com maior volume em largura total 2025-12-13 21:25:09 -03:00
esdrasrenan
e0530299b1 ui: padroniza altura e filtros em automações/checklists
- Usa size=sm nos botões (Nova automação/Novo template) para bater com Empréstimos\n- Ajusta altura do input e selects na listagem de automações e deixa filtros autoexplicativos
2025-12-13 21:22:15 -03:00
esdrasrenan
51c9cab79c fix: criação de template de checklist
- Permite isArchived na mutation checklistTemplates.create\n- Troca Select por SearchableCombobox no campo Empresa
2025-12-13 21:14:51 -03:00
esdrasrenan
8a045c0131 chore: atualiza Next.js para 16.0.10 2025-12-13 21:08:57 -03:00
esdrasrenan
548c2e44d4 fix: estabiliza templates de e-mail no CI
- Usa @react-email/components nos templates para evitar módulos ausentes no Bun 1.3.1\n- Ajusta preload do bun test para expor global Element (evita crash do PrismJS)
2025-12-13 21:06:06 -03:00
esdrasrenan
88a9ef454e feat: checklists em tickets + automações
- Adiciona checklist no ticket (itens obrigatórios/opcionais) e bloqueia encerramento com pendências\n- Cria templates de checklist (globais/por empresa) + tela em /settings/checklists\n- Nova ação de automação: aplicar template de checklist\n- Corrige crash do Select (value vazio), warnings de Dialog e dimensionamento de charts\n- Ajusta SMTP (STARTTLS) e melhora teste de integração
2025-12-13 20:51:47 -03:00
esdrasrenan
b3da53805f
Merge pull request #22 from esdrasrenan/feat/checklists-e-automacoes
feat: checklists em tickets + automações
2025-12-13 20:46:44 -03:00
esdrasrenan
4306b0504d feat(email): adota React Email em notificações e automações 2025-12-13 13:11:41 -03:00
esdrasrenan
58a1ed6b36 feat: automações com envio de e-mail 2025-12-13 12:38:08 -03:00
esdrasrenan
469608a10b ui: ajustar tabelas de automações e sidebar 2025-12-13 11:59:34 -03:00
esdrasrenan
e4d0c95791 fix: automações (gatilhos, histórico) e chat desktop 2025-12-13 11:26:42 -03:00
esdrasrenan
8ab510bfe9 feat: automações de tickets e testes de regressão 2025-12-13 10:30:29 -03:00
esdrasrenan
9f1a6a7401 web: ajustar espaçamento botões do chat 2025-12-12 23:44:37 -03:00
esdrasrenan
5df68c3917 desktop: fechar chat ao encerrar sessao 2025-12-12 23:41:52 -03:00
esdrasrenan
ce5ea5dad5 chat: remover placeholder [Anexo] 2025-12-12 23:31:35 -03:00
esdrasrenan
119ada60a6 chore(desktop): remover branding do NSIS no instalador 2025-12-12 23:03:49 -03:00
esdrasrenan
bf94dd9f7a fix(desktop): foco de scroll e contagem de não lidas no chat 2025-12-12 22:36:43 -03:00
esdrasrenan
b5b51b638b fix(desktop): permitir dialog de anexo no chat 2025-12-12 22:13:55 -03:00
esdrasrenan
09c212da6e fix(desktop): ajusta opener no chat 2025-12-12 21:51:19 -03:00
esdrasrenan
2f9cd56153 fix(desktop): corrige autostart do Raven no Windows 2025-12-12 21:36:49 -03:00
esdrasrenan
8cf13c43de fix(chat): melhora realtime e anexos no desktop 2025-12-12 21:36:32 -03:00
rever-tecnologia
3d45fe3b04 Ajusta auto-minimização do chat web e unifica realtime no desktop 2025-12-11 17:46:33 -03:00
rever-tecnologia
c65e37e232 fix: scroll para ultima mensagem apos enviar no chat web
Adiciona scroll explicito apos enviar mensagem com sucesso para
garantir que a mensagem mais recente fique visivel.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 16:43:54 -03:00
rever-tecnologia
8798566b7f fix: corrige query listChatMessages para mostrar mensagens recentes
A query estava usando .take(50) sem .order("desc"), retornando as 50
mensagens mais antigas ao inves das mais recentes. Isso fazia com que
novas mensagens do desktop nao aparecessem na web quando o ticket
tinha mais de 50 mensagens.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 16:34:48 -03:00
rever-tecnologia
efc3af3fde fix: corrige contador de mensagens nao lidas e chat desktop abrindo expandido
- Web: adiciona ref hasMarkedReadRef para evitar chamadas duplicadas ao
  markChatRead e garante que mensagens sejam marcadas como lidas mesmo
  quando o chat carrega apos isOpen se tornar true
- Desktop: aumenta periodo de estabilizacao do resize handler para 500ms,
  evitando que eventos transitórios alterem o estado isMinimized

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 16:16:23 -03:00
rever-tecnologia
082f2d67f1 fix: corrige sincronizacao de estado do chat entre abas
O BroadcastChannel nao estava funcionando corretamente porque
cada aba restaurava do localStorage independentemente na montagem.

Mudanca:
- Substituido BroadcastChannel pelo evento 'storage' do localStorage
- O evento storage dispara automaticamente em TODAS as outras abas
  quando o localStorage e alterado (mais confiavel)
- Removido broadcastChannelRef e CHAT_WIDGET_CHANNEL nao mais usados

Comportamento:
- Abrir/fechar/minimizar chat em uma aba sincroniza com todas as outras
- Estado persiste entre reloads via localStorage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 15:48:33 -03:00
rever-tecnologia
366bc4bf12 fix: corrige chat desktop abrindo expandido ao inves de minimizado
O problema era que o resize handler executava imediatamente na
montagem do componente, potencialmente lendo window.innerHeight
incorreto antes do Tauri aplicar o tamanho da janela.

Isso fazia o isMinimized ser setado como false (expandido) mesmo
quando a janela foi criada minimizada pelo Rust.

Correcao:
- Ignorar a primeira execucao do resize handler
- Preservar o estado inicial isMinimized=true definido no useState
- Sincronizar apenas em resizes reais (interacao do usuario)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 15:42:33 -03:00
rever-tecnologia
9a13e9c6fb fix: corrige chat abrindo automaticamente e zerando contador
O bug era causado pelo useEffect de auto-open que executava
na montagem inicial do componente, sobrescrevendo o estado
do localStorage (isMinimized) e fazendo o chat abrir expandido.

Isso causava o useEffect de markChatRead executar e zerar
o contador de mensagens nao lidas.

Correcao:
- prevSessionCountRef agora inicia como -1 (nao inicializado)
- Primeira execucao apenas inicializa o ref, sem abrir o chat
- Auto-open so acontece para sessoes NOVAS criadas APOS montagem
- Estado do localStorage (minimizado/expandido) e preservado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 15:39:03 -03:00
rever-tecnologia
f4880f32d2 Corrige comportamentos do chat e melhora UX
- Corrige contador de mensagens resetando sozinho (web)
  - Adiciona verificacao de visibilidade antes de marcar como lido
  - Verifica se aba esta ativa antes de marcar como lido

- Adiciona sincronizacao de estado do chat entre abas (web)
  - Usa BroadcastChannel para sincronizar aberto/fechado/minimizado
  - Persiste estado no localStorage

- Corrige chat minimizando sozinho no desktop (Rust)
  - Verifica se chat esta expandido antes de minimizar
  - Mantem chat aberto quando usuario esta usando

- Melhora encerramento automatico de sessoes de chat
  - Muda criterio de inatividade de chat para maquina offline
  - Sessao permanece ativa enquanto Raven estiver aberto
  - Encerra apenas quando maquina fica offline por 5+ min

- Corrige tabela de tickets em /devices
  - Adiciona acentuacao correta (Ultima atualizacao, Responsavel)
  - Torna linha inteira clicavel para abrir ticket

- Ajusta sidebar
  - Menu Tickets agora expande ao clicar (igual Cadastros)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 15:21:24 -03:00
rever-tecnologia
2682b6e8ac Adiciona endpoint de arquivamento e ajustes de infra
- Adiciona rota API para arquivar tickets por ID
- Atualiza configuracao do Prisma para PostgreSQL
- Simplifica workflow CI/CD
- Adiciona src/generated ao gitignore
- Atualiza documentacao e dependencias

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 13:25:36 -03:00
esdrasrenan
33a59634e7 Migra banco de dados de SQLite para PostgreSQL
- Muda provider Prisma de sqlite para postgresql
- Remove dependencias SQLite (better-sqlite3, adapter)
- Atualiza Better Auth para provider postgresql
- Simplifica prisma.ts removendo adapter SQLite
- Atualiza stack.yml para usar PostgreSQL existente com 2 replicas
- Remove logica de rebuild better-sqlite3 do start-web.sh
- Adiciona script de migracao de dados SQLite -> PostgreSQL
- Atualiza healthcheck para testar PostgreSQL via Prisma
- Habilita start-first deploy para zero-downtime

Melhoria: permite multiplas replicas e deploys sem downtime.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-11 00:35:27 -03:00
esdrasrenan
fb97d9bec8 fix: corrige multiplos problemas de chat e infra
- stack.yml: reduz replicas web para 1 (SQLite nao suporta escrita concorrente)
- chat.rs: janela de chat ja abre minimizada para evitar marcar mensagens como lidas prematuramente
- rustdesk.rs: preserva ID existente do RustDesk ao reprovisionar (evita criar novo ID a cada reinstalacao do Raven)
- ChatWidget.tsx: remove isMinimized das dependencias do useEffect para evitar memory leak de resubscriptions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 23:28:31 -03:00
esdrasrenan
695a44781a Corrige badge de mensagens nao lidas no chat web e desktop
- Web: markChatRead agora zera unreadByAgent na sessao ativa
- Desktop: usa unreadCount do backend ao inves de calcular localmente
- Backend: listMachineMessages retorna unreadCount da sessao
- Centraliza colunas da tabela de tickets do dispositivo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 22:48:18 -03:00
esdrasrenan
2f766af902 Centraliza colunas e ajusta padding na lista de tickets do dispositivo 2025-12-10 22:19:40 -03:00
esdrasrenan
afd24452cf Marca mensagens como lidas ao abrir chat web e zera badge 2025-12-10 22:17:45 -03:00
esdrasrenan
06b09f3da8 Sincroniza minimização pelo tamanho da janela e zera badge ao ler 2025-12-10 21:58:28 -03:00
esdrasrenan
15a6b5ca87 Mantém badge de novas mensagens enquanto chat está minimizado 2025-12-10 21:44:35 -03:00
esdrasrenan
6ab1789c0f Reabre chat minimizado quando chegam novas mensagens 2025-12-10 21:33:50 -03:00
esdrasrenan
b663268791 Evita múltiplos acessos remotos por provider ao reaproveitar entrada existente 2025-12-10 21:19:10 -03:00
esdrasrenan
81d1a7c7f7 Usa heartbeats reais para marcar máquina online no chat 2025-12-10 20:37:02 -03:00
esdrasrenan
da5a8f8380 Corrige verificação de heartbeat no chat ao vivo 2025-12-10 20:23:25 -03:00
rever-tecnologia
821cb7faa7 chore: upgrade next 16.0.8 and tidy local archive 2025-12-10 17:10:52 -03:00
rever-tecnologia
0df1e87f61 docs: note cron for local ticket archive 2025-12-10 15:17:55 -03:00
rever-tecnologia
0a6b808d99 feat: add health dashboard and local ticket archive 2025-12-10 14:43:13 -03:00
rever-tecnologia
0d78abbb6f docs(operations): documentar solucao definitiva do problema de OOM (10/12/2025)
Reescrita completa das secoes 10-13 do OPERATIONS.md:

- Secao 10: Historico completo do problema de memoria OOM
  - Cronologia do problema (Nov/2025 ate 10/12/2025)
  - Causa raiz identificada (versoes de documentos em memoria)
  - Solucao em 3 partes: crons, limpeza, codigo do heartbeat
  - Resultados: 450MB -> 17MB banco, 19GB -> 395MB memoria

- Secao 11: Erros shape_inference marcados como RESOLVIDOS
  - Problema eliminado pela limpeza do banco
  - Crons movidos = nenhum novo registro problematico

- Secao 12: Arquitetura final dos crons
  - Diagrama da nova arquitetura (Linux crontab -> Next.js -> Convex)
  - Mapeamento completo dos crons migrados
  - Configuracao atual do crontab na VPS

- Secao 13 (NOVA): Guia de monitoramento pos-correcao
  - Metricas esperadas para sistema saudavel
  - Comandos de verificacao
  - Procedimento de limpeza (se necessario)
  - Resumo de commits e backups disponiveis

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 10:51:58 -03:00
rever-tecnologia
b6f69d7046 fix(heartbeat): evitar versoes desnecessarias comparando dados antes de atualizar
- Compara inventory/metrics com valores atuais antes de incluir no patch
- Usa JSON.stringify para comparacao eficiente de objetos
- Filtra campos de metadata que realmente mudaram
- Evita criacao de versoes quando heartbeat envia dados identicos

Isso previne o acumulo de versoes no Convex que causava OOM.

Reducao de memoria apos limpeza manual:
- DB: 450MB -> 16MB (96%)
- RAM: 7GB -> 189MB (97%)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 10:21:35 -03:00
rever-tecnologia
178c7d7341 fix(convex): mover cron jobs para API HTTP + crontab do Linux
Problema:
- Cron jobs do Convex criam registros em _scheduled_job_logs
- Convex self-hosted carrega TODAS as versoes em memoria
- 1488 execucoes/dia = ~45k registros/mes acumulando
- Uso de memoria chegando a 19GB, causando 12 OOM kills/dia

Solucao:
- Criar endpoints HTTP em /api/cron/* para substituir crons
- Desabilitar crons no Convex (comentados em crons.ts)
- Chamar endpoints via crontab do Linux

Novos arquivos:
- src/app/api/cron/chat-cleanup/route.ts
- src/app/api/cron/usb-cleanup/route.ts
- scripts-static/* (copiado da VPS para versionamento)

Documentacao:
- docs/OPERATIONS.md secao 12 com instrucoes do crontab

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-10 08:51:32 -03:00
esdrasrenan
e2dde8510a fix(convex): adicionar logs obrigatorios em cron jobs para evitar shape_inference errors
- Adicionar console.log no inicio de autoEndInactiveSessions (liveChat.ts)
- Adicionar console.log no inicio de cleanupStalePendingPolicies (usbPolicy.ts)
- Documentar problema de shape_inference e solucao em OPERATIONS.md (Secao 11)
- Atualizar .env.example com BETTER_AUTH_SECRET de 32+ caracteres

O shape_inference do Convex self-hosted falha ao unificar arrays vazios
(logLines: []) com arrays de strings (logLines: ["msg"]). Garantindo que
todo cron job produza ao menos um log, evitamos o conflito de tipos.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-10 00:01:49 -03:00
esdrasrenan
48d9de8dd1 test(machines): atualizar mock para suportar tabela machineHeartbeats
Adicionar mock da nova tabela machineHeartbeats com metodo first()
para corrigir teste getById apos refatoracao do heartbeat.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 23:30:25 -03:00
esdrasrenan
4fbd521fa8 refactor(convex): separar heartbeat em tabela dedicada para evitar OOM
- Criar tabela machineHeartbeats para armazenar lastHeartbeatAt separadamente
- Modificar heartbeat para so atualizar machines quando ha mudancas reais
- Atualizar queries listByTenant e getById para usar nova tabela
- Reducao drastica de versoes de documentos criadas a cada heartbeat

Antes: ~54 versoes por maquina (3524 linhas para 65 maquinas)
Agora: heartbeat atualiza documento leve, machines so muda com dados novos

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 23:26:30 -03:00
esdrasrenan
cf28ad2ee4 fix(convex): prevent OOM by filtering large inventory fields
- Add INVENTORY_BLOCKLIST to filter 'software' and 'extended' fields
  from machine metadata (these fields can be 100KB+ each)
- Add compactMachineMetadata migration to clean existing large documents
- Preserve essential fields: metrics, postureAlerts, collaborator,
  inventory.os, cpu, memory, disks, network, services

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 22:36:03 -03:00
esdrasrenan
508f915cf9 fix: corrigir memory leaks e testes de mocks
- Fechar ConvexClient antigo antes de criar novo (evita memory leak)
- Adicionar flag disposed para prevenir race condition em useEffect
- Reduzir polling SSE de 1s para 5s (balanco entre responsividade e carga)
- Adicionar .take() aos mocks de testes para compatibilidade

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 21:49:04 -03:00
esdrasrenan
638faeb287 fix(convex): corrigir memory leak com .collect() sem limite e adicionar otimizacoes
Problema: Convex backend consumindo 16GB+ de RAM causando OOM kills

Correcoes aplicadas:
- Substituido todos os .collect() por .take(LIMIT) em 27+ arquivos
- Adicionado indice by_usbPolicyStatus para otimizar query de maquinas
- Corrigido N+1 problem em alerts.ts usando Map lookup
- Corrigido full table scan em usbPolicy.ts
- Corrigido subscription leaks no frontend (tickets-view, use-ticket-categories)
- Atualizado versao do Convex backend para precompiled-2025-12-04-cc6af4c

Arquivos principais modificados:
- convex/*.ts - limites em todas as queries .collect()
- convex/schema.ts - novo indice by_usbPolicyStatus
- convex/alerts.ts - N+1 fix com Map
- convex/usbPolicy.ts - uso do novo indice
- src/components/tickets/tickets-view.tsx - skip condicional
- src/hooks/use-ticket-categories.ts - skip condicional

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-09 21:41:30 -03:00
rever-tecnologia
a4b46b08ba fix(desktop): adicionar attemptSelfHeal nas dependencias do useEffect
Corrige warning do eslint sobre dependencia faltante no hook useEffect
da assinatura de chat updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 20:32:26 -03:00
rever-tecnologia
3396e930d4 feat(frontend): implementar paginacao numerada em listagens de tickets
- Adiciona tickets.listPaginated no backend com paginacao nativa Convex
- Converte TicketsView para usePaginatedQuery com controles numerados
- Converte PortalTicketList para usePaginatedQuery com controles numerados
- Atualiza tauri e @tauri-apps/api para versao 2.9

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 20:17:22 -03:00
rever-tecnologia
91ac6c416c fix(convex): use collect+slice instead of take for test compatibility
The take() method isn't available in test mocks, so using
collect() followed by slice() to limit results.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 19:34:58 -03:00
rever-tecnologia
3a37892864 refactor(convex): replace collect() with take() to prevent OOM
- liveChat.ts: limit sessions/messages queries (take 50-500)
- tickets.ts: batch delete operations, limit playNext/reassign (take 100-2000)
- reports.ts: limit ticket/user/machine queries (take 500-2000)
- machines.ts: limit machine queries for registration/listing (take 500)
- metrics.ts: limit device health summary (take 200)
- users.ts: limit user search in claimInvite (take 5000)
- alerts.ts: limit company/alert queries (take 500-1000)
- migrations.ts: limit batch operations (take 1000-2000)

These changes prevent the Convex backend from loading entire tables
into memory, which was causing OOM kills at 16GB and WebSocket
disconnections (code 1006).

Expected RAM reduction: 60-80% at peak usage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 19:23:10 -03:00
rever-tecnologia
c3eb2d3301 Scale web service to 2 replicas for zero-downtime rollouts 2025-12-09 16:52:57 -03:00
rever-tecnologia
58319ec0f9 Reset desktop chat unread badge when opened 2025-12-09 16:46:53 -03:00
rever-tecnologia
2ea0952f16 Fix chat badge padding and clamp relative time labels 2025-12-09 16:38:52 -03:00
rever-tecnologia
1d3580b187 Use Convex WS client in desktop chat runtime 2025-12-09 15:31:08 -03:00
rever-tecnologia
988bf25010 Use only WS for machine chat subscriptions 2025-12-09 14:56:53 -03:00
rever-tecnologia
d2108ce16b fix(desktop): corrigir formato do path das funcoes Convex
- Usar formato "module.js:function" exigido pelo Convex self-hosted
- checkMachineUpdates, listMachineMessages, postMachineMessage, etc.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 13:29:27 -03:00
rever-tecnologia
0a55c2e66c fix(desktop): protecao extra contra localhost em redirects
- Adicionar verificacao final antes de window.location.href
- Substituir localhost por URL de producao como fallback
- Adicionar logs de debug para diagnostico

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 13:27:03 -03:00
rever-tecnologia
1249b4ec26 fix(desktop): passar ticket_ref em todas as chamadas open_chat_window
- Adicionar ticket_ref ao comando Tauri open_chat_window
- Passar ticket_ref nas chamadas do tray menu
- Usar 0 como fallback para deeplinks

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 13:12:33 -03:00
rever-tecnologia
d20ebf7416 fix(chat): ajustes de texto, layout e icone do menu
- Mudar texto 'Chat #' para 'Ticket #' no desktop
- Passar ticketRef via URL para exibir numero correto
- Ajustar tamanho da janela minimizada (240px)
- Incluir ticketRef no checkMachineUpdates (Convex)
- Ajustar padding dos botoes no chat web
- Mudar icone 'Todos os tickets' para ClipboardList

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 13:06:03 -03:00
rever-tecnologia
3c2d1824fb fix(chat): correções de SSE, inicialização e área de clique
- Substituir WebSocket por SSE para real-time (chat.rs)
- Corrigir inicialização do chat runtime em lib.rs
- Iniciar unreadByMachine em 0 ao criar sessão (liveChat.ts)
- Corrigir área de clique do chip minimizado (pointer-events)
- Corrigir roteamento SPA no Tauri (index.html?view=chat)
- Corrigir estado inicial isMinimized como true

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 11:51:33 -03:00
esdrasrenan
3aee1a6694 desktop/chat: tipar reduce e manter fallback 2025-12-09 02:11:22 -03:00
esdrasrenan
e08dc21003 desktop/chat: fallback de polling se WS falhar 2025-12-09 01:53:51 -03:00
esdrasrenan
fe361ff4d8 livechat: inicializar sessão com não lidas prévias 2025-12-09 01:44:24 -03:00
esdrasrenan
c7711dfda5 fix(chat desktop): abrir/minimizar chat ao detectar unread via Convex 2025-12-09 01:20:02 -03:00
esdrasrenan
daba03d25d fix(handshake): evitar redirect para localhost quando em produção 2025-12-09 01:13:36 -03:00
esdrasrenan
a7d9803c97 chore(chat): remover WS dedicado e usar apenas Convex 2025-12-09 01:08:59 -03:00
esdrasrenan
f8a472ee46 chore(chat): tipar cliente Convex do desktop sem any 2025-12-09 01:04:53 -03:00
esdrasrenan
a8f5ff9d51 feat(chat): desktop usando Convex WS direto e fallback WS dedicado 2025-12-09 01:01:54 -03:00
Seu Nome
8db7c3c810 fix: adiciona Card ao histórico de chat e lint no deploy
Histórico de chat:
- Adiciona Card branco igual à linha do tempo
- Corrige acentuações (Histórico, sessão, sessões)

CI/CD:
- Adiciona step de lint antes do build no workflow de deploy
- Se o lint falhar, o deploy é cancelado (fail fast)
- Evita que código com erros de lint seja deployado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 17:05:34 -03:00
Seu Nome
6147de138b fix: corrige erro de lint no useMemo do historico de chat
Extrai chatHistory?.sessions para variavel antes do useMemo
para satisfazer o React Compiler que exigia dependencias inferidas
iguais as especificadas manualmente.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 16:20:12 -03:00
Seu Nome
f89541c467 feat: melhora visualizacao do historico de chat
- Remove fundo cinza/Card do historico de chat
- Agrupa sessoes por dia (Hoje, Ontem, data completa)
- Adiciona expansao/colapso por dia e por sessao
- Implementa paginacao de dias (5 por vez) e mensagens (20 por vez)
- Move historico para baixo da timeline (web e portal)

Estrutura escalavel para muitas interacoes de chat.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 16:10:48 -03:00
Seu Nome
b916ce3083 fix: remove docker service update redundante que causava downtime
O step "Ensure Convex service envs" fazia docker service update
para adicionar env vars que já são passadas pelo stack.yml via
substituição de variáveis. Isso disparava um rolling update
desnecessário com ~60s de downtime a cada deploy.

As env vars (MACHINE_PROVISIONING_SECRET, etc) já são definidas
no stack.yml e interpoladas do .env durante o docker stack deploy.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 15:29:53 -03:00
Seu Nome
5e4b3fd5a5 chore: oculta menu Incidentes da sidebar
- Adiciona hidden: true no item Incidentes
- Página /incidentes continua acessível via URL direta

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 15:22:40 -03:00
Seu Nome
40e2c01abd fix: zero-downtime deploy com start-first e healthcheck
- Remove docker service update --force que causava downtime
- Agrupa env vars do Convex em um único update (evita múltiplos restarts)
- Adiciona delay: 10s e monitor: 30s no update_config
- Healthcheck do web usa /api/health com timeout
- Ajusta start_period: 180s (web) e 60s (convex)
- Convex backend não é mais forçado a reiniciar após stack deploy

Fluxo correto de deploy:
1. docker stack deploy detecta mudança
2. Novo container é criado (start-first)
3. Swarm espera healthcheck passar
4. Swarm espera monitor period (30s)
5. Container antigo é removido
6. Zero downtime durante todo o processo

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 15:07:13 -03:00
Seu Nome
d8936899ee fix: chat nativo só aparece quando agente envia primeira mensagem
- Remove abertura automática do chat quando sessão é iniciada
- Chat só aparece minimizado quando há novas mensagens (primeira msg do agente)
- Remove notificação nativa redundante na criação da sessão
- Mantém evento session-started para outros usos internos

Fluxo correto:
1. Agente inicia chat → nada aparece no desktop
2. Agente envia mensagem → chat aparece minimizado com badge
3. Usuário clica → chat expande

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 14:36:40 -03:00
Seu Nome
2b17d278f1 fix: melhora mensagem de erro de conexão no portal
- Substitui mensagem técnica por "Não foi possível conectar ao servidor"
- Muda cor do alerta de vermelho para âmbar (menos alarmante)
- Remove detalhes técnicos do erro (ficam apenas no console)
- Adiciona dica para verificar conexão com internet

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 13:58:40 -03:00
Seu Nome
69bb9b8acf fix: aumenta largura da janela minimizada para não cortar badge
- Aumenta de 210px para 220px para acomodar badge completamente

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 13:55:14 -03:00
Seu Nome
0afdba1635 fix: corrige chat reabrindo sozinho e melhora mensagens
- Remove window.show() que forçava chat reabrir a cada polling
- Chat só abre minimizado quando há NOVAS mensagens (janela não existia)
- Se usuário fechou o chat, não reabre automaticamente
- Corrige acentuação: "Voce" → "Você", "nao" → "não"
- Simplifica toast para "Chat ao vivo iniciado"
- Melhora mensagem de erro quando máquina está offline
- Loga erro técnico no console ao invés de exibir para usuário

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 12:22:44 -03:00
Seu Nome
e66b3cce92 fix: ajustes visuais na janela de chat do desktop
- Remove scrollbars com overflow: hidden no CSS
- Aumenta tamanho da janela minimizada (210x52) para não cortar badge
- Adiciona bordas arredondadas (rounded-2xl) no chat expandido
- Adiciona sombra (shadow-xl) no chat expandido

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 11:31:43 -03:00
Seu Nome
d2b8c27206 feat: melhorias visuais na janela de chat do desktop
- Desabilita sombra da janela para transparência funcionar corretamente
- Adiciona badge de mensagens não lidas no chip minimizado
- Ajusta tamanho da janela minimizada para acomodar badge e texto offline
- Mostra chat minimizado com badge quando há novas mensagens (menos intrusivo)
- Adiciona listener para atualização de unread count em tempo real

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 11:14:15 -03:00
Seu Nome
24dee5d5eb feat: melhorias no sistema de chat ao vivo
- Chat do agente abre expandido automaticamente ao iniciar nova sessao
- Toasts fecham apos tempo fixo independente do foco da janela
- Janela de chat do desktop com transparencia (sem fundo branco)
- Chat reabre quando usuario abre o Raven (duplo clique no tray)
- Chat nao reabre sozinho com novas mensagens (apenas notificacao)
- Mensagem de toast simplificada: "Chat ao vivo iniciado"
- Reduz intervalo de polling SSE de 2s para 1s (mais responsivo)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-08 11:06:01 -03:00
esdrasrenan
3700ac9dad fix: adiciona detalhes de erro na API de preferências 2025-12-07 21:16:53 -03:00
esdrasrenan
7ecb4c1110 fix: corrige tipo JSON para String no SQLite e acentuação nos textos
- Altera typePreferences e categoryPreferences de Json para String no Prisma
- Atualiza API de preferências para fazer parse/stringify de JSON
- Corrige todos os textos sem acentuação nos componentes de notificação

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 21:09:02 -03:00
esdrasrenan
f2c0298285 feat: sistema completo de notificacoes por e-mail
Implementa sistema de notificacoes por e-mail com:

- Notificacoes de ciclo de vida (abertura, resolucao, atribuicao, status)
- Sistema de avaliacao de chamados com estrelas (1-5)
- Deep linking via protocolo raven:// para abrir chamados no desktop
- Tokens de acesso seguro para visualizacao sem login
- Preferencias de notificacao configuraveis por usuario
- Templates HTML responsivos com design tokens da plataforma
- API completa para preferencias, tokens e avaliacoes

Modelos Prisma:
- TicketRating: avaliacoes de chamados
- TicketAccessToken: tokens de acesso direto
- NotificationPreferences: preferencias por usuario

Turbopack como bundler padrao (Next.js 16)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 20:45:37 -03:00
esdrasrenan
cb6add1a4a chore: remove campo is_using_sse nao usado do ChatPollerHandle
O campo estava duplicado - ChatRuntime ja tem seu proprio is_using_sse.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 18:11:49 -03:00
esdrasrenan
28598cb04d fix: corrige link do ticket no chat widget
Remove /dashboard do path do ticket.
Antes: /dashboard/tickets/{id}
Depois: /tickets/{id}

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 17:26:03 -03:00
esdrasrenan
a0edaf8adb fix: redimensiona janela de chat ao minimizar/expandir
- Adiciona funcao set_chat_minimized que redimensiona a janela
- Modo minimizado: 200x56 (tamanho do chip)
- Modo expandido: 380x520 (tamanho completo)
- Janela reposiciona automaticamente no canto inferior direito
- Adiciona comando is_chat_using_sse para verificar modo de conexao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 17:12:29 -03:00
esdrasrenan
53376fe5b0 fix: convert strings to Uint8Array for @noble/hashes v2
@noble/hashes v2 no longer accepts strings directly, only Uint8Array.
Added utf8() helper to encode strings before hashing.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 16:34:28 -03:00
esdrasrenan
d01c37522f feat: SSE para chat desktop, rate limiting, retry, testes e atualizacao de stack
- Implementa Server-Sent Events (SSE) para chat no desktop com fallback HTTP
- Adiciona rate limiting nas APIs de chat (poll, messages, sessions)
- Adiciona retry com backoff exponencial para mutations
- Cria testes para modulo liveChat (20 testes)
- Corrige testes de SMTP (unit tests para extractEnvelopeAddress)
- Adiciona indice by_status_lastActivity para cron de sessoes inativas
- Atualiza stack: Bun 1.3.4, React 19, recharts 3, noble/hashes 2, etc

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 16:29:18 -03:00
esdrasrenan
0e0bd9a49c Otimizações de performance e correções no chat ao vivo
- Corrigir acentuações (sessão, sessões, duração)
- Auto-minimizar chat nativo quando sessão termina
- Corrigir race condition em markMachineMessagesRead (Promise.all)
- Adicionar paginação no cron autoEndInactiveSessions (.take(50))
- Otimizar listMachineMessages com limite de 100 mensagens
- Corrigir memory leak no ChatWidget (limite de 200 mensagens)
- Exibir estado offline quando não há sessão ativa

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 15:14:47 -03:00
esdrasrenan
115c5128a6 Consolidate chat timeline events + auto-end inactive sessions
Timeline consolidation:
- Replace multiple LIVE_CHAT_STARTED/ENDED events with single LIVE_CHAT_SUMMARY
- Show total duration accumulated across all sessions
- Display session count (e.g., "23min 15s total - 3 sessoes")
- Show "Ativo" badge when session is active

Auto-end inactive chat sessions:
- Add cron job running every minute to check inactive sessions
- Automatically end sessions after 5 minutes of client inactivity
- Mark auto-ended sessions with "(encerrado por inatividade)" flag

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 14:44:34 -03:00
esdrasrenan
409da8afda Fix duration format and minimized chat layout
- Format duration as hours+minutes when > 60min (e.g., 2h 26min)
- Change minimized chat to compact chip style (matching web)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 14:37:28 -03:00
esdrasrenan
229c9aa1c7 Match desktop chat layout with web version
- Change agent icon from Headphones to MessageCircle
- Adjust avatar size to size-7 and icons to size-3.5
- Reposition attach button next to send button (textarea -> attach -> send)
- Add Online indicator in header with animated green dot
- Implement minimized state (collapsed view like web)
- Hide web chat widget when running in Tauri context (avoid duplicate)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 13:58:36 -03:00
esdrasrenan
7e270bcd3b Update chat layout to match web version
- Client messages on left with white background and border
- Agent messages on right with black background
- Added circular avatars (User icon for client, Headphones for agent)
- Improved spacing and visual consistency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 13:24:40 -03:00
esdrasrenan
fd0e29514a fix(desktop): restore custom chat window header
Revert to frameless window with custom header containing
minimize/close buttons and drag region.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 13:20:35 -03:00
esdrasrenan
c217a40030 feat(desktop): add file attachments and native chat window
- Add file upload support in chat (PDF, images, txt, docs, xlsx)
  - Limited to 10MB max file size
  - Only allowed extensions for security
- Use native Windows decorations for chat window
- Remove ChatFloatingWidget (replaced by native window)
- Simplify chat event listeners (window managed by Rust)
- Fix typo "sessao" -> "sessão"

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 13:09:55 -03:00
esdrasrenan
2f89fa33fe fix(desktop): use correct store path in chat widgets
ChatWidget and ChatFloatingWidget were using relative path
"machine-agent.json" instead of the full path with appLocalDataDir().
This caused "Maquina nao registrada" error in chat window.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 12:46:32 -03:00
esdrasrenan
d00c59e0b5 feat(desktop): auto-open chat window on new message
When a new message arrives from a support agent, the chat window
now opens automatically without user interaction.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 12:34:37 -03:00
esdrasrenan
faa6e28765 Add visual debug indicator to chat widget
Shows unread count and sessions count next to the chat button
for debugging purposes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 12:18:46 -03:00
esdrasrenan
b10548157e Add debug logs for chat and red border to end chat button
- Add detailed debug logs in Rust (chat.rs) to trace polling flow
- Add console.log in frontend (main.tsx) to trace event reception
- Add red border to "Encerrar" button in chat panels for better visibility

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 12:03:40 -03:00
esdrasrenan
4f81f62429 Fix unread badge - increment unreadByMachine when agent sends message
The unreadByMachine counter was never being incremented when agents sent
chat messages, causing the badge to always show 0. Now when an agent
(ADMIN/MANAGER/AGENT) posts a message to a ticket with an active chat
session, the counter is incremented properly.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 11:34:47 -03:00
esdrasrenan
88a3b37f2f Fix chat session management and add floating widget
- Fix session sync: events now send complete ChatSession data instead of
  partial ChatSessionSummary, ensuring proper ticket/agent info display
- Add session-ended event detection to remove closed sessions from client
- Add ChatFloatingWidget component for in-app chat experience
- Restrict endSession to ADMIN/MANAGER/AGENT roles only
- Improve polling logic to detect new and ended sessions properly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 11:16:56 -03:00
esdrasrenan
e4f8f465de Fix chat window behavior - remove auto-open on session start
- Remove automatic window opening when agent starts a chat session
- Remove automatic window focus when new messages arrive
- Only show Windows notification, user opens chat manually via tray icon
- Update notification message to instruct user to click Raven icon

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 04:07:38 -03:00
esdrasrenan
cd4b702198 Fix Tauri ACL permissions for chat windows
- Add chat-* window pattern to allow chat popup windows
- Add event permissions (listen, unlisten, emit)
- Add window permissions (close, hide, show, set-focus)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:58:49 -03:00
esdrasrenan
9c6f19f9a5 Fix chat permission for machine-linked users
Allow COLLABORATOR users who are linked to the ticket's machine
(via assignedUserId or linkedUserIds) to access the chat, not
just the ticket requester.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:49:09 -03:00
esdrasrenan
ebeda62cfb Fix lint errors and improve chat UI
Lint fixes:
- Move HIDDEN_EVENT_TYPES constant outside component to fix useMemo dependency
- Add eslint-disable comments for img elements using blob URLs

Chat widget improvements:
- Add view and download buttons with loading and success indicators
- Click image to open in new tab, download button to save file
- Show check icon after successful download

Chat history fixes:
- Fix title to "Histórico de chat" with proper accents
- Change agent icon from Headphones to MessageCircle
- Change agent icon background from primary to gray

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:39:14 -03:00
esdrasrenan
f0882c612f Fix ticket link hover color to black
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:20:45 -03:00
esdrasrenan
d766de4fda Add chat widget improvements and chat history component
Widget improvements:
- Pulsating badge with unread message count on floating button
- Clickable ticket reference link in chat header
- ExternalLink icon on hover

Desktop (Raven) improvements:
- Track previous unread count for new message detection
- Send native Windows notifications for new messages
- Focus chat window when new messages arrive

Chat history:
- New query getTicketChatHistory for fetching chat sessions and messages
- New component TicketChatHistory displaying chat sessions
- Sessions can be expanded/collapsed to view messages
- Pagination support for long conversations
- Added to both dashboard and portal ticket views

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:20:22 -03:00
esdrasrenan
b194d77d57 Add Windows native notifications for chat sessions
- Add tauri-plugin-notification for native Windows notifications
- Send notification when new chat session is started
- Configure notification permissions in capabilities

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:13:41 -03:00
esdrasrenan
f45ee91804 Fix chat widget UI and allow attachment-only messages
- Allow sending messages with only attachments (no text required)
- Change "Chat Ativo" header to just "Chat"
- Replace Headphones icon with MessageCircle for own messages
- Replace PhoneOff icon with XCircle for end chat button

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 03:09:51 -03:00
esdrasrenan
60e98dd47c Retorna status da maquina mesmo sem sessao de chat ativa
A query getTicketSession agora sempre retorna machineOnline,
permitindo que o botao de chat seja habilitado corretamente
quando a maquina esta online mas nao ha sessao ativa.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 02:54:56 -03:00
esdrasrenan
0bd9e993d5 Corrige verificacao de machineId para botao de chat
Usa ticket.machine?.id ao inves de ticket.machineId inexistente
para determinar se o botao de chat deve aparecer.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 02:42:22 -03:00
esdrasrenan
c9c08f8e38 Substitui emoji por icone no estado vazio de ticket
Troca o emoji de lupa pelo icone SearchX do lucide-react
para manter consistencia visual com o resto da plataforma.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 02:34:52 -03:00
esdrasrenan
3b1cde79df Melhora chat ao vivo com anexos e eventos de timeline
- Reestrutura visual do widget de chat (header branco, status emerald)
- Adiciona sistema de anexos com upload e drag-and-drop
- Substitui select nativo por componente Select do shadcn
- Adiciona eventos LIVE_CHAT_STARTED e LIVE_CHAT_ENDED na timeline
- Traduz labels de chat para portugues (Chat iniciado/finalizado)
- Filtra CHAT_MESSAGE_ADDED da timeline (apenas inicio/fim aparecem)
- Restringe inicio de chat a tickets com responsavel atribuido
- Exibe duracao da sessao ao finalizar chat

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 02:20:11 -03:00
esdrasrenan
9e676b06f9 Corrige widget de chat para nao quebrar sem API
- Verifica se api.liveChat.listAgentSessions existe antes de renderizar
- Retorna null no provider se Convex nao estiver sincronizado

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:32:34 -03:00
esdrasrenan
8c465008bf Adiciona widget de chat flutuante global
- Widget no canto inferior direito em todas as paginas
- Mostra sessoes de chat ativas do agente
- Suporta multiplas sessoes com seletor
- Badge com contador de mensagens nao lidas
- Pode minimizar ou fechar
- Query listAgentSessions para buscar sessoes ativas

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:31:00 -03:00
esdrasrenan
2a78d14a74 Redesenha widget de chat com visual moderno
- Layout estilo messenger com baloes de mensagem
- Avatares para agente (headphones) e usuario (user)
- Cores distintas: preto para agente, branco para cliente
- Header com status online/offline da maquina
- Input com Enter para enviar
- Scroll automatico para novas mensagens

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:24:33 -03:00
esdrasrenan
b7f150e2b7 Atualiza bun.lock apos remocao de dependencias
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:08:37 -03:00
esdrasrenan
5aa5736ba4 Remove dependencias nao utilizadas do desktop
Remove @assistant-ui/react, @assistant-ui/react-markdown e remark-gfm
que foram adicionadas mas nao estao sendo usadas (componente customizado)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:06:22 -03:00
esdrasrenan
ba91c1e0f5 Implementa sistema de chat em tempo real entre agente e cliente
- Adiciona tabela liveChatSessions no schema Convex
- Cria convex/liveChat.ts com mutations e queries para chat
- Adiciona API routes para maquinas (sessions, messages, poll)
- Cria modulo chat.rs no Tauri com ChatRuntime e polling
- Adiciona comandos de chat no lib.rs (start/stop polling, open/close window)
- Cria componentes React do chat widget (ChatWidget, types)
- Adiciona botao "Iniciar Chat" no dashboard (ticket-chat-panel)
- Implementa menu de chat no system tray
- Polling de 2 segundos para maior responsividade
- Janela de chat flutuante, frameless, always-on-top

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-07 01:00:27 -03:00
esdrasrenan
0c8d53c0b6 Add status badge to USB policy chip in device details
- Replace parentheses text with styled badge
- Use color-coded badges: success (green), warning (amber), error (red)
- Add APPLYING status support
- Match badge style with custom fields counter

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:52:56 -03:00
esdrasrenan
71757a1d59 Use neutral gray colors for info card
- Info icon in black (text-foreground)
- Badges in gray (bg-slate-100, border-slate-200)
- Badge text in black (text-foreground)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:44:54 -03:00
esdrasrenan
a1292df245 Improve info card styling with system colors
- Use primary/accent colors instead of hardcoded blue
- Increase text sizes (text-sm for title, text-xs for chips and note)
- Increase icon and chip sizes for better readability
- Use text-secondary for chip text, text-muted-foreground for note

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:43:46 -03:00
esdrasrenan
3aa348dc4a Add info card showing affected USB devices
- Shows affected devices: pen drives, external HDs, SD cards
- Clarifies that keyboards, mice, printers are not affected
- Uses subtle blue info card design with device chips

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:42:39 -03:00
esdrasrenan
d476761f22 Improve USB policy filters layout and sizing
- Use grid layout with 2 columns for better space usage
- Increase filter height to h-9 for better readability
- Move "Limpar" button to header row
- Add proper padding and spacing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:26:07 -03:00
esdrasrenan
873305fa7f Improve USB policy history with filters and pagination
- Fix bug where APPLYING status would not transition to APPLIED
- Add status and date range filters to policy history
- Add cursor-based pagination with "Load more" button
- Use DateRangeButton component for date filtering
- Reset filters and pagination when switching filters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:24:23 -03:00
esdrasrenan
5846c299ce Fix Convex validator to accept null for optional string fields
Rust serializes Option<String>::None as null, not undefined.
Updated reportUsbPolicyStatus mutation to accept both null and undefined
for error and currentPolicy fields.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 23:00:16 -03:00
esdrasrenan
20b63f4ad6 Fix USB policy reporting and improve agent reliability
- Fix Zod schema to accept null values (nullable) for error/currentPolicy
- Add robust logging system writing to raven-agent.log file
- Rewrite auto-start using winreg with validation
- Auto-start agent on app setup if credentials exist
- Reduce retry attempts to 2 (1 + 1 retry after 2s)
- Replace provider's remote access on ID change (prevents duplicates)
- Update agent to v0.2.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 22:43:15 -03:00
esdrasrenan
b60255fe03 Improve USB policy responsiveness and reliability
- Reduce USB policy polling from 60s to 15s for faster response
- Add retry with exponential backoff (2s, 4s, 8s) on report failures
- Add APPLYING state for real-time progress bar feedback
- Check if policy is already applied locally before re-applying
- Fix API schema to accept APPLYING status
- Update agent to v0.1.9

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 17:51:57 -03:00
esdrasrenan
23e7cf58ae Redesenho da UI de dispositivos e correcao de VRAM
- Reorganiza layout da tela de dispositivos admin
- Renomeia secao "Controles do dispositivo" para "Atalhos"
- Adiciona botao de Tickets com badge de quantidade
- Simplifica textos de botoes (Acesso, Resetar)
- Remove email da maquina do cabecalho
- Move empresa e status para mesma linha
- Remove chip de Build do resumo
- Corrige deteccao de VRAM para GPUs >4GB usando nvidia-smi
- Adiciona prefixo "VRAM" na exibicao de memoria da GPU
- Documenta sincronizacao RustDesk

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 17:01:40 -03:00
rever-tecnologia
c5150fee8f Fix useCallback missing dependency warning
Add config.machineId to openSystem dependency array

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 17:25:41 -03:00
rever-tecnologia
cf31e78edb Add requester filter and improve error messages
- Add requester filter to device tickets history page
- Create listMachineRequesters query to list unique requesters
- Add friendly API error formatting in desktop agent
- Translate validation errors to user-friendly Portuguese messages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 17:22:44 -03:00
rever-tecnologia
bb82efa9d3 Preserve machine identity based on hardware
- Add by_tenant_hostname index for hostname-based lookup
- Add third search in register mutation for hardware matching
- Search by hostname + MAC/serial when fingerprint/email differ
- Fallback to MAC/serial match across all tenant machines
- Preserves ticket history when user changes on same physical machine

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 17:00:19 -03:00
rever-tecnologia
376e81c9c7 Fix RustDesk remote access sync after agent install
- Reload config/token directly from store before syncing
- Avoid race condition when register() calls ensureRustdesk before React state updates
- Ensures machineId and token are always fresh from disk

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:52:05 -03:00
rever-tecnologia
bc41f6ae34 Update SMTP password
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:28:27 -03:00
rever-tecnologia
4b1198271d Add SMTP configuration and documentation
- Update .env.example with SMTP variables
- Create docs/SMTP.md with credentials and usage examples
- Tested successfully on 2025-12-05

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:14:15 -03:00
rever-tecnologia
bfcec46328 Fix RustDesk deep link to include self-hosted server
- Add RUSTDESK_SERVER and RUSTDESK_SERVER_KEY constants
- Update buildRustDeskUri to use ID@SERVER format
- Include server key in URI for proper server identification
- This allows 1-click connection without client pre-configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 16:10:30 -03:00
rever-tecnologia
90d2221240 Fix RustDesk sync before auto-launch redirect
The RustDesk data saved by Rust directly to file was not being
synced to the backend because the app redirected to the web platform
before the sync could complete.

- Reload store from disk in openSystem to get Rust-saved data
- Sync RustDesk before redirecting with 3s timeout
- Fire-and-forget sync to avoid blocking the redirect

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 15:38:35 -03:00
rever-tecnologia
2c47e84cab Salvar dados do RustDesk diretamente no machine-agent.json pelo Rust
- Resolve conflito entre Tauri Store (TypeScript) e escrita direta (Rust)
- O Rust agora salva a chave "rustdesk" no arquivo apos provisionamento
- O TypeScript pode ler os dados via Store.load() normalmente
- Garante que os dados do RustDesk estejam disponiveis para sincronizacao

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:56:12 -03:00
rever-tecnologia
736282a805 Corrigir sincronizacao automatica do RustDesk com o backend
- Corrigir indentacao do useEffect que dispara ensureRustdesk
- Adicionar logs detalhados em handleRustdeskProvision e ensureRustdesk
- Logs ajudam a diagnosticar falhas na sincronizacao do acesso remoto

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:42:49 -03:00
rever-tecnologia
01a16b7e5e Fix lint error: replace Spinner with IconRefresh
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 14:01:41 -03:00
rever-tecnologia
ff90f16cef Replace spinner with skeleton loading in emprestimos page
- Remove initial spinner and show skeleton layout immediately
- Add skeleton to stats cards during loading
- Show skeleton rows in table while data loads
- Provides better visual feedback with layout structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 13:55:55 -03:00
rever-tecnologia
b5b74a674d Fix emprestimos table column widths consistency
- Use colgroup with fixed pixel widths instead of percentages
- Add tableLayout: fixed with minWidth for stable layout
- Add truncate to cells to prevent content overflow
- Show placeholder in Actions column for non-active items
- Column widths now stay consistent across all filter states

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 13:44:55 -03:00
rever-tecnologia
51ce3e61c7 Fix emprestimos page accents and table layout
- Fix Portuguese accents in UI text (devolucao -> devolução, emprestimo -> empréstimo, etc.)
- Restructure table to always show header with fixed column widths
- Move empty state inside TableBody with colSpan to maintain layout consistency
- Column widths now stay consistent regardless of filter selection

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 13:40:18 -03:00
rever-tecnologia
0054b93d3c Improve emprestimos UX with skeleton loading and optimistic devolver
- Replace spinner with skeleton table rows during loading
- Apply optimistic closing for devolver dialog (instant feedback)
- Use toast.promise for background mutation with loading state
- Remove unnecessary isSubmitting state from devolver button
- Maintain table layout during filter changes (no layout shift)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 10:24:53 -03:00
rever-tecnologia
b7e7f99f99 Fix emprestimos table layout shift during filter changes
- Replace centered spinner with skeleton table rows
- Maintain table structure and width during loading
- Show 5 skeleton rows with proper column widths
- Prevents layout collapse when changing filters

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 10:11:43 -03:00
rever-tecnologia
7c5bc828cf Fix getById not returning USB policy fields
The getByIdHandler was missing usbPolicy, usbPolicyStatus,
and usbPolicyError fields, causing the chip to always show
"Permitido" instead of the actual policy value.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 10:06:18 -03:00
rever-tecnologia
4d158237c6 Fix X button focus ring showing on modal open
Change focus: to focus-visible: so the ring only appears
during keyboard navigation, not on initial modal open

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 09:51:40 -03:00
rever-tecnologia
1ee5b34158 Refactor USB control to modal with improved UX
- Move USB policy control from bottom of page to modal dialog
- Add "Controle USB" button in device controls section
- Show USB chip for all Windows devices (default to ALLOW)
- Add close button (X) with hover effect in modal header
- Fix all Portuguese accents in USB control component
- Position status badge at top of modal content
- Add variant prop to UsbPolicyControl (card/inline)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 09:50:11 -03:00
rever-tecnologia
06ebad930c Move USB policy control to modal dialog
- Add USB modal state and clickable InfoChip for USB policy chip
- Create Dialog with UsbPolicyControl component for USB management
- Add variant prop to UsbPolicyControl (card/inline) for flexible rendering
- Remove inline UsbPolicyControl from bottom of device page
- USB control now accessible by clicking USB chip in device summary

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 09:36:50 -03:00
rever-tecnologia
6007cf6740 Fix USB policy token hash bug
The getPendingUsbPolicy and reportUsbPolicyStatus functions were
comparing the plain token against the tokenHash in the database,
which would never match. Now properly hashing the token before
database lookup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 09:12:30 -03:00
rever-tecnologia
7469d3b5e6 Add USB policy improvements and emprestimos details modal
- Add cron job to cleanup stale pending USB policies every 30 min
- Add cleanupStalePendingPolicies mutation to usbPolicy.ts
- Add USB policy fields to machines listByTenant query
- Display USB status chip in device details and bulk control modal
- Add details modal for emprestimos with all loan information
- Add observacoesDevolucao field to preserve original observations
- Fix status text size in details modal title

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 08:24:56 -03:00
rever-tecnologia
e493ec9d5d Fix better-sqlite3 binding for Prisma adapter
- Force SKIP_SQLITE_REBUILD=false in stack.yml
- Copy binding to multiple locations including @prisma/client paths
- Include version-specific paths for Node.js compiled bindings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 16:31:51 -03:00
rever-tecnologia
e9a658341f Fix auth trustedOrigins and improve emprestimos page layout
- Add production domain to trustedOrigins explicitly
- Add health API endpoint for diagnostics
- Improve emprestimos page layout to match tickets design
- Use correct primary color for buttons
- Fix segmented control styling with rounded borders
- Use Empty component for empty state

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 16:20:04 -03:00
rever-tecnologia
326da8dae6 Skip TypeScript checking in production build
Adds typescript.ignoreBuildErrors to prevent OOM during build
on production server with limited memory.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 15:49:43 -03:00
rever-tecnologia
79bda730d5 Fix accent on page title "Empréstimos de Equipamentos"
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:53:45 -03:00
rever-tecnologia
38995b95c6 Improve loan page and add company filter to USB bulk control
- Update Next.js to 16.0.7
- Fix accent on menu item "Emprestimos" to "Empréstimos"
- Standardize loan page with project patterns (DateRangeButton, cyan color scheme, ToggleGroup)
- Add company filter to USB bulk policy dialog
- Update CardDescription text in devices overview
- Fix useEffect dependency warning in desktop main.tsx

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 14:52:27 -03:00
rever-tecnologia
063c5dfde7 Add equipment loan feature and USB bulk control
- Add emprestimos (equipment loan) module in Convex with queries/mutations
- Create emprestimos page with full CRUD and status tracking
- Add USB bulk control to admin devices overview
- Fix Portuguese accents in USB policy control component
- Fix dead code warnings in Rust agent
- Fix tiptap type error in rich text editor
2025-12-04 14:23:58 -03:00
rever-tecnologia
49aa143a80 Add USB storage device control feature
- Add USB policy fields to machines schema (policy, status, error)
- Create usbPolicyEvents table for audit logging
- Implement Convex mutations/queries for USB policy management
- Add REST API endpoints for desktop agent communication
- Create Rust usb_control module for Windows registry manipulation
- Integrate USB policy check in agent heartbeat loop
- Add USB policy control component in admin device overview
- Add localhost:3001 to auth trustedOrigins for dev

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 13:30:59 -03:00
Esdras Renan
0e9310d6e4 Handle RustDesk ID fallback to reported ID 2025-11-27 09:21:32 -03:00
Esdras Renan
2e735a7df4 Avoid recursive import in api mock for bun tests 2025-11-27 08:38:11 -03:00
Esdras Renan
09711d0465 Fix api mock import for bun test 2025-11-27 08:32:16 -03:00
Esdras Renan
cb0ee989c0 Make api mock preserve other Convex routes in rename test 2025-11-27 08:31:53 -03:00
Esdras Renan
35de633b6f Delay RustDesk provisioning until machineId is loaded 2025-11-27 08:29:33 -03:00
Esdras Renan
59e68b0032 Use RustDesk key param in launch URI 2025-11-27 08:20:30 -03:00
Esdras Renan
040a9e4569 Improve RustDesk connect button fallback 2025-11-27 08:14:18 -03:00
Esdras Renan
f7ad7f6a17 Fix admin rename payload and harden RustDesk ID sync 2025-11-26 21:00:08 -03:00
Esdras Renan
bd1bd4bef1 Expose visit status and performedAt in ticket list 2025-11-26 16:54:25 -03:00
Esdras Renan
66559eafbf feat(visits): concluir/reabrir visita sem poluir agenda 2025-11-26 14:21:31 -03:00
Esdras Renan
8f2c00a75a fix: stabilize web build path e cores do SLA 2025-11-25 17:32:40 -03:00
Esdras Renan
99b4d50589 chore(tray): adiciona tooltip 'Raven' 2025-11-25 16:34:33 -03:00
Esdras Renan
a1bd41c6db fix(tray): usa items sem ? e build()? direto 2025-11-25 15:00:49 -03:00
Esdras Renan
d65c0b9920 fix(tray): usa items()? com map_err para menu build 2025-11-25 14:56:21 -03:00
Esdras Renan
ffd42bd719 fix(tray): evita uso de ? em MenuBuilder (E0277) 2025-11-25 14:54:54 -03:00
Esdras Renan
54a07bc281 fix(tray): ajusta tipos para AppHandle e build do menu 2025-11-25 14:29:39 -03:00
Esdras Renan
06bb1133a8 feat(raven): adiciona tray, hide-on-close e autostart no Windows 2025-11-25 14:25:34 -03:00
Esdras Renan
e8b58187c9 feat(rustdesk): remove auto-run/atalho para evitar abrir GUI ao iniciar Raven 2025-11-25 14:16:45 -03:00
Esdras Renan
ccd8642629 chore(rustdesk): consome resultado de start_sequence para evitar warning 2025-11-25 14:02:43 -03:00
Esdras Renan
6329949c64 fix(rustdesk): evita inferência ambígua e ignora erros de start de serviço 2025-11-25 14:00:30 -03:00
Esdras Renan
f63dc1f2b8 fix(rustdesk): consome resultado do start_sequence para ajustar tipo 2025-11-25 13:58:58 -03:00
Esdras Renan
d01e28f481 fix(tauri): remove aspas no cmd /C para tauri.cmd com espaços 2025-11-25 13:57:52 -03:00
Esdras Renan
a5acc87588 fix(tauri): no Windows usa cmd /C tauri.cmd para evitar EINVAL 2025-11-25 13:56:35 -03:00
Esdras Renan
ff68348442 fix(tauri): usa tauri.cmd real via node_modules/.bin e cwd correto 2025-11-25 13:54:05 -03:00
Esdras Renan
bb6c3c16a1 fix: resolve tauri spawn path e alinhar cor do texto ao SLA 2025-11-25 13:52:16 -03:00
Esdras Renan
153b05efc8 chore(tauri): evita shell=true no stub para suprimir DEP0190 2025-11-25 13:47:47 -03:00
Esdras Renan
d76e9ed0cb fix(rustdesk): corrige retorno ao re-tentar start do serviço 2025-11-25 13:46:02 -03:00
Esdras Renan
24656afff3 fix(start-web): adiciona fallback de rebuild para better-sqlite3 2025-11-25 13:23:57 -03:00
Esdras Renan
e9ba1790fb fix(rustdesk): reaplica install-service e valida estado RUNNING 2025-11-25 13:18:10 -03:00
Esdras Renan
56cd5fa344 chore(web): força rebuild do better-sqlite3 em deploys 2025-11-25 13:05:00 -03:00
Esdras Renan
c871710826 fix(start-web): normaliza blocos if/else do rebuild sqlite 2025-11-25 12:14:12 -03:00
Esdras Renan
f68b5b0990 fix(start-web): fecha if e remove brace extra 2025-11-25 11:58:37 -03:00
Esdras Renan
e7d1af5013 fix(start-web): corrige sintaxe do bloco else no rebuild do sqlite 2025-11-25 11:53:17 -03:00
Esdras Renan
16bc56ae18 chore(web): auto-rebuild better-sqlite3 e valida permissão do SQLite 2025-11-25 11:36:51 -03:00
Esdras Renan
463c0aeccd chore(rustdesk): reforça autostart e recuperação do serviço 2025-11-25 11:20:39 -03:00
Esdras Renan
a1436ea729 docs: add runbook steps forbetter-sqlite3 rebuild + login 2025-11-19 21:44:55 -03:00
Esdras Renan
43548ab4b8 chore: add node22 bun base image and tune start 2025-11-19 21:22:12 -03:00
Esdras Renan
eebe1a717f deploy: start-first updates to reduce downtime 2025-11-19 20:53:41 -03:00
Esdras Renan
8787599ac7 chore(stack): bump web to bun 1.3.2, root user, skip auth seed 2025-11-19 20:49:44 -03:00
Esdras Renan
7508bd5f82 chore: lock start to node22 and rebuild sqlite deps 2025-11-19 20:24:56 -03:00
Esdras Renan
60da918434 chore: rebuild better-sqlite3 and ensure curl install 2025-11-19 19:57:12 -03:00
Esdras Renan
0bba4fd1f1 chore: ensure node22/openssl in start-web 2025-11-19 19:53:18 -03:00
Esdras Renan
ddb20059c2 chore: ensure node symlink for next start 2025-11-19 18:39:11 -03:00
Esdras Renan
9bc9e7e7db chore: install node/openssl with apt-get output (runtime) 2025-11-19 18:37:01 -03:00
Esdras Renan
06d767b01e chore: start with node when available 2025-11-19 18:34:41 -03:00
Esdras Renan
c9c8b66ea3 chore: install node/openssl in start script when apt-get available 2025-11-19 18:29:49 -03:00
Esdras Renan
3e4015cecb chore: best-effort openssl install before start 2025-11-19 18:17:40 -03:00
Esdras Renan
c36670605b chore: allow skipping auth seed via env 2025-11-19 17:21:20 -03:00
Esdras Renan
724300c965 fix: allow null timeline entry in ticket schema 2025-11-19 16:53:33 -03:00
Esdras Renan
784962947a chore: relax env parsing for deploy 2025-11-19 16:30:39 -03:00
Esdras Renan
e1ecf20346 fix: allow 90d analytics window 2025-11-19 16:02:37 -03:00
Esdras Renan
a4b407c56c fix: provide prisma datasource fallback 2025-11-19 15:53:49 -03:00
Esdras Renan
81eca14de4 fix: re-export prisma types 2025-11-19 15:48:32 -03:00
Esdras Renan
514b190a65 fix: align prisma client typing 2025-11-19 15:42:21 -03:00
Esdras Renan
307a291c71 Upgrade Prisma to v7 2025-11-19 13:24:08 -03:00
Esdras Renan
54343f61d5 Add SLA tone to queue cards 2025-11-19 11:43:29 -03:00
Esdras Renan
7401ee366d Adjust admin devices card layout 2025-11-19 11:34:47 -03:00
Esdras Renan
188518e06c Refresh device cards layout 2025-11-19 11:17:07 -03:00
Esdras Renan
2c975edd09 Update Next.js and dependencies 2025-11-19 11:07:47 -03:00
Esdras Renan
c79ba4638c Allow reassigning during active sessions 2025-11-19 09:57:04 -03:00
Esdras Renan
feac6c2bb7 Center report filter controls 2025-11-19 09:40:39 -03:00
Esdras Renan
ff9d95746e Align report filters and update work session flows 2025-11-19 09:24:30 -03:00
Esdras Renan
17c1de2272 Adjust machines report layout spacing 2025-11-19 08:32:15 -03:00
Esdras Renan
5e232ba161 Adjust remote access tooltip border 2025-11-19 08:15:02 -03:00
Esdras Renan
6c0f854a69 Adjust remote access tooltip colors 2025-11-18 23:06:30 -03:00
Esdras Renan
ea5fb35762 Adjust reports filters layout and date range picker 2025-11-18 23:02:58 -03:00
Esdras Renan
698e082719 feat: improve hours report filter layout 2025-11-18 22:20:35 -03:00
Esdras Renan
b83c37d51f fix: polish remote tooltip and device status 2025-11-18 21:29:56 -03:00
Esdras Renan
4f8dad2255 feat: improve quick actions and remote access 2025-11-18 21:16:00 -03:00
Esdras Renan
aeb6d50377 style: anchor dashboard controls to card footer 2025-11-18 20:20:37 -03:00
Esdras Renan
dccfe40c7a chore: align dashboard builder controls 2025-11-18 20:17:13 -03:00
Esdras Renan
4c25f3f83e fix: avoid multiple paginated queries in metrics 2025-11-18 20:08:30 -03:00
Esdras Renan
744dcd1895 fix: avoid nested links in device card 2025-11-18 20:03:08 -03:00
Esdras Renan
72a4748a81 feat: enforce visit scheduling ux 2025-11-18 19:59:27 -03:00
Esdras Renan
6473e8d40f feat: enhance visit scheduling and closing flow 2025-11-18 17:42:38 -03:00
Esdras Renan
a7f9191e1d feat: configurable machine report export 2025-11-18 15:54:49 -03:00
Esdras Renan
c1ce7f1ab9 ci: fix convex tmp dir 2025-11-18 13:44:22 -03:00
Esdras Renan
b707b56ba1 fix: type pagination error metadata 2025-11-18 13:34:31 -03:00
Esdras Renan
2724746cb7 docs: registrar correção do export do convex 2025-11-18 11:58:58 -03:00
Esdras Renan
36d6ba9a64 Doc: note about Convex CLI 2025-11-18 10:34:50 -03:00
Esdras Renan
499c7f335c Gate Convex crons behind env flags 2025-11-18 10:28:56 -03:00
Esdras Renan
fa8672543a Hardening CI for prisma and convex deploy 2025-11-18 09:43:28 -03:00
Esdras Renan
674c62208f Remove hours usage cron/action 2025-11-18 09:34:56 -03:00
Esdras Renan
07a6f101b8 fix: handle Convex InvalidCursor and align backend config 2025-11-17 23:53:43 -03:00
Esdras Renan
3565500e9c fix: ensure convex client fallback url 2025-11-17 16:17:16 -03:00
Esdras Renan
36fe4a7943 chore: trigger convex deploy 2025-11-17 16:02:12 -03:00
Esdras Renan
99661ba451 chore: trigger deploy 2025-11-17 15:52:40 -03:00
Esdras Renan
94bbefbc48 infra: stop-first convex updates and lower reservation 2025-11-17 15:18:14 -03:00
Esdras Renan
fba294db71 ci: hard-restart convex service when scheduler lacks memory 2025-11-17 14:45:12 -03:00
Esdras Renan
69fbfc2299 ci: run convex admin key fallback with shell entrypoint 2025-11-17 14:34:34 -03:00
Esdras Renan
dbee5c28c8 fix: stabilize convex pagination and ci fallback 2025-11-17 14:23:46 -03:00
Esdras Renan
a08b0d6d27 ci: auto-restart convex service before acquiring admin key 2025-11-17 13:57:27 -03:00
Esdras Renan
3b0484a9ad feat: aggregate dashboard metrics server-side 2025-11-17 13:48:59 -03:00
Esdras Renan
c3ee23f967 Reduce Convex report memory footprint 2025-11-17 10:53:06 -03:00
Esdras Renan
87f729b80f feat: exibir tempo médio em horas e minutos 2025-11-17 09:10:45 -03:00
Esdras Renan
b33cb6c89a chore: tweak devices sync description 2025-11-15 02:13:18 -03:00
Esdras Renan
fd88475999 fix: wait for convex container before acquiring key 2025-11-15 01:58:39 -03:00
Esdras Renan
b721348e19 feat: improve custom fields admin and date filters 2025-11-15 01:51:55 -03:00
Esdras Renan
11a4b903c4 Corrige fallback HTML e consulta tickets por canal 2025-11-14 19:56:47 -03:00
Esdras Renan
15d11b6b12 feat: improve reports filters and ticket flows 2025-11-14 19:41:47 -03:00
Esdras Renan
9c74e10675 fix: alinhar searchParams da página de erro de autenticação ao contrato do Next 16 2025-11-14 15:08:18 -03:00
Esdras Renan
72b25fafab chore: ajustar tipos e remover any em rotas e tickets 2025-11-14 14:41:17 -03:00
Esdras Renan
50a80f5244 feat(portal): enable ticket reopen and improve loading UX 2025-11-14 13:08:59 -03:00
Esdras Renan
8b905dc467 ux(portal): simplificar exibicao do botao de reabertura 2025-11-14 11:58:35 -03:00
Esdras Renan
2468892d77 ux(portal): evitar flash de estado vazio antes de carregar tickets 2025-11-14 11:52:47 -03:00
Esdras Renan
b2de4b8480 ux: alinhar pagina de erro do handshake ao layout do portal 2025-11-14 11:46:36 -03:00
Esdras Renan
3ee072854f ux: evitar estados intermediarios incorretos no portal e desktop 2025-11-14 11:43:09 -03:00
Esdras Renan
61d3573830 ux(desktop): remover texto de preparacao do RustDesk no onboarding 2025-11-14 11:34:27 -03:00
Esdras Renan
08003ddcd8 ux: localizar evento de reabertura na linha do tempo 2025-11-14 11:31:45 -03:00
Esdras Renan
a2a186dcf5 ux: mensagem especifica para play desativado em ticket encerrado 2025-11-14 11:23:56 -03:00
Esdras Renan
9d30d970a4 ui: ajustar tamanho/espacamento do texto de prazo de reabertura 2025-11-14 11:19:02 -03:00
Esdras Renan
16bbd1e4b8 ui: ajustar layout do prazo de reabertura abaixo da linha de acoes 2025-11-14 11:17:51 -03:00
Esdras Renan
4d4a2e3aaf ui: remover reabrir duplicado e alinhar prazo ao botao principal 2025-11-14 11:16:26 -03:00
Esdras Renan
6560e7047c ui: alternar botao encerrar/reabrir no header do ticket 2025-11-14 11:11:40 -03:00
Esdras Renan
aadf156ba5 fix: alinhar janela de reabertura do portal com regras do backend 2025-11-14 11:09:53 -03:00
Esdras Renan
2b0b72cd8b fix(portal): permitir reabertura por e-mail do solicitante 2025-11-14 10:50:52 -03:00
Esdras Renan
0171157f12 ops: aumentar limite de memória do convex backend 2025-11-14 10:15:42 -03:00
Esdras Renan
e8ba171839 fix(desktop): corrigir endpoint do updater 2025-11-14 08:29:03 -03:00
Esdras Renan
4cd026ef02 ci: corrigir permissões do sqlite em produção 2025-11-14 08:27:46 -03:00
Esdras Renan
645829950c fix: garantir dependência do common controls 2025-11-14 08:16:48 -03:00
Esdras Renan
a49e36c25e feat: remover menu de perfil do portal 2025-11-14 07:59:09 -03:00
Esdras Renan
06fdb54480 Fix report filters and zod resolver 2025-11-14 01:25:28 -03:00
Esdras Renan
5b22065609 feat(reports): add date range filters and extend machine reports 2025-11-14 00:59:11 -03:00
Esdras Renan
82875a2252 feat: machine reports with filters and hours 2025-11-13 23:45:24 -03:00
Esdras Renan
6938bebdbb chore: refine report filters and access gating 2025-11-13 23:35:05 -03:00
Esdras Renan
52c03ff1cf feat: portal reopen, reports, templates and remote access 2025-11-13 23:22:17 -03:00
Esdras Renan
6a75a0a9ed Wire quick actions to reopen admin modals in-place 2025-11-13 21:55:56 -03:00
Esdras Renan
8db3b20a40 Add tests for quick modal auto-open 2025-11-13 21:45:46 -03:00
Esdras Renan
abb29d9116 Auto-open modals from global quick actions 2025-11-13 21:43:36 -03:00
Esdras Renan
59a94744b3 Polish filters combobox alignment 2025-11-13 21:08:34 -03:00
Esdras Renan
feca5dd4a7 Enhance tickets filters UI 2025-11-13 20:19:19 -03:00
Esdras Renan
a08545fd40 Refine tickets filter layout 2025-11-13 19:50:06 -03:00
Esdras Renan
12acbc5b1c Improve today highlighting in calendar 2025-11-13 18:43:41 -03:00
Esdras Renan
a419965aca Refina filtros de tickets e seletor de período 2025-11-13 17:40:44 -03:00
Esdras Renan
b00e52475f Melhora filtros no desktop e adiciona ícone em Todos os tickets 2025-11-13 15:06:00 -03:00
Esdras Renan
11cd4927e9 chore(desktop): update .env.example for RustDesk defaults 2025-11-13 13:22:39 -03:00
Esdras Renan
5b1d73ea43 desktop(portal): ocultar filtros avançados (fila/empresa/responsável) para colaboradores e gestores quando aberto via app desktop; manter categoria/status/ordenação/período; docs atualizados 2025-11-13 13:04:17 -03:00
Esdras Renan
cc68c85246 Adjust ticket filters visibility and add date range 2025-11-13 09:54:36 -03:00
Esdras Renan
5f7ef3fd03 fix: avoid double paginated queries 2025-11-12 22:42:45 -03:00
Esdras Renan
f9a72c8154 Fix open ticket pagination and CSAT fallback 2025-11-12 22:24:31 -03:00
Esdras Renan
1ba1f4a63c Fix reports pagination helper and CSAT handling 2025-11-12 22:21:35 -03:00
Esdras Renan
3e4943f79c Optimize Convex queries and stack config 2025-11-12 22:13:50 -03:00
Esdras Renan
004f345d92 feat(portal): add desktop-style filters and breadcrumbs 2025-11-12 21:03:42 -03:00
Esdras Renan
f5898153fe feat: improve ticket navigation and filters 2025-11-12 20:40:38 -03:00
Esdras Renan
ff41a8bd4e Auto-pause internal work during lunch 2025-11-12 17:48:12 -03:00
Esdras Renan
c6a7e0dd0b Apply password files before propagating 2025-11-12 15:36:10 -03:00
Esdras Renan
05a273466a Stop RustDesk service via UAC and sync RustDesk2 security 2025-11-12 14:53:40 -03:00
Esdras Renan
daca17a93d Purge legacy RustDesk configs before provisioning 2025-11-12 14:25:50 -03:00
Esdras Renan
ddcff6768d Hardening RustDesk provisioning flow 2025-11-12 14:10:56 -03:00
Esdras Renan
f3d622eedd feat: auto-install/start RustDesk service and enforce permanent password 2025-11-12 13:41:25 -03:00
Esdras Renan
e446882519 fix: enforce permanent RustDesk password fallback 2025-11-12 13:30:46 -03:00
Esdras Renan
eb41cc4ac5 fix: call new service profile preflight helper 2025-11-12 12:00:39 -03:00
Esdras Renan
b20db33d7b feat: persist RustDesk password via service profiles 2025-11-12 11:58:24 -03:00
Esdras Renan
a535a6625b feat: support LocalSystem RustDesk profile auto-fix 2025-11-12 11:43:07 -03:00
Esdras Renan
2339d5010f fix: improve RustDesk ACL automation 2025-11-12 11:25:24 -03:00
Esdras Renan
5105d2cfed chore: treat icacls exit 1 as recoverable 2025-11-12 11:01:30 -03:00
Esdras Renan
4b0bdd7026 fix: handle icacls exit code and copy overwrites 2025-11-12 10:55:08 -03:00
Esdras Renan
dd57bc9886 chore: remove duplicate localservice preflight defs 2025-11-12 10:41:38 -03:00
Esdras Renan
484eb8fbe9 fix: improve RustDesk ACL preflight and file writes 2025-11-12 10:39:36 -03:00
Esdras Renan
77f88f2aa6 fix: add localservice ACL preflight 2025-11-12 10:30:09 -03:00
Esdras Renan
a8cbfee03b refactor: preflight ACL repair for RustDesk 2025-11-12 10:26:46 -03:00
Esdras Renan
7972ac207d fix: self-heal RustDesk ACL and restart 2025-11-12 10:14:13 -03:00
Esdras Renan
c1d8181abf chore: add detailed logging for RustDesk password propagation 2025-11-12 09:42:13 -03:00
Esdras Renan
aa0d861778 fix: propagate RustDesk password to service profiles 2025-11-12 09:26:46 -03:00
Esdras Renan
a76c6724f1 fix: send machineId when calling admin device APIs 2025-11-12 09:11:15 -03:00
Esdras Renan
16ed53e24a fix: persist RustDesk password in service profile 2025-11-12 08:54:21 -03:00
Esdras Renan
ec18133a6d style: polish device controls ui 2025-11-11 23:57:28 -03:00
Esdras Renan
2872c6e73c chore: hide rustdesk provisioning consoles 2025-11-11 23:00:29 -03:00
Esdras Renan
20f80083f2 fix: skip rustdesk sync before registration 2025-11-11 20:41:12 -03:00
Esdras Renan
e0bb6bb80f feat: event-driven rustdesk sync 2025-11-11 20:26:29 -03:00
Esdras Renan
e410a4874c docs: add rustdesk provisioning dossier 2025-11-11 18:36:58 -03:00
Esdras Renan
cdf3feaa96 chore(agent): add remote access logging and state 2025-11-11 18:26:37 -03:00
Esdras Renan
07d304b5b1 fix(agent): align machine profile fields 2025-11-11 17:59:25 -03:00
Esdras Renan
115d4a62e8 feat(agent): self-heal rustdesk remote access 2025-11-11 17:50:09 -03:00
Esdras Renan
308f7b5712 fix(remote-access): allow short-lived revoked token grace 2025-11-11 16:46:54 -03:00
Esdras Renan
130ab3bbdc fix(env): allow build fallback for auth secret 2025-11-11 16:17:38 -03:00
Esdras Renan
da46fa448b feat(convex): add internal url and remote access fixes 2025-11-11 16:06:11 -03:00
Esdras Renan
feb31d48c1 Sincroniza remote_id com ID final 2025-11-11 14:57:39 -03:00
Esdras Renan
0120748cc5 Adiciona logs ao provisionamento RustDesk 2025-11-11 14:47:53 -03:00
Esdras Renan
ddcb7ac326 Remove import não usado 2025-11-11 14:22:16 -03:00
Esdras Renan
3b32ae2f5f Garante remote_id local do RustDesk 2025-11-11 14:13:32 -03:00
Esdras Renan
5daa954840 Alinha config do RustDesk 2025-11-11 13:34:04 -03:00
Esdras Renan
ef1db284fa Provisiona RustDesk automaticamente 2025-11-11 11:54:46 -03:00
Esdras Renan
967d4bf1c6 Substitui alertas por painel de SLA 2025-11-11 10:24:46 -03:00
Esdras Renan
e1ec0de7a4 style: harmonize billing filter pills 2025-11-10 11:15:30 -03:00
Esdras Renan
e7c1c4b950 fix: restore time range toggle layout 2025-11-10 11:13:12 -03:00
Esdras Renan
616fe42e10 feat: modernize report scheduling UI and date inputs 2025-11-10 11:05:53 -03:00
Esdras Renan
8cc513c532 fix: allow nullable company filters in report exporters 2025-11-10 03:23:01 -03:00
Esdras Renan
d8515d08e1 fix: ensure tooltip formatter returns string 2025-11-10 03:15:56 -03:00
Esdras Renan
fa96768fa9 fix: sanitize report keys when listing schedules 2025-11-10 03:11:57 -03:00
Esdras Renan
a2c264a3ea fix: sanitize report keys before serializing schedule 2025-11-10 03:07:22 -03:00
Esdras Renan
00c75d14ee fix: return arraybuffer artifacts for report exports 2025-11-10 03:03:26 -03:00
Esdras Renan
8ecead47f2 fix: return buffer objects from report exporters 2025-11-10 02:58:16 -03:00
Esdras Renan
56dd48d6b9 fix: await search params on incident page 2025-11-10 02:56:24 -03:00
Esdras Renan
a7921ffffb fix: use Next RouteContext helpers for dynamic API routes 2025-11-10 02:47:36 -03:00
Esdras Renan
d859c1196c fix: align report schedule route context types 2025-11-10 02:37:17 -03:00
Esdras Renan
127e117ea9 fix: prevent report schedule helpers from being treated as server actions 2025-11-10 02:32:48 -03:00
Esdras Renan
7098b70088 chore: trim dashboard tickets and clean references 2025-11-10 02:02:08 -03:00
Esdras Renan
561b19cf66 chore: sync staging 2025-11-10 01:57:45 -03:00
Esdras Renan
c5ddd54a3e chore: prep platform improvements 2025-11-09 21:09:38 -03:00
Esdras Renan
a62f3d5283 feat: add SLA category breakdown report 2025-11-08 02:47:39 -03:00
Esdras Renan
6ab8a6ce89 feat: agenda polish, SLA sync, filters 2025-11-08 02:34:43 -03:00
Esdras Renan
7fb6c65d9a Fix form template labels and guard admin auth tables 2025-11-08 00:40:32 -03:00
Esdras Renan
003d068c56 Patch performance measure and ignore nova calendar 2025-11-08 00:36:24 -03:00
Esdras Renan
d8eb38fe52 Update Prisma and harden tests 2025-11-08 00:28:52 -03:00
Esdras Renan
a2f9d4bd1a Improve custom field timeline and toasts 2025-11-07 23:59:16 -03:00
Esdras Renan
f7aa17f229 fix: enviar machineId e preencher autocomplete no acesso remoto 2025-11-07 16:08:40 -03:00
Esdras Renan
d1569083de fix: ensure remote access fields default to null 2025-11-07 15:48:52 -03:00
Esdras Renan
07d631de40 feat: integrar credenciais rustdesk aos acessos remotos 2025-11-07 15:39:36 -03:00
Esdras Renan
4079f67fcb feat: padroniza tickets recentes nos dashboards 2025-11-07 14:22:14 -03:00
Esdras Renan
4655c7570a Atualiza dashboards e painel de tickets 2025-11-07 00:56:59 -03:00
Esdras Renan
c66ffa6e0b Ajusta grid do resumo de filas 2025-11-06 23:19:41 -03:00
Esdras Renan
b94cea2f9a Ajusta placeholders, formulários e widgets 2025-11-06 23:13:41 -03:00
Esdras Renan
343f0c8c64 feat: ensure queue summary widget in dashboards 2025-11-06 17:23:29 -03:00
Esdras Renan
a542846313 feat: add queue summary widget and layout fixes 2025-11-06 17:05:31 -03:00
Esdras Renan
f7976e2c39 fix(ci): keep deployed build owned by app user 2025-11-06 15:47:18 -03:00
Esdras Renan
86848a141b fix: normalize play next ticket context 2025-11-06 15:27:44 -03:00
Esdras Renan
8b1f83d7f5 fix: normalize text input values 2025-11-06 15:02:16 -03:00
Esdras Renan
f1ff3be9e0 fix: handle nullable ticket custom fields 2025-11-06 14:57:18 -03:00
Esdras Renan
55631d339a fix: align device combobox props 2025-11-06 14:52:11 -03:00
Esdras Renan
164d72e3ce fix: normalize dashboard export buffers 2025-11-06 14:36:59 -03:00
Esdras Renan
0f0f367b3a feat: custom fields improvements 2025-11-06 14:05:51 -03:00
Esdras Renan
9495b54a28 feat: melhorias no vínculo de tickets e exportação 2025-11-06 13:07:01 -03:00
Esdras Renan
1b32638eb5 fix: ajustes dashboards tv e titulos 2025-11-06 11:21:40 -03:00
Esdras Renan
80abd92e78 ci: drop convex yes flag 2025-11-06 01:56:26 -03:00
Esdras Renan
a7a7c30d0e ci: use bun for convex deploy 2025-11-06 01:48:48 -03:00
Esdras Renan
0f8e9f0071 fix: darken widget picker icons 2025-11-06 01:46:16 -03:00
Esdras Renan
d7d6b748cc feat: refresh dashboards experience 2025-11-06 01:40:10 -03:00
Esdras Renan
1900f65e5e merge: bring fixes for dashboards/export/site-header/auth/prisma and env hygiene into main 2025-11-06 00:03:35 -03:00
Esdras Renan
9f99284621 chore(env): restore .env.example template and ignore accidental 'env (N)' files; remove tracked 'env (1)' 2025-11-06 00:03:30 -03:00
Esdras Renan
b62e14d8eb fix(dashboards): prevent render loops with stable ready handlers and idempotent updates; improve filter hydration guards
fix(export): return 501 with hint when Playwright browsers missing; nicer error toast in UI

fix(site-header): export primary/secondary buttons as named for SC safety; keep static props for compat

fix(portal): add DialogDescription for a11y; tidy preview dialog

fix(csats): avoid reinit state loops with timestamp guard

chore(prisma): default dev DB to prisma/db.dev.sqlite and log path

chore(auth): add dev bypass flags wiring (server/client) for local testing

dev: seed script for Convex demo data
2025-11-06 00:01:45 -03:00
Esdras Renan
ff0254df18 fix: avoid dashboard ready state loop 2025-11-05 21:32:53 -03:00
Esdras Renan
ea8612b8fd fix: switch production build to webpack 2025-11-05 21:15:10 -03:00
Esdras Renan
1079111de2 chore: update docs and configs 2025-11-05 20:49:19 -03:00
Esdras Renan
7718f77d4c ci: retain previous next static assets during publish 2025-11-05 20:00:42 -03:00
Esdras Renan
38651ca706 fix(dashboards): guard convex query and refine empty state CTA 2025-11-05 19:43:36 -03:00
Esdras Renan
fb17882dad docs: atualizar instrucoes para bun e convex self-hosted 2025-11-05 19:39:44 -03:00
Esdras Renan
aec9b1ff85 chore: run web service with bun runtime 2025-11-05 19:04:09 -03:00
Esdras Renan
2b9bc77228 chore: ensure prisma client generation before build 2025-11-05 16:03:15 -03:00
Esdras Renan
8031c9c29d Use Bun filter to skip desktop workspace in deploy job 2025-11-04 23:37:33 -03:00
Esdras Renan
4ec5f15c9d Fix Bun install step by stubbing desktop workspace 2025-11-04 23:34:49 -03:00
Esdras Renan
775956c160 Switch workflows to Bun install/test and update pnpm 2025-11-04 23:21:41 -03:00
Esdras Renan
c3237dfb64 Adopt Bun runtime across build pipelines 2025-11-04 22:52:46 -03:00
Esdras Renan
281ecd5f6f feat: upgrade tiptap and handle clipboard uploads 2025-11-04 21:35:18 -03:00
Esdras Renan
fa9efdb5af fix: resolver avisos de build e tipagem 2025-11-04 21:02:53 -03:00
Esdras Renan
741f1d7f9c feat: adicionar construtor de dashboards e api de métricas 2025-11-04 20:37:34 -03:00
Esdras Renan
c2acd65764 refine queue metrics and devices ui 2025-11-04 19:53:54 -03:00
codex-bot
1e45324460 fix(portal,lint):\n- Move useMemo para antes de returns na lista do portal (regras de hooks)\n- Remove usos de any na overview de dispositivos 2025-11-04 14:17:22 -03:00
codex-bot
c2c5707a97 feat(devices,custom-fields,csat,portal):\n- Editor de campos personalizados (inclui multiselect) e exibição no detalhe\n- Campos personalizados disponíveis nas colunas/templates de exportação\n- Move cópia de e-mail para ícone inline abaixo do nome do dispositivo\n- Portal: banner para avaliar último chamado e CSAT no detalhe\n- Tickets list inclui campos de CSAT para detectar pendências 2025-11-04 14:12:21 -03:00
codex-bot
06deb99bcd feat(export,tickets,forms,emails):\n- Corrige scroll de Dialogs e melhora UI de seleção de colunas (ícones e separador)\n- Ajusta rota/params da exportação em massa e adiciona modal de exportação individual\n- Renomeia 'Chamado padrão' para 'Chamado' e garante visibilidade total para admin/agente\n- Adiciona toggles por empresa/usuário para habilitar Admissão/Desligamento\n- Exibe badge do tipo de solicitação na listagem e no cabeçalho do ticket\n- Prepara notificações por e-mail (comentário público e encerramento) via SMTP\n 2025-11-04 13:41:32 -03:00
codex-bot
a8333c010f fix(reports): remove truncation cap in range collectors to avoid dropped records
feat(calendar): migrate to react-day-picker v9 and polish UI
- Update classNames and CSS import (style.css)
- Custom Dropdown via shadcn Select
- Nav arrows aligned with caption (around)
- Today highlight with cyan tone, weekdays in sentence case
- Wider layout to avoid overflow; remove inner wrapper

chore(tickets): make 'Patrimônio do computador (se houver)' optional
- Backend hotfix to enforce optional + label on existing tenants
- Hide required asterisk for this field in portal/new-ticket

refactor(new-ticket): remove channel dropdown from admin/agent flow
- Keep default channel as MANUAL

feat(ux): simplify requester section and enlarge combobox trigger
- Remove RequesterPreview redundancy; show company badge in trigger
2025-11-04 11:51:08 -03:00
codex-bot
e0ef66555d feat: dispositivos e ajustes de csat e relatórios 2025-11-03 19:29:50 -03:00
codex-bot
25d2a9b062 feat: add agent reset flow and document machine handover 2025-11-03 15:16:34 -03:00
codex-bot
28796bf105 refactor: enhance user tables and machine ticket views 2025-11-03 11:55:14 -03:00
Esdras Renan
bd2f22d046 refactor: use Convex skip sentinel in query args 2025-11-01 02:31:17 -03:00
Esdras Renan
3de7eccaa8 fix: avoid Convex skip sentinel in queries 2025-11-01 02:09:16 -03:00
Esdras Renan
3880ff57bd feat: add granular filters to machines inventory export 2025-11-01 02:00:47 -03:00
Esdras Renan
a3d431efa8 fix: harden ticket client data guards 2025-11-01 01:48:15 -03:00
Esdras Renan
5c5207ceb8 fix: refine ticket UI styling 2025-11-01 01:13:41 -03:00
codex-bot
8b82284e8c chore: expand reports coverage and upgrade next 2025-10-31 17:27:51 -03:00
codex-bot
2fb587b01d Expand machine inventory export with detailed sheets 2025-10-31 16:09:05 -03:00
codex-bot
ee7dbb1ee7 Ensure BETTER_AUTH_SECRET is passed to web service 2025-10-31 15:44:19 -03:00
codex-bot
9d569d987d Fix Excel export XML order and gate time adjustments on close 2025-10-31 14:47:37 -03:00
codex-bot
be9816a3a8 docs: add quick vps deploy guide 2025-10-30 16:45:50 -03:00
codex-bot
4c848486a6 perf: optimize machine token lookup 2025-10-30 16:30:59 -03:00
codex-bot
38b46f32ce feat: improve machines inventory exports 2025-10-30 16:09:06 -03:00
codex-bot
d92c817e7b fix: avoid broken font and speed up backlog overview 2025-10-30 14:42:09 -03:00
Esdras Renan
38e4bbea7f fix: restaurar utilitários de acesso remoto 2025-10-28 11:52:27 -03:00
Esdras Renan
192a5c2909 feat: melhorar gerenciamento de acesso remoto de máquinas 2025-10-28 11:45:16 -03:00
Esdras Renan
714b199879 feat: export reports as xlsx and add machine inventory 2025-10-27 18:00:28 -03:00
Esdras Renan
29b865885c fix: ensure legacy companies jsonify 2025-10-27 14:56:19 -03:00
Esdras Renan
d23987eda8 feat: ajustar board de tickets 2025-10-27 14:50:17 -03:00
Esdras Renan
e9a8bd6b9b Abrevia fila Laboratorio para Lab 2025-10-27 12:03:43 -03:00
Esdras Renan
5154d34cde Refina separadores e prioridade 2025-10-27 12:02:36 -03:00
Esdras Renan
292de05039 Ajusta layout da tabela de tickets 2025-10-27 11:45:15 -03:00
Esdras Renan
f3a7045691 feat: cadastro manual de acesso remoto e ajustes de horas 2025-10-24 23:52:58 -03:00
codex-bot
8e3cbc7a9a Remove unused ReactNode dependency in sidebar 2025-10-24 17:31:53 -03:00
codex-bot
b1130c2a3d Fix sidebar trigger hydration imports 2025-10-24 17:26:56 -03:00
codex-bot
ddbf019d12 Add live ticket animations and fix sidebar hydration 2025-10-24 17:24:51 -03:00
codex-bot
2a9170f7dd Make ticket mention extension override link parsing 2025-10-24 16:53:36 -03:00
codex-bot
6702811f4a Align ticket status colors across views 2025-10-24 16:48:24 -03:00
codex-bot
296e02cf0c Normalize ticket mentions in editor and server 2025-10-24 16:35:55 -03:00
codex-bot
cf11ac9bcb Fix CSS syntax: remove stray closing brace in globals.css 2025-10-24 14:59:42 -03:00
codex-bot
b3fa6955b7 Editor mentions: style [data-type=ticketMention] with colored status dot via ::before to match display chips 2025-10-24 14:31:55 -03:00
codex-bot
3b013f205a Rich text: unify ticket mention chip style while editing (Tiptap span) 2025-10-24 14:18:37 -03:00
codex-bot
12a6d231fa Improve admin actions and ticket board layout 2025-10-24 14:04:12 -03:00
codex-bot
92ac0fafc6 fix: restore mention keyboard shortcuts 2025-10-24 13:12:55 -03:00
Esdras Renan
a319aa0eff Align admin tables with ticket styling and add board view 2025-10-24 12:23:27 -03:00
Esdras Renan
63cf9f9d45 Keep ticket mention chip styling during edits 2025-10-24 10:55:33 -03:00
Esdras Renan
e47ea5eecc Refine admin users filters with searchable combobox 2025-10-24 10:15:30 -03:00
Esdras Renan
b51d0770d3 Restore client user editing sheet and tweak pagination copy 2025-10-24 09:50:40 -03:00
Esdras Renan
c7aaa60d9a Fix admin cleanup hook order and lint setup 2025-10-24 09:21:31 -03:00
Esdras Renan
37c32149a6 feat: improve requester combobox and admin cleanup flows 2025-10-24 00:45:41 -03:00
codex-bot
788f6928a1 Garante tipos no timeline de solicitante 2025-10-23 18:31:54 -03:00
codex-bot
a7c3c743d7 Corrige flag de alteração de solicitante 2025-10-23 18:00:12 -03:00
codex-bot
4aee7d7719 Permite selecionar solicitante e empresa nos tickets 2025-10-23 17:47:23 -03:00
codex-bot
25321224a6 Redesenha top clientes por horas 2025-10-23 16:54:43 -03:00
codex-bot
34018bed04 Centraliza coluna de empresa na lista 2025-10-23 16:37:30 -03:00
codex-bot
aef5e66718 Exibe loading em máquinas e moderniza time picker 2025-10-23 15:06:41 -03:00
codex-bot
9bfdb451bc fix(editor/mentions): do not cache empty results so numeric queries like @41005 revalidate after deploy 2025-10-23 10:37:09 -03:00
codex-bot
66fe34868c feat(mentions): switch to Convex-backed search so @<ref> and text queries return visible tickets for the current user; keep permissions 2025-10-23 10:34:32 -03:00
codex-bot
e6c841383e fix(mentions): search numeric references directly in DB and avoid Tiptap duplicate 'link' extension by configuring link via StarterKit 2025-10-23 10:17:24 -03:00
codex-bot
4374b1c777 fix(new-ticket-dialog): place allowTicketMentions inside component to satisfy TS and restore build 2025-10-23 10:05:14 -03:00
codex-bot
904134604c feat(editor): enable ticket mentions on new-ticket forms and fix @mention popup layering\n\n- New Ticket page/dialog/portal now support @ to link tickets\n- Mention popup uses fixed strategy + high z-index\n- Add minimal Tippy box styling to globals.css\n- Keeps existing server-side permissions for mentions 2025-10-23 09:48:16 -03:00
Esdras Renan
b0f57009ac feat: link tickets in comments and align admin sidebars 2025-10-23 00:46:50 -03:00
Esdras Renan
c35eb673d3 fix(machines): hydrate company name without slug flash 2025-10-22 22:39:17 -03:00
Esdras Renan
20a5c902bc docs: add admin machines skeleton troubleshooting 2025-10-22 22:19:49 -03:00
Esdras Renan
2a359b7a65 fix(machines): derive machine id from router params 2025-10-22 22:08:53 -03:00
Esdras Renan
1017d563b5 fix(machines): guard hydration before loading machine details 2025-10-22 21:56:28 -03:00
Esdras Renan
3417388cb0 chore(machines): verbose probe diagnostics 2025-10-22 21:24:32 -03:00
Esdras Renan
a1f456a5fd chore(machines): add probe logging 2025-10-22 21:10:09 -03:00
Esdras Renan
55316e51c6 fix(machines): ensure machine details probe resolves 2025-10-22 20:49:43 -03:00
Esdras Renan
3fce36d4e5 fix(machines): remove artificial delay; always probe details immediately; fallback tri-state guarded only by fallback state 2025-10-22 20:07:08 -03:00
Esdras Renan
39726b360e feat(machines): robust probe for machine details + clear error/not-found states\n\n- Probe Convex (browser) and server route when query is undefined or null\n- Tri-state fallback (undefined|null|data) to disambiguate not-found\n- Restore skeleton + not-found + error rendering with actionable retry\n- No behavior change when data is available 2025-10-22 19:40:07 -03:00
Esdras Renan
c640e288b1 chore(types): remove anys and harden Convex data fetch
- Strongly type company-service and API routes
- Fix Next.js searchParams (promise) in admin/machines page
- Add vitest module marker + stub for tsconfig-paths/register
- Use Convex query in client as primary fallback for machine details
- Replace any casts in admin machines components

Build + lint are clean locally; details page no longer skeleton-loops.
2025-10-22 19:19:38 -03:00
Esdras Renan
eee0f432e7 chore: document and stabilize vitest browser setup 2025-10-22 17:19:12 -03:00
codex-bot
42942350dc feat(admin): exibir mensagem de erro no detalhe da máquina quando Convex/fallback falham e oferecer retry; prioriza caminho Convex e esclarece 404 2025-10-22 11:15:20 -03:00
codex-bot
ee1f19f7f2 fix(machines): reduce HTTP fallback delay on admin machine details to 300ms to bypass Convex client loading edge cases; Convex already returns full data for machine id 2025-10-22 10:22:23 -03:00
codex-bot
7a7154775c fix(web): suppress hydration mismatch at root html to avoid React 418 on production (extensions/content-scripts DOM changes) 2025-10-22 10:16:59 -03:00
codex-bot
a18536dd5f fix(api): Next.js 16 route handler types — params is Promise in context
- Update GET signature to (req: NextRequest, ctx: { params: Promise<{id:string}> })
- Await ctx.params and pass id to Convex client
- Keeps NextResponse return type
2025-10-22 09:46:50 -03:00
codex-bot
4cfbd22cf2 admin(machines): add resilient fallback for details page
- Keep Convex useQuery for machines.getById
- Add HTTP fallback via /api/admin/machines/[id]/details if query stays loading (>1.2s)
- Helps when websocket/convex-react doesn’t initialize and avoids permanent skeleton
2025-10-22 09:39:51 -03:00
codex-bot
e0f65cc774 chore(convex): no-op touch to trigger Convex functions deploy (fix prod detail view loading) 2025-10-22 09:03:29 -03:00
codex-bot
49173cdf69 fix(machines): guard Convex getById calls with 'skip' when missing id to avoid ArgumentValidationError; add unit test for getById metadata; fix build by loosening Prisma types in company service 2025-10-22 08:47:55 -03:00
codex-bot
5ff37195f5 admin(machines): fix machine detail not loading by switching to server-side fetch by ID
- Add Convex query machines.getById with full payload (metrics/inventory)
- Update AdminMachineDetailsClient to use getById instead of listByTenant+find
- Update MachineBreadcrumbs to fetch hostname by ID

This prevents the empty state when the list query hasn’t loaded or filtered out the machine.
2025-10-22 08:25:12 -03:00
Esdras Renan
6333a3fc07 chore: trigger convex rebuild 2025-10-22 02:16:32 -03:00
Esdras Renan
dad84d7d0e refactor: align routes with next 16 and local fonts 2025-10-22 02:08:18 -03:00
Esdras Renan
2e3b46a7b5 feat: expand admin companies and users modules 2025-10-22 01:27:43 -03:00
codex-bot
a043b1203c Ajusta layout dos detalhes de atualizações do Windows 2025-10-21 15:58:53 -03:00
codex-bot
3e5e1c759e Refina espaçamento e tooltip do gráfico de horas 2025-10-21 15:44:00 -03:00
codex-bot
c4620102ae Ajusta tooltips e espaçamento nos gráficos de backlog 2025-10-21 15:30:19 -03:00
codex-bot
dbb8d7fa63 feat(alerts): format hours in admin alert emails using minutes when <1h; minor null-safe for contracted/month 2025-10-21 15:20:05 -03:00
codex-bot
0a3e1a0130 chore(ui): finalize hour/date formatting rollout; adjust contracted hours display; clean up unused formatter 2025-10-21 15:06:38 -03:00
codex-bot
f255a4c780 feat(ui): improve chart spacing and labels; format hours <1h as minutes; unify date format to dd/MM (ticks) and dd/MM/yyyy (tooltips); fix tooltips labels ('Total', 'Resolvidos') 2025-10-21 14:52:57 -03:00
codex-bot
4b4c0d8e69 fix(reports): gate report queries behind staff check; prevent non-staff crashes; trigger Convex deploy by touching convex/reports.ts 2025-10-21 14:29:31 -03:00
codex-bot
2cdc856009 fix(dashboard): guard nested fields (resolution, firstResponse, awaitingAction) to prevent undefined access and runtime crash; add safe fallbacks in UI 2025-10-21 14:12:41 -03:00
codex-bot
68b897c30c Reports: add charts to Produtividade (areas + channels), CSAT (bar), Backlog (pie+bar), Horas (stacked bar); deploy Convex reports agent productivity 2025-10-21 13:35:06 -03:00
codex-bot
67df0d4308 Rename menus: 'Acessos', 'Filas', 'Produtividade'; add agent productivity section with bar chart; adjust CSV label; update channels page title 2025-10-21 13:17:41 -03:00
codex-bot
347609a186 Remove tenant UI; restrict machine links to non-admin users; polish Users/Machines UX 2025-10-21 11:55:05 -03:00
codex-bot
4a30a1b564 chore(convex): bump revision to force functions deploy 2025-10-21 11:34:14 -03:00
codex-bot
e5d395208d Users list: default filter to 'Pessoas'; finalize cleanup 2025-10-21 11:17:41 -03:00
codex-bot
8b02b8a564 Fix types: remove any; clean unused states; add machines summary in unified Users list; capitalize 'Gerenciar usuários'; correct Convex linkUser typing 2025-10-21 11:16:31 -03:00
codex-bot
89c8e0cdb3 Docs: add Phase 2 (multi-user links) semantics and admin API; clarify email-as-identity for reinstall 2025-10-21 11:06:43 -03:00
codex-bot
22f0768492 Phase 2: multi-user links for machines (Convex schema + mutations + admin API); UI to add/remove links; user editor lists machines via linkedUsers 2025-10-21 11:06:21 -03:00
codex-bot
6653ef250e Machines: show 'Usuários vinculados' in details; Cards show linked user chip; Users editor shows 'Máquinas vinculadas' via Convex listByTenant filter 2025-10-21 10:56:04 -03:00
codex-bot
af0658af26 Docs: document Users/Machines unification, company filter in Machines, Windows OS label, and identity/email/history guidance in OPERATIONS.md 2025-10-21 10:55:07 -03:00
codex-bot
231310a9fe Admin Users: unify People and Machine Agents into single 'Usuários' tab with type filter; keep Team/Convites tabs 2025-10-21 10:41:38 -03:00
codex-bot
e04888ff4d Machines: replace OS filter with searchable company dropdown; remove OS filter logic 2025-10-21 09:52:36 -03:00
codex-bot
904c2ef457 UI: Avoid duplicate Windows major version in machine OS label (e.g., 'Windows 11 Pro (26100)') 2025-10-21 09:08:59 -03:00
codex-bot
66bf0cd9e1 Ajusta layout das atualizações do Windows 2025-10-21 08:40:11 -03:00
codex-bot
6e3df454d6 Adjust login page copy for first access 2025-10-21 08:11:31 -03:00
Esdras Renan
a96cb1747e Remove ticket share button 2025-10-21 00:15:51 -03:00
Esdras Renan
6007babad3 Align ticket status colors and polish Windows insights 2025-10-21 00:12:39 -03:00
Esdras Renan
6a8f7a63aa Normalize Windows edition and install date parsing 2025-10-20 23:23:46 -03:00
Esdras Renan
694bda22cd Guard Windows defender checks 2025-10-20 23:02:22 -03:00
Esdras Renan
374c60ce79 Fix Windows extended type fallback 2025-10-20 22:54:16 -03:00
Esdras Renan
037891485d Enrich Windows diagnostics and admin UI 2025-10-20 22:43:42 -03:00
Esdras Renan
49496f3663 Run PowerShell commands via script blocks 2025-10-20 22:21:37 -03:00
Esdras Renan
680d49ddc5 Log PowerShell raw output preview in tests 2025-10-20 22:18:44 -03:00
Esdras Renan
8312330c2e Log PowerShell stdout in Windows tests 2025-10-20 22:15:15 -03:00
Esdras Renan
6234924878 Log PowerShell stderr in Windows tests 2025-10-20 22:05:58 -03:00
Esdras Renan
0a0106c0f3 Use encoded PowerShell commands for Windows inventory 2025-10-20 21:55:54 -03:00
Esdras Renan
0aa474c88e Handle PowerShell UTF-16 output and show Windows edition 2025-10-20 21:52:17 -03:00
Esdras Renan
459bd53693 Fix Windows PowerShell UTF-16 parsing 2025-10-20 21:43:11 -03:00
Esdras Renan
04a0127c6b Add Windows diagnostics test 2025-10-20 21:36:57 -03:00
Esdras Renan
2e7f575682 feat: standardize table pagination styling 2025-10-20 21:05:50 -03:00
Esdras Renan
50f6796ffa feat: paginate ticket timeline 2025-10-20 20:39:16 -03:00
Esdras Renan
96a6f73e30 style: restyle ticket details panel 2025-10-20 19:54:36 -03:00
Esdras Renan
3972f66c92 feat: enforce ticket ownership during work sessions 2025-10-20 19:46:20 -03:00
Esdras Renan
81657e52d8 fix: improve closure templates and hover styling 2025-10-20 19:00:38 -03:00
Esdras Renan
3012ad4348 fix: align ticket timer icon size 2025-10-20 18:28:15 -03:00
codex-bot
9b633bd8e0 CI: habilitar cache do Next.js (.next/cache) em GitHub Actions (quality-checks e deploy self-hosted) e preservar cache entre builds 2025-10-20 17:58:36 -03:00
codex-bot
d5a11d0536 Login: aumentar levemente o espaçamento do subtítulo ('Por Rever Tecnologia') 2025-10-20 17:51:23 -03:00
codex-bot
2f798d934a UI: corrigir duplicidade do Encerrar, padronizar ciano, ajustar tamanho do ícone de tempo; atualizar cabeçalho do login; inverter ordem (Novo ticket à esquerda, Compartilhar à direita) no ticket individual 2025-10-20 17:40:17 -03:00
codex-bot
741a0b5b70 ui: keep close dialog content unchanged; refactor to shared component; layout tweaks per request 2025-10-20 17:16:29 -03:00
codex-bot
040965b148 ui(header): reorder tempo total before edit icon; status close as icon with tooltip 2025-10-20 17:01:36 -03:00
codex-bot
9b31a47f82 ui: header cleanup (edit icon, time tooltip), delete button style; filters: server-side assignee + company mapping; UX: toasts on save/clear default filter 2025-10-20 16:40:27 -03:00
codex-bot
f5b3abd277 docs: reorganize and simplify
- Add docs/README.md as index
- Consolidate ops in docs/operations.md; mark legacy runbooks as archive
- Create docs/desktop/ and docs/admin/ structure and move relevant docs
- Update root README to link docs index
- Keep historical and planning notes under docs/archive/
2025-10-20 16:24:16 -03:00
codex-bot
0dd0e67458 feat(filters): usar filtro por responsável no servidor (assigneeId)\n\nci: tocar convex/tickets para forçar deploy das funções 2025-10-20 16:07:54 -03:00
codex-bot
e4bf7c801b ci(convex): remove CONVEX_DEPLOYMENT when using self-hosted URL+ADMIN_KEY; rely on convex.json or link 2025-10-20 15:47:50 -03:00
codex-bot
b2f393035c ci(convex): touch convex/README to trigger Convex deploy job 2025-10-20 15:35:48 -03:00
codex-bot
7f5a2668b7 fix(filters): não enviar assigneeId ao Convex; filtrar no cliente até o backend atualizar
- Evita erro de validação em produção enquanto as funções Convex não estão publicadas
- Mantém UX do filtro por responsável funcionando
2025-10-20 15:21:34 -03:00
codex-bot
c35c669831 ci(convex): set CONVEX_DEPLOYMENT=default and fetch admin key in convex_deploy job
- Add Acquire Convex admin key step in convex_deploy
- Provide CONVEX_DEPLOYMENT env and stop unsetting it
- Pass envs into container for env list + deploy
2025-10-20 15:19:59 -03:00
codex-bot
c4265341ee ci(convex): fix self-hosted deploy
- Acquire admin key in convex_deploy job (id:key)
- Copy existing convex.json from APP_DIR to build dir (EFFECTIVE_APP_DIR)
- Prevent ‘No CONVEX_DEPLOYMENT set’ by ensuring project link present
2025-10-20 15:08:30 -03:00
codex-bot
7ed7775c05 fix(editor): reativar edição ao atribuir responsável
- Atualiza RichTextEditor para refletir mudanças de disabled (setEditable)
- Corrige bug onde editor permanecia travado até F5 após atribuição
2025-10-20 15:06:30 -03:00
codex-bot
5535ba81e6 feat: status + queue updates, filters e UI
- Status renomeados e cores (Em andamento azul, Pausado amarelo)
- Transições automáticas: iniciar=Em andamento, pausar=Pausado
- Fila padrão: Chamados ao criar ticket
- Admin/Empresas: renomeia ‘Slug’ → ‘Apelido’ + mensagens
- Dashboard: últimos tickets priorizam sem responsável (mais antigos)
- Tickets: filtro por responsável + salvar filtro por usuário
- Encerrar ticket: adiciona botão ‘Cancelar’
- Strings atualizadas (PDF, relatórios, badges)
2025-10-20 14:57:22 -03:00
Esdras Renan
e91192a1f6 Fix pnpm lock for desktop icon tooling 2025-10-20 13:35:20 -03:00
Esdras Renan
eff5e2bdcf Document NSIS icon regeneration steps 2025-10-20 12:14:01 -03:00
Esdras Renan
c26cb140ae Adjust NSIS installer icon sizes 2025-10-20 12:00:13 -03:00
Esdras Renan
9008fe5c30 fix(desktop): generate ICO without 512px (NSIS expects <=256) — use 256/128/64/32 sources only 2025-10-20 11:49:15 -03:00
Esdras Renan
0f62936146 fix(desktop): remove unsupported nsis.uninstallerIcon (Tauri v2 schema) — keep installerIcon only 2025-10-20 11:46:37 -03:00
Esdras Renan
761b28e185 chore(desktop): pin png-to-ico@^3.0.1 for icon generation script 2025-10-20 11:43:25 -03:00
Esdras Renan
29d5a07588 desktop(installer): add icon generation script and ensure installer/uninstaller use multi-res ICO\n\n- Add apps/desktop/scripts/build-icon.mjs using png-to-ico\n- Add script in apps/desktop/package.json\n- Set NSIS uninstallerIcon to icons/icon.ico\n\nUsage: pnpm -C apps/desktop install && pnpm -C apps/desktop gen:icon && pnpm -C apps/desktop tauri build --bundles nsis 2025-10-20 11:39:16 -03:00
Esdras Renan
f986fc667d ci: add self-hosted Convex deploy job + restart step; deploy functions when convex/** changes or forced via workflow_dispatch 2025-10-20 10:44:14 -03:00
Esdras Renan
216feca971 feat(tickets): preserve requester/assignee/company snapshots + timeline fallbacks; chore: add requester index\n\n- Add requesterSnapshot, assigneeSnapshot, companySnapshot to tickets\n- Use snapshots as fallback in list/get/play\n- Update snapshots on assignee changes/startWork\n- Preserve snapshots before deleting users/companies\n- Add index tickets.by_tenant_requester\n- Add migrations.backfillTicketSnapshots\n\nchore(convex): upgrade to ^1.28.0 and run codegen\nchore(next): upgrade Next.js to 15.5.6 and update React/eslint-config-next\nfix: remove any and lint warnings; tighten types across API routes and components\ndocs: add docs/ticket-snapshots.md 2025-10-20 10:13:37 -03:00
Esdras Renan
0d82162a0e docs: registrar sincronizacao do cronometro 2025-10-19 20:39:09 -03:00
Esdras Renan
090ebb9607 fix: align ticket timers to server clock 2025-10-19 20:27:11 -03:00
Esdras Renan
3b5676ed35 fix: reconcile ticket timer with server start 2025-10-19 19:52:42 -03:00
Esdras Renan
1df7e13c8f tickets: prevent inflated running time by clamping session start to max(remote, local click); reset on pause; tighten interval deps 2025-10-19 19:12:57 -03:00
Esdras Renan
f550fa5952 ui: move 'Alertas enviados' from sidebar to Settings actions; update docs 2025-10-19 18:03:51 -03:00
Esdras Renan
a40072f428 docs: atualiza agentes.md com abas Equipe/Usuários, ações em massa e correção do temporizador; remove campo de Espaço dos Convites 2025-10-19 17:24:04 -03:00
Esdras Renan
7c3bf00790 admin: remove 'Espaço (ID interno)' from Convites and improve filter bar alignment; tickets: fix running timer by optimistic updating start/pause times 2025-10-19 16:27:12 -03:00
Esdras Renan
a325d612cb admin: split Equipe/Usuários, add bulk select/actions for users, machines and invites; add company/tenant filters 2025-10-19 16:08:46 -03:00
Esdras Renan
515d1718a6 fix: allow removing orphaned machine agents 2025-10-19 15:36:00 -03:00
Esdras Renan
30dd503082 fix: clear persona links and use esm vitest config 2025-10-19 15:00:25 -03:00
Esdras Renan
149d46e7d3 desktop: onboarding – substituir textos ('Agente Desktop' -> 'Sistema de chamados'; 'Portal do Cliente' -> 'Raven' com mesmo espaçamento da sidebar) 2025-10-19 14:59:44 -03:00
Esdras Renan
63d6a65334 chore: snapshot comment authors before user deletion 2025-10-19 14:30:59 -03:00
Esdras Renan
846e575637 fix: harden machine session fallback and clean lint 2025-10-19 03:16:50 -03:00
Esdras Renan
2607ca5ce3 Assignee picker: return only ADMIN/AGENT (exclude collaborators/managers) 2025-10-19 03:03:10 -03:00
Esdras Renan
51d92b230e chore: enlarge raven icon in tauri bundle 2025-10-19 02:47:44 -03:00
Esdras Renan
7c025a0398 Allow managers to comment without assignee; keep assignee requirement for admin/agent; reflect in UI 2025-10-19 02:45:53 -03:00
Esdras Renan
b468c6c9e7 Enforce assignee-required commenting for staff; UI disables commenting until responsible is set; poll machine session for live deactivation; desktop deactivation screen update 2025-10-19 02:34:05 -03:00
Esdras Renan
2a8fb4330c Desktop register: validate email format client-side and disable submit to avoid 400 2025-10-19 02:25:12 -03:00
Esdras Renan
2c7c22d70b Desktop: switch deactivation screen background to solid black 2025-10-19 02:16:35 -03:00
Esdras Renan
77f48652cd Auth: poll machine session to reflect deactivation in real time; Desktop: refresh deactivation screen to match design system 2025-10-19 02:13:39 -03:00
Esdras Renan
01461d031b Machine details: live-update 'Última atualização' every second 2025-10-19 02:02:34 -03:00
Esdras Renan
3a752b88c6 Dropzone: clear local items when parent attachments reset (prevents stale 'Pronto' rows after submit) 2025-10-19 01:43:24 -03:00
Esdras Renan
fc1bdc248b Actions: relax perms before cleanup to avoid Permission denied; keep docker root fallback 2025-10-19 01:38:26 -03:00
Esdras Renan
f3cb9038b7 Regenerate installer icon using full-size artwork 2025-10-19 01:33:35 -03:00
Esdras Renan
275daa7c6e Fix portal comment submission with attachments 2025-10-19 01:15:55 -03:00
Esdras Renan
f606ac1570 Update desktop icon assets and allow attachment-only comments 2025-10-19 01:08:52 -03:00
Esdras Renan
1251468b77 Refine machine details layout and improve download feedback 2025-10-19 00:52:42 -03:00
Esdras Renan
5f7efa13e6 Remove unused icon import 2025-10-19 00:09:18 -03:00
Esdras Renan
5f7dccff71 Refine desktop onboarding and NSIS branding 2025-10-19 00:01:27 -03:00
Esdras Renan
36f34d81d3 Adjust NSIS config for current Tauri schema 2025-10-18 23:40:06 -03:00
Esdras Renan
78030dbcdb Improve desktop branding and NSIS assets 2025-10-18 23:31:10 -03:00
Esdras Renan
9439890488 chore(desktop): drop unused react import 2025-10-18 23:07:17 -03:00
Esdras Renan
83cc02fd1a chore: tighten dashboard in-progress copy 2025-10-18 22:54:20 -03:00
Esdras Renan
0e27d6b113 fix: normalize server session expiresAt for better-auth 2025-10-18 22:30:06 -03:00
Esdras Renan
83aabce8cc fix: rely on auth API for server session lookup 2025-10-18 22:22:11 -03:00
Esdras Renan
bad090ab0d style: remove placeholder tint in admin companies 2025-10-18 22:14:16 -03:00
Esdras Renan
f90bbf5d54 fix: align machine summary typing with convex 2025-10-18 22:04:37 -03:00
Esdras Renan
5de8b2bf7f fix: align company data with machines 2025-10-18 21:57:13 -03:00
Esdras Renan
40e92cf2b9 fix: add spacing to chart tooltip 2025-10-18 21:39:48 -03:00
Esdras Renan
422cba2b83 fix: reuse sync helper on machines fallback 2025-10-18 21:26:53 -03:00
Esdras Renan
7a3eca9361 feat: sync convex companies and dashboard metrics 2025-10-18 21:14:01 -03:00
Esdras Renan
4f52114b48 Aumenta espaço entre label e editor de mensagem 2025-10-18 20:43:55 -03:00
Esdras Renan
5bf77844c3 Move machine view action into companies dropdown 2025-10-18 19:59:21 -03:00
Esdras Renan
1c7309a2b6 Add confirmation dialog for client deletion and align machine badges 2025-10-18 19:52:05 -03:00
Esdras Renan
2400f34c80 Fix role selection defaults and phone input typing 2025-10-18 19:28:37 -03:00
Esdras Renan
a69d37a672 feat: refine admin access management 2025-10-18 01:32:19 -03:00
Esdras Renan
dded6d1927 Reorganiza gestão de usuários e remove dados mock 2025-10-18 01:15:15 -03:00
Esdras Renan
630110bf3a Atualiza portal e admin com bloqueio de máquinas desativadas 2025-10-18 00:02:15 -03:00
Esdras Renan
e5085962e9 Impede acesso ao portal para máquinas desativadas 2025-10-18 00:01:35 -03:00
Esdras Renan
0e97e4c0d6 fix: corrige tipagem do componente de abas 2025-10-17 21:44:58 -03:00
Esdras Renan
c00b4300c1 feat: refresh Raven branding 2025-10-17 15:29:08 -03:00
Esdras Renan
6e2bbb3494 docs(desktop): document NSIS language selector (PortugueseBR) and perMachine install mode with file references 2025-10-17 11:18:51 -03:00
Esdras Renan
ad371f04ad chore(desktop): NSIS perMachine install + enable language selector + set PortugueseBR language 2025-10-17 09:40:48 -03:00
Esdras Renan
b1bb554e72 fix: ajustar script web e config tauri 2025-10-17 00:11:36 -03:00
Esdras Renan
d80712098b fix: gracefully degrade shader background when WebGL is unavailable 2025-10-16 23:37:36 -03:00
Esdras Renan
604216ddec feat: improve company forms, phone input, and auth redirects 2025-10-16 23:35:20 -03:00
Esdras Renan
6962d5e5b5 fix: align tauri bundle config with v2 schema 2025-10-16 23:31:54 -03:00
Esdras Renan
f1a0b9dae5 feat: enrich companies with phone input and machine overview 2025-10-16 23:19:12 -03:00
Esdras Renan
4c228e908a feat: enhance machine insights and solidify admin workflows 2025-10-16 22:56:57 -03:00
Esdras Renan
ac986410a3 feat: disable automatic machine alert tickets 2025-10-16 22:31:05 -03:00
Esdras Renan
7951bc25a3 feat: allow company deletion by detaching dependents 2025-10-16 22:28:12 -03:00
Esdras Renan
2980885bf8 fix: ensure pnpm is available in web entrypoint 2025-10-16 22:13:03 -03:00
Esdras Renan
0f835efc3e fix: drop turbopack dev cache flag 2025-10-16 21:41:16 -03:00
Esdras Renan
91d0608838 fix: align next config with next 15.5.5 2025-10-16 21:39:43 -03:00
Esdras Renan
0cf1892256 chore: voltar para Next 15.5.5 e React 18 2025-10-16 21:12:00 -03:00
Esdras Renan
508ffe5022 chore: trust host header e validação centralizada de domínios 2025-10-16 20:55:48 -03:00
Esdras Renan
c424febf1f chore: update Next 16 beta config 2025-10-16 20:33:32 -03:00
Esdras Renan
a10c7dd15d chore: update next 16 beta config and docs 2025-10-16 20:28:49 -03:00
Esdras Renan
b3a0fb09db upgrade: Next.js 16 beta 2025-10-16 20:25:46 -03:00
Esdras Renan
70c73db907 atualização para next.js 16 beta 2025-10-16 19:42:58 -03:00
Esdras Renan
1ce402cdd7 ajustes nos teste, adições e remoções 2025-10-16 19:29:52 -03:00
Esdras Renan
68ace0a858 refactor: quality workflow, docs, tests 2025-10-16 19:14:46 -03:00
Esdras Renan
a9caf36b01 admin/companies: evitar 500 ao excluir — pré-checar vínculos (users/tickets) e retornar 409 com detalhes 2025-10-16 17:40:39 -03:00
Esdras Renan
f60a48e7b3 admin/companies: melhorar criação/edição\n\n- Aceita isAvulso e contractedHoursPerMonth no POST\n- Retorna 409 para duplicidade (slug/provisioningCode)\n- Retorna 409 para duplicidade no PATCH 2025-10-16 17:29:08 -03:00
Esdras Renan
e6e343fe38 desktop: remover leitura de isValidatingToken para corrigir TS6133 no build Tauri 2025-10-16 16:53:37 -03:00
Esdras Renan
91d53ba10a api: /api/machines/session em runtime nodejs para leitura estável de cookies 2025-10-16 16:10:23 -03:00
Esdras Renan
05ffdf7876 auth: evitar loop login<->dashboard para sessão de máquina\n\n- Login direciona máquina para portal (ou dashboard se manager)\n- Middleware redireciona máquina colaborador do dashboard para /portal/tickets\n- Middleware evita mostrar login quando já autenticado como máquina 2025-10-16 16:09:49 -03:00
Esdras Renan
f2e25dfe4d desktop: validar token no startup + fallback onboarding; corrigir openSystem e pós-registro 2025-10-16 16:04:33 -03:00
Esdras Renan
12cbf564a7 desktop: onboarding seguro quando token inválido/expirado\n\n- Valida token no startup via /api/machines/heartbeat\n- Se inválido/expirado/revogado: limpa token/config e mostra onboarding\n- Auto-launch só após token validado\n- Fluxo Abrir sistema detecta token inválido e reprovisiona\n- Pós-registro abre sistema imediatamente e inicia heartbeat 2025-10-16 16:04:17 -03:00
Esdras Renan
945b29f317 ci(web): fix permission errors publishing to stable APP_DIR (docker chown+chmod, exclude .pnpm-store); runtime: use container-local PNPM store to avoid host writes 2025-10-16 15:08:12 -03:00
Esdras Renan
009291f6a2 ci(web): publish build to stable path (/home/renan/apps/sistema) and deploy using that fixed APP_DIR; cleanup only web.build.* 2025-10-16 14:53:59 -03:00
Esdras Renan
9c498245e9 ci(web): remove redundant docker service update --force; rely on stack deploy update to avoid mount race/rollback 2025-10-16 14:02:49 -03:00
Esdras Renan
df4ae45458 ci(web): pass resolved APP_DIR path (no symlink) to docker stack deploy to satisfy bind mount requirement 2025-10-16 13:47:58 -03:00
Esdras Renan
03d31d082c ci(web): use stable symlink APP_DIR (/home/renan/apps/sistema.current) and skip active dir during cleanup to prevent mount path disappearance 2025-10-16 13:37:12 -03:00
Esdras Renan
5dbe6986e6 ci: isolate build dirs per job (web.build.* vs convex.build.*) to avoid cross-job cleanup deleting live mounts 2025-10-16 13:22:52 -03:00
Esdras Renan
377ba39bac web: use start-first update to minimize downtime during rolling updates 2025-10-16 13:10:42 -03:00
Esdras Renan
0562c13630 ci: improve cleanup fallback (chown+chmod before delete) and run web container as uid 1000 to prevent root-owned caches 2025-10-16 11:59:32 -03:00
Esdras Renan
4376ed9c3c ci(convex): avoid root-owned pnpm store by using container-local store; add root (docker) fallback for cleaning old build dirs 2025-10-16 11:49:48 -03:00
Esdras Renan
8639491ba2 ci: fail on service restart errors to catch deployment issues 2025-10-16 11:38:12 -03:00
Esdras Renan
c785094e4f ci: cleanup build dirs; add web start script; fix NEXT_PUBLIC_CONVEX_URL usage 2025-10-16 11:18:56 -03:00
Esdras Renan
4724255e79 feat: refina portal/desktop para play responsável, anexos e perfil 2025-10-16 03:10:44 -03:00
Esdras Renan
c90e99820f feat: aprimora upload/anexos e regras de atendimento no portal 2025-10-16 03:01:27 -03:00
Esdras Renan
7e8023ed87 fix(ci/deploy): stop-first no web + start script com retry de migrations e seed não fatal 2025-10-16 00:32:26 -03:00
Esdras Renan
1282621995 fix: torna migration de provisioning idempotente e libera deploy 2025-10-16 00:20:21 -03:00
Esdras Renan
9e3b3a9bde fix: corrige migration de provisioning e destrava deploy 2025-10-16 00:07:29 -03:00
Esdras Renan
70fdd8deb8 fix: ajustes de provisioning e layout nas empresas 2025-10-15 23:49:19 -03:00
Esdras Renan
ce6c8fdb3a fix: layout de empresas e ajustes no fluxo de provisioning 2025-10-15 23:44:00 -03:00
Esdras Renan
444b910429 fix: refina layout das colunas de empresas e corrige leitura de cookies e provisioning 2025-10-15 23:39:46 -03:00
Esdras Renan
7668c478f1 fix: refina layout das colunas de empresas e corrige leitura de cookies e provisioning 2025-10-15 23:34:36 -03:00
Esdras Renan
5211ea45df fix: refina layout das colunas de empresas e corrige leitura de cookies no middleware 2025-10-15 23:30:16 -03:00
Esdras Renan
43230e0310 Refine admin companies layout and relax provisioning schema 2025-10-15 23:19:24 -03:00
Esdras Renan
2cba553efa Implement company provisioning codes and session tweaks 2025-10-15 20:45:25 -03:00
Esdras Renan
0fb9bf59b2 Docs: document machine-session fixes, desktop handshake, portal UX changes, and Windows osInfo fallback 2025-10-15 00:21:11 -03:00
Esdras Renan
328415d9e9 Windows inventory: add osInfo fallback from sysinfo; portal detail spacing + subcategory 2025-10-14 23:15:11 -03:00
Esdras Renan
2a4bc486cf Fix JSX block in detail (RTE + Dropzone), hide priority in detail for customers, use isCustomer and hide queue 2025-10-14 22:42:37 -03:00
Esdras Renan
3f49e349f7 Hide Sign out when running as machine (even if session is null) 2025-10-14 22:30:22 -03:00
Esdras Renan
6df49ba956 Portal polishing: hide queue/priority for customers; use RTE + attachments in detail; filter list to requester only for collaborators 2025-10-14 22:29:38 -03:00
Esdras Renan
d1871ba232 Allow requester (collaborator) to add PUBLIC comments to own tickets; fix 'Autor não possui permissão' 2025-10-14 22:10:07 -03:00
Esdras Renan
8292abee88 Auth client: fetch machine context even if session is null; derive role/id from machineContext 2025-10-14 21:57:21 -03:00
Esdras Renan
43576ff8d7 Desktop: redirect handshake to /portal/debug to inspect session in WebView 2025-10-14 21:47:27 -03:00
Esdras Renan
da339a767b Fix type: use third param type for cookies.set options 2025-10-14 21:37:09 -03:00
Esdras Renan
b703561a7e Set session cookies via NextResponse.cookies.set for reliability (handshake + sessions) 2025-10-14 21:33:11 -03:00
Esdras Renan
f1f52f7c30 Fix TS build: remove unused res var in desktop session POST 2025-10-14 21:18:50 -03:00
Esdras Renan
5773aa69f3 Desktop: always navigate through /machines/handshake to set cookies in first-party context 2025-10-14 21:08:30 -03:00
Esdras Renan
9eb3a63e90 CORS: enable credentials for allowed origins (fix cookies set from WebView) 2025-10-14 20:57:31 -03:00
Esdras Renan
1e850ed11e Add /portal/debug page and navigate there after session; shows auth + machine context 2025-10-14 20:47:46 -03:00
Esdras Renan
6754af769b Fix session cookie propagation; desktop creates session via POST before opening portal 2025-10-14 20:33:40 -03:00
Esdras Renan
69955ae80c Surface machine context when collaborator missing 2025-10-14 19:37:19 -03:00
Esdras Renan
a1bd3bb7b9 Expose machine context debug info in console 2025-10-14 19:01:21 -03:00
Esdras Renan
0fb95147f4 Log machine context errors in portal 2025-10-14 18:49:58 -03:00
Esdras Renan
545d5bea4b desktop/devtools: habilitar feature 'devtools' do Tauri v2 (WebviewWindow::open_devtools) 2025-10-14 16:00:07 -03:00
Esdras Renan
737c737be6 desktop: corrigir build (remover uso de @tauri-apps/api/fs e StoreOptions.dir)
- Usa Store.load com caminho absoluto em C:\Raven\data quando possível; fallback para AppData
- Remove import de fs (plugin) e opção 'dir' não suportada pelo plugin-store v2
2025-10-14 15:51:55 -03:00
Esdras Renan
7bc8a1d945 desktop: armazenar dados em pasta 'data' ao lado do executável (fallback AppData) e remover NSIS v2 inválido
- main.tsx: resolveDataDir usa executableDir/data quando possível; se falhar, cai para appLocalDataDir
- tauri.conf.json: remove bloco windows.nsis inválido no schema v2 para build passar

Objetivo: instalar dados fora do AppData (ex.: C:\Raven\data) quando o diretório de instalação permitir escrita.
2025-10-14 15:49:41 -03:00
Esdras Renan
0b39bcb56c desktop/devtools: habilitar F12/Ctrl+Shift+I e menu de contexto para abrir DevTools
- src-tauri: adiciona comando open_devtools que chama window.open_devtools()
- frontend: listeners para F12/Ctrl+Shift+I e botão direito com Ctrl/Shift

Facilita depuração de UI no executável Tauri.
2025-10-14 15:26:59 -03:00
Esdras Renan
087170e321 desktop/windows: ajustar NSIS (perMachine, escolher diretório, atalho) e mover store para AppData
- tauri.conf.json: configura nsis (installMode perMachine, allowToChangeInstallDirectory, installDirName, createDesktopShortcut)
- main.tsx: Store.load agora usa appLocalDataDir (dados do app em AppData do usuário)
- README: documenta instalação Windows, dados em AppData e build leve (--bundles nsis)
2025-10-14 15:09:54 -03:00
Esdras Renan
682d39db70 desktop/machines: liberar handshake e ocultar 'Sair' em sessão de máquina
- middleware: torna /machines/handshake público para permitir criação de sessão de máquina sem login prévio
- nav-user: oculta botão 'Encerrar sessão' quando a sessão é de máquina (role === 'machine')

Efeito: no app desktop (Tauri), o handshake passa a autenticar corretamente, o cabeçalho exibe nome/e-mail do colaborador/gestor e o portal permite abrir chamados.

Nota: em navegação web comum, 'Sair' permanece visível para usuários humanos.
2025-10-14 11:22:01 -03:00
Esdras Renan
9ac584dcb6 auth: guarda server-side em rotas protegidas
- /tickets/[id], /tickets/resolved, /views, /play, /reports/*, /settings agora exigem sessão via requireAuthenticatedSession()
- Complementa o middleware e evita casos não-redirecionados em DEV/SSG
- Mantém /settings/templates com requireStaffSession() como já estava.
2025-10-14 10:59:09 -03:00
Esdras Renan
d7dd37f90f middleware: voltar runtime nodejs para alinhar leitura de sessão\n\n- Edge estava inconsistindo com getCookieCache e gerando loop login↔dashboard\n- Mantém matcher enxuto para ignorar assets 2025-10-14 10:48:11 -03:00
Esdras Renan
79f21e738c tickets: exigir sessão no servidor em /tickets
- Chama requireAuthenticatedSession() no page.tsx para redirecionar anônimos a /login
- Garante comportamento idêntico ao /dashboard quando acessado diretamente.
2025-10-14 10:46:57 -03:00
Esdras Renan
da7b866aeb app: desabilitar SSG global (force-dynamic)
- Define dynamic="force-dynamic" no layout global para garantir que o middleware rode para todas as páginas
- Evita cache s-maxage de páginas protegidas (ex.: /tickets) que impedia redirect em anônimo.
2025-10-14 10:40:25 -03:00
Esdras Renan
0177391326 middleware: aplicar em todas as páginas (edge)\n\n- Remove runtime nodejs (middleware roda em Edge por padrão)\n- Ajusta matcher para ignorar assets e imagens estáticas\n- Mantém /login como público e gateia o restante por sessão\n- Admin-only aplicado em produção.\n\nEvita casos em que páginas eram renderizadas sem dados em anônimo ao acessar rotas diretas. 2025-10-14 10:34:26 -03:00
Esdras Renan
7e480fe864 dashboard: exigir sessão no servidor
- Chama requireAuthenticatedSession() em /dashboard para redirecionar anônimos a /login
- Evita página vazia quando aberto diretamente em janela anônima.
2025-10-14 10:32:29 -03:00
Esdras Renan
2cf416da60 home: redirecionar por sessão no servidor
- Em /, decidir destino via getServerSession():
  - Sem sessão: /login
  - Staff: /dashboard
  - Colaborador: /portal
- Evita depender do redirect client-side e garante comportamento correto em aba anônima.
2025-10-14 10:23:58 -03:00
Esdras Renan
c88622d762 auth: eliminar flash do login
- Simplifica AuthGuard para não redirecionar no cliente (gate feito no middleware)
- Adiciona skeleton de carregamento no AppShell enquanto
- Troca anchors por Next Link no sidebar para navegação client-side

Sem mudanças de schema/DB; apenas UX e roteamento no cliente.
2025-10-14 09:52:39 -03:00
Esdras Renan
32488d48ca Ajusta sessão de máquina para vincular colaborador do metadata 2025-10-13 22:01:33 -03:00
Esdras Renan
ecbd32e735 Atualiza desktop para versão 0.1.6 2025-10-13 21:08:37 -03:00
Esdras Renan
a578d7b906 Publica instalador desktop 0.1.5 2025-10-13 20:43:52 -03:00
Esdras Renan
92ec006c09 Garante vínculo do colaborador mesmo sem persona inicial 2025-10-13 20:36:58 -03:00
Esdras Renan
549d9bcbd0 Atualiza chave pública do updater 2025-10-13 20:20:18 -03:00
Esdras Renan
974f8954e9 Refresca contexto após vincular colaborador 2025-10-13 20:10:25 -03:00
Esdras Renan
6a9759e12f Tipa chamada ao ensureUser na sessão de máquina 2025-10-13 19:43:23 -03:00
Esdras Renan
11390a9d83 Enriquece inventário do Windows e layout dos cards 2025-10-13 19:42:19 -03:00
Esdras Renan
26ae2aa8e5 Corrige detecção de ativação do Windows 2025-10-13 19:34:45 -03:00
Esdras Renan
2f47c40894 Garante typing ao associar usuário de máquina 2025-10-13 19:32:10 -03:00
Esdras Renan
4f6d0265b5 Alinha cabeçalho do desktop ao layout do portal 2025-10-13 19:22:34 -03:00
Esdras Renan
ba0dcddefb Ajusta portal e desktop para máquina vinculada 2025-10-13 19:20:16 -03:00
Esdras Renan
0cac7aa23a Corrige reset de subcategoria 2025-10-13 18:12:40 -03:00
Esdras Renan
9f85cbaba5 Reverte opcionalidade de subcategoria no portal 2025-10-13 18:08:34 -03:00
Esdras Renan
6a04ef4843 Ajusta portal do cliente e desbloqueia abertura de chamados 2025-10-13 17:47:39 -03:00
Esdras Renan
12c7fa23ae Ajusta estilo do painel de tickets recentes 2025-10-13 17:27:48 -03:00
Esdras Renan
4c987d4447 Align status and priority badges in recent tickets 2025-10-13 17:14:13 -03:00
Esdras Renan
490ff1a219 Fine-tune recent ticket badges 2025-10-13 17:10:27 -03:00
Esdras Renan
5785322c07 Restyle recent tickets panel layout 2025-10-13 16:53:31 -03:00
Esdras Renan
0cd477b8ef Improve Windows OS metadata parsing 2025-10-13 16:34:28 -03:00
Esdras Renan
cf31158a9e Allow staff access to admin UI with scoped permissions 2025-10-13 16:30:52 -03:00
Esdras Renan
d6956cd99d Document machine status derivation and Windows fields 2025-10-13 16:07:01 -03:00
Esdras Renan
388ab5feb4 Derive machine online status from heartbeat 2025-10-13 16:05:18 -03:00
Esdras Renan
4d8b9a0e39 Harden machine deletion via Convex API 2025-10-13 15:53:07 -03:00
Esdras Renan
64e4e02a9a Expose detailed Windows OS info in machine inventory 2025-10-13 15:45:24 -03:00
Esdras Renan
3d89c5fd32 Handle single-object Windows GPU and disk payloads 2025-10-13 15:26:36 -03:00
Esdras Renan
17f9f00343 Add company management editing and deletion 2025-10-13 15:23:53 -03:00
Esdras Renan
b60f27b2dc Auto-expire revoked invites and allow reactivation 2025-10-13 15:17:11 -03:00
Esdras Renan
05f5af5ba6 Enable admin user removals and invitation UX polish 2025-10-13 15:08:51 -03:00
Esdras Renan
aa12ebfe0a Add padding to admin user edit drawer 2025-10-13 14:42:49 -03:00
Esdras Renan
016fef34d3 Fix admin user edit select values 2025-10-13 14:32:50 -03:00
Esdras Renan
11efad0312 Fix company search filters and build regressions 2025-10-13 14:18:57 -03:00
Esdras Renan
a8abb68e36 Fix domain user role typing 2025-10-13 14:03:55 -03:00
Esdras Renan
4f812a2e4c Fix GPU inventory typing and user role mapping 2025-10-13 13:59:48 -03:00
Esdras Renan
42611df0f5 fix: improve admin machine details and role gating 2025-10-13 13:32:50 -03:00
Esdras Renan
076c0df7f9 fix: adjust admin user routes and sidebar deps 2025-10-13 11:59:09 -03:00
Esdras Renan
4951e82834 fix: include slug helpers 2025-10-13 11:45:34 -03:00
Esdras Renan
ecad81b0ea feat: overhaul admin user management and desktop UX 2025-10-13 10:36:38 -03:00
Esdras Renan
7d6f3bea01 feat: improve ticket export and navigation 2025-10-13 00:08:18 -03:00
Esdras Renan
0731c5d1ea docs: registrar release 0.1.7 e novo latest.json 2025-10-12 05:12:31 -03:00
Esdras Renan
faa36a2b70 chore(desktop): atualizar latest.json para v0.1.7 2025-10-12 05:07:13 -03:00
Esdras Renan
d65be7d6fd style(portal): alinhar estado vazio com layout administrativo 2025-10-12 05:01:01 -03:00
Esdras Renan
cf212adeec chore(portal): ocultar responsável quando ainda não atribuído 2025-10-12 04:54:14 -03:00
Esdras Renan
0007bc02e7 chore(portal): ocultar opção de sair para sessões do agente 2025-10-12 04:53:24 -03:00
Esdras Renan
53f4b73a32 feat(portal): exibir responsável do ticket para o colaborador 2025-10-12 04:52:40 -03:00
Esdras Renan
0adf8801fc fix(portal): garantir criação do ticket antes de anexos 2025-10-12 04:51:10 -03:00
Esdras Renan
112cf52f81 feat(portal): habilitar editor rico e anexos no formulário 2025-10-12 04:49:17 -03:00
Esdras Renan
d117d8d59f feat(portal): aprimorar formulário e layout para colaboradores 2025-10-12 04:47:27 -03:00
Esdras Renan
d6a164df0e chore(desktop): apontar updater para latest.json do repo 2025-10-12 04:35:15 -03:00
Esdras Renan
c0b51a22d5 chore(desktop): atualizar latest.json para v0.1.6 2025-10-12 04:33:28 -03:00
esdrasrenan
4d28970135
Create latest.json 2025-10-12 04:30:54 -03:00
Esdras Renan
9a0d0ccfbc docs: registrar cópia da chave do updater para builds locais 2025-10-12 04:16:36 -03:00
Esdras Renan
b5fd920efd docs: registrar fluxo do updater e atualizar chaves 2025-10-12 04:06:29 -03:00
Esdras Renan
206d00700e [skip ci] Fix agent for sysinfo 0.31 2025-10-12 01:48:14 -03:00
Esdras Renan
4075c048ca chore: limpar builds antigos com sudo 2025-10-10 23:46:30 -03:00
Esdras Renan
09bb9cac28 fix: remover defaultWindowIcon do tauri config 2025-10-10 23:35:40 -03:00
Esdras Renan
35f50b2dd0 chore: tornar smoke opcional via RUN_MACHINE_SMOKE 2025-10-10 23:32:06 -03:00
Esdras Renan
a456a8e209 fix: usar defaultWindowIcon no tauri 2025-10-10 23:29:43 -03:00
Esdras Renan
3f0702d80b feat: melhorar inventário e gestão de máquinas 2025-10-10 23:20:21 -03:00
Esdras Renan
b1d334045d feat: abrir sistema dentro do app e usar ícones do raven 2025-10-10 22:42:43 -03:00
Esdras Renan
ee6e32ca79 fix: derivar origem do handshake usando host 2025-10-10 22:41:32 -03:00
Esdras Renan
1577d1e0da fix: preservar domínio público no handshake 2025-10-10 22:41:04 -03:00
Esdras Renan
619f311daa fix: evitar URLs localhost em builds de produção 2025-10-10 22:39:38 -03:00
Esdras Renan
a1937c7515 fix: permitir tauri build em windows 2025-10-10 22:29:49 -03:00
Esdras Renan
c463530757 fix: reparar build do desktop tauri 2025-10-10 22:28:35 -03:00
Esdras Renan
e291770417 fix(ci/windows): remove NTFS Zone.Identifier ADS artifacts from fonts and ignore future additions; move Tauri updater config under plugins (v2 schema), bump to 0.1.5 2025-10-10 21:42:35 -03:00
Esdras Renan
81ff7211ee feat(updater): bake Tauri public key into config; remove key injection steps; bump desktop to 0.1.4 2025-10-10 21:28:59 -03:00
Esdras Renan
55791a0503 ci: fix expressions using secrets.* in if by moving to env-based guard; add Windows-safe key injection 2025-10-10 21:26:09 -03:00
Esdras Renan
66a5e2751b ci(desktop): add GITHUB_TOKEN to desktop-release for GitHub Releases + updater 2025-10-10 21:14:30 -03:00
Esdras Renan
4ab15decce chore(desktop): bump version to 0.1.3 for icon fix 2025-10-10 21:14:02 -03:00
Esdras Renan
4a7741de85 chore(desktop): reference icons/Raven.png in Tauri bundle icon (case-sensitive) 2025-10-10 21:13:45 -03:00
698 changed files with 127576 additions and 25831 deletions

View file

@ -0,0 +1,91 @@
{
"permissions": {
"allow": [
"Bash(ssh:*)",
"Bash(bun run lint)",
"Bash(bun run prisma:generate:*)",
"Bash(bun run build:bun:*)",
"WebSearch",
"Bash(bun add:*)",
"Bash(bun run tauri:*)",
"Bash(curl:*)",
"Bash(dir \"D:\\Projetos IA\\sistema-de-chamados\")",
"Bash(findstr:*)",
"Bash(cat:*)",
"Bash(chmod:*)",
"Bash(find:*)",
"Bash(grep:*)",
"WebFetch(domain:medium.com)",
"WebFetch(domain:henrywithu.com)",
"WebFetch(domain:hub.docker.com)",
"Bash(python3:*)",
"WebFetch(domain:www.npmjs.com)",
"WebFetch(domain:docs.strapi.io)",
"Bash(tablename)",
"Bash(\"\"\" OWNER TO renan; FROM pg_tables WHERE schemaname = public;\"\" | docker exec -i c95ebc27eb82 psql -U sistema -d strapi_blog\")",
"Bash(sequence_name)",
"Bash(\"\"\" OWNER TO renan; FROM information_schema.sequences WHERE sequence_schema = public;\"\" | docker exec -i c95ebc27eb82 psql -U sistema -d strapi_blog\")",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(git push:*)",
"Bash(cargo check:*)",
"Bash(bun run:*)",
"Bash(icacls \"D:\\Projetos IA\\sistema-de-chamados\\codex_ed25519\")",
"Bash(copy \"D:\\Projetos IA\\sistema-de-chamados\\codex_ed25519\" \"%TEMP%\\codex_key\")",
"Bash(icacls \"%TEMP%\\codex_key\" /inheritance:r /grant:r \"%USERNAME%:R\")",
"Bash(cmd /c \"echo %TEMP%\")",
"Bash(cmd /c \"dir \"\"%TEMP%\\codex_key\"\"\")",
"Bash(where:*)",
"Bash(ssh-keygen:*)",
"Bash(/c/Program\\ Files/Git/usr/bin/ssh:*)",
"Bash(npx convex deploy:*)",
"Bash(dir \"%LOCALAPPDATA%\\Raven\")",
"Bash(dir \"%APPDATA%\\Raven\")",
"Bash(dir \"%LOCALAPPDATA%\\com.raven.app\")",
"Bash(dir \"%APPDATA%\\com.raven.app\")",
"Bash(tasklist:*)",
"Bash(dir /s /b %LOCALAPPDATA%*raven*)",
"Bash(cmd /c \"tasklist | findstr /i raven\")",
"Bash(cmd /c \"dir /s /b %LOCALAPPDATA%\\*raven* 2>nul\")",
"Bash(powershell -Command \"Get-Process | Where-Object {$_ProcessName -like ''*raven*'' -or $_ProcessName -like ''*appsdesktop*''} | Select-Object ProcessName, Id\")",
"Bash(node:*)",
"Bash(bun scripts/test-all-emails.tsx:*)",
"Bash(bun scripts/send-test-react-email.tsx:*)",
"Bash(dir:*)",
"Bash(git reset:*)",
"Bash(npx convex:*)",
"Bash(bun tsc:*)",
"Bash(scp:*)",
"Bash(docker run:*)",
"Bash(cmd /c \"docker run -d --name postgres-dev -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18\")",
"Bash(cmd /c \"docker ps -a --filter name=postgres-dev\")",
"Bash(cmd /c \"docker --version && docker ps -a\")",
"Bash(powershell -Command \"docker --version\")",
"Bash(powershell -Command \"docker run -d --name postgres-dev -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18\")",
"Bash(dir \"D:\\Projetos IA\\sistema-de-chamados\" /b)",
"Bash(bunx prisma migrate:*)",
"Bash(bunx prisma db push:*)",
"Bash(bun run auth:seed:*)",
"Bash(set DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados:*)",
"Bash(bun tsx:*)",
"Bash(DATABASE_URL=\"postgresql://postgres:dev@localhost:5432/sistema_chamados\" bun tsx:*)",
"Bash(docker stop:*)",
"Bash(docker rm:*)",
"Bash(git commit -m \"$(cat <<''EOF''\nfeat(checklist): exibe descricao do template e do item no ticket\n\n- Adiciona campo templateDescription ao schema do checklist\n- Copia descricao do template ao aplicar checklist no ticket\n- Exibe ambas descricoes na visualizacao do ticket (template em italico)\n- Adiciona documentacao de desenvolvimento local (docs/LOCAL-DEV.md)\n- Corrige prisma-client.mjs para usar PostgreSQL em vez de SQLite\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>\nEOF\n)\")",
"Bash(timeout 90 git push:*)",
"Bash(docker ps:*)",
"Bash(docker start:*)",
"Bash(docker inspect:*)",
"Bash(docker exec:*)",
"Bash(timeout 90 git push)",
"Bash(bun test:*)",
"Bash(git restore:*)",
"Bash(cd:*)",
"Bash(dir \"D:\\Projetos IA\\sistema-de-chamados\\src\\components\\ui\" /b)",
"Bash(timeout 120 bun:*)",
"Bash(bun run tauri:build:*)",
"Bash(git remote:*)",
"Bash(powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"D:/Projetos IA/sistema-de-chamados/scripts/test-windows-collection.ps1\")"
]
}
}

View file

@ -1,62 +1,38 @@
# Ambiente local — Sistema de Chamados
# Copie este arquivo para `.env` e preencha os valores sensíveis.
# Nunca faça commit de `.env` com segredos reais.
NODE_ENV=development
# Convex
CONVEX_DEPLOYMENT=anonymous:anonymous-sistema-de-chamados
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
CONVEX_SYNC_SECRET=dev-sync-secret
# Next.js / App URL
# Public app URL
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Better Auth
# Gere um segredo forte (ex.: `openssl rand -hex 32`)
BETTER_AUTH_SECRET=change-me
BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=your-secret-key-at-least-32-chars-long
# Banco de dados (Prisma)
DATABASE_URL=file:./prisma/db.sqlite
# Convex (dev server URL)
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
CONVEX_INTERNAL_URL=http://127.0.0.1:3210
# Intervalo (ms) para aceitar token revogado ao sincronizar acessos remotos (opcional)
REMOTE_ACCESS_TOKEN_GRACE_MS=900000
# Token interno opcional para o dashboard de saude (/admin/health) e queries internas
INTERNAL_HEALTH_TOKEN=dev-health-token
# Segredo para crons HTTP (reutilize em prod se preferir um unico token)
REPORTS_CRON_SECRET=reports-cron-secret
# Diretório para arquivamento local de tickets (JSONL/backup)
ARCHIVE_DIR=./archives
# Seeds automáticos (Better Auth)
# Por padrão (true), garantindo apenas existência dos usuários padrão sem resetar senhas
SEED_ENSURE_ONLY=true
# PostgreSQL database (versao 18)
# Para desenvolvimento local, use Docker:
# docker run -d --name postgres-chamados -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18
DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados
# Provisionamento e inventário de máquinas
# Segredo obrigatório para registrar/atualizar máquinas (Convex)
MACHINE_PROVISIONING_SECRET=change-me-provisioning
# Tempo de vida do token de máquina (ms) — padrão 30 dias
MACHINE_TOKEN_TTL_MS=2592000000
# Opcional: segredo dedicado para webhook do FleetDM (senão usa o de provisionamento)
FLEET_SYNC_SECRET=
# SMTP Configuration (production values in docs/SMTP.md)
SMTP_HOST=smtp.c.inova.com.br
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=envio@rever.com.br
SMTP_PASS=CAAJQm6ZT6AUdhXRTDYu
SMTP_FROM_NAME=Sistema de Chamados
SMTP_FROM_EMAIL=envio@rever.com.br
# SMTP (envio de e-mails)
SMTP_ADDRESS=
SMTP_PORT=465
SMTP_DOMAIN=
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_AUTHENTICATION=login
SMTP_ENABLE_STARTTLS_AUTO=false
SMTP_TLS=true
MAILER_SENDER_EMAIL="Suporte <no-reply@seu-dominio.com>"
# Alertas (actions do Convex)
# Hora local (America/Sao_Paulo) para rodar alertas automáticos
ALERTS_LOCAL_HOUR=8
# Seeds e sincronizações auxiliares
SYNC_TENANT_ID=tenant-atlas
SYNC_DEFAULT_ASSIGNEE=agent@example.com
SEED_TENANT_ID=tenant-atlas
SEED_ADMIN_PASSWORD=admin123
SEED_AGENT_PASSWORD=agent123
SEED_USER_TENANT=tenant-atlas
SEED_USER_EMAIL=
SEED_USER_PASSWORD=
SEED_USER_NAME=
SEED_USER_ROLE=admin
# Desenvolvimento Desktop (Tauri/Vite)
# Em redes locais, defina o IP do host para HMR.
TAURI_DEV_HOST=
# Dev-only bypass to simplify local testing (do NOT enable in prod)
# DEV_BYPASS_AUTH=0
# NEXT_PUBLIC_DEV_BYPASS_AUTH=0

View file

@ -0,0 +1,492 @@
name: CI/CD Web + Desktop
on:
push:
branches: [ main ]
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
force_web_deploy:
description: 'Forcar deploy do Web (ignorar filtro)?'
type: boolean
required: false
default: false
force_convex_deploy:
description: 'Forcar deploy do Convex (ignorar filtro)?'
type: boolean
required: false
default: false
env:
APP_DIR: /srv/apps/sistema
VPS_UPDATES_DIR: /var/www/updates
jobs:
changes:
name: Detect changes
runs-on: [ self-hosted, linux, vps ]
timeout-minutes: 5
outputs:
convex: ${{ steps.filter.outputs.convex }}
web: ${{ steps.filter.outputs.web }}
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Paths filter
id: filter
uses: https://github.com/dorny/paths-filter@v3
with:
filters: |
convex:
- 'convex/**'
web:
- 'src/**'
- 'public/**'
- 'prisma/**'
- 'next.config.ts'
- 'package.json'
- 'bun.lock'
- 'tsconfig.json'
- 'middleware.ts'
- 'stack.yml'
deploy:
name: Deploy (VPS Linux)
needs: changes
timeout-minutes: 30
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' }}
runs-on: [ self-hosted, linux, vps ]
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Determine APP_DIR (fallback safe path)
id: appdir
run: |
TS=$(date +%s)
FALLBACK_DIR="$HOME/apps/web.build.$TS"
mkdir -p "$FALLBACK_DIR"
echo "Using APP_DIR (fallback)=$FALLBACK_DIR"
echo "EFFECTIVE_APP_DIR=$FALLBACK_DIR" >> "$GITHUB_ENV"
- name: Setup Bun
uses: https://github.com/oven-sh/setup-bun@v2
with:
bun-version: 1.3.4
- name: Sync workspace to APP_DIR (preserving local env)
run: |
mkdir -p "$EFFECTIVE_APP_DIR"
RSYNC_FLAGS="-az --inplace --no-times --no-perms --no-owner --no-group --delete"
EXCLUDE_ENV="--exclude '.env*' --exclude 'apps/desktop/.env*' --exclude 'convex/.env*'"
if [ "$EFFECTIVE_APP_DIR" != "${APP_DIR:-/srv/apps/sistema}" ]; then
EXCLUDE_ENV=""
fi
rsync $RSYNC_FLAGS \
--filter='protect .next.old*' \
--exclude '.next.old*' \
--filter='protect node_modules' \
--filter='protect node_modules/**' \
--filter='protect .pnpm-store' \
--filter='protect .pnpm-store/**' \
--filter='protect .env' \
--filter='protect .env*' \
--filter='protect apps/desktop/.env*' \
--filter='protect convex/.env*' \
--exclude '.git' \
--exclude '.next' \
--exclude 'node_modules' \
--exclude 'node_modules/**' \
--exclude '.pnpm-store' \
--exclude '.pnpm-store/**' \
$EXCLUDE_ENV \
./ "$EFFECTIVE_APP_DIR"/
- name: Acquire Convex admin key
id: key
run: |
echo "Waiting for Convex container..."
CID=""
for attempt in $(seq 1 12); do
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
if [ -n "$CID" ]; then
echo "Convex container ready (CID=$CID)"
break
fi
echo "Attempt $attempt/12: container not ready yet; waiting 5s..."
sleep 5
done
CONVEX_IMAGE="ghcr.io/get-convex/convex-backend:latest"
if [ -n "$CID" ]; then
KEY=$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "No running convex container detected; attempting offline admin key extraction..."
VOLUME="sistema_convex_data"
if docker volume inspect "$VOLUME" >/dev/null 2>&1; then
KEY=$(docker run --rm --entrypoint /bin/sh -v "$VOLUME":/convex/data "$CONVEX_IMAGE" -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "Volume $VOLUME nao encontrado; nao foi possivel extrair a chave admin"
fi
fi
echo "ADMIN_KEY=$KEY" >> $GITHUB_OUTPUT
echo "Admin key acquired? $([ -n "$KEY" ] && echo yes || echo no)"
if [ -z "$KEY" ]; then
echo "ERRO: Nao foi possivel obter a chave admin do Convex"
docker service ps sistema_convex_backend || true
exit 1
fi
- name: Copy production .env if present
run: |
DEFAULT_DIR="${APP_DIR:-/srv/apps/sistema}"
if [ "$EFFECTIVE_APP_DIR" != "$DEFAULT_DIR" ] && [ -f "$DEFAULT_DIR/.env" ]; then
echo "Copying production .env from $DEFAULT_DIR to $EFFECTIVE_APP_DIR"
cp -f "$DEFAULT_DIR/.env" "$EFFECTIVE_APP_DIR/.env"
fi
- name: Ensure Next.js cache directory exists and is writable
run: |
cd "$EFFECTIVE_APP_DIR"
mkdir -p .next/cache
chmod -R u+rwX .next || true
- name: Cache Next.js build cache (.next/cache)
uses: https://github.com/actions/cache@v4
with:
path: ${{ env.EFFECTIVE_APP_DIR }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('bun.lock') }}-${{ hashFiles('next.config.ts') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('bun.lock') }}-
${{ runner.os }}-nextjs-
- name: Lint check (fail fast before build)
run: |
cd "$EFFECTIVE_APP_DIR"
docker run --rm \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
sistema_web:node22-bun \
bash -lc "set -euo pipefail; bun install --frozen-lockfile --filter '!appsdesktop'; bun run lint"
- name: Install and build (Next.js)
env:
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: "1"
run: |
cd "$EFFECTIVE_APP_DIR"
docker run --rm \
-e PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING="$PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING" \
-e NODE_OPTIONS="--max-old-space-size=4096" \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
sistema_web:node22-bun \
bash -lc "set -euo pipefail; bun install --frozen-lockfile --filter '!appsdesktop'; bun run prisma:generate; bun run build:bun"
- name: Fix Docker-created file permissions
run: |
# Docker cria arquivos como root - corrigir para o usuario runner (UID 1000)
docker run --rm -v "$EFFECTIVE_APP_DIR":/target alpine:3 \
chown -R 1000:1000 /target
echo "Permissoes do build corrigidas"
- name: Atualizar symlink do APP_DIR estavel (deploy atomico)
run: |
set -euo pipefail
ROOT="$HOME/apps"
STABLE_LINK="$ROOT/sistema.current"
mkdir -p "$ROOT"
# Sanidade: se esses arquivos nao existirem, o container vai falhar no boot.
test -f "$EFFECTIVE_APP_DIR/scripts/start-web.sh" || { echo "ERROR: scripts/start-web.sh nao encontrado em $EFFECTIVE_APP_DIR" >&2; exit 1; }
test -f "$EFFECTIVE_APP_DIR/stack.yml" || { echo "ERROR: stack.yml nao encontrado em $EFFECTIVE_APP_DIR" >&2; exit 1; }
test -d "$EFFECTIVE_APP_DIR/node_modules" || { echo "ERROR: node_modules nao encontrado em $EFFECTIVE_APP_DIR (necessario para next start)" >&2; exit 1; }
test -d "$EFFECTIVE_APP_DIR/.next" || { echo "ERROR: .next nao encontrado em $EFFECTIVE_APP_DIR (build nao gerado)" >&2; exit 1; }
PREV=""
if [ -L "$STABLE_LINK" ]; then
PREV="$(readlink -f "$STABLE_LINK" || true)"
fi
echo "PREV_APP_DIR=$PREV" >> "$GITHUB_ENV"
ln -sfn "$EFFECTIVE_APP_DIR" "$STABLE_LINK"
# Compat: mantem $HOME/apps/sistema como symlink quando possivel (nao mexe se for pasta).
if [ -L "$ROOT/sistema" ] || [ ! -e "$ROOT/sistema" ]; then
ln -sfn "$STABLE_LINK" "$ROOT/sistema"
fi
echo "APP_DIR estavel -> $(readlink -f "$STABLE_LINK")"
- name: Swarm deploy (stack.yml)
run: |
APP_DIR_STABLE="$HOME/apps/sistema.current"
if [ ! -d "$APP_DIR_STABLE" ]; then
echo "ERROR: Stable APP_DIR does not exist: $APP_DIR_STABLE" >&2; exit 1
fi
cd "$APP_DIR_STABLE"
set -o allexport
if [ -f .env ]; then
echo "Loading .env from $APP_DIR_STABLE"
. ./.env
else
echo "WARNING: No .env found at $APP_DIR_STABLE - stack vars may be empty!"
fi
set +o allexport
echo "Using APP_DIR (stable)=$APP_DIR_STABLE"
echo "NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-<not set>}"
echo "NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-<not set>}"
APP_DIR="$APP_DIR_STABLE" RELEASE_SHA=${{ github.sha }} docker stack deploy --with-registry-auth -c stack.yml sistema
- name: Wait for services to be healthy
run: |
echo "Aguardando servicos ficarem saudaveis..."
for i in $(seq 1 18); do
WEB_STATUS=$(docker service ls --filter "name=sistema_web" --format "{{.Replicas}}" 2>/dev/null || echo "0/0")
CONVEX_STATUS=$(docker service ls --filter "name=sistema_convex_backend" --format "{{.Replicas}}" 2>/dev/null || echo "0/0")
echo "Tentativa $i/18: web=$WEB_STATUS convex=$CONVEX_STATUS"
if echo "$WEB_STATUS" | grep -q "2/2" && echo "$CONVEX_STATUS" | grep -q "1/1"; then
echo "Todos os servicos estao saudaveis!"
exit 0
fi
sleep 10
done
echo "ERRO: Timeout aguardando servicos. Status atual:"
docker service ls --filter "label=com.docker.stack.namespace=sistema" || true
docker service ps sistema_web --no-trunc || true
docker service logs sistema_web --since 5m --raw 2>/dev/null | tail -n 200 || true
if [ -n "${PREV_APP_DIR:-}" ]; then
echo "Rollback: revertendo APP_DIR estavel para: $PREV_APP_DIR"
ln -sfn "$PREV_APP_DIR" "$HOME/apps/sistema.current"
cd "$HOME/apps/sistema.current"
set -o allexport
if [ -f .env ]; then
. ./.env
fi
set +o allexport
APP_DIR="$HOME/apps/sistema.current" RELEASE_SHA=${{ github.sha }} docker stack deploy --with-registry-auth -c stack.yml sistema || true
fi
exit 1
- name: Cleanup old build workdirs (keep last 2)
run: |
set -e
ROOT="$HOME/apps"
KEEP=2
PATTERN='web.build.*'
ACTIVE="$(readlink -f "$HOME/apps/sistema.current" 2>/dev/null || true)"
echo "Scanning $ROOT for old $PATTERN dirs"
LIST=$(find "$ROOT" -maxdepth 1 -type d -name "$PATTERN" | sort -r || true)
echo "$LIST" | sed -n "1,${KEEP}p" | sed 's/^/Keeping: /' || true
echo "$LIST" | sed "1,${KEEP}d" | while read dir; do
[ -z "$dir" ] && continue
if [ -n "$ACTIVE" ] && [ "$(readlink -f "$dir")" = "$ACTIVE" ]; then
echo "Skipping active dir (in use by APP_DIR): $dir"; continue
fi
echo "Removing $dir"
chmod -R u+rwX "$dir" 2>/dev/null || true
rm -rf "$dir" || {
echo "Local rm failed, falling back to docker (root) cleanup for $dir..."
docker run --rm -v "$dir":/target alpine:3 sh -lc 'chown -R 1000:1000 /target 2>/dev/null || true; chmod -R u+rwX /target 2>/dev/null || true; rm -rf /target/* /target/.[!.]* /target/..?* 2>/dev/null || true' || true
rm -rf "$dir" 2>/dev/null || rmdir "$dir" 2>/dev/null || true
}
done
echo "Disk usage (top 10 under $ROOT):"
du -sh "$ROOT"/* 2>/dev/null | sort -rh | head -n 10 || true
convex_deploy:
name: Deploy Convex functions
needs: changes
timeout-minutes: 20
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.convex == 'true' }}
runs-on: [ self-hosted, linux, vps ]
env:
APP_DIR: /srv/apps/sistema
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Determine APP_DIR (fallback safe path)
id: appdir
run: |
TS=$(date +%s)
FALLBACK_DIR="$HOME/apps/convex.build.$TS"
mkdir -p "$FALLBACK_DIR"
echo "Using APP_DIR (fallback)=$FALLBACK_DIR"
echo "EFFECTIVE_APP_DIR=$FALLBACK_DIR" >> "$GITHUB_ENV"
- name: Sync workspace to APP_DIR (preserving local env)
run: |
mkdir -p "$EFFECTIVE_APP_DIR"
RSYNC_FLAGS="-az --inplace --no-times --no-perms --no-owner --no-group --delete"
rsync $RSYNC_FLAGS \
--filter='protect .next.old*' \
--exclude '.next.old*' \
--exclude '.env*' \
--exclude 'apps/desktop/.env*' \
--exclude 'convex/.env*' \
--filter='protect node_modules' \
--filter='protect node_modules/**' \
--filter='protect .pnpm-store' \
--filter='protect .pnpm-store/**' \
--exclude '.git' \
--exclude '.next' \
--exclude 'node_modules' \
--exclude 'node_modules/**' \
--exclude '.pnpm-store' \
--exclude '.pnpm-store/**' \
./ "$EFFECTIVE_APP_DIR"/
- name: Acquire Convex admin key
id: key
run: |
echo "Waiting for Convex container..."
CID=""
for attempt in $(seq 1 12); do
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
if [ -n "$CID" ]; then
echo "Convex container ready (CID=$CID)"
break
fi
echo "Attempt $attempt/12: container not ready yet; waiting 5s..."
sleep 5
done
CONVEX_IMAGE="ghcr.io/get-convex/convex-backend:latest"
if [ -n "$CID" ]; then
KEY=$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "No running convex container detected; attempting offline admin key extraction..."
VOLUME="sistema_convex_data"
if docker volume inspect "$VOLUME" >/dev/null 2>&1; then
KEY=$(docker run --rm --entrypoint /bin/sh -v "$VOLUME":/convex/data "$CONVEX_IMAGE" -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "Volume $VOLUME nao encontrado; nao foi possivel extrair a chave admin"
fi
fi
echo "ADMIN_KEY=$KEY" >> $GITHUB_OUTPUT
echo "Admin key acquired? $([ -n "$KEY" ] && echo yes || echo no)"
if [ -z "$KEY" ]; then
echo "ERRO: Nao foi possivel obter a chave admin do Convex"
docker service ps sistema_convex_backend || true
exit 1
fi
- name: Bring convex.json from live app if present
run: |
if [ -f "$APP_DIR/convex.json" ]; then
echo "Copying $APP_DIR/convex.json -> $EFFECTIVE_APP_DIR/convex.json"
cp -f "$APP_DIR/convex.json" "$EFFECTIVE_APP_DIR/convex.json"
else
echo "No existing convex.json found at $APP_DIR; convex CLI will need self-hosted vars"
fi
- name: Set Convex env vars (self-hosted)
env:
CONVEX_SELF_HOSTED_URL: https://convex.esdrasrenan.com.br
CONVEX_SELF_HOSTED_ADMIN_KEY: ${{ steps.key.outputs.ADMIN_KEY }}
MACHINE_PROVISIONING_SECRET: ${{ secrets.MACHINE_PROVISIONING_SECRET }}
MACHINE_TOKEN_TTL_MS: ${{ secrets.MACHINE_TOKEN_TTL_MS }}
FLEET_SYNC_SECRET: ${{ secrets.FLEET_SYNC_SECRET }}
run: |
set -e
docker run --rm -i \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
-e CONVEX_SELF_HOSTED_URL \
-e CONVEX_SELF_HOSTED_ADMIN_KEY \
-e MACHINE_PROVISIONING_SECRET \
-e MACHINE_TOKEN_TTL_MS \
-e FLEET_SYNC_SECRET \
-e CONVEX_TMPDIR=/app/.convex-tmp \
node:20-bullseye bash -lc "set -euo pipefail; curl -fsSL https://bun.sh/install | bash >/tmp/bun-install.log; export BUN_INSTALL=\"\${BUN_INSTALL:-/root/.bun}\"; export PATH=\"\$BUN_INSTALL/bin:\$PATH\"; export CONVEX_TMPDIR=/app/.convex-tmp; bun install --frozen-lockfile; \
if [ -n \"$MACHINE_PROVISIONING_SECRET\" ]; then bunx convex env set MACHINE_PROVISIONING_SECRET \"$MACHINE_PROVISIONING_SECRET\"; fi; \
if [ -n \"$MACHINE_TOKEN_TTL_MS\" ]; then bunx convex env set MACHINE_TOKEN_TTL_MS \"$MACHINE_TOKEN_TTL_MS\"; fi; \
if [ -n \"$FLEET_SYNC_SECRET\" ]; then bunx convex env set FLEET_SYNC_SECRET \"$FLEET_SYNC_SECRET\"; fi; \
bunx convex env list"
- name: Prepare Convex deploy workspace
run: |
cd "$EFFECTIVE_APP_DIR"
if [ -f .env ]; then
echo "Renaming .env -> .env.bak (Convex self-hosted deploy)"
mv -f .env .env.bak
fi
mkdir -p .convex-tmp
- name: Deploy functions to Convex self-hosted
env:
CONVEX_SELF_HOSTED_URL: https://convex.esdrasrenan.com.br
CONVEX_SELF_HOSTED_ADMIN_KEY: ${{ steps.key.outputs.ADMIN_KEY }}
run: |
docker run --rm -i \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
-e CI=true \
-e CONVEX_SELF_HOSTED_URL \
-e CONVEX_SELF_HOSTED_ADMIN_KEY \
-e CONVEX_TMPDIR=/app/.convex-tmp \
node:20-bullseye bash -lc "set -euo pipefail; curl -fsSL https://bun.sh/install | bash >/tmp/bun-install.log; export BUN_INSTALL=\"\${BUN_INSTALL:-/root/.bun}\"; export PATH=\"\$BUN_INSTALL/bin:\$PATH\"; export CONVEX_TMPDIR=/app/.convex-tmp; bun install --frozen-lockfile; bunx convex deploy"
- name: Cleanup old convex build workdirs (keep last 2)
run: |
set -e
ROOT="$HOME/apps"
KEEP=2
PATTERN='convex.build.*'
LIST=$(find "$ROOT" -maxdepth 1 -type d -name "$PATTERN" | sort -r || true)
echo "$LIST" | sed -n "1,${KEEP}p" | sed 's/^/Keeping: /' || true
echo "$LIST" | sed "1,${KEEP}d" | while read dir; do
[ -z "$dir" ] && continue
echo "Removing $dir"
chmod -R u+rwX "$dir" 2>/dev/null || true
rm -rf "$dir" || {
echo "Local rm failed, falling back to docker (root) cleanup for $dir..."
docker run --rm -v "$dir":/target alpine:3 sh -lc 'chown -R 1000:1000 /target 2>/dev/null || true; chmod -R u+rwX /target 2>/dev/null || true; rm -rf /target/* /target/.[!.]* /target/..?* 2>/dev/null || true' || true
rm -rf "$dir" 2>/dev/null || rmdir "$dir" 2>/dev/null || true
}
done
# NOTA: Job comentado porque nao ha runner Windows configurado.
# Descomentar quando configurar um runner com labels: [self-hosted, windows, desktop]
#
# desktop_release:
# name: Desktop Release (Windows)
# timeout-minutes: 30
# if: ${{ startsWith(github.ref, 'refs/tags/v') }}
# runs-on: [ self-hosted, windows, desktop ]
# defaults:
# run:
# working-directory: apps/desktop
# steps:
# - name: Checkout
# uses: https://github.com/actions/checkout@v4
#
# - name: Setup pnpm
# uses: https://github.com/pnpm/action-setup@v4
# with:
# version: 10.20.0
#
# - name: Setup Node.js
# uses: https://github.com/actions/setup-node@v4
# with:
# node-version: 20
#
# - name: Install deps (desktop)
# run: pnpm install --frozen-lockfile
#
# - name: Build with Tauri
# uses: https://github.com/tauri-apps/tauri-action@v0
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
# TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
# with:
# projectPath: apps/desktop
#
# - name: Upload bundles to VPS
# run: |
# # Upload via SCP (configurar chave SSH no runner Windows)
# # scp -r src-tauri/target/release/bundle/* user@vps:/var/www/updates/
# echo "TODO: Configurar upload para VPS"

View file

@ -0,0 +1,54 @@
name: Quality Checks
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
lint-test-build:
name: Lint, Test and Build
runs-on: [ self-hosted, linux, vps ]
env:
BETTER_AUTH_SECRET: test-secret
NEXT_PUBLIC_APP_URL: http://localhost:3000
BETTER_AUTH_URL: http://localhost:3000
NEXT_PUBLIC_CONVEX_URL: http://localhost:3210
DATABASE_URL: file:./prisma/db.dev.sqlite
steps:
- name: Checkout
uses: https://github.com/actions/checkout@v4
- name: Setup Bun
uses: https://github.com/oven-sh/setup-bun@v2
with:
bun-version: 1.3.4
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Cache Next.js build cache
uses: https://github.com/actions/cache@v4
with:
path: |
${{ github.workspace }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('bun.lock') }}-${{ hashFiles('**/*.{js,jsx,ts,tsx}') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('bun.lock') }}-
- name: Generate Prisma client
env:
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: "1"
run: bun run prisma:generate
- name: Lint
run: bun run lint
- name: Test
run: bun test
- name: Build
run: bun run build:bun

View file

@ -0,0 +1,639 @@
name: CI/CD Web + Desktop
on:
push:
branches: [ main ]
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
force_web_deploy:
description: 'Forçar deploy do Web (ignorar filtro)?'
required: false
default: 'false'
force_convex_deploy:
description: 'Forçar deploy do Convex (ignorar filtro)?'
required: false
default: 'false'
env:
APP_DIR: /srv/apps/sistema
VPS_UPDATES_DIR: /var/www/updates
RUN_MACHINE_SMOKE: ${{ vars.RUN_MACHINE_SMOKE || secrets.RUN_MACHINE_SMOKE || 'false' }}
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
convex: ${{ steps.filter.outputs.convex }}
web: ${{ steps.filter.outputs.web }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Paths filter
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
convex:
- 'convex/**'
web:
- 'src/**'
- 'public/**'
- 'prisma/**'
- 'next.config.ts'
- 'package.json'
- 'pnpm-lock.yaml'
- 'tsconfig.json'
- 'middleware.ts'
- 'stack.yml'
deploy:
name: Deploy (VPS Linux)
needs: changes
timeout-minutes: 30
# Executa em qualquer push na main (independente do filtro) ou quando disparado manualmente
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' }}
runs-on: [ self-hosted, linux, vps ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Determine APP_DIR (fallback safe path)
id: appdir
run: |
TS=$(date +%s)
# Use a web-specific build dir to avoid clashes with convex job
FALLBACK_DIR="$HOME/apps/web.build.$TS"
mkdir -p "$FALLBACK_DIR"
echo "Using APP_DIR (fallback)=$FALLBACK_DIR"
echo "EFFECTIVE_APP_DIR=$FALLBACK_DIR" >> "$GITHUB_ENV"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.20.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.1
- name: Verify Bun runtime
run: bun --version
- name: Permissions diagnostic (server paths)
run: |
set +e
echo "== Basic context =="
whoami || true
id || true
groups || true
umask || true
echo "HOME=$HOME"
echo "APP_DIR(default)=${APP_DIR:-/srv/apps/sistema}"
echo "EFFECTIVE_APP_DIR=$EFFECTIVE_APP_DIR"
echo "\n== Permissions check =="
check_path() {
P="$1"
echo "-- $P"
if [ -e "$P" ]; then
stat -c '%A %U:%G %n' "$P" 2>/dev/null || ls -ld "$P" || true
echo -n "WRITABLE? "; [ -w "$P" ] && echo yes || echo no
if command -v namei >/dev/null 2>&1; then
namei -l "$P" || true
fi
TMP="$P/.permtest.$$"
(echo test > "$TMP" 2>/dev/null && echo "CREATE_FILE: ok" && rm -f "$TMP") || echo "CREATE_FILE: failed"
else
echo "(missing)"
fi
}
check_path "/srv/apps/sistema"
check_path "/srv/apps/sistema/src/app/machines/handshake"
check_path "/srv/apps/sistema/apps/desktop/node_modules"
check_path "/srv/apps/sistema/node_modules"
check_path "$EFFECTIVE_APP_DIR"
check_path "$EFFECTIVE_APP_DIR/node_modules"
- name: Sync workspace to APP_DIR (preserving local env)
run: |
mkdir -p "$EFFECTIVE_APP_DIR"
RSYNC_FLAGS="-az --inplace --no-times --no-perms --no-owner --no-group --delete"
# Excluir .env apenas quando copiando para o diretório padrão (/srv) para preservar segredos locais
EXCLUDE_ENV="--exclude '.env*' --exclude 'apps/desktop/.env*' --exclude 'convex/.env*'"
if [ "$EFFECTIVE_APP_DIR" != "${APP_DIR:-/srv/apps/sistema}" ]; then
EXCLUDE_ENV=""
fi
rsync $RSYNC_FLAGS \
--filter='protect .next.old*' \
--exclude '.next.old*' \
--filter='protect node_modules' \
--filter='protect node_modules/**' \
--filter='protect .pnpm-store' \
--filter='protect .pnpm-store/**' \
--filter='protect .env' \
--filter='protect .env*' \
--filter='protect apps/desktop/.env*' \
--filter='protect convex/.env*' \
--exclude '.git' \
--exclude '.next' \
--exclude 'node_modules' \
--exclude 'node_modules/**' \
--exclude '.pnpm-store' \
--exclude '.pnpm-store/**' \
$EXCLUDE_ENV \
./ "$EFFECTIVE_APP_DIR"/
- name: Acquire Convex admin key
id: key
run: |
echo "Waiting for Convex container..."
CID=""
# Aguarda ate 60s (12 tentativas x 5s) pelo container ficar pronto
# Nao forca restart - deixa o Swarm gerenciar via health checks
for attempt in $(seq 1 12); do
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
if [ -n "$CID" ]; then
echo "Convex container ready (CID=$CID)"
break
fi
echo "Attempt $attempt/12: container not ready yet; waiting 5s..."
sleep 5
done
CONVEX_IMAGE="ghcr.io/get-convex/convex-backend:latest"
if [ -n "$CID" ]; then
KEY=$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "No running convex container detected; attempting offline admin key extraction..."
VOLUME="sistema_convex_data"
if docker volume inspect "$VOLUME" >/dev/null 2>&1; then
KEY=$(docker run --rm --entrypoint /bin/sh -v "$VOLUME":/convex/data "$CONVEX_IMAGE" -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "Volume $VOLUME nao encontrado; nao foi possivel extrair a chave admin"
fi
fi
echo "ADMIN_KEY=$KEY" >> $GITHUB_OUTPUT
echo "Admin key acquired? $([ -n "$KEY" ] && echo yes || echo no)"
if [ -z "$KEY" ]; then
echo "ERRO: Nao foi possivel obter a chave admin do Convex"
docker service ps sistema_convex_backend || true
exit 1
fi
- name: Copy production .env if present
run: |
DEFAULT_DIR="${APP_DIR:-/srv/apps/sistema}"
if [ "$EFFECTIVE_APP_DIR" != "$DEFAULT_DIR" ] && [ -f "$DEFAULT_DIR/.env" ]; then
echo "Copying production .env from $DEFAULT_DIR to $EFFECTIVE_APP_DIR"
cp -f "$DEFAULT_DIR/.env" "$EFFECTIVE_APP_DIR/.env"
fi
- name: Prune workspace for server-only build
run: |
cd "$EFFECTIVE_APP_DIR"
# Keep only root (web) as a package in this effective workspace
printf "packages:\n - .\n\nignoredBuiltDependencies:\n - '@prisma/client'\n - '@prisma/engines'\n - '@tailwindcss/oxide'\n - esbuild\n - prisma\n - sharp\n - unrs-resolver\n" > pnpm-workspace.yaml
- name: Ensure Next.js cache directory exists and is writable
run: |
cd "$EFFECTIVE_APP_DIR"
mkdir -p .next/cache
chmod -R u+rwX .next || true
- name: Cache Next.js build cache (.next/cache)
uses: actions/cache@v4
with:
path: ${{ env.EFFECTIVE_APP_DIR }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml', 'bun.lock') }}-${{ hashFiles('src/**/*.ts', 'src/**/*.tsx', 'src/**/*.js', 'src/**/*.jsx', 'next.config.ts') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml', 'bun.lock') }}-
- name: Lint check (fail fast before build)
run: |
cd "$EFFECTIVE_APP_DIR"
docker run --rm \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
sistema_web:node22-bun \
bash -lc "set -euo pipefail; bun install --frozen-lockfile --filter '!appsdesktop'; bun run lint"
- name: Install and build (Next.js)
env:
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: "1"
run: |
cd "$EFFECTIVE_APP_DIR"
docker run --rm \
-e PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING="$PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING" \
-e NODE_OPTIONS="--max-old-space-size=4096" \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
sistema_web:node22-bun \
bash -lc "set -euo pipefail; bun install --frozen-lockfile --filter '!appsdesktop'; bun run prisma:generate; bun run build:bun"
- name: Publish build to stable APP_DIR directory
run: |
set -e
DEST="$HOME/apps/sistema"
mkdir -p "$DEST"
mkdir -p "$DEST/.next/static"
# One-time fix for old root-owned files (esp. .pnpm-store) left by previous containers
docker run --rm -v "$DEST":/target alpine:3 sh -lc 'chown -R 1000:1000 /target 2>/dev/null || true; chmod -R u+rwX /target 2>/dev/null || true' || true
# Preserve previously published static assets to keep stale chunks available for clients mid-navigation
if [ -d "$EFFECTIVE_APP_DIR/.next/static" ]; then
rsync -a \
"$EFFECTIVE_APP_DIR/.next/static/" "$DEST/.next/static/"
fi
# Publish new build; exclude .pnpm-store to avoid Permission denied on old entries
rsync -a --delete \
--chown=1000:1000 \
--exclude '.pnpm-store' --exclude '.pnpm-store/**' \
--exclude '.next/static' \
"$EFFECTIVE_APP_DIR"/ "$DEST"/
echo "Published build to: $DEST"
- name: Swarm deploy (stack.yml)
run: |
APP_DIR_STABLE="$HOME/apps/sistema"
if [ ! -d "$APP_DIR_STABLE" ]; then
echo "ERROR: Stable APP_DIR does not exist: $APP_DIR_STABLE" >&2; exit 1
fi
cd "$APP_DIR_STABLE"
# Exporta variáveis do .env (do diretório de produção) para substituição no stack
# IMPORTANTE: Usar o .env do APP_DIR_STABLE, não do EFFECTIVE_APP_DIR (build temporário)
set -o allexport
if [ -f .env ]; then
echo "Loading .env from $APP_DIR_STABLE"
. ./.env
else
echo "WARNING: No .env found at $APP_DIR_STABLE - stack vars may be empty!"
fi
set +o allexport
echo "Using APP_DIR (stable)=$APP_DIR_STABLE"
echo "NEXT_PUBLIC_CONVEX_URL=${NEXT_PUBLIC_CONVEX_URL:-<not set>}"
echo "NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL:-<not set>}"
APP_DIR="$APP_DIR_STABLE" RELEASE_SHA=${{ github.sha }} docker stack deploy --with-registry-auth -c stack.yml sistema
- name: Wait for services to be healthy
run: |
echo "Aguardando servicos ficarem saudaveis..."
# Aguarda ate 3 minutos (18 tentativas x 10s) pelos servicos
for i in $(seq 1 18); do
WEB_STATUS=$(docker service ls --filter "name=sistema_web" --format "{{.Replicas}}" 2>/dev/null || echo "0/0")
CONVEX_STATUS=$(docker service ls --filter "name=sistema_convex_backend" --format "{{.Replicas}}" 2>/dev/null || echo "0/0")
echo "Tentativa $i/18: web=$WEB_STATUS convex=$CONVEX_STATUS"
# Verifica se web tem 2/2 replicas e convex tem 1/1
if echo "$WEB_STATUS" | grep -q "2/2" && echo "$CONVEX_STATUS" | grep -q "1/1"; then
echo "Todos os servicos estao saudaveis!"
exit 0
fi
sleep 10
done
echo "AVISO: Timeout aguardando servicos. Status atual:"
docker service ls --filter "label=com.docker.stack.namespace=sistema"
# Nao falha o deploy, apenas avisa (o Swarm continua o rolling update em background)
- name: Smoke test — register + heartbeat
run: |
set -e
if [ "${RUN_MACHINE_SMOKE:-false}" != "true" ]; then
echo "RUN_MACHINE_SMOKE != true — pulando smoke test"; exit 0
fi
# Load MACHINE_PROVISIONING_SECRET from production .env on the host
if [ -f /srv/apps/sistema/.env ]; then
set -o allexport
. /srv/apps/sistema/.env
set +o allexport
fi
if [ -z "${MACHINE_PROVISIONING_SECRET:-}" ]; then
echo "MACHINE_PROVISIONING_SECRET ausente — pulando smoke test"; exit 0
fi
HOSTNAME_TEST="ci-smoke-$(date +%s)"
BODY='{"provisioningSecret":"'"$MACHINE_PROVISIONING_SECRET"'","tenantId":"tenant-atlas","hostname":"'"$HOSTNAME_TEST"'","os":{"name":"Linux","version":"6.1.0","architecture":"x86_64"},"macAddresses":["AA:BB:CC:DD:EE:FF"],"serialNumbers":[],"metadata":{"inventory":{"cpu":"i7","ramGb":16}},"registeredBy":"ci-smoke"}'
HTTP=$(curl -sS -o resp.json -w "%{http_code}" -H 'Content-Type: application/json' -d "$BODY" https://tickets.esdrasrenan.com.br/api/machines/register || true)
echo "Register HTTP=$HTTP"
if [ "$HTTP" != "201" ]; then
echo "Register failed:"; tail -c 600 resp.json || true; exit 1; fi
TOKEN=$(node -e 'try{const j=require("fs").readFileSync("resp.json","utf8");process.stdout.write(JSON.parse(j).machineToken||"");}catch(e){process.stdout.write("")}' )
if [ -z "$TOKEN" ]; then echo "Missing token in register response"; exit 1; fi
HB=$(curl -sS -o /dev/null -w "%{http_code}" -H 'Content-Type: application/json' -d '{"machineToken":"'"$TOKEN"'","status":"online","metrics":{"cpuPct":5,"memFreePct":70}}' https://tickets.esdrasrenan.com.br/api/machines/heartbeat || true)
echo "Heartbeat HTTP=$HB"
if [ "$HB" != "200" ]; then echo "Heartbeat failed"; exit 1; fi
- name: Cleanup old build workdirs (keep last 2)
run: |
set -e
ROOT="$HOME/apps"
KEEP=2
PATTERN='web.build.*'
ACTIVE="$HOME/apps/sistema"
echo "Scanning $ROOT for old $PATTERN dirs"
LIST=$(find "$ROOT" -maxdepth 1 -type d -name "$PATTERN" | sort -r || true)
echo "$LIST" | sed -n "1,${KEEP}p" | sed 's/^/Keeping: /' || true
echo "$LIST" | sed "1,${KEEP}d" | while read dir; do
[ -z "$dir" ] && continue
if [ -n "$ACTIVE" ] && [ "$(readlink -f "$dir")" = "$ACTIVE" ]; then
echo "Skipping active dir (in use by APP_DIR): $dir"; continue
fi
echo "Removing $dir"
chmod -R u+rwX "$dir" 2>/dev/null || true
rm -rf "$dir" || {
echo "Local rm failed, falling back to docker (root) cleanup for $dir..."
docker run --rm -v "$dir":/target alpine:3 sh -lc 'chown -R 1000:1000 /target 2>/dev/null || true; chmod -R u+rwX /target 2>/dev/null || true; rm -rf /target/* /target/.[!.]* /target/..?* 2>/dev/null || true' || true
rm -rf "$dir" 2>/dev/null || rmdir "$dir" 2>/dev/null || true
}
done
echo "Disk usage (top 10 under $ROOT):"
du -sh "$ROOT"/* 2>/dev/null | sort -rh | head -n 10 || true
- name: Restart web service with new code (skip — stack deploy already updated)
if: ${{ always() && false }}
run: |
docker service update --force sistema_web
# Comentado: o stack deploy já atualiza os serviços com update_config.order: start-first
# Forçar update aqui causa downtime porque ignora a estratégia de rolling update
# - name: Restart Convex backend service (optional)
# run: |
# docker service update --force sistema_convex_backend
convex_deploy:
name: Deploy Convex functions
needs: changes
timeout-minutes: 20
# Executa quando convex/** mudar ou via workflow_dispatch
if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.convex == 'true' }}
runs-on: [ self-hosted, linux, vps ]
env:
APP_DIR: /srv/apps/sistema
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Determine APP_DIR (fallback safe path)
id: appdir
run: |
TS=$(date +%s)
# Use a convex-specific build dir to avoid clashes with web job
FALLBACK_DIR="$HOME/apps/convex.build.$TS"
mkdir -p "$FALLBACK_DIR"
echo "Using APP_DIR (fallback)=$FALLBACK_DIR"
echo "EFFECTIVE_APP_DIR=$FALLBACK_DIR" >> "$GITHUB_ENV"
- name: Sync workspace to APP_DIR (preserving local env)
run: |
mkdir -p "$EFFECTIVE_APP_DIR"
RSYNC_FLAGS="-az --inplace --no-times --no-perms --no-owner --no-group --delete"
rsync $RSYNC_FLAGS \
--filter='protect .next.old*' \
--exclude '.next.old*' \
--exclude '.env*' \
--exclude 'apps/desktop/.env*' \
--exclude 'convex/.env*' \
--filter='protect node_modules' \
--filter='protect node_modules/**' \
--filter='protect .pnpm-store' \
--filter='protect .pnpm-store/**' \
--exclude '.git' \
--exclude '.next' \
--exclude 'node_modules' \
--exclude 'node_modules/**' \
--exclude '.pnpm-store' \
--exclude '.pnpm-store/**' \
./ "$EFFECTIVE_APP_DIR"/
- name: Acquire Convex admin key
id: key
run: |
echo "Waiting for Convex container..."
CID=""
# Aguarda ate 60s (12 tentativas x 5s) pelo container ficar pronto
# Nao forca restart - deixa o Swarm gerenciar via health checks
for attempt in $(seq 1 12); do
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
if [ -n "$CID" ]; then
echo "Convex container ready (CID=$CID)"
break
fi
echo "Attempt $attempt/12: container not ready yet; waiting 5s..."
sleep 5
done
CONVEX_IMAGE="ghcr.io/get-convex/convex-backend:latest"
if [ -n "$CID" ]; then
KEY=$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "No running convex container detected; attempting offline admin key extraction..."
VOLUME="sistema_convex_data"
if docker volume inspect "$VOLUME" >/dev/null 2>&1; then
KEY=$(docker run --rm --entrypoint /bin/sh -v "$VOLUME":/convex/data "$CONVEX_IMAGE" -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "Volume $VOLUME nao encontrado; nao foi possivel extrair a chave admin"
fi
fi
echo "ADMIN_KEY=$KEY" >> $GITHUB_OUTPUT
echo "Admin key acquired? $([ -n "$KEY" ] && echo yes || echo no)"
if [ -z "$KEY" ]; then
echo "ERRO: Nao foi possivel obter a chave admin do Convex"
docker service ps sistema_convex_backend || true
exit 1
fi
- name: Bring convex.json from live app if present
run: |
if [ -f "$APP_DIR/convex.json" ]; then
echo "Copying $APP_DIR/convex.json -> $EFFECTIVE_APP_DIR/convex.json"
cp -f "$APP_DIR/convex.json" "$EFFECTIVE_APP_DIR/convex.json"
else
echo "No existing convex.json found at $APP_DIR; convex CLI will need self-hosted vars"
fi
- name: Set Convex env vars (self-hosted)
env:
CONVEX_SELF_HOSTED_URL: https://convex.esdrasrenan.com.br
CONVEX_SELF_HOSTED_ADMIN_KEY: ${{ steps.key.outputs.ADMIN_KEY }}
MACHINE_PROVISIONING_SECRET: ${{ secrets.MACHINE_PROVISIONING_SECRET }}
MACHINE_TOKEN_TTL_MS: ${{ secrets.MACHINE_TOKEN_TTL_MS }}
FLEET_SYNC_SECRET: ${{ secrets.FLEET_SYNC_SECRET }}
run: |
set -e
docker run --rm -i \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
-e CONVEX_SELF_HOSTED_URL \
-e CONVEX_SELF_HOSTED_ADMIN_KEY \
-e MACHINE_PROVISIONING_SECRET \
-e MACHINE_TOKEN_TTL_MS \
-e FLEET_SYNC_SECRET \
-e CONVEX_TMPDIR=/app/.convex-tmp \
node:20-bullseye bash -lc "set -euo pipefail; curl -fsSL https://bun.sh/install | bash >/tmp/bun-install.log; export BUN_INSTALL=\"\${BUN_INSTALL:-/root/.bun}\"; export PATH=\"\$BUN_INSTALL/bin:\$PATH\"; export CONVEX_TMPDIR=/app/.convex-tmp; bun install --frozen-lockfile; \
if [ -n \"$MACHINE_PROVISIONING_SECRET\" ]; then bunx convex env set MACHINE_PROVISIONING_SECRET \"$MACHINE_PROVISIONING_SECRET\"; fi; \
if [ -n \"$MACHINE_TOKEN_TTL_MS\" ]; then bunx convex env set MACHINE_TOKEN_TTL_MS \"$MACHINE_TOKEN_TTL_MS\"; fi; \
if [ -n \"$FLEET_SYNC_SECRET\" ]; then bunx convex env set FLEET_SYNC_SECRET \"$FLEET_SYNC_SECRET\"; fi; \
bunx convex env list"
- name: Prepare Convex deploy workspace
run: |
cd "$EFFECTIVE_APP_DIR"
if [ -f .env ]; then
echo "Renaming .env -> .env.bak (Convex self-hosted deploy)"
mv -f .env .env.bak
fi
# Dedicated tmp dir outside convex/_generated so CLI cleanups don't remove it
mkdir -p .convex-tmp
- name: Deploy functions to Convex self-hosted
env:
CONVEX_SELF_HOSTED_URL: https://convex.esdrasrenan.com.br
CONVEX_SELF_HOSTED_ADMIN_KEY: ${{ steps.key.outputs.ADMIN_KEY }}
run: |
docker run --rm -i \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
-e CI=true \
-e CONVEX_SELF_HOSTED_URL \
-e CONVEX_SELF_HOSTED_ADMIN_KEY \
-e CONVEX_TMPDIR=/app/.convex-tmp \
node:20-bullseye bash -lc "set -euo pipefail; curl -fsSL https://bun.sh/install | bash >/tmp/bun-install.log; export BUN_INSTALL=\"\${BUN_INSTALL:-/root/.bun}\"; export PATH=\"\$BUN_INSTALL/bin:\$PATH\"; export CONVEX_TMPDIR=/app/.convex-tmp; bun install --frozen-lockfile; bunx convex deploy"
- name: Cleanup old convex build workdirs (keep last 2)
run: |
set -e
ROOT="$HOME/apps"
KEEP=2
PATTERN='convex.build.*'
LIST=$(find "$ROOT" -maxdepth 1 -type d -name "$PATTERN" | sort -r || true)
echo "$LIST" | sed -n "1,${KEEP}p" | sed 's/^/Keeping: /' || true
echo "$LIST" | sed "1,${KEEP}d" | while read dir; do
[ -z "$dir" ] && continue
echo "Removing $dir"
chmod -R u+rwX "$dir" 2>/dev/null || true
rm -rf "$dir" || {
echo "Local rm failed, falling back to docker (root) cleanup for $dir..."
docker run --rm -v "$dir":/target alpine:3 sh -lc 'chown -R 1000:1000 /target 2>/dev/null || true; chmod -R u+rwX /target 2>/dev/null || true; rm -rf /target/* /target/.[!.]* /target/..?* 2>/dev/null || true' || true
rm -rf "$dir" 2>/dev/null || rmdir "$dir" 2>/dev/null || true
}
done
desktop_release:
name: Desktop Release (Windows)
timeout-minutes: 30
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: [ self-hosted, windows, desktop ]
defaults:
run:
working-directory: apps/desktop
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.20.0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install deps (desktop)
run: pnpm install --frozen-lockfile
- name: Build with Tauri
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
projectPath: apps/desktop
- name: Upload latest.json + bundles to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
source: |
**/bundle/**/latest.json
**/bundle/**/*
target: ${{ env.VPS_UPDATES_DIR }}
overwrite: true
diagnose_convex:
name: Diagnose Convex (env + register test)
timeout-minutes: 10
if: ${{ github.event_name == 'workflow_dispatch' }}
runs-on: [ self-hosted, linux, vps ]
steps:
- name: Print service env and .env subset
run: |
echo "=== Convex service env ==="
docker service inspect sistema_convex_backend --format '{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' || true
echo
echo "=== /srv/apps/sistema/.env subset ==="
[ -f /srv/apps/sistema/.env ] && grep -E '^(MACHINE_PROVISIONING_SECRET|MACHINE_TOKEN_TTL_MS|FLEET_SYNC_SECRET|NEXT_PUBLIC_CONVEX_URL)=' -n /srv/apps/sistema/.env || echo '(no .env)'
- name: Acquire Convex admin key
id: key
run: |
echo "Waiting for Convex container..."
CID=""
# Aguarda ate 60s (12 tentativas x 5s) pelo container ficar pronto
for attempt in $(seq 1 12); do
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
if [ -n "$CID" ]; then
echo "Convex container ready (CID=$CID)"
break
fi
echo "Attempt $attempt/12: container not ready yet; waiting 5s..."
sleep 5
done
CONVEX_IMAGE="ghcr.io/get-convex/convex-backend:latest"
if [ -n "$CID" ]; then
KEY=$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "No running convex container detected; attempting offline admin key extraction..."
VOLUME="sistema_convex_data"
if docker volume inspect "$VOLUME" >/dev/null 2>&1; then
KEY=$(docker run --rm --entrypoint /bin/sh -v "$VOLUME":/convex/data "$CONVEX_IMAGE" -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
else
echo "Volume $VOLUME nao encontrado; nao foi possivel extrair a chave admin"
fi
fi
echo "ADMIN_KEY=$KEY" >> $GITHUB_OUTPUT
echo "Admin key acquired? $([ -n "$KEY" ] && echo yes || echo no)"
- name: List Convex env and set missing
env:
CONVEX_SELF_HOSTED_URL: https://convex.esdrasrenan.com.br
ADMIN_KEY: ${{ steps.key.outputs.ADMIN_KEY }}
run: |
set -e
if [ -f /srv/apps/sistema/.env ]; then
set -o allexport
. /srv/apps/sistema/.env
set +o allexport
fi
docker run --rm -i \
-v /srv/apps/sistema:/app -w /app \
-e CONVEX_SELF_HOSTED_URL -e CONVEX_SELF_HOSTED_ADMIN_KEY="$ADMIN_KEY" \
-e MACHINE_PROVISIONING_SECRET -e MACHINE_TOKEN_TTL_MS -e FLEET_SYNC_SECRET \
node:20-bullseye bash -lc "set -euo pipefail; curl -fsSL https://bun.sh/install | bash >/tmp/bun-install.log; export BUN_INSTALL=\"\${BUN_INSTALL:-/root/.bun}\"; export PATH=\"\$BUN_INSTALL/bin:\$PATH\"; bun install --frozen-lockfile; \
unset CONVEX_DEPLOYMENT; bunx convex env list; \
if [ -n \"$MACHINE_PROVISIONING_SECRET\" ]; then bunx convex env set MACHINE_PROVISIONING_SECRET \"$MACHINE_PROVISIONING_SECRET\"; fi; \
if [ -n \"$MACHINE_TOKEN_TTL_MS\" ]; then bunx convex env set MACHINE_TOKEN_TTL_MS \"$MACHINE_TOKEN_TTL_MS\"; fi; \
if [ -n \"$FLEET_SYNC_SECRET\" ]; then bunx convex env set FLEET_SYNC_SECRET \"$FLEET_SYNC_SECRET\"; fi; \
bunx convex env list"
- name: Test register from runner
run: |
HOST="vm-teste-$(date +%s)"
DATA='{"provisioningSecret":"'"${MACHINE_PROVISIONING_SECRET:-"71daa9ef54cb224547e378f8121ca898b614446c142a132f73c2221b4d53d7d6"}"'","tenantId":"tenant-atlas","hostname":"'"$HOST"'","os":{"name":"Linux","version":"6.1.0","architecture":"x86_64"},"macAddresses":["AA:BB:CC:DD:EE:FF"],"serialNumbers":[],"metadata":{"inventario":{"cpu":"i7","ramGb":16}},"registeredBy":"diag-test"}'
HTTP=$(curl -sS -o resp.json -w "%{http_code}" -H 'Content-Type: application/json' -d "$DATA" https://tickets.esdrasrenan.com.br/api/machines/register || true)
echo "Register HTTP=$HTTP" && tail -c 400 resp.json || true

View file

@ -36,7 +36,7 @@ jobs:
node-version: 20
- name: Enable Corepack
run: corepack enable && corepack prepare pnpm@9 --activate
run: corepack enable && corepack prepare pnpm@10.20.0 --activate
- name: Install Rust (stable)
uses: dtolnay/rust-toolchain@stable
@ -50,16 +50,10 @@ jobs:
- name: Install pnpm deps
run: pnpm -C apps/desktop install --frozen-lockfile
- name: Inject Tauri public key
if: ${{ secrets.TAURI_PUBLIC_KEY != '' }}
env:
TAURI_PUBLIC_KEY: ${{ secrets.TAURI_PUBLIC_KEY }}
run: |
set -euo pipefail
sed -i "s/REPLACE_WITH_TAURI_PUBLIC_KEY/${TAURI_PUBLIC_KEY//\//\\\/}/" apps/desktop/src-tauri/tauri.conf.json
- name: Build desktop
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_APP_URL: https://tickets.esdrasrenan.com.br

View file

@ -0,0 +1,62 @@
name: Quality Checks
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
lint-test-build:
name: Lint, Test and Build
runs-on: ubuntu-latest
env:
BETTER_AUTH_SECRET: test-secret
NEXT_PUBLIC_APP_URL: http://localhost:3000
BETTER_AUTH_URL: http://localhost:3000
NEXT_PUBLIC_CONVEX_URL: http://localhost:3210
DATABASE_URL: file:./prisma/db.dev.sqlite
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: 1.3.1
- name: Verify Bun
run: bun --version
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Cache Next.js build cache
uses: actions/cache@v4
with:
path: |
${{ github.workspace }}/.next/cache
key: ${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml', 'bun.lock') }}-${{ hashFiles('**/*.{js,jsx,ts,tsx}') }}
restore-keys: |
${{ runner.os }}-nextjs-${{ hashFiles('pnpm-lock.yaml', 'bun.lock') }}-
- name: Generate Prisma client
env:
PRISMA_ENGINES_CHECKSUM_IGNORE_MISSING: "1"
run: bun run prisma:generate
- name: Lint
run: bun run lint
- name: Test
run: bun test
- name: Build
run: bun run build:bun

View file

@ -1,430 +0,0 @@
name: CI/CD Web + Desktop
on:
push:
branches: [ main ]
tags:
- 'v*.*.*'
workflow_dispatch:
inputs:
force_web_deploy:
description: 'Forçar deploy do Web (ignorar filtro)?'
required: false
default: 'false'
force_convex_deploy:
description: 'Forçar deploy do Convex (ignorar filtro)?'
required: false
default: 'false'
env:
APP_DIR: /srv/apps/sistema
VPS_UPDATES_DIR: /var/www/updates
jobs:
changes:
name: Detect changes
runs-on: ubuntu-latest
outputs:
convex: ${{ steps.filter.outputs.convex }}
web: ${{ steps.filter.outputs.web }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Paths filter
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
convex:
- 'convex/**'
web:
- 'src/**'
- 'public/**'
- 'prisma/**'
- 'next.config.ts'
- 'package.json'
- 'pnpm-lock.yaml'
- 'tsconfig.json'
- 'middleware.ts'
- 'stack.yml'
deploy:
name: Deploy (VPS Linux)
needs: changes
# Executa em qualquer push na main (independente do filtro) ou quando disparado manualmente
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' }}
runs-on: [ self-hosted, linux, vps ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Determine APP_DIR (fallback safe path)
id: appdir
run: |
TS=$(date +%s)
FALLBACK_DIR="$HOME/apps/sistema.build.$TS"
mkdir -p "$FALLBACK_DIR"
echo "Using APP_DIR (fallback)=$FALLBACK_DIR"
echo "EFFECTIVE_APP_DIR=$FALLBACK_DIR" >> "$GITHUB_ENV"
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Permissions diagnostic (server paths)
run: |
set +e
echo "== Basic context =="
whoami || true
id || true
groups || true
umask || true
echo "HOME=$HOME"
echo "APP_DIR(default)=${APP_DIR:-/srv/apps/sistema}"
echo "EFFECTIVE_APP_DIR=$EFFECTIVE_APP_DIR"
echo "\n== Permissions check =="
check_path() {
P="$1"
echo "-- $P"
if [ -e "$P" ]; then
stat -c '%A %U:%G %n' "$P" 2>/dev/null || ls -ld "$P" || true
echo -n "WRITABLE? "; [ -w "$P" ] && echo yes || echo no
if command -v namei >/dev/null 2>&1; then
namei -l "$P" || true
fi
TMP="$P/.permtest.$$"
(echo test > "$TMP" 2>/dev/null && echo "CREATE_FILE: ok" && rm -f "$TMP") || echo "CREATE_FILE: failed"
else
echo "(missing)"
fi
}
check_path "/srv/apps/sistema"
check_path "/srv/apps/sistema/src/app/machines/handshake"
check_path "/srv/apps/sistema/apps/desktop/node_modules"
check_path "/srv/apps/sistema/node_modules"
check_path "$EFFECTIVE_APP_DIR"
check_path "$EFFECTIVE_APP_DIR/node_modules"
- name: Sync workspace to APP_DIR (preserving local env)
run: |
mkdir -p "$EFFECTIVE_APP_DIR"
RSYNC_FLAGS="-az --inplace --no-times --no-perms --no-owner --no-group --delete"
# Excluir .env apenas quando copiando para o diretório padrão (/srv) para preservar segredos locais
EXCLUDE_ENV="--exclude '.env*' --exclude 'apps/desktop/.env*' --exclude 'convex/.env*'"
if [ "$EFFECTIVE_APP_DIR" != "${APP_DIR:-/srv/apps/sistema}" ]; then
EXCLUDE_ENV=""
fi
rsync $RSYNC_FLAGS \
--filter='protect .next.old*' \
--exclude '.next.old*' \
--filter='protect node_modules' \
--filter='protect node_modules/**' \
--filter='protect .pnpm-store' \
--filter='protect .pnpm-store/**' \
--filter='protect .env' \
--filter='protect .env*' \
--filter='protect apps/desktop/.env*' \
--filter='protect convex/.env*' \
--exclude '.git' \
--exclude '.next' \
--exclude 'node_modules' \
--exclude 'node_modules/**' \
--exclude '.pnpm-store' \
--exclude '.pnpm-store/**' \
$EXCLUDE_ENV \
./ "$EFFECTIVE_APP_DIR"/
- name: Copy production .env if present
run: |
DEFAULT_DIR="${APP_DIR:-/srv/apps/sistema}"
if [ "$EFFECTIVE_APP_DIR" != "$DEFAULT_DIR" ] && [ -f "$DEFAULT_DIR/.env" ]; then
echo "Copying production .env from $DEFAULT_DIR to $EFFECTIVE_APP_DIR"
cp -f "$DEFAULT_DIR/.env" "$EFFECTIVE_APP_DIR/.env"
fi
- name: Prune workspace for server-only build
run: |
cd "$EFFECTIVE_APP_DIR"
# Keep only root (web) as a package in this effective workspace
printf "packages:\n - .\n\nignoredBuiltDependencies:\n - '@prisma/client'\n - '@prisma/engines'\n - '@tailwindcss/oxide'\n - esbuild\n - prisma\n - sharp\n - unrs-resolver\n" > pnpm-workspace.yaml
# Remove desktop app to avoid pnpm touching its node_modules on this runner
rm -rf apps/desktop || true
- name: Clean Next.js cache (.next) to avoid EACCES
run: |
cd "$EFFECTIVE_APP_DIR"
if [ -e .next ]; then
echo "Removing existing .next (may be root-owned from previous container)"
rm -rf .next || (mv .next ".next.old.$(date +%s)" || true)
fi
mkdir -p .next
chmod -R u+rwX .next || true
- name: Install and build (Next.js)
run: |
cd "$EFFECTIVE_APP_DIR"
corepack enable || true
pnpm --filter web install --no-frozen-lockfile
pnpm prisma:generate
pnpm build
- name: Swarm deploy (stack.yml)
run: |
cd "$EFFECTIVE_APP_DIR"
# Exporta variáveis do .env para substituição no stack (ex.: MACHINE_PROVISIONING_SECRET)
set -o allexport
if [ -f .env ]; then . ./.env; fi
set +o allexport
APP_DIR="$EFFECTIVE_APP_DIR" RELEASE_SHA=${{ github.sha }} docker stack deploy --with-registry-auth -c stack.yml sistema
- name: Ensure Convex service envs and restart
run: |
cd "$EFFECTIVE_APP_DIR"
set -o allexport
if [ -f .env ]; then . ./.env; fi
set +o allexport
echo "Ensuring Convex envs on service: sistema_convex_backend"
if [ -n "${MACHINE_PROVISIONING_SECRET:-}" ]; then
docker service update --env-add MACHINE_PROVISIONING_SECRET="${MACHINE_PROVISIONING_SECRET}" sistema_convex_backend || true
fi
if [ -n "${MACHINE_TOKEN_TTL_MS:-}" ]; then
docker service update --env-add MACHINE_TOKEN_TTL_MS="${MACHINE_TOKEN_TTL_MS}" sistema_convex_backend || true
fi
if [ -n "${FLEET_SYNC_SECRET:-}" ]; then
docker service update --env-add FLEET_SYNC_SECRET="${FLEET_SYNC_SECRET}" sistema_convex_backend || true
fi
echo "Current envs:"
docker service inspect sistema_convex_backend --format '{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' || true
echo "Forcing service restart..."
docker service update --force sistema_convex_backend || true
- name: Smoke test — register + heartbeat
run: |
set -e
# Load MACHINE_PROVISIONING_SECRET from production .env on the host
if [ -f /srv/apps/sistema/.env ]; then
set -o allexport
. /srv/apps/sistema/.env
set +o allexport
fi
if [ -z "${MACHINE_PROVISIONING_SECRET:-}" ]; then
echo "MACHINE_PROVISIONING_SECRET ausente — pulando smoke test"; exit 0
fi
HOSTNAME_TEST="ci-smoke-$(date +%s)"
BODY='{"provisioningSecret":"'"$MACHINE_PROVISIONING_SECRET"'","tenantId":"tenant-atlas","hostname":"'"$HOSTNAME_TEST"'","os":{"name":"Linux","version":"6.1.0","architecture":"x86_64"},"macAddresses":["AA:BB:CC:DD:EE:FF"],"serialNumbers":[],"metadata":{"inventory":{"cpu":"i7","ramGb":16}},"registeredBy":"ci-smoke"}'
HTTP=$(curl -sS -o resp.json -w "%{http_code}" -H 'Content-Type: application/json' -d "$BODY" https://tickets.esdrasrenan.com.br/api/machines/register || true)
echo "Register HTTP=$HTTP"
if [ "$HTTP" != "201" ]; then
echo "Register failed:"; tail -c 600 resp.json || true; exit 1; fi
TOKEN=$(node -e 'try{const j=require("fs").readFileSync("resp.json","utf8");process.stdout.write(JSON.parse(j).machineToken||"");}catch(e){process.stdout.write("")}' )
if [ -z "$TOKEN" ]; then echo "Missing token in register response"; exit 1; fi
HB=$(curl -sS -o /dev/null -w "%{http_code}" -H 'Content-Type: application/json' -d '{"machineToken":"'"$TOKEN"'","status":"online","metrics":{"cpuPct":5,"memFreePct":70}}' https://tickets.esdrasrenan.com.br/api/machines/heartbeat || true)
echo "Heartbeat HTTP=$HB"
if [ "$HB" != "200" ]; then echo "Heartbeat failed"; exit 1; fi
- name: Cleanup old build workdirs (keep last 3)
run: |
ls -1dt $HOME/apps/sistema.build.* 2>/dev/null | tail -n +4 | xargs -r rm -rf || true
- name: Restart web service with new code
run: |
docker service update --force sistema_web || true
- name: Restart Convex backend service (optional)
run: |
docker service update --force sistema_convex_backend || true
convex_deploy:
name: Deploy Convex functions
needs: changes
# Executa em workflow_dispatch, push na main, ou quando convex/** mudar
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' || needs.changes.outputs.convex == 'true' }}
runs-on: [ self-hosted, linux, vps ]
env:
APP_DIR: /srv/apps/sistema
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Determine APP_DIR (fallback safe path)
id: appdir
run: |
TS=$(date +%s)
FALLBACK_DIR="$HOME/apps/sistema.build.$TS"
mkdir -p "$FALLBACK_DIR"
echo "Using APP_DIR (fallback)=$FALLBACK_DIR"
echo "EFFECTIVE_APP_DIR=$FALLBACK_DIR" >> "$GITHUB_ENV"
- name: Sync workspace to APP_DIR (preserving local env)
run: |
mkdir -p "$EFFECTIVE_APP_DIR"
RSYNC_FLAGS="-az --inplace --no-times --no-perms --no-owner --no-group --delete"
rsync $RSYNC_FLAGS \
--filter='protect .next.old*' \
--exclude '.next.old*' \
--exclude '.env*' \
--exclude 'apps/desktop/.env*' \
--exclude 'convex/.env*' \
--filter='protect node_modules' \
--filter='protect node_modules/**' \
--filter='protect .pnpm-store' \
--filter='protect .pnpm-store/**' \
--exclude '.git' \
--exclude '.next' \
--exclude 'node_modules' \
--exclude 'node_modules/**' \
--exclude '.pnpm-store' \
--exclude '.pnpm-store/**' \
./ "$EFFECTIVE_APP_DIR"/
- name: Set Convex env vars (self-hosted)
env:
CONVEX_SELF_HOSTED_URL: ${{ secrets.CONVEX_SELF_HOSTED_URL }}
CONVEX_SELF_HOSTED_ADMIN_KEY: ${{ secrets.CONVEX_SELF_HOSTED_ADMIN_KEY }}
MACHINE_PROVISIONING_SECRET: ${{ secrets.MACHINE_PROVISIONING_SECRET }}
MACHINE_TOKEN_TTL_MS: ${{ secrets.MACHINE_TOKEN_TTL_MS }}
FLEET_SYNC_SECRET: ${{ secrets.FLEET_SYNC_SECRET }}
run: |
set -e
docker run --rm -i \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
-e CONVEX_SELF_HOSTED_URL \
-e CONVEX_SELF_HOSTED_ADMIN_KEY \
-e MACHINE_PROVISIONING_SECRET \
-e MACHINE_TOKEN_TTL_MS \
-e FLEET_SYNC_SECRET \
node:20-bullseye bash -lc "set -euo pipefail; unset CONVEX_DEPLOYMENT; corepack enable; corepack prepare pnpm@9 --activate; pnpm install --frozen-lockfile --prod=false; \
if [ -n \"$MACHINE_PROVISIONING_SECRET\" ]; then pnpm exec convex env set MACHINE_PROVISIONING_SECRET \"$MACHINE_PROVISIONING_SECRET\" -y; fi; \
if [ -n \"$MACHINE_TOKEN_TTL_MS\" ]; then pnpm exec convex env set MACHINE_TOKEN_TTL_MS \"$MACHINE_TOKEN_TTL_MS\" -y; fi; \
if [ -n \"$FLEET_SYNC_SECRET\" ]; then pnpm exec convex env set FLEET_SYNC_SECRET \"$FLEET_SYNC_SECRET\" -y; fi; \
pnpm exec convex env list"
- name: Ensure .env is not present for Convex deploy
run: |
cd "$EFFECTIVE_APP_DIR"
if [ -f .env ]; then
echo "Renaming .env -> .env.bak (Convex self-hosted deploy)"
mv -f .env .env.bak
fi
- name: Deploy functions to Convex self-hosted
env:
CONVEX_SELF_HOSTED_URL: ${{ secrets.CONVEX_SELF_HOSTED_URL }}
CONVEX_SELF_HOSTED_ADMIN_KEY: ${{ secrets.CONVEX_SELF_HOSTED_ADMIN_KEY }}
run: |
docker run --rm -i \
-v "$EFFECTIVE_APP_DIR":/app \
-w /app \
-e CI=true \
-e CONVEX_SELF_HOSTED_URL \
-e CONVEX_SELF_HOSTED_ADMIN_KEY \
node:20-bullseye bash -lc "set -euo pipefail; unset CONVEX_DEPLOYMENT; corepack enable; corepack prepare pnpm@9 --activate; pnpm install --frozen-lockfile --prod=false; pnpm exec convex deploy"
desktop_release:
name: Desktop Release (Windows)
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
runs-on: [ self-hosted, windows, desktop ]
defaults:
run:
working-directory: apps/desktop
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
- name: Install deps (desktop)
run: pnpm install --frozen-lockfile
- name: Build with Tauri
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
projectPath: apps/desktop
- name: Inject Tauri public key (post)
if: ${{ secrets.TAURI_PUBLIC_KEY != '' }}
env:
TAURI_PUBLIC_KEY: ${{ secrets.TAURI_PUBLIC_KEY }}
run: |
sed -i "s/REPLACE_WITH_TAURI_PUBLIC_KEY/${TAURI_PUBLIC_KEY//\//\\\/}/" apps/desktop/src-tauri/tauri.conf.json || true
- name: Upload latest.json + bundles to VPS
uses: appleboy/scp-action@v0.1.7
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
source: |
**/bundle/**/latest.json
**/bundle/**/*
target: ${{ env.VPS_UPDATES_DIR }}
overwrite: true
diagnose_convex:
name: Diagnose Convex (env + register test)
if: ${{ github.event_name == 'workflow_dispatch' }}
runs-on: [ self-hosted, linux, vps ]
steps:
- name: Print service env and .env subset
run: |
echo "=== Convex service env ==="
docker service inspect sistema_convex_backend --format '{{range .Spec.TaskTemplate.ContainerSpec.Env}}{{println .}}{{end}}' || true
echo
echo "=== /srv/apps/sistema/.env subset ==="
[ -f /srv/apps/sistema/.env ] && grep -E '^(MACHINE_PROVISIONING_SECRET|MACHINE_TOKEN_TTL_MS|FLEET_SYNC_SECRET|NEXT_PUBLIC_CONVEX_URL)=' -n /srv/apps/sistema/.env || echo '(no .env)'
- name: Acquire Convex admin key
id: key
run: |
CID=$(docker ps --format '{{.ID}} {{.Names}}' | awk '/sistema_convex_backend/{print $1; exit}')
if [ -z "$CID" ]; then echo "No convex container"; exit 1; fi
KEY=$(docker exec -i "$CID" /bin/sh -lc './generate_admin_key.sh' | tr -d '\r' | grep -o 'convex-self-hosted|[^ ]*' | tail -n1)
echo "ADMIN_KEY=$KEY" >> $GITHUB_OUTPUT
echo "Admin key acquired? $([ -n "$KEY" ] && echo yes || echo no)"
- name: List Convex env and set missing
env:
CONVEX_SELF_HOSTED_URL: https://convex.esdrasrenan.com.br
ADMIN_KEY: ${{ steps.key.outputs.ADMIN_KEY }}
run: |
set -e
if [ -f /srv/apps/sistema/.env ]; then
set -o allexport
. /srv/apps/sistema/.env
set +o allexport
fi
docker run --rm -i \
-v /srv/apps/sistema:/app -w /app \
-e CONVEX_SELF_HOSTED_URL -e CONVEX_SELF_HOSTED_ADMIN_KEY="$ADMIN_KEY" \
-e MACHINE_PROVISIONING_SECRET -e MACHINE_TOKEN_TTL_MS -e FLEET_SYNC_SECRET \
node:20-bullseye bash -lc "set -euo pipefail; corepack enable; corepack prepare pnpm@9 --activate; pnpm i --frozen-lockfile --prod=false; \
unset CONVEX_DEPLOYMENT; pnpm exec convex env list; \
if [ -n \"$MACHINE_PROVISIONING_SECRET\" ]; then pnpm exec convex env set MACHINE_PROVISIONING_SECRET \"$MACHINE_PROVISIONING_SECRET\" -y; fi; \
if [ -n \"$MACHINE_TOKEN_TTL_MS\" ]; then pnpm exec convex env set MACHINE_TOKEN_TTL_MS \"$MACHINE_TOKEN_TTL_MS\" -y; fi; \
if [ -n \"$FLEET_SYNC_SECRET\" ]; then pnpm exec convex env set FLEET_SYNC_SECRET \"$FLEET_SYNC_SECRET\" -y; fi; \
pnpm exec convex env list"
- name: Test register from runner
run: |
HOST="vm-teste-$(date +%s)"
DATA='{"provisioningSecret":"'"${MACHINE_PROVISIONING_SECRET:-"71daa9ef54cb224547e378f8121ca898b614446c142a132f73c2221b4d53d7d6"}"'","tenantId":"tenant-atlas","hostname":"'"$HOST"'","os":{"name":"Linux","version":"6.1.0","architecture":"x86_64"},"macAddresses":["AA:BB:CC:DD:EE:FF"],"serialNumbers":[],"metadata":{"inventario":{"cpu":"i7","ramGb":16}},"registeredBy":"diag-test"}'
HTTP=$(curl -sS -o resp.json -w "%{http_code}" -H 'Content-Type: application/json' -d "$DATA" https://tickets.esdrasrenan.com.br/api/machines/register || true)
echo "Register HTTP=$HTTP" && tail -c 400 resp.json || true

82
.gitignore vendored
View file

@ -1,34 +1,40 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# React Email
/.react-email/
/emails/out/
# production
/build
# misc
.DS_Store
*.pem
*.sqlite
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# external experiments
nova-calendar-main/
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
@ -36,6 +42,10 @@ yarn-error.log*
!.env.example
!apps/desktop/.env.example
# Accidental Windows duplicate downloads (e.g., "env (1)")
env (*)
env (1)
# vercel
.vercel
@ -49,3 +59,15 @@ next-env.d.ts
# arquivos locais temporários
Captura de tela *.png
Screenshot*.png
# Ignore NTFS ADS streams accidentally committed from Windows downloads
*:*Zone.Identifier
*:\:Zone.Identifier
# Infrastructure secrets
.ci.env
# ferramentas externas
rustdesk/
# Prisma generated files
src/generated/
apps/desktop/service/target/

29
Dockerfile.prod Normal file
View file

@ -0,0 +1,29 @@
# Runtime image with Node 22 + Bun 1.3.4 and build toolchain preinstalled
FROM node:22-bullseye-slim
ENV BUN_INSTALL=/root/.bun
ENV PATH="$BUN_INSTALL/bin:$PATH"
RUN apt-get update -y \
&& apt-get install -y --no-install-recommends \
ca-certificates \
curl \
gnupg \
unzip \
build-essential \
python3 \
make \
pkg-config \
git \
&& rm -rf /var/lib/apt/lists/*
# Install Bun 1.3.4
RUN curl -fsSL https://bun.sh/install \
| bash -s -- bun-v1.3.4 \
&& ln -sf /root/.bun/bin/bun /usr/local/bin/bun \
&& ln -sf /root/.bun/bin/bun /usr/local/bin/bunx
WORKDIR /app
# We'll mount the app code at runtime; image just provides runtimes/toolchains.
CMD ["bash"]

101
README.md
View file

@ -1,51 +1,65 @@
## Sistema de Chamados
Aplicação Next.js 15 com Convex e Better Auth para gestão de tickets da Rever. Todo o código-fonte está organizado diretamente na raiz do repositório, conforme convenções do Next.js.
Aplicacao **Next.js 16 (App Router)** com **React 19**, **Convex** e **Better Auth** para gestao de tickets da Rever. A stack ainda inclui **Prisma 7** (PostgreSQL), **Tailwind** e **Turbopack** como bundler padrao (webpack permanece disponivel como fallback). Todo o codigo-fonte fica na raiz do monorepo seguindo as convencoes do App Router.
## Requisitos
- Node.js >= 20
- pnpm >= 8
- CLI do Convex (`pnpm dlx convex dev` instalará automaticamente no primeiro uso)
- Bun >= 1.3 (recomendado 1.3.1). Após instalar via script oficial, adicione `export PATH="$HOME/.bun/bin:$PATH"` ao seu shell (ex.: `.bashrc`) para ter `bun` disponível globalmente.
- Node.js >= 20 (necessário para ferramentas auxiliares como Prisma CLI e Next.js em modo fallback).
- CLI do Convex (`bunx convex dev` instalará automaticamente no primeiro uso, se ainda não estiver presente).
- GitHub Actions/autodeploy dependem dessas versões e do CLI do Convex disponível; use `npx convex --help` para confirmar.
## Configuração rápida
1. Instale as dependências:
```bash
pnpm install
bun install
```
2. Ajuste o arquivo `.env` (ou crie a partir de `.env.example`) e confirme os valores de:
- `NEXT_PUBLIC_CONVEX_URL` (gerado pelo Convex Dev)
- `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, `DATABASE_URL`
- `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`, `DATABASE_URL` (PostgreSQL, ex: `postgresql://postgres:dev@localhost:5432/sistema_chamados`)
3. Aplique as migrações e gere o client Prisma:
```bash
pnpm prisma migrate deploy
pnpm prisma:generate
bunx prisma migrate deploy
bun run prisma:generate
```
4. Popule usuários padrão do Better Auth:
```bash
pnpm auth:seed
bun run auth:seed
```
5. (Opcional) Para re-sincronizar manualmente as filas padrão, execute:
> Sempre que trocar de máquina ou quiser “zerar” o ambiente local, basta repetir os passos 3 e 4 com a mesma `DATABASE_URL`.
### Resetar rapidamente o ambiente local
1. Suba um PostgreSQL local (Docker recomendado):
```bash
pnpm queues:ensure
docker run -d --name postgres-dev -p 5432:5432 -e POSTGRES_PASSWORD=dev -e POSTGRES_DB=sistema_chamados postgres:18
```
6. Em um terminal, execute o backend em tempo real do Convex:
2. Aplique as migracoes:
```bash
pnpm convex:dev
bunx prisma migrate deploy
```
7. Em outro terminal, suba o frontend Next.js:
3. Recrie/garanta as contas padrao de login:
```bash
pnpm dev
bun run auth:seed
```
8. Com o Convex ativo, acesse `http://localhost:3000/dev/seed` uma vez para popular dados de demonstração (tickets, usuários, comentários) diretamente no banco do Convex.
4. Suba o servidor normalmente com `bun run dev`.
### Subir serviços locais
- (Opcional) Para re-sincronizar manualmente as filas padrão, execute `bun run queues:ensure`.
- Em um terminal, rode o backend em tempo real do Convex com `bun run convex:dev:bun` (ou `bun run convex:dev` para o runtime Node).
- Em outro terminal, suba o frontend Next.js (Turbopack) com `bun run dev:bun` (`bun run dev:webpack` serve como fallback).
- Com o Convex rodando, acesse `http://localhost:3000/dev/seed` uma vez para popular dados de demonstração (tickets, usuários, comentários).
> Se o CLI perguntar sobre configuração do projeto Convex, escolha criar um novo deployment local (opção padrão) e confirme. As credenciais são armazenadas em `.convex/` automaticamente.
### Deploy em produção (Traefik + Convex selfhosted)
- Guia completo: `docs/OPERACAO-PRODUCAO.md:1`.
- Histórico de setup/decisões: `docs/SETUP-HISTORICO.md:1`.
- Stack Swarm: `stack.yml:1` (roteado por Traefik, rede `traefik_public`).
### Documentação
- Índice de docs: `docs/README.md`
- Operações (produção): `docs/OPERATIONS.md` (versão EN) e `docs/OPERACAO-PRODUCAO.md` (PT-BR)
- Guia de DEV: `docs/DEV.md`
- Testes automatizados (Vitest/Playwright): `docs/testes-vitest.md`
- Stack Swarm: `stack.yml` (roteado por Traefik, rede `traefik_public`).
### Variáveis de ambiente
@ -55,32 +69,63 @@ Aplicação Next.js 15 com Convex e Better Auth para gestão de tickets da Rever
### Guia de DEV (Prisma, Auth e Desktop/Tauri)
Para fluxos detalhados de desenvolvimento — banco de dados local (SQLite/Prisma), seed do Better Auth, ajustes do Prisma CLI no DEV e build do Desktop (Tauri) — consulte `docs/DEV.md`.
Para fluxos detalhados de desenvolvimento — banco de dados local (PostgreSQL/Prisma), seed do Better Auth, ajustes do Prisma CLI no DEV e build do Desktop (Tauri) — consulte `docs/DEV.md`.
## Scripts úteis
- `pnpm lint` — ESLint com as regras do projeto.
- `pnpm exec vitest run` — suíte de testes unitários.
- `pnpm auth:seed` — atualiza/cria contas padrão do Better Auth (credenciais em `agents.md`).
- `pnpm prisma migrate deploy` — aplica migrações ao banco SQLite local.
- `pnpm convex:dev` — roda o Convex em modo desenvolvimento, gerando tipos em `convex/_generated`.
- `bun run dev:bun` — padrão atual para o Next.js com runtime Bun (`bun run dev:webpack` permanece como fallback).
- `bun run convex:dev:bun` — runtime Bun para o Convex (`bun run convex:dev` mantém o fluxo antigo usando Node).
- `bun run build:bun` / `bun run start:bun` — build e serve com Bun usando Turbopack (padrão atual).
- `bun run dev:webpack` — fallback do Next.js em modo desenvolvimento (webpack).
- `bun run lint` — ESLint com as regras do projeto.
- `bun test` — suíte de testes unitários usando o runner do Bun (o teste de screenshot fica automaticamente ignorado se o matcher não existir).
- `bun run build` — executa `next build --turbopack` (runtime Node, caso prefira evitar o `--bun`).
- `bun run build:webpack` — executa `next build --webpack` como fallback oficial.
- `bun run auth:seed` — atualiza/cria contas padrao do Better Auth (credenciais em `agents.md`).
- `bunx prisma migrate deploy` — aplica migracoes ao banco PostgreSQL.
- `bun run convex:dev` — roda o Convex em modo desenvolvimento com Node, gerando tipos em `convex/_generated`.
## Transferir dispositivo entre colaboradores
Quando uma dispositivo trocar de responsável:
1. Abra `Admin > Dispositivos`, selecione o equipamento e clique em **Resetar agente**.
2. No equipamento, execute o reset local do agente (`rever-agent reset` ou reinstale o serviço) e reprovisione com o código da empresa.
3. Após o agente gerar um novo token, associe a dispositivo ao novo colaborador no painel.
Sem o reset de agente, o Convex reaproveita o token anterior e o inventário continua vinculado ao usuário antigo.
## Estrutura principal
- `app/` dentro de `src/` — rotas e layouts do Next.js (App Router).
- `components/` — componentes reutilizáveis (UI, formulários, layouts).
- `convex/` — queries, mutations e seeds do Convex.
- `prisma/` — schema, migrações e banco SQLite (`prisma/db.sqlite`).
- `prisma/` — schema e migracoes do Prisma (PostgreSQL).
- `scripts/` — utilitários em Node para sincronização e seeds adicionais.
- `agents.md` — guia operacional e contexto funcional (em PT-BR).
- `PROXIMOS_PASSOS.md` — backlog de melhorias futuras.
## Credenciais de demonstração
Após executar `pnpm auth:seed`, as credenciais padrão ficam disponíveis conforme descrito em `agents.md` (seção “Credenciais padrão”). Ajuste variáveis `SEED_USER_*` se precisar sobrepor usuários ou senhas durante o seed.
Após executar `bun run auth:seed`, as credenciais padrão ficam disponíveis conforme descrito em `agents.md` (seção “Credenciais padrão”). Ajuste variáveis `SEED_USER_*` se precisar sobrepor usuários ou senhas durante o seed.
## Próximos passos
Consulte `PROXIMOS_PASSOS.md` para acompanhar o backlog funcional e o progresso das iniciativas planejadas.
### Executar com Bun
- `bun install` é o fluxo padrão (o arquivo `bun.lock` deve ser versionado; use `bun install --frozen-lockfile` em CI).
- `bun run dev:bun`, `bun run convex:dev:bun`, `bun run build:bun` e `bun run start:bun` já estão configurados; internamente executam `bun run --bun <script>` para usar o runtime do Bun sem abrir mão dos scripts existentes. O `cross-env` garante os valores esperados de `NODE_ENV` (`development`/`production`).
- O bundler padrão é o Turbopack; se precisar comparar/debugar com webpack, use `bun run build:webpack`.
- `bun test` utiliza o test runner do Bun. O teste de snapshot de screenshot é automaticamente ignorado quando o matcher não está disponível; testes de navegador completos continuam via `bun run test:browser` (Vitest + Playwright).
<!-- ci: smoke test 3 -->
## Diagnóstico de sessão da dispositivo (Desktop)
- Quando o portal for aberto via app desktop, use a página `https://seu-app/portal/debug` para validar cookies e contexto:
- `/api/auth/get-session` deve idealmente mostrar `user.role = "machine"` (em alguns ambientes WebView pode retornar `null`, o que não é bloqueante).
- `/api/machines/session` deve retornar `200` com `assignedUserId/assignedUserEmail`.
- O frontend agora preenche `machineContext` mesmo que `get-session` retorne `null`, e deriva o papel efetivo a partir desse contexto.
- Se `machines/session` retornar `401/403`, revise CORS/credenciais e o fluxo de handshake documentados em `docs/OPERACAO-PRODUCAO.md`.

5965
RENAN.txt

File diff suppressed because it is too large Load diff

371
agents.md
View file

@ -1,199 +1,214 @@
# Plano de Desenvolvimento — Sistema de Chamados
# Plano de Desenvolvimento — Sistema de Chamados
> **Diretriz máxima**: documentação, comunicação e respostas sempre em português brasileiro.
## Contatos
- **Esdras Renan** — monkeyesdras@gmail.com
> **Diretriz máxima:** todas as respostas, comunicações e documentações devem ser redigidas em português brasileiro.
## Contato principal
- **Esdras Renan** — monkeyesdras@gmail.com
## Credenciais padrão (Better Auth)
- Administrador: `admin@sistema.dev` / `admin123`
- Agente Demo: `agente.demo@sistema.dev` / `agent123`
- Cliente Demo: `cliente.demo@sistema.dev` / `cliente123`
> Execute `pnpm auth:seed` após configurar `.env`. O script atualiza as contas acima ou cria novas conforme variáveis `SEED_USER_*`.
## Sincronização com Convex
- Usuários e tickets demo são garantidos via `convex/seed.ts`.
- Após iniciar `pnpm convex:dev`, acesse `/dev/seed` uma vez por ambiente local para carregar dados reais de demonstração no banco do Convex.
## Setup local rápido
1. `pnpm install`
2. Ajuste `.env` (ou crie a partir do exemplo) e confirme `NEXT_PUBLIC_CONVEX_URL` apontando para o Convex local.
3. `pnpm auth:seed`
4. (Opcional) `pnpm queues:ensure`
5. `pnpm convex:dev`
6. Em outro terminal: `pnpm dev`
| Papel | Usuário | Senha |
| --- | --- | --- |
| Administrador | `admin@sistema.dev` | `admin123` |
| Painel telão | `suporte@rever.com.br` | `agent123` |
## App Desktop (Agente de Máquinas)
- Código: `apps/desktop` (Tauri v2 + Vite).
- Padrões de URL:
- Produção: usa `https://tickets.esdrasrenan.com.br` por padrão (fallback em release).
- Desenvolvimento: use `apps/desktop/.env` (ver `.env.example`).
- Comandos úteis:
- `pnpm -C apps/desktop tauri dev` — dev completo (abre WebView em 1420 + backend Rust).
- `pnpm -C apps/desktop build` — build do frontend (dist).
- `pnpm -C apps/desktop tauri build` — gera instaladores (bundle) por SO.
- Saída dos pacotes: `apps/desktop/src-tauri/target/release/bundle/`.
- Fluxo:
1) Coleta perfil (hostname/OS/MAC/seriais/métricas).
2) Provisiona via `POST /api/machines/register` com `MACHINE_PROVISIONING_SECRET`.
3) Envia heartbeats a cada 5 min para `/api/machines/heartbeat` com inventário básico.
4) Abre `APP_URL/machines/handshake?token=...` para autenticar sessão na UI.
- Segurança: token salvo no cofre do SO (Keyring). Store guarda apenas metadados não sensíveis.
- Endpoint extra: `POST /api/machines/inventory` (atualiza inventário por token ou provisioningSecret).
Os demais colaboradores reais são provisionados via **Convites & acessos**. Caso existam vestígios de dados demo, execute `node scripts/remove-legacy-demo-users.mjs` para limpá-los.
## Desenvolvimento local — boas práticas (atualizado)
- Ambientes separados: mantenha seu `.env.local` só para DEV e o `.env` da VPS só para PROD. Nunca commitar arquivos `.env`.
- Convex em DEV: rode `pnpm convex:dev` e aponte o front para `http://127.0.0.1:3210` via `NEXT_PUBLIC_CONVEX_URL`.
- Banco local: por padrão `DATABASE_URL=file:./prisma/db.sqlite`. Se quiser isolar por projeto, use `db.dev.sqlite`.
- Seeds em DEV: use `pnpm auth:seed` (usuários Better Auth) e acesse `/dev/seed` uma vez para dados de demonstração do Convex.
- Seeds em PROD: só quando realmente necessário; não fazem parte do deploy automático.
> Execute `bun run auth:seed` após configurar `.env` para (re)criar os usuários acima (campos `SEED_USER_*` podem sobrescrever credenciais).
### Exemplo de `.env.local` (DEV)
```
# Base do app
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=dev-only-long-random-string
## Backend Convex
- Seeds de usuários/tickets demo: `convex/seed.ts`.
- Para DEV: rode `bun run convex:dev:bun` e acesse `/dev/seed` uma vez para popular dados realistas.
# Convex (DEV)
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
## Stack atual (18/12/2025)
- **Next.js**: `16.0.10` (Turbopack por padrão; webpack fica como fallback).
- Whitelist de domínios em `src/config/allowed-hosts.ts` é aplicada pelo `middleware.ts`.
- **React / React DOM**: `19.2.1`.
- **Trilha de testes**: Vitest (`bun test`) sem modo watch por padrão (`--run --passWithNoTests`).
- **CI**: workflow `Quality Checks` (`.github/workflows/quality-checks.yml`) roda `bun install`, `bun run prisma:generate`, `bun run lint`, `bun test`, `bun run build:bun`. Variáveis críticas (`BETTER_AUTH_SECRET`, `NEXT_PUBLIC_APP_URL`, etc.) são definidas apenas no runner — não afetam a VPS.
- **Disciplina pós-mudanças**: sempre que fizer alterações locais, rode **obrigatoriamente** `bun run lint`, `bun run build:bun` e `bun test` antes de entregar ou abrir PR. Esses comandos são mandatórios também para os agentes/automations, garantindo que o projeto continua íntegro.
- **Deploy**: pipeline `ci-cd-web-desktop.yml` (runner self-hosted). Build roda com Bun 1.3 + Node 20. Web é publicado em `/home/renan/apps/sistema` e o Swarm aponta `sistema_web` para essa pasta.
# Banco local (Prisma)
DATABASE_URL=file:./prisma/db.sqlite
## Setup local (atualizado)
1. `bun install`
2. Copie `.env.example``.env.local`.
- Principais variáveis para DEV:
```
NODE_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
BETTER_AUTH_URL=http://localhost:3000
BETTER_AUTH_SECRET=dev-only-long-random-string
NEXT_PUBLIC_CONVEX_URL=http://127.0.0.1:3210
DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados
```
3. `bun run auth:seed`
4. (Opcional) `bun run queues:ensure`
5. `bun run convex:dev:bun`
6. Em outro terminal: `bun run dev:bun`
7. Acesse `http://localhost:3000` e valide login com os usuários padrão.
# SMTP de desenvolvimento (ex.: Mailpit)
SMTP_ADDRESS=localhost
SMTP_PORT=1025
SMTP_TLS=false
SMTP_USERNAME=
SMTP_PASSWORD=
MAILER_SENDER_EMAIL="Dev <no-reply@localhost>"
### Banco de dados
- Local (DEV): PostgreSQL local (ex.: `postgres:18`) com `DATABASE_URL=postgresql://postgres:dev@localhost:5432/sistema_chamados`.
- Produção: PostgreSQL no Swarm (serviço `postgres` em uso hoje; `postgres18` provisionado para migração). Migrations em PROD devem apontar para o `DATABASE_URL` ativo (ver `docs/OPERATIONS.md`).
- Limpeza de legados: `node scripts/remove-legacy-demo-users.mjs` remove contas demo antigas (Cliente Demo, gestores fictícios etc.).
# (Opcional) OAuth DEV não usado por padrão neste projeto
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
### Verificações antes de PR/deploy
```bash
bun run lint
bun test
bun run build:bun
```
Observações:
- `COOKIE_DOMAIN` não é necessário em DEV neste projeto.
- Variáveis de provisionamento de máquinas (`MACHINE_PROVISIONING_SECRET`, etc.) só se você for testar as rotas de máquinas/inventário.
## Aplicativo Desktop (Tauri)
- Código-fonte: `apps/desktop` (Tauri v2 + Vite + React 19).
- URLs:
- Produção: `https://tickets.esdrasrenan.com.br`
- DEV: configure `apps/desktop/.env` (exemplo fornecido).
- Comandos:
- `bun run --cwd apps/desktop tauri dev` — desenvolvimento (porta 1420).
- `bun run --cwd apps/desktop tauri build` — gera instaladores.
- **Fluxo do agente**:
1. Coleta perfil da dispositivo (hostname, OS, MAC, seriais, métricas).
2. Provisiona via `POST /api/machines/register` usando `MACHINE_PROVISIONING_SECRET`, informando perfil de acesso (Colaborador/Gestor) + dados do colaborador.
3. Envia heartbeats periódicos (`/api/machines/heartbeat`) com inventário básico + estendido (discos SMART, GPUs, serviços, softwares, CPU window).
4. Realiza handshake em `APP_URL/machines/handshake?token=...&redirect=...` para receber cookies Better Auth + sessão (colaborador → `/portal`, gestor → `/dashboard`).
5. Token persistido no cofre do SO (Keyring); store guarda apenas metadados.
6. Envio manual de inventário via botão (POST `/api/machines/inventory`).
7. Updates automáticos: plugin `@tauri-apps/plugin-updater` consulta `latest.json` publicado nos releases do GitHub.
- **Admin ▸ Dispositivos**: permite ajustar perfil/email associado, visualizar inventário completo e remover dispositivo.
### Passo a passo local
1) `pnpm install`
2) `pnpm prisma:generate`
3) `pnpm convex:dev` (terminal A)
4) `pnpm dev` (terminal B)
5) (Opcional) `pnpm auth:seed` e visitar `http://localhost:3000/dev/seed`
### Sessão "machine" no frontend
- Ao autenticar como dispositivo, o front chama `/api/machines/session`, popula `machineContext` (assignedUser*, persona) e deriva role/`viewerId`.
- Mesmo quando `get-session` é `null` na WebView, o portal utiliza `machineContext` para saber o colaborador/gestor logado.
- UI remove opção "Sair" no menu do usuário quando detecta sessão de dispositivo.
- `/portal/debug` exibe JSON de `get-session` e `machines/session` (útil para diagnosticar cookies/bearer).
## Deploy via GitHub Actions (produção)
- Fluxo: `git push main` ⇒ runner selfhosted na VPS sincroniza código e aplica o stack (Traefik/Swarm) sem derrubar o serviço (start-first).
- Disparo do deploy web: apenas quando há mudanças em arquivos do app (src/, public/, prisma/, next.config.ts, package.json, pnpm-lock.yaml, tsconfig.json, middleware.ts, stack.yml).
- Disparo do deploy Convex: apenas quando há mudanças em `convex/**`.
- O `.env` da VPS é preservado; caches do servidor (`node_modules`, `.pnpm-store`) não são tocados.
- Banco Prisma (SQLite) persiste em volume nomeado (`sistema_db`); não é recriado a cada deploy.
### Observações adicionais
- Planejamos usar um cookie `desktop_shell` no futuro para diferenciar acessos do desktop vs navegador (não implementado).
## Bancos e seeds — DEV x PROD
- DEV: use os seeds à vontade (usuários com `pnpm auth:seed`, dados demo do Convex em `/dev/seed`).
- PROD: evite seeds automáticos; para criar um admin use `SEED_USER_*` e `pnpm auth:seed` em um container Node efêmero.
- Alterações de schema: sempre via migrações (`prisma migrate`). O CI aplica `migrate deploy` no start do container web.
## Qualidade e testes
- **Lint**: `bun run lint` (ESLint flat config).
- **Testes unitários/integrados (Vitest)**:
- Cobertura atual inclui utilitários (`tests/*.test.ts`), rotas `/api/machines/*` e `sendSmtpMail`.
- Executar `bun test -- --watch` apenas quando precisar de modo interativo.
- **Build**: `bun run build:bun` (`next build --turbopack`). Quando precisar do fallback oficial, rode `bun run build:webpack`.
- **CI**: falhas mais comuns
- `ERR_BUN_LOCKFILE_OUTDATED`: confirme que o `bun.lock` foi regenerado (`bun install`) após alterar dependências, especialmente do app desktop.
- Variáveis Better Auth ausentes (`BETTER_AUTH_SECRET`): definidas no workflow (`Quality Checks`).
- Falha de host: confira `src/config/allowed-hosts.ts`; o middleware retorna 403 quando o domínio do Traefik não está listado.
## Dicas rápidas
- Imagens em `public/`: trocou o arquivo → push. Para bust de cache, versionar o nome (ex.: `logo.v2.png`) ou usar query (`?v=sha`).
- Problemas de permissão de build: garanta que `.next` pertence ao usuário do runner (se necessário, remover `.next` no host e rebuildar).
- Se precisar inspecionar/backup do SQLite em PROD, prefira um bind dedicado (`/srv/apps/sistema-data:/app/data`) ou use `docker run -v sistema_db:/data` para copiar o arquivo.
## Produção / Deploy
- Runner self-hosted (VPS). Build roda fora de `/srv/apps/sistema` e rsync publica em `/home/renan/apps/sistema`.
- Swarm: `stack.yml` monta `/home/renan/apps/sistema.current``/app` (via symlink).
- Para liberar novo release manualmente:
```bash
ln -sfn /home/renan/apps/sistema.build.<novo> /home/renan/apps/sistema.current
docker service update --force sistema_web
```
- Resolver `P3009` (migration falhou) no PostgreSQL ativo:
```bash
docker service scale sistema_web=0
docker run --rm -it --network traefik_public \
--env-file /home/renan/apps/sistema.current/.env \
-v /home/renan/apps/sistema.current:/app \
oven/bun:1 bash -lc "bun install --frozen-lockfile && bun x prisma migrate resolve --rolled-back <migration> && bun x prisma migrate deploy"
docker service scale sistema_web=1
```
## Checklist para novo computador
1. Instale Node.js 20+ e habilite o Corepack (`corepack enable`) para usar o `pnpm`.
2. Garanta o `pnpm` atualizado (`corepack prepare pnpm@latest --activate`) antes de clonar o repositório.
3. Clone o projeto: `git clone git@github.com:esdrasrenan/sistema-de-chamados.git` e entre na pasta.
4. Copie o arquivo `.env` já configurado do computador atual para a raiz do repositório (nunca faça commit desse arquivo).
5. Instale as dependências com `pnpm install`.
6. Gere os clientes locais necessários: `pnpm prisma:generate`.
7. Semeie as credenciais Better Auth: `pnpm auth:seed`.
8. Se for trabalhar com filas padrão, execute `pnpm queues:ensure`.
9. Inicie o backend Convex em um terminal (`pnpm convex:dev`) e, em outro, suba a aplicação Next.js (`pnpm dev`).
10. Acesse `http://localhost:3000` e teste login com os usuários padrão listados acima antes de continuar o desenvolvimento.
## Estado atual
- Autenticação Better Auth com guardas client-side (`AuthGuard`) bloqueando rotas protegidas.
- Menu de usuário (rodapé da sidebar) concentra acesso às configurações ("Meu perfil" → `/settings`) e logout. Removemos o item redundante "Configurações" do menu lateral.
- Formulários de novo ticket (dialog, página e portal) com seleção de responsável, placeholders claros e validação obrigatória de assunto/descrição/categorias.
- Relatórios, dashboards e páginas administrativas utilizam `AppShell`, garantindo header/sidebar consistentes.
- Use `SiteHeader` no `header` do `AppShell` para título/lead e ações.
- O conteúdo deve ficar dentro de `<div className="mx-auto w-full max-w-6xl px-4 pb-12 lg:px-6">`.
- Persistir filtro global de empresa com `usePersistentCompanyFilter` (localStorage) para manter consistência entre relatórios.
## Entregas recentes
- Exportações CSV (Backlog, Canais, CSAT, SLA e Horas por cliente) com parâmetros de período.
- PDF do ticket (via pdfkit standalone), com espaçamento e traduções PT-BR.
- Play interno/externo com somatório por tipo por ticket e relatório por cliente.
- Admin > Empresas & clientes: cadastro/edição, `Cliente avulso?` e `Horas contratadas/mês`.
## Estado do portal / app web
- Autenticação Better Auth com `AuthGuard`.
- Sidebar inferior agrega avatar, link para `/settings` e logout (oculto em sessões de dispositivo).
- Formulários de ticket (novo/editar/comentários) usam editor rico + anexos; placeholders e validação PT-BR.
- Relatórios e painéis utilizam `AppShell` + `SiteHeader`.
- `usePersistentCompanyFilter` mantém filtro global de empresa em relatórios/admin.
- Exportações CSV: backlog, canais, CSAT, SLA, horas (rotas `/api/reports/*.csv`).
- PDF do ticket (`/api/tickets/[id]/export/pdf`).
- Play interno/externo com métricas por tipo.
- Admin > Empresas: cadastro + “Cliente avulso?”, horas contratadas, vínculos de usuários.
- Admin > Usuários/Equipe:
- Abas separadas: "Equipe" (administradores e agentes) e "Usuários" (gestores e colaboradores).
- Multiseleção + ações em massa: excluir usuários, remover agentes de dispositivo e revogar convites pendentes.
- Filtros por papel, empresa e espaço (tenant) quando aplicável; busca unificada.
- Convites: campo "Espaço (ID interno)" removido da UI de geração.
- Admin > Usuários: vincular colaborador à empresa.
- Dashboard: cards de filas (Chamados/Laboratório/Visitas) e indicadores principais.
- Lista de tickets: filtro por Empresa, coluna Empresa, alinhamento vertical e melhor espaçamento entre colunas.
## Entregas recentes relevantes
- Correção do redirecionamento após logout evitando retorno imediato ao dashboard.
- Validações manuais dos formulários de rich text para eliminar `ZodError` durante edição.
- Dropdown de responsáveis na criação de tickets com preenchimento automático pelo autor e evento inicial de comentário.
- Indicadores visuais de campos obrigatórios e botão "Novo ticket" funcional no cabeçalho do detalhe.
- Seeds (Better Auth e Convex) ampliados para incluir agente e cliente de teste.
## Fluxos suportados
### Equipe interna (admin/agent/collaborator)
- Criar tickets com categorias, responsável inicial e anexos.
- Abrir novos tickets diretamente a partir do detalhe via dialog reutilizável.
- Acessar `/settings` para ajustes pessoais e efetuar logout pelo menu.
### Papéis
- Papéis válidos: `admin`, `manager`, `agent`, `collaborator` (papel `customer` removido).
- Gestores veem os tickets da própria empresa e só podem registrar comentários públicos.
## Próximos passos sugeridos
1. Disparo de e-mails automáticos quando uso de horas ≥ 90% do contratado.
2. Ações rápidas (status/fila) diretamente na listagem de tickets.
3. Limites e monitoramento para anexos por tenant.
4. PDF do ticket com layout idêntico ao app (logo/cores/fontes).
- Alertas enviados: acessível agora em Configurações → Administração do workspace (link direto para /admin/alerts). Removido da sidebar.
- Dashboard: cards por fila e indicadores principais.
## Referências de endpoints úteis
- Backlog CSV: `/api/reports/backlog.csv?range=7d|30d|90d[&companyId=...]`
- Canais CSV: `/api/reports/tickets-by-channel.csv?range=7d|30d|90d[&companyId=...]`
- CSAT CSV: `/api/reports/csat.csv?range=7d|30d|90d`
- SLA CSV: `/api/reports/sla.csv`
- Horas por cliente CSV: `/api/reports/hours-by-client.csv?range=7d|30d|90d`
## Referências de inventário de máquinas
- UI (Admin > Máquinas): filtros, pesquisa e export detalhados — ver docs/admin-inventory-ui.md
- Endpoints do agente:
## Fluxos suportados
- **Equipe interna** (`admin`, `agent`, `collaborator`): cria/acompanha tickets, comenta, altera status/fila, gera relatórios.
- **Gestores** (`manager`): visualizam tickets da empresa, comentam publicamente, acessam dashboards.
- **Colaboradores** (`collaborator`): portal (`/portal`), tickets próprios, comentários públicos, editor rico, anexos.
- **Sessão Dispositivo**: desktop registra heartbeat/inventário e redireciona colaborador/gestor ao portal apropriado com cookies válidos.
### Correções recentes
- Temporizador do ticket (atendimento em andamento): a UI passa a aplicar atualização otimista na abertura/pausa da sessão para que o tempo corrente não "salte" para minutos indevidos. O backend continua a fonte da verdade (total acumulado é reconciliado ao pausar).
## Backlog recomendado
1. E-mails automáticos quando uso de horas ≥ 90% do contratado.
2. Ações rápidas (status/fila) diretamente na lista de tickets.
3. Limites de anexos por tenant + monitoramento.
4. Layout do PDF do ticket alinhado ao visual da aplicação.
5. Experimentos com React Compiler (Next 16).
## Referências rápidas
- **Endpoints agent desktop**:
- `POST /api/machines/register`
- `POST /api/machines/heartbeat`
- `POST /api/machines/inventory`
## Rotina antes de abrir PR
- `pnpm lint`
- `pnpm build --turbopack`
- `pnpm exec vitest run`
- Revisar toasts/labels em PT-BR e ausência de segredos no diff.
## Convenções
- Convex deve retornar apenas tipos primitivos; converta datas via mappers em `src/lib/mappers`.
- Manter textos em PT-BR e evitar comentários supérfluos no código.
- Reutilizar componentes shadcn existentes e seguir o estilo do arquivo editado.
- Validações client-side críticas devem sinalizar erros inline e exibir toast.
## Estrutura útil
- `convex/` — queries e mutations (ex.: `tickets.ts`, `users.ts`).
- `src/components/tickets/` — UI interna (dialog, listas, header, timeline).
- `src/components/portal/` — formulários e fluxos do portal do cliente.
- `scripts/` — seeds Better Auth e utilidades.
- `src/components/auth/auth-guard.tsx` — proteção de rotas client-side.
## Histórico resumido
- Scaffold Next.js + Turbopack configurado com Better Auth e Convex.
- Portal do cliente entregue com isolamento por `viewerId`.
- Fluxo de convites e painel administrativo operacionais.
- Iteração atual focada em UX de criação de tickets, consistência de layout e guardas de sessão.
- **Relatórios XLSX**:
- Backlog: `/api/reports/backlog.xlsx?range=7d|30d|90d[&companyId=...]`
- Canais: `/api/reports/tickets-by-channel.xlsx?...`
- CSAT: `/api/reports/csat.xlsx?...`
- SLA: `/api/reports/sla.xlsx?...`
- Horas: `/api/reports/hours-by-client.xlsx?...`
- Inventário de dispositivos: `/api/reports/machines-inventory.xlsx?[companyId=...]`
- **Docs complementares**:
- `docs/DEV.md` — guia diário atualizado.
- `docs/STATUS-2025-10-16.md` — snapshot do estado atual e backlog.
- `docs/OPERATIONS.md` — runbook do Swarm.
- `docs/admin-inventory-ui.md`, `docs/plano-app-desktop-maquinas.md` — detalhes do inventário/agente.
## Regras de Codigo
### Tooltips Nativos do Navegador
**NAO use o atributo `title` em elementos HTML** (button, span, a, div, etc).
O atributo `title` causa tooltips nativos do navegador que sao inconsistentes visualmente e nao seguem o design system da aplicacao.
```tsx
// ERRADO - causa tooltip nativo do navegador
<button title="Remover item">
<Trash2 className="size-4" />
</button>
// CORRETO - sem tooltip nativo
<button>
<Trash2 className="size-4" />
</button>
// CORRETO - se precisar de tooltip, use o componente Tooltip do shadcn/ui
<Tooltip>
<TooltipTrigger asChild>
<button>
<Trash2 className="size-4" />
</button>
</TooltipTrigger>
<TooltipContent>Remover item</TooltipContent>
</Tooltip>
```
**Excecoes:**
- Props `title` de componentes customizados (CardTitle, DialogTitle, etc) sao permitidas pois nao geram tooltips nativos.
### Acessibilidade
Para manter acessibilidade em botoes apenas com icone, prefira usar `aria-label`:
```tsx
<button aria-label="Remover item">
<Trash2 className="size-4" />
</button>
```
---
_Última atualização: 18/12/2025 (Next.js 16, build padrão com Turbopack e fallback webpack documentado)._

View file

@ -9,6 +9,14 @@ VITE_APP_URL=http://localhost:3000
# Se não definir, cai no mesmo valor de VITE_APP_URL
VITE_API_BASE_URL=
# RustDesk provisioning (opcionais; se vazios, o app usa o TOML padrão embutido)
VITE_RUSTDESK_CONFIG_STRING=
VITE_RUSTDESK_DEFAULT_PASSWORD=FMQ9MA>e73r.FI<b*34Vmx_8P
# Assinatura Tauri (dev/CI). Em producao, pode sobrescrever por env seguro.
TAURI_SIGNING_PRIVATE_KEY=dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5WkhWOUtzd1BvV0ZlSjEvNzYwaHYxdEloNnV4cmZlNGhha1BNbmNtZEkrZ0FBQkFBQUFBQUFBQUFBQUlBQUFBQS9JbCtsd3VFbHN4empFRUNiU0dva1hKK3ZYUzE2S1V6Q1FhYkRUWGtGMTBkUmJodi9PaXVub3hEMisyTXJoYU5UeEdwZU9aMklacG9ualNWR1NaTm1PMVBpVXYrNTltZU1YOFdwYzdkOHd2STFTc0x4ZktpNXFENnFTdW0xNzY3WC9EcGlIRGFmK2c9Cg==
TAURI_SIGNING_PRIVATE_KEY_PASSWORD=revertech
# Opcional: IP do host para desenvolvimento com HMR fora do localhost
# Ex.: 192.168.0.10
TAURI_DEV_HOST=

View file

@ -1,11 +1,11 @@
# Sistema de Chamados — App Desktop (Tauri)
Cliente desktop (Tauri v2 + Vite) que:
- Coleta perfil/métricas da máquina via comandos Rust.
- Registra a máquina com um código de provisionamento.
- Coleta perfil/métricas da dispositivo via comandos Rust.
- Registra a dispositivo com um código de provisionamento.
- Envia heartbeat periódico ao backend (`/api/machines/heartbeat`).
- Redireciona para a UI web do sistema após provisionamento.
- Armazena o token da máquina com segurança no cofre do SO (Keyring).
- Armazena o token da dispositivo com segurança no cofre do SO (Keyring).
- Exibe abas de Resumo, Inventário, Diagnóstico e Configurações; permite “Enviar inventário agora”.
## URLs e ambiente
@ -22,13 +22,42 @@ VITE_API_BASE_URL=
## Comandos
- Dev (abre janela Tauri e Vite em 1420):
- `pnpm -C apps/desktop tauri dev`
- `bun run --cwd apps/desktop tauri dev`
- Build frontend (somente Vite):
- `pnpm -C apps/desktop build`
- `bun run --cwd apps/desktop build`
- Build executável (bundle):
- `pnpm -C apps/desktop tauri build`
- `bun run --cwd apps/desktop tauri build`
Saída dos pacotes: `apps/desktop/src-tauri/target/release/bundle/` (AppImage/deb/msi/dmg conforme SO).
Saída dos pacotes: `apps/desktop/src-tauri/target/release/bundle/`.
### Windows (NSIS) — instalação e dados
- Instalador NSIS com suporte a “perMachine” (Arquivos de Programas) e diretório customizável (ex.: `C:\Raven`).
- Atalho é criado na Área de Trabalho apontando para o executável instalado.
- Dados do app (token/config) ficam em AppData local do usuário (via `@tauri-apps/plugin-store` com `appLocalDataDir`).
#### NSIS — Idiomas e modo de instalação
- Idioma: o instalador inclui Português do Brasil e exibe seletor de idioma.
- Arquivo: `apps/desktop/src-tauri/tauri.conf.json:54``"displayLanguageSelector": true`
- Arquivo: `apps/desktop/src-tauri/tauri.conf.json:57``"languages": ["PortugueseBR"]`
- Comportamento: usa o idioma do SO; sem correspondência, cai no primeiro da lista.
- Referência de idiomas NSIS: NSIS “Language files/PortugueseBR”.
- Modo de instalação: Program Files (requer elevação/UAC).
- Arquivo: `apps/desktop/src-tauri/tauri.conf.json:56``"installMode": "perMachine"`
- Alternativas: `"currentUser"` (padrão) ou `"both"` (usuário escolhe; exige UAC).
Build rápido e leve em dev:
```bash
bun run --cwd apps/desktop tauri build --bundles nsis
```
Assinatura do updater (opcional em dev):
```powershell
$privB64 = '<COLE_SUA_CHAVE_PRIVADA_EM_BASE64>'
$env:TAURI_SIGNING_PRIVATE_KEY = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($privB64))
$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD = 'SENHA_AQUI'
bun run --cwd apps/desktop tauri build --bundles nsis
```
## Prérequisitos Tauri
- Rust toolchain instalado.
@ -36,7 +65,7 @@ Saída dos pacotes: `apps/desktop/src-tauri/target/release/bundle/` (AppImage/de
Consulte https://tauri.app/start/prerequisites/
## Fluxo (resumo)
1) Ao abrir, o app coleta o perfil da máquina e exibe um resumo.
1) Ao abrir, o app coleta o perfil da dispositivo e exibe um resumo.
2) Informe o “código de provisionamento” (chave definida no servidor) e confirme.
3) O servidor retorna um `machineToken`; o app salva e inicia o heartbeat.
4) O app abre `APP_URL/machines/handshake?token=...` no WebView para autenticar a sessão na UI.

View file

@ -159,10 +159,11 @@
## 5. Gerar chaves do updater Tauri
1. Em qualquer máquina com Node/pnpm (pode ser seu computador local):
1. Em qualquer dispositivo com Bun instalado (pode ser seu computador local):
```bash
pnpm install
pnpm --filter appsdesktop tauri signer generate
bun install
bun install --cwd apps/desktop
bun run --cwd apps/desktop tauri signer generate
```
2. O comando gera:
- Chave privada (`tauri.private.key`).
@ -236,19 +237,16 @@
1. Baixe e instale os pré-requisitos:
- Git para Windows.
- Node.js 20 (instalação inclui npm).
- Habilite o Corepack: abra o PowerShell como administrador e rode:
```powershell
corepack enable
corepack prepare pnpm@latest --activate
```
- Bun 1.3+: instale via instalador oficial (`iwr https://bun.sh/install.ps1 | invoke-expression`) e garanta que `bun` esteja no `PATH`.
- Node.js 20 (opcional, caso precise rodar scripts em Node durante o build).
- Rust toolchain: https://rustup.rs (instale padrão).
- Visual Studio Build Tools (C++ build tools) ou `Desktop development with C++`.
- WebView2 Runtime (https://developer.microsoft.com/microsoft-edge/webview2/).
2. Opcional: instale as dependências do Tauri rodando uma vez:
```powershell
pnpm install
pnpm --filter appsdesktop tauri info
bun install
bun install --cwd apps/desktop
bun run --cwd apps/desktop tauri info
```
3. No GitHub → *Settings**Actions**Runners**New self-hosted runner* → escolha Windows x64 e copie URL/token.
4. Em `C:\actions-runner` (recomendado):
@ -267,10 +265,10 @@
.\svc start
```
6. Confirme no GitHub que o runner aparece como `online`.
7. Mantenha a máquina ligada e conectada durante o período em que o workflow precisa rodar:
7. Mantenha a dispositivo ligada e conectada durante o período em que o workflow precisa rodar:
- Para releases desktop, o runner só precisa estar ligado enquanto o job `desktop_release` estiver em execução (crie a tag e aguarde o workflow terminar).
- Após a conclusão, você pode desligar o computador até a próxima release.
8. Observação importante: o runner Windows pode ser sua máquina pessoal. Garanta apenas que:
8. Observação importante: o runner Windows pode ser sua dispositivo pessoal. Garanta apenas que:
- Você confia no código que será executado (o runner processa os jobs do repositório).
- O serviço do runner esteja ativo enquanto o workflow rodar (caso desligue o PC, as releases ficam na fila).
- Há espaço em disco suficiente e nenhuma política corporativa bloqueando a instalação dos pré-requisitos.
@ -326,14 +324,10 @@
steps:
- uses: actions/checkout@v4
- name: Setup pnpm & Node
uses: pnpm/action-setup@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
bun-version: 1.3.1
- name: Deploy stack (Docker Swarm)
working-directory: ${{ env.APP_DIR }}
@ -350,20 +344,16 @@
steps:
- uses: actions/checkout@v4
- name: Setup pnpm & Node
uses: pnpm/action-setup@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v1
with:
version: 9
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
bun-version: 1.3.1
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install deps
run: pnpm install --frozen-lockfile
run: bun install --frozen-lockfile
- name: Build + Sign + Release (tauri-action)
uses: tauri-apps/tauri-action@v0
@ -389,7 +379,7 @@
target: ${{ env.VPS_UPDATES_DIR }}
overwrite: true
```
2. Ajuste o bloco de deploy conforme seu processo (por exemplo, use `pnpm build && pm2 restart` se não usar Docker ou substitua por chamada à API do Portainer caso faça o deploy por lá).
2. Ajuste o bloco de deploy conforme seu processo (por exemplo, use `bun run build && pm2 restart` se não usar Docker ou substitua por chamada à API do Portainer caso faça o deploy por lá).
3. Faça commit desse arquivo e suba para o GitHub (`git add .github/workflows/ci-cd-web-desktop.yml`, `git commit`, `git push`).
---
@ -429,7 +419,7 @@
- Garanta que o certificado TLS usado pelo Nginx é renovado (p. ex. `certbot renew`).
4. Manter runners:
- VPS: monitore serviço `actions.runner.*`. Reinicie se necessário (`sudo ./svc.sh restart`).
- Windows: mantenha máquina ligada e atualizada. Se o serviço parar, abra `services.msc``GitHub Actions Runner` → Start.
- Windows: mantenha dispositivo ligada e atualizada. Se o serviço parar, abra `services.msc``GitHub Actions Runner` → Start.
---
@ -451,7 +441,7 @@
| Job `desktop_release` falha na etapa `tauri-action` | Toolchain incompleto no Windows | Reinstale Rust, WebView2 e componentes C++ do Visual Studio. |
| Artefatos não chegam à VPS | Caminho incorreto ou chave SSH inválida | Verifique `VPS_HOST`, `VPS_USER`, `VPS_SSH_KEY` e se a pasta `/var/www/updates` existe. |
| App não encontra update | URL ou chave pública divergente no `tauri.conf.json` | Confirme que `endpoints` bate com o domínio HTTPS e que `pubkey` é exatamente a chave pública gerada. |
| Runner aparece offline no GitHub | Serviço parado ou máquina desligada | VPS: `sudo ./svc.sh status`; Windows: abra `Services` e reinicie o `GitHub Actions Runner`. |
| Runner aparece offline no GitHub | Serviço parado ou dispositivo desligada | VPS: `sudo ./svc.sh status`; Windows: abra `Services` e reinicie o `GitHub Actions Runner`. |
---

View file

@ -7,13 +7,21 @@
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"tauri": "tauri"
"tauri": "node ./scripts/tauri-with-stub.mjs",
"gen:icon": "node ./scripts/build-icon.mjs",
"build:service": "cd service && cargo build --release",
"build:all": "bun run build:service && bun run tauri build"
},
"dependencies": {
"@radix-ui/react-scroll-area": "^1.2.3",
"@radix-ui/react-tabs": "^1.1.13",
"@tauri-apps/api": "^2",
"@tauri-apps/api": "^2.9.1",
"@tauri-apps/plugin-dialog": "^2.4.2",
"@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-process": "^2",
"@tauri-apps/plugin-store": "^2",
"@tauri-apps/plugin-updater": "^2",
"convex": "^1.31.0",
"lucide-react": "^0.544.0",
"react": "^19.0.0",
"react-dom": "^19.0.0"
@ -21,6 +29,8 @@
"devDependencies": {
"@tauri-apps/cli": "^2",
"@vitejs/plugin-react": "^4.3.4",
"baseline-browser-mapping": "^2.9.2",
"png-to-ico": "^3.0.1",
"typescript": "~5.6.2",
"vite": "^6.0.3"
}

View file

@ -0,0 +1,11 @@
{
"version": "0.1.6",
"notes": "Correções e melhorias do desktop",
"pub_date": "2025-10-14T12:00:00Z",
"platforms": {
"windows-x86_64": {
"signature": "ZFc1MGNuVnpkR1ZrSUdOdmJXMWxiblE2SUhOcFoyNWhkSFZ5WlNCbWNtOXRJSFJoZFhKcElITmxZM0psZENCclpYa0tVbFZVZDNFeFUwRlJRalJVUjJOU1NqUnpTVmhXU1ZoeVUwZElNSGxETW5KSE1FTnBWa3BWU1dzelVYVlRNV1JTV0Vrdk1XMUZVa0Z3YTBWc2QySnZhVnBxUWs5bVoyODNNbEZaYUZsMFVHTlRLMUFyT0hJMVdGZ3lWRkZYT1V3ekwzZG5QUXAwY25WemRHVmtJR052YlcxbGJuUTZJSFJwYldWemRHRnRjRG94TnpZd016azVOVEkzQ1dacGJHVTZVbUYyWlc1Zk1DNHhMalZmZURZMExYTmxkSFZ3TG1WNFpRcHdkME15THpOVlZtUXpiSG9yZGpRd1pFZHFhV1JvVkZCb0wzVnNabWh1ZURJdmFtUlZOalEwTkRSVVdVY3JUVGhLTUdrNU5scFNUSFZVWkRsc1lYVTJUR2dyWTNWeWJuWTVhRGh3ZVVnM1dFWjVhSFZDUVQwOUNnPT0=",
"url": "https://github.com/esdrasrenan/sistema-de-chamados/raw/main/apps/desktop/public/releases/Raven_0.1.6_x64-setup.exe"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

View file

@ -0,0 +1 @@
dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVUd3ExU0FRQjRUR2NSSjRzSVhWSVhyU0dIMHlDMnJHMENpVkpVSWszUXVTMWRSWEkvMW1FUkFwa0Vsd2JvaVpqQk9mZ283MlFZaFl0UGNTK1ArOHI1WFgyVFFXOUwzL3dnPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzYwMzk5NTI3CWZpbGU6UmF2ZW5fMC4xLjVfeDY0LXNldHVwLmV4ZQpwd0MyLzNVVmQzbHordjQwZEdqaWRoVFBoL3VsZmhueDIvamRVNjQ0NDRUWUcrTThKMGk5NlpSTHVUZDlsYXU2TGgrY3VybnY5aDhweUg3WEZ5aHVCQT09Cg==

View file

@ -0,0 +1,38 @@
#!/usr/bin/env node
import { promises as fs } from 'node:fs'
import path from 'node:path'
import pngToIco from 'png-to-ico'
async function fileExists(p) {
try { await fs.access(p); return true } catch { return false }
}
async function main() {
const root = path.resolve(process.cwd(), 'src-tauri', 'icons')
// Inclua apenas tamanhos suportados pelo NSIS (até 256px).
// Evite 512px para não gerar ICO inválido para o instalador.
const candidates = [
'icon-256.png', // preferencial
'128x128@2x.png', // alias de 256
'icon-128.png',
'icon-64.png',
'icon-32.png',
]
const sources = []
for (const name of candidates) {
const p = path.join(root, name)
if (await fileExists(p)) sources.push(p)
}
if (sources.length === 0) {
console.error('[gen:icon] Nenhuma imagem base encontrada em src-tauri/icons')
process.exit(1)
}
console.log('[gen:icon] Gerando icon.ico a partir de:', sources.map((s) => path.basename(s)).join(', '))
const buffer = await pngToIco(sources)
const outPath = path.join(root, 'icon.ico')
await fs.writeFile(outPath, buffer)
console.log('[gen:icon] Escrito:', outPath)
}
main().catch((err) => { console.error(err); process.exit(1) })

View file

@ -0,0 +1,237 @@
#!/usr/bin/env python3
"""
Generate icon PNGs/ICO for the desktop installer using the high-resolution Raven artwork.
The script reads the square logo (`logo-raven-fund-azul.png`) and resizes it to the
target sizes with a simple bilinear filter implemented with the Python standard library,
avoiding additional dependencies.
"""
from __future__ import annotations
import math
import struct
import zlib
from binascii import crc32
from pathlib import Path
ICON_DIR = Path(__file__).resolve().parents[1] / "src-tauri" / "icons"
BASE_IMAGE = ICON_DIR / "logo-raven-fund-azul.png"
TARGET_SIZES = [32, 64, 128, 256, 512]
def read_png(path: Path) -> tuple[int, int, list[list[tuple[int, int, int, int]]]]:
data = path.read_bytes()
if not data.startswith(b"\x89PNG\r\n\x1a\n"):
raise ValueError(f"{path} is not a PNG")
pos = 8
width = height = bit_depth = color_type = None
compressed_parts = []
while pos < len(data):
length = struct.unpack(">I", data[pos : pos + 4])[0]
pos += 4
ctype = data[pos : pos + 4]
pos += 4
chunk = data[pos : pos + length]
pos += length
pos += 4 # CRC
if ctype == b"IHDR":
width, height, bit_depth, color_type, _, _, _ = struct.unpack(">IIBBBBB", chunk)
if bit_depth != 8 or color_type not in (2, 6):
raise ValueError("Only 8-bit RGB/RGBA PNGs are supported")
elif ctype == b"IDAT":
compressed_parts.append(chunk)
elif ctype == b"IEND":
break
if width is None or height is None or bit_depth is None or color_type is None:
raise ValueError("PNG missing IHDR chunk")
raw = zlib.decompress(b"".join(compressed_parts))
bpp = 4 if color_type == 6 else 3
stride = width * bpp
rows = []
idx = 0
prev = bytearray(stride)
for _ in range(height):
filter_type = raw[idx]
idx += 1
row = bytearray(raw[idx : idx + stride])
idx += stride
if filter_type == 1:
for i in range(stride):
left = row[i - bpp] if i >= bpp else 0
row[i] = (row[i] + left) & 0xFF
elif filter_type == 2:
for i in range(stride):
row[i] = (row[i] + prev[i]) & 0xFF
elif filter_type == 3:
for i in range(stride):
left = row[i - bpp] if i >= bpp else 0
up = prev[i]
row[i] = (row[i] + ((left + up) // 2)) & 0xFF
elif filter_type == 4:
for i in range(stride):
left = row[i - bpp] if i >= bpp else 0
up = prev[i]
up_left = prev[i - bpp] if i >= bpp else 0
p = left + up - up_left
pa = abs(p - left)
pb = abs(p - up)
pc = abs(p - up_left)
if pa <= pb and pa <= pc:
pr = left
elif pb <= pc:
pr = up
else:
pr = up_left
row[i] = (row[i] + pr) & 0xFF
elif filter_type not in (0,):
raise ValueError(f"Unsupported PNG filter type {filter_type}")
rows.append(bytes(row))
prev[:] = row
pixels: list[list[tuple[int, int, int, int]]] = []
for row in rows:
if color_type == 6:
pixels.append([tuple(row[i : i + 4]) for i in range(0, len(row), 4)])
else:
pixels.append([tuple(row[i : i + 3] + b"\xff") for i in range(0, len(row), 3)])
return width, height, pixels
def write_png(path: Path, width: int, height: int, pixels: list[list[tuple[int, int, int, int]]]) -> None:
raw = bytearray()
for row in pixels:
raw.append(0) # filter type 0
for r, g, b, a in row:
raw.extend((r & 0xFF, g & 0xFF, b & 0xFF, a & 0xFF))
compressed = zlib.compress(raw, level=9)
def chunk(name: bytes, payload: bytes) -> bytes:
return (
struct.pack(">I", len(payload))
+ name
+ payload
+ struct.pack(">I", crc32(name + payload) & 0xFFFFFFFF)
)
ihdr = struct.pack(">IIBBBBB", width, height, 8, 6, 0, 0, 0)
out = bytearray(b"\x89PNG\r\n\x1a\n")
out += chunk(b"IHDR", ihdr)
out += chunk(b"IDAT", compressed)
out += chunk(b"IEND", b"")
path.write_bytes(out)
def bilinear_sample(pixels: list[list[tuple[int, int, int, int]]], x: float, y: float) -> tuple[int, int, int, int]:
height = len(pixels)
width = len(pixels[0])
x = min(max(x, 0.0), width - 1.0)
y = min(max(y, 0.0), height - 1.0)
x0 = int(math.floor(x))
y0 = int(math.floor(y))
x1 = min(x0 + 1, width - 1)
y1 = min(y0 + 1, height - 1)
dx = x - x0
dy = y - y0
def lerp(a: float, b: float, t: float) -> float:
return a + (b - a) * t
result = []
for channel in range(4):
c00 = pixels[y0][x0][channel]
c10 = pixels[y0][x1][channel]
c01 = pixels[y1][x0][channel]
c11 = pixels[y1][x1][channel]
top = lerp(c00, c10, dx)
bottom = lerp(c01, c11, dx)
result.append(int(round(lerp(top, bottom, dy))))
return tuple(result)
def resize_image(pixels: list[list[tuple[int, int, int, int]]], target: int) -> list[list[tuple[int, int, int, int]]]:
src_height = len(pixels)
src_width = len(pixels[0])
scale = min(target / src_width, target / src_height)
dest_width = max(1, int(round(src_width * scale)))
dest_height = max(1, int(round(src_height * scale)))
offset_x = (target - dest_width) // 2
offset_y = (target - dest_height) // 2
background = (0, 0, 0, 0)
canvas = [[background for _ in range(target)] for _ in range(target)]
for dy in range(dest_height):
src_y = (dy + 0.5) / scale - 0.5
for dx in range(dest_width):
src_x = (dx + 0.5) / scale - 0.5
canvas[offset_y + dy][offset_x + dx] = bilinear_sample(pixels, src_x, src_y)
return canvas
def build_ico(output: Path, png_paths: list[Path]) -> None:
entries = []
offset = 6 + 16 * len(png_paths)
for path in png_paths:
data = path.read_bytes()
width, height, _ = read_png(path)
entries.append(
{
"width": width if width < 256 else 0,
"height": height if height < 256 else 0,
"size": len(data),
"offset": offset,
"payload": data,
}
)
offset += len(data)
header = struct.pack("<HHH", 0, 1, len(entries))
body = bytearray(header)
for entry in entries:
body.extend(
struct.pack(
"<BBBBHHII",
entry["width"],
entry["height"],
0,
0,
1,
32,
entry["size"],
entry["offset"],
)
)
for entry in entries:
body.extend(entry["payload"])
output.write_bytes(body)
def main() -> None:
width, height, pixels = read_png(BASE_IMAGE)
if width != height:
raise ValueError("Base icon must be square")
generated: list[Path] = []
for size in TARGET_SIZES:
resized = resize_image(pixels, size)
out_path = ICON_DIR / f"icon-{size}.png"
write_png(out_path, size, size, resized)
generated.append(out_path)
print(f"Generated {out_path} ({size}x{size})")
largest = max(generated, key=lambda p: int(p.stem.split("-")[-1]))
(ICON_DIR / "icon.png").write_bytes(largest.read_bytes())
ico_sources = sorted(
[p for p in generated if int(p.stem.split("-")[-1]) <= 256],
key=lambda p: int(p.stem.split("-")[-1]),
)
build_ico(ICON_DIR / "icon.ico", ico_sources)
print("icon.ico rebuilt.")
if __name__ == "__main__":
main()

View file

@ -0,0 +1,239 @@
#!/usr/bin/env python3
"""
Utility script to convert a PNG file (non-interlaced, 8-bit RGBA/RGB)
into a 24-bit BMP with optional letterboxing resize.
The script is intentionally lightweight and relies only on Python's
standard library so it can run in constrained build environments.
"""
from __future__ import annotations
import argparse
import struct
import sys
import zlib
from pathlib import Path
PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"
def parse_png(path: Path):
data = path.read_bytes()
if not data.startswith(PNG_SIGNATURE):
raise ValueError("Input is not a PNG file")
idx = len(PNG_SIGNATURE)
width = height = bit_depth = color_type = None
compressed = bytearray()
interlaced = False
while idx < len(data):
if idx + 8 > len(data):
raise ValueError("Corrupted PNG (unexpected EOF)")
length = struct.unpack(">I", data[idx : idx + 4])[0]
idx += 4
chunk_type = data[idx : idx + 4]
idx += 4
chunk_data = data[idx : idx + length]
idx += length
crc = data[idx : idx + 4] # noqa: F841 - crc skipped (validated by reader)
idx += 4
if chunk_type == b"IHDR":
width, height, bit_depth, color_type, compression, filter_method, interlace = struct.unpack(
">IIBBBBB", chunk_data
)
if compression != 0 or filter_method != 0:
raise ValueError("Unsupported PNG compression/filter method")
interlaced = interlace != 0
elif chunk_type == b"IDAT":
compressed.extend(chunk_data)
elif chunk_type == b"IEND":
break
if interlaced:
raise ValueError("Interlaced PNGs are not supported by this script")
if bit_depth != 8:
raise ValueError(f"Unsupported bit depth: {bit_depth}")
if color_type not in (2, 6):
raise ValueError(f"Unsupported color type: {color_type}")
raw = zlib.decompress(bytes(compressed))
bytes_per_pixel = 3 if color_type == 2 else 4
stride = width * bytes_per_pixel
expected = (stride + 1) * height
if len(raw) != expected:
raise ValueError("Corrupted PNG data")
# Apply PNG scanline filters
image = bytearray(width * height * 4) # Force RGBA output
prev_row = [0] * (stride)
def paeth(a, b, c):
p = a + b - c
pa = abs(p - a)
pb = abs(p - b)
pc = abs(p - c)
if pa <= pb and pa <= pc:
return a
if pb <= pc:
return b
return c
out_idx = 0
for y in range(height):
offset = y * (stride + 1)
filter_type = raw[offset]
row = bytearray(raw[offset + 1 : offset + 1 + stride])
if filter_type == 1: # Sub
for i in range(stride):
left = row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0
row[i] = (row[i] + left) & 0xFF
elif filter_type == 2: # Up
for i in range(stride):
row[i] = (row[i] + prev_row[i]) & 0xFF
elif filter_type == 3: # Average
for i in range(stride):
left = row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0
up = prev_row[i]
row[i] = (row[i] + ((left + up) >> 1)) & 0xFF
elif filter_type == 4: # Paeth
for i in range(stride):
left = row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0
up = prev_row[i]
up_left = prev_row[i - bytes_per_pixel] if i >= bytes_per_pixel else 0
row[i] = (row[i] + paeth(left, up, up_left)) & 0xFF
elif filter_type != 0:
raise ValueError(f"Unsupported PNG filter type: {filter_type}")
# Convert to RGBA
for x in range(width):
if color_type == 2:
r, g, b = row[x * 3 : x * 3 + 3]
a = 255
else:
r, g, b, a = row[x * 4 : x * 4 + 4]
image[out_idx : out_idx + 4] = bytes((r, g, b, a))
out_idx += 4
prev_row = list(row)
return width, height, image
def resize_with_letterbox(image, width, height, target_w, target_h, background, scale_factor=1.0):
if width == target_w and height == target_h and abs(scale_factor - 1.0) < 1e-6:
return image, width, height
bg_r, bg_g, bg_b = background
base_scale = min(target_w / width, target_h / height)
base_scale *= scale_factor
base_scale = max(base_scale, 1 / max(width, height)) # avoid zero / collapse
scaled_w = max(1, int(round(width * base_scale)))
scaled_h = max(1, int(round(height * base_scale)))
output = bytearray(target_w * target_h * 4)
# Fill background
for i in range(0, len(output), 4):
output[i : i + 4] = bytes((bg_r, bg_g, bg_b, 255))
offset_x = (target_w - scaled_w) // 2
offset_y = (target_h - scaled_h) // 2
for y in range(scaled_h):
src_y = min(height - 1, int(round(y / base_scale)))
for x in range(scaled_w):
src_x = min(width - 1, int(round(x / base_scale)))
src_idx = (src_y * width + src_x) * 4
dst_idx = ((y + offset_y) * target_w + (x + offset_x)) * 4
output[dst_idx : dst_idx + 4] = image[src_idx : src_idx + 4]
return output, target_w, target_h
def blend_to_rgb(image):
rgb = bytearray(len(image) // 4 * 3)
for i in range(0, len(image), 4):
r, g, b, a = image[i : i + 4]
if a == 255:
rgb[(i // 4) * 3 : (i // 4) * 3 + 3] = bytes((b, g, r)) # BMP stores BGR
else:
alpha = a / 255.0
bg = (255, 255, 255)
rr = int(round(r * alpha + bg[0] * (1 - alpha)))
gg = int(round(g * alpha + bg[1] * (1 - alpha)))
bb = int(round(b * alpha + bg[2] * (1 - alpha)))
rgb[(i // 4) * 3 : (i // 4) * 3 + 3] = bytes((bb, gg, rr))
return rgb
def write_bmp(path: Path, width: int, height: int, rgb: bytearray):
row_stride = (width * 3 + 3) & ~3 # align to 4 bytes
padding = row_stride - width * 3
pixel_data = bytearray()
for y in range(height - 1, -1, -1):
start = y * width * 3
end = start + width * 3
pixel_data.extend(rgb[start:end])
if padding:
pixel_data.extend(b"\0" * padding)
file_size = 14 + 40 + len(pixel_data)
header = struct.pack("<2sIHHI", b"BM", file_size, 0, 0, 14 + 40)
dib_header = struct.pack(
"<IIIHHIIIIII",
40, # header size
width,
height,
1, # planes
24, # bits per pixel
0, # compression
len(pixel_data),
2835, # horizontal resolution (px/m ~72dpi)
2835, # vertical resolution
0,
0,
)
path.write_bytes(header + dib_header + pixel_data)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("input", type=Path)
parser.add_argument("output", type=Path)
parser.add_argument("--width", type=int, help="Target width (px)")
parser.add_argument("--height", type=int, help="Target height (px)")
parser.add_argument(
"--scale",
type=float,
default=1.0,
help="Optional multiplier applied to the fitted image size (e.g. 0.7 adds padding).",
)
parser.add_argument(
"--background",
type=str,
default="FFFFFF",
help="Background hex color used for transparent pixels (default: FFFFFF)",
)
args = parser.parse_args()
try:
width, height, image = parse_png(args.input)
if args.width and args.height:
bg = tuple(int(args.background[i : i + 2], 16) for i in (0, 2, 4))
image, width, height = resize_with_letterbox(
image, width, height, args.width, args.height, bg, max(args.scale, 0.05)
)
rgb = blend_to_rgb(image)
write_bmp(args.output, width, height, rgb)
except Exception as exc: # noqa: BLE001
print(f"Error: {exc}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,80 @@
#!/usr/bin/env python3
"""
Utility to build an .ico file from a list of PNGs of different sizes.
Uses only Python's standard library so it can run in restricted environments.
"""
from __future__ import annotations
import argparse
import struct
from pathlib import Path
PNG_SIGNATURE = b"\x89PNG\r\n\x1a\n"
def read_png_dimensions(data: bytes) -> tuple[int, int]:
if not data.startswith(PNG_SIGNATURE):
raise ValueError("All inputs must be PNG files.")
width, height = struct.unpack(">II", data[16:24])
return width, height
def build_icon(png_paths: list[Path], output: Path) -> None:
png_data = [p.read_bytes() for p in png_paths]
entries = []
offset = 6 + 16 * len(png_data) # icon header + entries
for data in png_data:
width, height = read_png_dimensions(data)
entry = {
"width": width if width < 256 else 0,
"height": height if height < 256 else 0,
"colors": 0,
"reserved": 0,
"planes": 1,
"bit_count": 32,
"size": len(data),
"offset": offset,
"data": data,
}
entries.append(entry)
offset += entry["size"]
header = struct.pack("<HHH", 0, 1, len(entries))
table = bytearray()
for entry in entries:
table.extend(
struct.pack(
"<BBBBHHII",
entry["width"],
entry["height"],
entry["colors"],
entry["reserved"],
entry["planes"],
entry["bit_count"],
entry["size"],
entry["offset"],
)
)
payload = header + table + b"".join(entry["data"] for entry in entries)
output.write_bytes(payload)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("output", type=Path)
parser.add_argument("inputs", nargs="+", type=Path)
args = parser.parse_args()
if not args.inputs:
raise SystemExit("Provide at least one PNG input.")
build_icon(args.inputs, args.output)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,56 @@
import { spawn } from "node:child_process"
import { fileURLToPath } from "node:url"
import { dirname, resolve } from "node:path"
import { existsSync } from "node:fs"
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const appRoot = resolve(__dirname, "..")
const pathKey = process.platform === "win32" ? "Path" : "PATH"
const currentPath = process.env[pathKey] ?? process.env[pathKey.toUpperCase()] ?? ""
const separator = process.platform === "win32" ? ";" : ":"
const stubDir = resolve(__dirname)
process.env[pathKey] = [stubDir, currentPath].filter(Boolean).join(separator)
if (pathKey !== "PATH") {
process.env.PATH = process.env[pathKey]
}
if (!process.env.TAURI_BUNDLE_TARGETS) {
if (process.platform === "linux") {
process.env.TAURI_BUNDLE_TARGETS = "deb rpm"
} else if (process.platform === "win32") {
process.env.TAURI_BUNDLE_TARGETS = "nsis"
}
}
// Assinatura: fallback seguro para builds locais/CI. Em prod, pode sobrescrever por env.
if (!process.env.TAURI_SIGNING_PRIVATE_KEY) {
process.env.TAURI_SIGNING_PRIVATE_KEY =
"dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5WkhWOUtzd1BvV0ZlSjEvNzYwaHYxdEloNnV4cmZlNGhha1BNbmNtZEkrZ0FBQkFBQUFBQUFBQUFBQUlBQUFBQS9JbCtsd3VFbHN4empFRUNiU0dva1hKK3ZYUzE2S1V6Q1FhYkRUWGtGMTBkUmJodi9PaXVub3hEMisyTXJoYU5UeEdwZU9aMklacG9ualNWR1NaTm1PMVBpVXYrNTltZU1YOFdwYzdkOHd2STFTc0x4ZktpNXFENnFTdW0xNzY3WC9EcGlIRGFmK2c9Cg=="
}
if (!process.env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD) {
process.env.TAURI_SIGNING_PRIVATE_KEY_PASSWORD = "revertech"
}
const winTauriPath = resolve(appRoot, "node_modules", ".bin", "tauri.cmd")
const usingWinTauri = process.platform === "win32" && existsSync(winTauriPath)
const executable = process.platform === "win32" && usingWinTauri ? "cmd.exe" : "tauri"
const args =
process.platform === "win32" && usingWinTauri
? ["/C", winTauriPath, ...process.argv.slice(2)]
: process.argv.slice(2)
const child = spawn(executable, args, {
stdio: "inherit",
shell: false,
cwd: appRoot,
})
child.on("exit", (code, signal) => {
if (signal) {
process.kill(process.pid, signal)
} else {
process.exit(code ?? 0)
}
})

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
# Minimal stub to satisfy tools that expect xdg-open during bundling.
# Fails silently when the real binary is unavailable.
if command -v xdg-open >/dev/null 2>&1; then
exec xdg-open "$@"
else
exit 0
fi

1931
apps/desktop/service/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,70 @@
[package]
name = "raven-service"
version = "0.1.0"
description = "Raven Windows Service - Executa operacoes privilegiadas para o Raven Desktop"
authors = ["Esdras Renan"]
edition = "2021"
[[bin]]
name = "raven-service"
path = "src/main.rs"
[dependencies]
# Windows Service
windows-service = "0.7"
# Async runtime
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "io-util", "net", "signal"] }
# IPC via Named Pipes
interprocess = { version = "2", features = ["tokio"] }
# Serialization
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# Logging
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# Windows Registry
winreg = "0.55"
# Error handling
thiserror = "1.0"
# HTTP client (para RustDesk)
reqwest = { version = "0.12", features = ["json", "rustls-tls", "blocking"], default-features = false }
# Date/time
chrono = { version = "0.4", features = ["serde"] }
# Crypto (para RustDesk ID)
sha2 = "0.10"
# UUID para request IDs
uuid = { version = "1", features = ["v4"] }
# Parking lot para locks
parking_lot = "0.12"
# Once cell para singletons
once_cell = "1.19"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.58", features = [
"Win32_Foundation",
"Win32_Security",
"Win32_System_Services",
"Win32_System_Threading",
"Win32_System_Pipes",
"Win32_System_IO",
"Win32_System_SystemServices",
"Win32_Storage_FileSystem",
] }
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
strip = true

View file

@ -0,0 +1,290 @@
//! Modulo IPC - Servidor de Named Pipes
//!
//! Implementa comunicacao entre o Raven UI e o Raven Service
//! usando Named Pipes do Windows com protocolo JSON-RPC simplificado.
use crate::{rustdesk, usb_policy};
use serde::{Deserialize, Serialize};
use std::io::{BufRead, BufReader, Write};
use thiserror::Error;
use tracing::{debug, info, warn};
#[derive(Debug, Error)]
pub enum IpcError {
#[error("Erro de IO: {0}")]
Io(#[from] std::io::Error),
#[error("Erro de serializacao: {0}")]
Json(#[from] serde_json::Error),
}
/// Requisicao JSON-RPC simplificada
#[derive(Debug, Deserialize)]
pub struct Request {
pub id: String,
pub method: String,
#[serde(default)]
pub params: serde_json::Value,
}
/// Resposta JSON-RPC simplificada
#[derive(Debug, Serialize)]
pub struct Response {
pub id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<ErrorResponse>,
}
#[derive(Debug, Serialize)]
pub struct ErrorResponse {
pub code: i32,
pub message: String,
}
impl Response {
pub fn success(id: String, result: serde_json::Value) -> Self {
Self {
id,
result: Some(result),
error: None,
}
}
pub fn error(id: String, code: i32, message: String) -> Self {
Self {
id,
result: None,
error: Some(ErrorResponse { code, message }),
}
}
}
/// Inicia o servidor de Named Pipes
pub async fn run_server(pipe_name: &str) -> Result<(), IpcError> {
info!("Iniciando servidor IPC em: {}", pipe_name);
loop {
match accept_connection(pipe_name).await {
Ok(()) => {
debug!("Conexao processada com sucesso");
}
Err(e) => {
warn!("Erro ao processar conexao: {}", e);
}
}
}
}
/// Aceita uma conexao e processa requisicoes
async fn accept_connection(pipe_name: &str) -> Result<(), IpcError> {
use windows::Win32::Foundation::INVALID_HANDLE_VALUE;
use windows::Win32::Security::{
InitializeSecurityDescriptor, SetSecurityDescriptorDacl,
PSECURITY_DESCRIPTOR, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR,
};
use windows::Win32::Storage::FileSystem::PIPE_ACCESS_DUPLEX;
use windows::Win32::System::Pipes::{
ConnectNamedPipe, CreateNamedPipeW, DisconnectNamedPipe,
PIPE_READMODE_MESSAGE, PIPE_TYPE_MESSAGE, PIPE_UNLIMITED_INSTANCES, PIPE_WAIT,
};
use windows::Win32::System::SystemServices::SECURITY_DESCRIPTOR_REVISION;
use windows::core::PCWSTR;
// Cria o named pipe com seguranca que permite acesso a todos os usuarios
let pipe_name_wide: Vec<u16> = pipe_name.encode_utf16().chain(std::iter::once(0)).collect();
// Cria security descriptor com DACL nulo (permite acesso a todos)
let mut sd = SECURITY_DESCRIPTOR::default();
unsafe {
let sd_ptr = PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _);
let _ = InitializeSecurityDescriptor(sd_ptr, SECURITY_DESCRIPTOR_REVISION);
// DACL nulo = acesso irrestrito
let _ = SetSecurityDescriptorDacl(sd_ptr, true, None, false);
}
let sa = SECURITY_ATTRIBUTES {
nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
lpSecurityDescriptor: &mut sd as *mut _ as *mut _,
bInheritHandle: false.into(),
};
let pipe_handle = unsafe {
CreateNamedPipeW(
PCWSTR::from_raw(pipe_name_wide.as_ptr()),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
4096, // out buffer
4096, // in buffer
0, // default timeout
Some(&sa), // seguranca permissiva
)
};
// Verifica se o handle e valido
if pipe_handle == INVALID_HANDLE_VALUE {
return Err(IpcError::Io(std::io::Error::last_os_error()));
}
// Aguarda conexao de um cliente
info!("Aguardando conexao de cliente...");
let connect_result = unsafe {
ConnectNamedPipe(pipe_handle, None)
};
if let Err(e) = connect_result {
// ERROR_PIPE_CONNECTED (535) significa que o cliente ja estava conectado
// o que e aceitavel
let error_code = e.code().0 as u32;
if error_code != 535 {
warn!("Erro ao aguardar conexao: {:?}", e);
}
}
info!("Cliente conectado");
// Processa requisicoes do cliente
let result = process_client(pipe_handle);
// Desconecta o cliente
unsafe {
let _ = DisconnectNamedPipe(pipe_handle);
}
result
}
/// Processa requisicoes de um cliente conectado
fn process_client(pipe_handle: windows::Win32::Foundation::HANDLE) -> Result<(), IpcError> {
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::fs::File;
// Cria File handle a partir do pipe
let raw_handle = pipe_handle.0 as RawHandle;
let file = unsafe { File::from_raw_handle(raw_handle) };
let reader = BufReader::new(file.try_clone()?);
let mut writer = file;
// Le linhas (cada linha e uma requisicao JSON)
for line in reader.lines() {
let line = match line {
Ok(l) => l,
Err(e) => {
if e.kind() == std::io::ErrorKind::BrokenPipe {
info!("Cliente desconectou");
break;
}
return Err(e.into());
}
};
if line.is_empty() {
continue;
}
debug!("Requisicao recebida: {}", line);
// Parse da requisicao
let response = match serde_json::from_str::<Request>(&line) {
Ok(request) => handle_request(request),
Err(e) => Response::error(
"unknown".to_string(),
-32700,
format!("Parse error: {}", e),
),
};
// Serializa e envia resposta
let response_json = serde_json::to_string(&response)?;
debug!("Resposta: {}", response_json);
writeln!(writer, "{}", response_json)?;
writer.flush()?;
}
// IMPORTANTE: Nao fechar o handle aqui, pois DisconnectNamedPipe precisa dele
std::mem::forget(writer);
Ok(())
}
/// Processa uma requisicao e retorna a resposta
fn handle_request(request: Request) -> Response {
info!("Processando metodo: {}", request.method);
match request.method.as_str() {
"health_check" => handle_health_check(request.id),
"apply_usb_policy" => handle_apply_usb_policy(request.id, request.params),
"get_usb_policy" => handle_get_usb_policy(request.id),
"provision_rustdesk" => handle_provision_rustdesk(request.id, request.params),
"get_rustdesk_status" => handle_get_rustdesk_status(request.id),
_ => Response::error(
request.id,
-32601,
format!("Metodo nao encontrado: {}", request.method),
),
}
}
// =============================================================================
// Handlers de Requisicoes
// =============================================================================
fn handle_health_check(id: String) -> Response {
Response::success(
id,
serde_json::json!({
"status": "ok",
"service": "RavenService",
"version": env!("CARGO_PKG_VERSION"),
"timestamp": chrono::Utc::now().timestamp_millis()
}),
)
}
fn handle_apply_usb_policy(id: String, params: serde_json::Value) -> Response {
let policy = match params.get("policy").and_then(|p| p.as_str()) {
Some(p) => p,
None => {
return Response::error(id, -32602, "Parametro 'policy' e obrigatorio".to_string())
}
};
match usb_policy::apply_policy(policy) {
Ok(result) => Response::success(id, serde_json::to_value(result).unwrap()),
Err(e) => Response::error(id, -32000, format!("Erro ao aplicar politica: {}", e)),
}
}
fn handle_get_usb_policy(id: String) -> Response {
match usb_policy::get_current_policy() {
Ok(policy) => Response::success(
id,
serde_json::json!({
"policy": policy
}),
),
Err(e) => Response::error(id, -32000, format!("Erro ao obter politica: {}", e)),
}
}
fn handle_provision_rustdesk(id: String, params: serde_json::Value) -> Response {
let config_string = params.get("config").and_then(|c| c.as_str()).map(String::from);
let password = params.get("password").and_then(|p| p.as_str()).map(String::from);
let machine_id = params.get("machineId").and_then(|m| m.as_str()).map(String::from);
match rustdesk::ensure_rustdesk(config_string.as_deref(), password.as_deref(), machine_id.as_deref()) {
Ok(result) => Response::success(id, serde_json::to_value(result).unwrap()),
Err(e) => Response::error(id, -32000, format!("Erro ao provisionar RustDesk: {}", e)),
}
}
fn handle_get_rustdesk_status(id: String) -> Response {
match rustdesk::get_status() {
Ok(status) => Response::success(id, serde_json::to_value(status).unwrap()),
Err(e) => Response::error(id, -32000, format!("Erro ao obter status: {}", e)),
}
}

View file

@ -0,0 +1,268 @@
//! Raven Service - Servico Windows para operacoes privilegiadas
//!
//! Este servico roda como LocalSystem e executa operacoes que requerem
//! privilegios de administrador, como:
//! - Aplicar politicas de USB
//! - Provisionar e configurar RustDesk
//! - Modificar chaves de registro em HKEY_LOCAL_MACHINE
//!
//! O app Raven UI comunica com este servico via Named Pipes.
mod ipc;
mod rustdesk;
mod usb_policy;
use std::ffi::OsString;
use std::time::Duration;
use tracing::{error, info};
use windows_service::{
define_windows_service,
service::{
ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,
ServiceType,
},
service_control_handler::{self, ServiceControlHandlerResult},
service_dispatcher,
};
const SERVICE_NAME: &str = "RavenService";
const SERVICE_DISPLAY_NAME: &str = "Raven Desktop Service";
const SERVICE_DESCRIPTION: &str = "Servico do Raven Desktop para operacoes privilegiadas (USB, RustDesk)";
const PIPE_NAME: &str = r"\\.\pipe\RavenService";
define_windows_service!(ffi_service_main, service_main);
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Configura logging
init_logging();
// Verifica argumentos de linha de comando
let args: Vec<String> = std::env::args().collect();
if args.len() > 1 {
match args[1].as_str() {
"install" => {
install_service()?;
return Ok(());
}
"uninstall" => {
uninstall_service()?;
return Ok(());
}
"run" => {
// Modo de teste: roda sem registrar como servico
info!("Executando em modo de teste (nao como servico)");
run_standalone()?;
return Ok(());
}
_ => {}
}
}
// Inicia como servico Windows
info!("Iniciando Raven Service...");
service_dispatcher::start(SERVICE_NAME, ffi_service_main)?;
Ok(())
}
fn init_logging() {
use tracing_subscriber::{fmt, prelude::*, EnvFilter};
// Tenta criar diretorio de logs
let log_dir = std::env::var("PROGRAMDATA")
.map(|p| std::path::PathBuf::from(p).join("RavenService").join("logs"))
.unwrap_or_else(|_| std::path::PathBuf::from("C:\\ProgramData\\RavenService\\logs"));
let _ = std::fs::create_dir_all(&log_dir);
// Arquivo de log
let log_file = log_dir.join("service.log");
let file = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(&log_file)
.ok();
let filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info"));
if let Some(file) = file {
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer().with_writer(file).with_ansi(false))
.init();
} else {
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer())
.init();
}
}
fn service_main(arguments: Vec<OsString>) {
if let Err(e) = run_service(arguments) {
error!("Erro ao executar servico: {}", e);
}
}
fn run_service(_arguments: Vec<OsString>) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
info!("Servico iniciando...");
// Canal para shutdown
let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>();
let shutdown_tx = std::sync::Arc::new(std::sync::Mutex::new(Some(shutdown_tx)));
// Registra handler de controle do servico
let shutdown_tx_clone = shutdown_tx.clone();
let status_handle = service_control_handler::register(SERVICE_NAME, move |control| {
match control {
ServiceControl::Stop | ServiceControl::Shutdown => {
info!("Recebido comando de parada");
if let Ok(mut guard) = shutdown_tx_clone.lock() {
if let Some(tx) = guard.take() {
let _ = tx.send(());
}
}
ServiceControlHandlerResult::NoError
}
ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,
_ => ServiceControlHandlerResult::NotImplemented,
}
})?;
// Atualiza status para Running
status_handle.set_service_status(ServiceStatus {
service_type: ServiceType::OWN_PROCESS,
current_state: ServiceState::Running,
controls_accepted: ServiceControlAccept::STOP | ServiceControlAccept::SHUTDOWN,
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})?;
info!("Servico em execucao, aguardando conexoes...");
// Cria runtime Tokio
let runtime = tokio::runtime::Runtime::new()?;
// Executa servidor IPC
runtime.block_on(async {
tokio::select! {
result = ipc::run_server(PIPE_NAME) => {
if let Err(e) = result {
error!("Erro no servidor IPC: {}", e);
}
}
_ = async {
let _ = shutdown_rx.await;
} => {
info!("Shutdown solicitado");
}
}
});
// Atualiza status para Stopped
status_handle.set_service_status(ServiceStatus {
service_type: ServiceType::OWN_PROCESS,
current_state: ServiceState::Stopped,
controls_accepted: ServiceControlAccept::empty(),
exit_code: ServiceExitCode::Win32(0),
checkpoint: 0,
wait_hint: Duration::default(),
process_id: None,
})?;
info!("Servico parado");
Ok(())
}
fn run_standalone() -> Result<(), Box<dyn std::error::Error>> {
let runtime = tokio::runtime::Runtime::new()?;
runtime.block_on(async {
info!("Servidor IPC iniciando em modo standalone...");
tokio::select! {
result = ipc::run_server(PIPE_NAME) => {
if let Err(e) = result {
error!("Erro no servidor IPC: {}", e);
}
}
_ = tokio::signal::ctrl_c() => {
info!("Ctrl+C recebido, encerrando...");
}
}
});
Ok(())
}
fn install_service() -> Result<(), Box<dyn std::error::Error>> {
use windows_service::{
service::{ServiceAccess, ServiceErrorControl, ServiceInfo, ServiceStartType},
service_manager::{ServiceManager, ServiceManagerAccess},
};
info!("Instalando servico...");
let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CREATE_SERVICE)?;
let exe_path = std::env::current_exe()?;
let service_info = ServiceInfo {
name: OsString::from(SERVICE_NAME),
display_name: OsString::from(SERVICE_DISPLAY_NAME),
service_type: ServiceType::OWN_PROCESS,
start_type: ServiceStartType::AutoStart,
error_control: ServiceErrorControl::Normal,
executable_path: exe_path,
launch_arguments: vec![],
dependencies: vec![],
account_name: None, // LocalSystem
account_password: None,
};
let service = manager.create_service(&service_info, ServiceAccess::CHANGE_CONFIG)?;
// Define descricao
service.set_description(SERVICE_DESCRIPTION)?;
info!("Servico instalado com sucesso: {}", SERVICE_NAME);
println!("Servico '{}' instalado com sucesso!", SERVICE_DISPLAY_NAME);
println!("Para iniciar: sc start {}", SERVICE_NAME);
Ok(())
}
fn uninstall_service() -> Result<(), Box<dyn std::error::Error>> {
use windows_service::{
service::ServiceAccess,
service_manager::{ServiceManager, ServiceManagerAccess},
};
info!("Desinstalando servico...");
let manager = ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT)?;
let service = manager.open_service(
SERVICE_NAME,
ServiceAccess::STOP | ServiceAccess::DELETE | ServiceAccess::QUERY_STATUS,
)?;
// Tenta parar o servico primeiro
let status = service.query_status()?;
if status.current_state != ServiceState::Stopped {
info!("Parando servico...");
let _ = service.stop();
std::thread::sleep(Duration::from_secs(2));
}
// Remove o servico
service.delete()?;
info!("Servico desinstalado com sucesso");
println!("Servico '{}' removido com sucesso!", SERVICE_DISPLAY_NAME);
Ok(())
}

View file

@ -0,0 +1,846 @@
//! Modulo RustDesk - Provisionamento e gerenciamento do RustDesk
//!
//! Gerencia a instalacao, configuracao e provisionamento do RustDesk.
//! Como o servico roda como LocalSystem, nao precisa de elevacao.
use chrono::Utc;
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use reqwest::blocking::Client;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::env;
use std::ffi::OsStr;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::os::windows::process::CommandExt;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
use std::thread;
use std::time::Duration;
use thiserror::Error;
use tracing::{error, info, warn};
const RELEASES_API: &str = "https://api.github.com/repos/rustdesk/rustdesk/releases/latest";
const USER_AGENT: &str = "RavenService/1.0";
const SERVER_HOST: &str = "rust.rever.com.br";
const SERVER_KEY: &str = "0mxocQKmK6GvTZQYKgjrG9tlNkKOqf81gKgqwAmnZuI=";
const DEFAULT_PASSWORD: &str = "FMQ9MA>e73r.FI<b*34Vmx_8P";
const SERVICE_NAME: &str = "RustDesk";
const CACHE_DIR_NAME: &str = "Rever\\RustDeskCache";
const LOCAL_SERVICE_CONFIG: &str = r"C:\Windows\ServiceProfiles\LocalService\AppData\Roaming\RustDesk\config";
const LOCAL_SYSTEM_CONFIG: &str = r"C:\Windows\System32\config\systemprofile\AppData\Roaming\RustDesk\config";
const SECURITY_VERIFICATION_VALUE: &str = "use-permanent-password";
const SECURITY_APPROVE_MODE_VALUE: &str = "password";
const CREATE_NO_WINDOW: u32 = 0x08000000;
static PROVISION_MUTEX: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
#[derive(Debug, Error)]
pub enum RustdeskError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("I/O error: {0}")]
Io(#[from] io::Error),
#[error("Release asset nao encontrado para Windows x86_64")]
AssetMissing,
#[error("Falha ao executar comando {command}: status {status:?}")]
CommandFailed { command: String, status: Option<i32> },
#[error("Falha ao detectar ID do RustDesk")]
MissingId,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RustdeskResult {
pub id: String,
pub password: String,
pub installed_version: Option<String>,
pub updated: bool,
pub last_provisioned_at: i64,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RustdeskStatus {
pub installed: bool,
pub running: bool,
pub id: Option<String>,
pub version: Option<String>,
}
#[derive(Debug, Deserialize)]
struct ReleaseAsset {
name: String,
browser_download_url: String,
}
#[derive(Debug, Deserialize)]
struct ReleaseResponse {
tag_name: String,
assets: Vec<ReleaseAsset>,
}
/// Provisiona o RustDesk
pub fn ensure_rustdesk(
config_string: Option<&str>,
password_override: Option<&str>,
machine_id: Option<&str>,
) -> Result<RustdeskResult, RustdeskError> {
let _guard = PROVISION_MUTEX.lock();
info!("Iniciando provisionamento do RustDesk");
// Prepara ACLs dos diretorios de servico
if let Err(e) = ensure_service_profiles_writable() {
warn!("Aviso ao preparar ACL: {}", e);
}
// Le ID existente antes de qualquer limpeza
let preserved_remote_id = read_remote_id_from_profiles();
if let Some(ref id) = preserved_remote_id {
info!("ID existente preservado: {}", id);
}
let exe_path = detect_executable_path();
let (installed_version, freshly_installed) = ensure_installed(&exe_path)?;
info!(
"RustDesk {}: {}",
if freshly_installed { "instalado" } else { "ja presente" },
exe_path.display()
);
// Para processos existentes
let _ = stop_rustdesk_processes();
// Limpa perfis apenas se instalacao fresca
if freshly_installed {
let _ = purge_existing_rustdesk_profiles();
}
// Aplica configuracao
if let Some(config) = config_string.filter(|c| !c.trim().is_empty()) {
if let Err(e) = run_with_args(&exe_path, &["--config", config]) {
warn!("Falha ao aplicar config inline: {}", e);
}
} else {
let config_path = write_config_files()?;
if let Err(e) = apply_config(&exe_path, &config_path) {
warn!("Falha ao aplicar config via CLI: {}", e);
}
}
// Define senha
let password = password_override
.map(|v| v.trim().to_string())
.filter(|v| !v.is_empty())
.unwrap_or_else(|| DEFAULT_PASSWORD.to_string());
if let Err(e) = set_password(&exe_path, &password) {
warn!("Falha ao definir senha: {}", e);
} else {
let _ = ensure_password_files(&password);
let _ = propagate_password_profile();
}
// Define ID customizado
let custom_id = if let Some(ref existing_id) = preserved_remote_id {
if !freshly_installed {
Some(existing_id.clone())
} else {
define_custom_id(&exe_path, machine_id)
}
} else {
define_custom_id(&exe_path, machine_id)
};
// Inicia servico
if let Err(e) = ensure_service_running(&exe_path) {
warn!("Falha ao iniciar servico: {}", e);
}
// Obtem ID final
let final_id = match query_id_with_retries(&exe_path, 5) {
Ok(id) => id,
Err(_) => {
read_remote_id_from_profiles()
.or_else(|| custom_id.clone())
.ok_or(RustdeskError::MissingId)?
}
};
// Garante ID em todos os arquivos
ensure_remote_id_files(&final_id);
let version = query_version(&exe_path).ok().or(installed_version);
let last_provisioned_at = Utc::now().timestamp_millis();
info!("Provisionamento concluido. ID: {}, Versao: {:?}", final_id, version);
Ok(RustdeskResult {
id: final_id,
password,
installed_version: version,
updated: freshly_installed,
last_provisioned_at,
})
}
/// Retorna status do RustDesk
pub fn get_status() -> Result<RustdeskStatus, RustdeskError> {
let exe_path = detect_executable_path();
let installed = exe_path.exists();
let running = if installed {
query_service_state().map(|s| s == "running").unwrap_or(false)
} else {
false
};
let id = if installed {
query_id(&exe_path).ok().or_else(read_remote_id_from_profiles)
} else {
None
};
let version = if installed {
query_version(&exe_path).ok()
} else {
None
};
Ok(RustdeskStatus {
installed,
running,
id,
version,
})
}
// =============================================================================
// Funcoes Auxiliares
// =============================================================================
fn detect_executable_path() -> PathBuf {
let program_files = env::var("PROGRAMFILES").unwrap_or_else(|_| "C:/Program Files".to_string());
Path::new(&program_files).join("RustDesk").join("rustdesk.exe")
}
fn ensure_installed(exe_path: &Path) -> Result<(Option<String>, bool), RustdeskError> {
if exe_path.exists() {
return Ok((None, false));
}
let cache_root = PathBuf::from(env::var("PROGRAMDATA").unwrap_or_else(|_| "C:/ProgramData".to_string()))
.join(CACHE_DIR_NAME);
fs::create_dir_all(&cache_root)?;
let (installer_path, version_tag) = download_latest_installer(&cache_root)?;
run_installer(&installer_path)?;
thread::sleep(Duration::from_secs(20));
Ok((Some(version_tag), true))
}
fn download_latest_installer(cache_root: &Path) -> Result<(PathBuf, String), RustdeskError> {
let client = Client::builder()
.user_agent(USER_AGENT)
.timeout(Duration::from_secs(60))
.build()?;
let release: ReleaseResponse = client.get(RELEASES_API).send()?.error_for_status()?.json()?;
let asset = release
.assets
.iter()
.find(|a| a.name.ends_with("x86_64.exe"))
.ok_or(RustdeskError::AssetMissing)?;
let target_path = cache_root.join(&asset.name);
if target_path.exists() {
return Ok((target_path, release.tag_name));
}
info!("Baixando RustDesk: {}", asset.name);
let mut response = client.get(&asset.browser_download_url).send()?.error_for_status()?;
let mut output = File::create(&target_path)?;
response.copy_to(&mut output)?;
Ok((target_path, release.tag_name))
}
fn run_installer(installer_path: &Path) -> Result<(), RustdeskError> {
let status = hidden_command(installer_path)
.arg("--silent-install")
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if !status.success() {
return Err(RustdeskError::CommandFailed {
command: format!("{} --silent-install", installer_path.display()),
status: status.code(),
});
}
Ok(())
}
fn program_data_config_dir() -> PathBuf {
PathBuf::from(env::var("PROGRAMDATA").unwrap_or_else(|_| "C:/ProgramData".to_string()))
.join("RustDesk")
.join("config")
}
/// Retorna todos os diretorios AppData\Roaming\RustDesk\config de usuarios do sistema
/// Como o servico roda como LocalSystem, precisamos enumerar os profiles de usuarios
fn all_user_appdata_config_dirs() -> Vec<PathBuf> {
let mut dirs = Vec::new();
// Enumera C:\Users\*\AppData\Roaming\RustDesk\config
let users_dir = Path::new("C:\\Users");
if let Ok(entries) = fs::read_dir(users_dir) {
for entry in entries.flatten() {
let path = entry.path();
// Ignora pastas de sistema
let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if name == "Public" || name == "Default" || name == "Default User" || name == "All Users" {
continue;
}
let rustdesk_config = path.join("AppData").join("Roaming").join("RustDesk").join("config");
// Verifica se o diretorio pai existe (usuario real)
if path.join("AppData").join("Roaming").exists() {
dirs.push(rustdesk_config);
}
}
}
// Tambem tenta o APPDATA do ambiente (pode ser util em alguns casos)
if let Ok(appdata) = env::var("APPDATA") {
let path = Path::new(&appdata).join("RustDesk").join("config");
if !dirs.contains(&path) {
dirs.push(path);
}
}
dirs
}
fn service_profile_dirs() -> Vec<PathBuf> {
vec![
PathBuf::from(LOCAL_SERVICE_CONFIG),
PathBuf::from(LOCAL_SYSTEM_CONFIG),
]
}
fn remote_id_directories() -> Vec<PathBuf> {
let mut dirs = Vec::new();
dirs.push(program_data_config_dir());
dirs.extend(service_profile_dirs());
dirs.extend(all_user_appdata_config_dirs());
dirs
}
fn write_config_files() -> Result<PathBuf, RustdeskError> {
let config_contents = format!(
r#"[options]
key = "{key}"
relay-server = "{host}"
custom-rendezvous-server = "{host}"
api-server = "https://{host}"
verification-method = "{verification}"
approve-mode = "{approve}"
"#,
host = SERVER_HOST,
key = SERVER_KEY,
verification = SECURITY_VERIFICATION_VALUE,
approve = SECURITY_APPROVE_MODE_VALUE,
);
let main_path = program_data_config_dir().join("RustDesk2.toml");
write_file(&main_path, &config_contents)?;
for service_dir in service_profile_dirs() {
let service_profile = service_dir.join("RustDesk2.toml");
let _ = write_file(&service_profile, &config_contents);
}
Ok(main_path)
}
fn write_file(path: &Path, contents: &str) -> Result<(), io::Error> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)?;
file.write_all(contents.as_bytes())
}
fn apply_config(exe_path: &Path, config_path: &Path) -> Result<(), RustdeskError> {
run_with_args(exe_path, &["--import-config", &config_path.to_string_lossy()])
}
fn set_password(exe_path: &Path, secret: &str) -> Result<(), RustdeskError> {
run_with_args(exe_path, &["--password", secret])
}
fn define_custom_id(exe_path: &Path, machine_id: Option<&str>) -> Option<String> {
let value = machine_id.and_then(|raw| {
let trimmed = raw.trim();
if trimmed.is_empty() { None } else { Some(trimmed) }
})?;
let custom_id = derive_numeric_id(value);
if run_with_args(exe_path, &["--set-id", &custom_id]).is_ok() {
info!("ID deterministico definido: {}", custom_id);
Some(custom_id)
} else {
None
}
}
fn derive_numeric_id(machine_id: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(machine_id.as_bytes());
let hash = hasher.finalize();
let mut bytes = [0u8; 8];
bytes.copy_from_slice(&hash[..8]);
let value = u64::from_le_bytes(bytes);
let num = (value % 900_000_000) + 100_000_000;
format!("{:09}", num)
}
fn ensure_service_running(exe_path: &Path) -> Result<(), RustdeskError> {
ensure_service_installed(exe_path)?;
let _ = run_sc(&["config", SERVICE_NAME, "start=", "auto"]);
let _ = run_sc(&["start", SERVICE_NAME]);
remove_rustdesk_autorun_artifacts();
Ok(())
}
fn ensure_service_installed(exe_path: &Path) -> Result<(), RustdeskError> {
if run_sc(&["query", SERVICE_NAME]).is_ok() {
return Ok(());
}
run_with_args(exe_path, &["--install-service"])
}
fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
let _ = run_sc(&["stop", SERVICE_NAME]);
thread::sleep(Duration::from_secs(2));
let status = hidden_command("taskkill")
.args(["/F", "/T", "/IM", "rustdesk.exe"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if status.success() || matches!(status.code(), Some(128)) {
Ok(())
} else {
Err(RustdeskError::CommandFailed {
command: "taskkill".into(),
status: status.code(),
})
}
}
fn purge_existing_rustdesk_profiles() -> Result<(), String> {
let files = [
"RustDesk.toml",
"RustDesk_local.toml",
"RustDesk2.toml",
"password",
"passwd",
"passwd.txt",
];
for dir in remote_id_directories() {
if !dir.exists() {
continue;
}
for name in files {
let path = dir.join(name);
if path.exists() {
let _ = fs::remove_file(&path);
}
}
}
Ok(())
}
fn ensure_password_files(secret: &str) -> Result<(), String> {
for dir in remote_id_directories() {
let password_path = dir.join("RustDesk.toml");
let _ = write_toml_kv(&password_path, "password", secret);
let local_path = dir.join("RustDesk_local.toml");
let _ = write_toml_kv(&local_path, "verification-method", SECURITY_VERIFICATION_VALUE);
let _ = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE);
}
Ok(())
}
fn propagate_password_profile() -> io::Result<bool> {
// Encontra um diretorio de usuario que tenha arquivos de config
let user_dirs = all_user_appdata_config_dirs();
let src_dir = user_dirs.iter().find(|d| d.join("RustDesk.toml").exists());
let Some(src_dir) = src_dir else {
// Se nenhum usuario tem config, usa ProgramData como fonte
let pd = program_data_config_dir();
if !pd.join("RustDesk.toml").exists() {
return Ok(false);
}
return propagate_from_dir(&pd);
};
propagate_from_dir(src_dir)
}
fn propagate_from_dir(src_dir: &Path) -> io::Result<bool> {
let propagation_files = ["RustDesk.toml", "RustDesk_local.toml", "RustDesk2.toml"];
let mut propagated = false;
for filename in propagation_files {
let src_path = src_dir.join(filename);
if !src_path.exists() {
continue;
}
for dest_root in remote_id_directories() {
if dest_root == src_dir {
continue; // Nao copiar para si mesmo
}
let target_path = dest_root.join(filename);
if copy_overwrite(&src_path, &target_path).is_ok() {
propagated = true;
}
}
}
Ok(propagated)
}
fn ensure_remote_id_files(id: &str) {
for dir in remote_id_directories() {
let path = dir.join("RustDesk_local.toml");
let _ = write_remote_id_value(&path, id);
}
}
fn write_remote_id_value(path: &Path, id: &str) -> io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let replacement = format!("remote_id = '{}'\n", id);
if let Ok(existing) = fs::read_to_string(path) {
let mut replaced = false;
let mut buffer = String::with_capacity(existing.len() + replacement.len());
for line in existing.lines() {
if line.trim_start().starts_with("remote_id") {
buffer.push_str(&replacement);
replaced = true;
} else {
buffer.push_str(line);
buffer.push('\n');
}
}
if !replaced {
buffer.push_str(&replacement);
}
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)?;
file.write_all(buffer.as_bytes())
} else {
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)?;
file.write_all(replacement.as_bytes())
}
}
fn write_toml_kv(path: &Path, key: &str, value: &str) -> io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let sanitized = value.replace('\\', "\\\\").replace('"', "\\\"");
let replacement = format!("{key} = \"{sanitized}\"\n");
let existing = fs::read_to_string(path).unwrap_or_default();
let mut replaced = false;
let mut buffer = String::with_capacity(existing.len() + replacement.len());
for line in existing.lines() {
let trimmed = line.trim_start();
if trimmed.starts_with(&format!("{key} ")) || trimmed.starts_with(&format!("{key}=")) {
buffer.push_str(&replacement);
replaced = true;
} else {
buffer.push_str(line);
buffer.push('\n');
}
}
if !replaced {
buffer.push_str(&replacement);
}
let mut file = OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(path)?;
file.write_all(buffer.as_bytes())
}
fn read_remote_id_from_profiles() -> Option<String> {
for dir in remote_id_directories() {
for candidate in [dir.join("RustDesk_local.toml"), dir.join("RustDesk.toml")] {
if let Some(id) = read_remote_id_file(&candidate) {
if !id.is_empty() {
return Some(id);
}
}
}
}
None
}
fn read_remote_id_file(path: &Path) -> Option<String> {
let content = fs::read_to_string(path).ok()?;
for line in content.lines() {
if let Some(value) = parse_assignment(line, "remote_id") {
return Some(value);
}
}
None
}
fn parse_assignment(line: &str, key: &str) -> Option<String> {
let trimmed = line.trim();
if !trimmed.starts_with(key) {
return None;
}
let (_, rhs) = trimmed.split_once('=')?;
let value = rhs.trim().trim_matches(|c| c == '\'' || c == '"');
if value.is_empty() {
None
} else {
Some(value.to_string())
}
}
fn query_id_with_retries(exe_path: &Path, attempts: usize) -> Result<String, RustdeskError> {
for attempt in 0..attempts {
match query_id(exe_path) {
Ok(value) if !value.trim().is_empty() => return Ok(value),
_ => {}
}
if attempt + 1 < attempts {
thread::sleep(Duration::from_millis(800));
}
}
Err(RustdeskError::MissingId)
}
fn query_id(exe_path: &Path) -> Result<String, RustdeskError> {
let output = hidden_command(exe_path).arg("--get-id").output()?;
if !output.status.success() {
return Err(RustdeskError::CommandFailed {
command: format!("{} --get-id", exe_path.display()),
status: output.status.code(),
});
}
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
if stdout.is_empty() {
return Err(RustdeskError::MissingId);
}
Ok(stdout)
}
fn query_version(exe_path: &Path) -> Result<String, RustdeskError> {
let output = hidden_command(exe_path).arg("--version").output()?;
if !output.status.success() {
return Err(RustdeskError::CommandFailed {
command: format!("{} --version", exe_path.display()),
status: output.status.code(),
});
}
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
fn query_service_state() -> Option<String> {
let output = hidden_command("sc")
.args(["query", SERVICE_NAME])
.output()
.ok()?;
if !output.status.success() {
return None;
}
let stdout = String::from_utf8_lossy(&output.stdout);
for line in stdout.lines() {
let lower = line.to_lowercase();
if lower.contains("running") {
return Some("running".to_string());
}
if lower.contains("stopped") {
return Some("stopped".to_string());
}
}
None
}
fn run_sc(args: &[&str]) -> Result<(), RustdeskError> {
let status = hidden_command("sc")
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if !status.success() {
return Err(RustdeskError::CommandFailed {
command: format!("sc {}", args.join(" ")),
status: status.code(),
});
}
Ok(())
}
fn run_with_args(exe_path: &Path, args: &[&str]) -> Result<(), RustdeskError> {
let status = hidden_command(exe_path)
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
if !status.success() {
return Err(RustdeskError::CommandFailed {
command: format!("{} {}", exe_path.display(), args.join(" ")),
status: status.code(),
});
}
Ok(())
}
fn remove_rustdesk_autorun_artifacts() {
// Remove atalhos de inicializacao automatica
let mut startup_paths: Vec<PathBuf> = Vec::new();
if let Ok(appdata) = env::var("APPDATA") {
startup_paths.push(
Path::new(&appdata)
.join("Microsoft\\Windows\\Start Menu\\Programs\\Startup\\RustDesk.lnk"),
);
}
startup_paths.push(PathBuf::from(
r"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\RustDesk.lnk",
));
for path in startup_paths {
if path.exists() {
let _ = fs::remove_file(&path);
}
}
// Remove entradas de registro
for hive in ["HKCU", "HKLM"] {
let reg_path = format!(r"{}\Software\Microsoft\Windows\CurrentVersion\Run", hive);
let _ = hidden_command("reg")
.args(["delete", &reg_path, "/v", "RustDesk", "/f"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
}
}
fn ensure_service_profiles_writable() -> Result<(), String> {
for dir in service_profile_dirs() {
if !can_write_dir(&dir) {
fix_profile_acl(&dir)?;
}
}
Ok(())
}
fn can_write_dir(dir: &Path) -> bool {
if fs::create_dir_all(dir).is_err() {
return false;
}
let probe = dir.join(".raven_acl_probe");
match OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.open(&probe)
{
Ok(mut file) => {
if file.write_all(b"ok").is_err() {
let _ = fs::remove_file(&probe);
return false;
}
let _ = fs::remove_file(&probe);
true
}
Err(_) => false,
}
}
fn fix_profile_acl(target: &Path) -> Result<(), String> {
let target_str = target.display().to_string();
// Como ja estamos rodando como LocalSystem, podemos usar takeown/icacls diretamente
let _ = hidden_command("takeown")
.args(["/F", &target_str, "/R", "/D", "Y"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status();
let status = hidden_command("icacls")
.args([
&target_str,
"/grant",
"*S-1-5-32-544:(OI)(CI)F",
"*S-1-5-19:(OI)(CI)F",
"*S-1-5-32-545:(OI)(CI)M",
"/T",
"/C",
"/Q",
])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.map_err(|e| format!("Erro ao executar icacls: {}", e))?;
if status.success() {
Ok(())
} else {
Err(format!("icacls retornou codigo {}", status.code().unwrap_or(-1)))
}
}
fn copy_overwrite(src: &Path, dst: &Path) -> io::Result<()> {
if let Some(parent) = dst.parent() {
fs::create_dir_all(parent)?;
}
if dst.is_dir() {
fs::remove_dir_all(dst)?;
} else if dst.exists() {
fs::remove_file(dst)?;
}
fs::copy(src, dst)?;
Ok(())
}
fn hidden_command(program: impl AsRef<OsStr>) -> Command {
let mut cmd = Command::new(program);
cmd.creation_flags(CREATE_NO_WINDOW);
cmd
}

View file

@ -0,0 +1,259 @@
//! Modulo USB Policy - Controle de dispositivos USB
//!
//! Implementa o controle de armazenamento USB no Windows.
//! Como o servico roda como LocalSystem, nao precisa de elevacao.
use serde::{Deserialize, Serialize};
use std::io;
use thiserror::Error;
use tracing::{error, info, warn};
use winreg::enums::*;
use winreg::RegKey;
// GUID para Removable Storage Devices (Disk)
const REMOVABLE_STORAGE_GUID: &str = "{53f56307-b6bf-11d0-94f2-00a0c91efb8b}";
// Chaves de registro
const REMOVABLE_STORAGE_PATH: &str = r"Software\Policies\Microsoft\Windows\RemovableStorageDevices";
const USBSTOR_PATH: &str = r"SYSTEM\CurrentControlSet\Services\USBSTOR";
const STORAGE_POLICY_PATH: &str = r"SYSTEM\CurrentControlSet\Control\StorageDevicePolicies";
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum UsbPolicy {
Allow,
BlockAll,
Readonly,
}
impl UsbPolicy {
pub fn from_str(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"ALLOW" => Some(Self::Allow),
"BLOCK_ALL" => Some(Self::BlockAll),
"READONLY" => Some(Self::Readonly),
_ => None,
}
}
pub fn as_str(&self) -> &'static str {
match self {
Self::Allow => "ALLOW",
Self::BlockAll => "BLOCK_ALL",
Self::Readonly => "READONLY",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UsbPolicyResult {
pub success: bool,
pub policy: String,
pub error: Option<String>,
pub applied_at: Option<i64>,
}
#[derive(Error, Debug)]
pub enum UsbControlError {
#[error("Politica USB invalida: {0}")]
InvalidPolicy(String),
#[error("Erro de registro do Windows: {0}")]
RegistryError(String),
#[error("Permissao negada")]
PermissionDenied,
#[error("Erro de I/O: {0}")]
Io(#[from] io::Error),
}
/// Aplica uma politica de USB
pub fn apply_policy(policy_str: &str) -> Result<UsbPolicyResult, UsbControlError> {
let policy = UsbPolicy::from_str(policy_str)
.ok_or_else(|| UsbControlError::InvalidPolicy(policy_str.to_string()))?;
let now = chrono::Utc::now().timestamp_millis();
info!("Aplicando politica USB: {:?}", policy);
// 1. Aplicar Removable Storage Policy
apply_removable_storage_policy(policy)?;
// 2. Aplicar USBSTOR
apply_usbstor_policy(policy)?;
// 3. Aplicar WriteProtect se necessario
if policy == UsbPolicy::Readonly {
apply_write_protect(true)?;
} else {
apply_write_protect(false)?;
}
// 4. Atualizar Group Policy (opcional)
if let Err(e) = refresh_group_policy() {
warn!("Falha ao atualizar group policy: {}", e);
}
info!("Politica USB aplicada com sucesso: {:?}", policy);
Ok(UsbPolicyResult {
success: true,
policy: policy.as_str().to_string(),
error: None,
applied_at: Some(now),
})
}
/// Retorna a politica USB atual
pub fn get_current_policy() -> Result<String, UsbControlError> {
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
// Verifica Removable Storage Policy primeiro
let full_path = format!(r"{}\{}", REMOVABLE_STORAGE_PATH, REMOVABLE_STORAGE_GUID);
if let Ok(key) = hklm.open_subkey_with_flags(&full_path, KEY_READ) {
let deny_read: u32 = key.get_value("Deny_Read").unwrap_or(0);
let deny_write: u32 = key.get_value("Deny_Write").unwrap_or(0);
if deny_read == 1 && deny_write == 1 {
return Ok("BLOCK_ALL".to_string());
}
if deny_read == 0 && deny_write == 1 {
return Ok("READONLY".to_string());
}
}
// Verifica USBSTOR como fallback
if let Ok(key) = hklm.open_subkey_with_flags(USBSTOR_PATH, KEY_READ) {
let start: u32 = key.get_value("Start").unwrap_or(3);
if start == 4 {
return Ok("BLOCK_ALL".to_string());
}
}
Ok("ALLOW".to_string())
}
fn apply_removable_storage_policy(policy: UsbPolicy) -> Result<(), UsbControlError> {
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
let full_path = format!(r"{}\{}", REMOVABLE_STORAGE_PATH, REMOVABLE_STORAGE_GUID);
match policy {
UsbPolicy::Allow => {
// Tenta remover as restricoes, se existirem
if let Ok(key) = hklm.open_subkey_with_flags(&full_path, KEY_ALL_ACCESS) {
let _ = key.delete_value("Deny_Read");
let _ = key.delete_value("Deny_Write");
let _ = key.delete_value("Deny_Execute");
}
// Tenta remover a chave inteira se estiver vazia
let _ = hklm.delete_subkey(&full_path);
}
UsbPolicy::BlockAll => {
let (key, _) = hklm
.create_subkey(&full_path)
.map_err(map_winreg_error)?;
key.set_value("Deny_Read", &1u32)
.map_err(map_winreg_error)?;
key.set_value("Deny_Write", &1u32)
.map_err(map_winreg_error)?;
key.set_value("Deny_Execute", &1u32)
.map_err(map_winreg_error)?;
}
UsbPolicy::Readonly => {
let (key, _) = hklm
.create_subkey(&full_path)
.map_err(map_winreg_error)?;
// Permite leitura, bloqueia escrita
key.set_value("Deny_Read", &0u32)
.map_err(map_winreg_error)?;
key.set_value("Deny_Write", &1u32)
.map_err(map_winreg_error)?;
key.set_value("Deny_Execute", &0u32)
.map_err(map_winreg_error)?;
}
}
Ok(())
}
fn apply_usbstor_policy(policy: UsbPolicy) -> Result<(), UsbControlError> {
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
let key = hklm
.open_subkey_with_flags(USBSTOR_PATH, KEY_ALL_ACCESS)
.map_err(map_winreg_error)?;
match policy {
UsbPolicy::Allow => {
// Start = 3 habilita o driver
key.set_value("Start", &3u32)
.map_err(map_winreg_error)?;
}
UsbPolicy::BlockAll => {
// Start = 4 desabilita o driver
key.set_value("Start", &4u32)
.map_err(map_winreg_error)?;
}
UsbPolicy::Readonly => {
// Readonly mantem driver ativo
key.set_value("Start", &3u32)
.map_err(map_winreg_error)?;
}
}
Ok(())
}
fn apply_write_protect(enable: bool) -> Result<(), UsbControlError> {
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
if enable {
let (key, _) = hklm
.create_subkey(STORAGE_POLICY_PATH)
.map_err(map_winreg_error)?;
key.set_value("WriteProtect", &1u32)
.map_err(map_winreg_error)?;
} else if let Ok(key) = hklm.open_subkey_with_flags(STORAGE_POLICY_PATH, KEY_ALL_ACCESS) {
let _ = key.set_value("WriteProtect", &0u32);
}
Ok(())
}
fn refresh_group_policy() -> Result<(), UsbControlError> {
use std::os::windows::process::CommandExt;
use std::process::Command;
const CREATE_NO_WINDOW: u32 = 0x08000000;
let output = Command::new("gpupdate")
.args(["/target:computer", "/force"])
.creation_flags(CREATE_NO_WINDOW)
.output()
.map_err(UsbControlError::Io)?;
if !output.status.success() {
warn!(
"gpupdate retornou erro: {}",
String::from_utf8_lossy(&output.stderr)
);
}
Ok(())
}
fn map_winreg_error(error: io::Error) -> UsbControlError {
if let Some(code) = error.raw_os_error() {
if code == 5 {
return UsbControlError::PermissionDenied;
}
}
UsbControlError::RegistryError(error.to_string())
}

File diff suppressed because it is too large Load diff

View file

@ -15,22 +15,36 @@ name = "appsdesktop_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
tauri-build = { version = "2", features = [] }
tauri-build = { version = "2.4.1", features = [] }
[dependencies]
tauri = { version = "2", features = ["wry"] }
tauri-plugin-opener = "2"
tauri-plugin-store = "2.4"
tauri-plugin-updater = "2"
tauri-plugin-process = "2"
tauri = { version = "2.9", features = ["wry", "devtools", "tray-icon"] }
tauri-plugin-dialog = "2.4.2"
tauri-plugin-opener = "2.5.0"
tauri-plugin-store = "2.4.0"
tauri-plugin-updater = "2.9.0"
tauri-plugin-process = "2.3.0"
tauri-plugin-notification = "2"
tauri-plugin-deep-link = "2"
tauri-plugin-single-instance = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sysinfo = { version = "0.31", default-features = false, features = ["multithread", "network", "system", "disk"] }
get_if_addrs = "0.5"
reqwest = { version = "0.12", features = ["json", "rustls-tls"], default-features = false }
reqwest = { version = "0.12", features = ["json", "rustls-tls", "blocking", "stream"], default-features = false }
futures-util = "0.3"
tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] }
once_cell = "1.19"
thiserror = "1.0"
chrono = { version = "0.4", features = ["serde"] }
parking_lot = "0.12"
hostname = "0.4"
base64 = "0.22"
sha2 = "0.10"
convex = "0.10.2"
uuid = { version = "1", features = ["v4"] }
dirs = "5"
# SSE usa reqwest com stream, nao precisa de websocket
[target.'cfg(windows)'.dependencies]
winreg = "0.55"

View file

@ -1,3 +1,31 @@
fn main() {
tauri_build::build()
// Custom manifest keeps Common-Controls v6 dependency to avoid TaskDialogIndirect errors.
let windows = tauri_build::WindowsAttributes::new().app_manifest(
r#"
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
"#,
);
let attrs = tauri_build::Attributes::new().windows_attributes(windows);
tauri_build::try_build(attrs).expect("failed to run Tauri build script");
}

View file

@ -1,10 +1,21 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capability for the main window",
"windows": ["main"],
"description": "Capability for all windows",
"windows": ["main", "chat-*", "chat-hub"],
"permissions": [
"core:default",
"core:event:default",
"core:event:allow-listen",
"core:event:allow-unlisten",
"core:event:allow-emit",
"core:window:default",
"core:window:allow-close",
"core:window:allow-hide",
"core:window:allow-show",
"core:window:allow-set-focus",
"core:window:allow-start-dragging",
"dialog:allow-open",
"opener:default",
"store:default",
"store:allow-load",
@ -13,6 +24,10 @@
"store:allow-save",
"store:allow-delete",
"updater:default",
"process:default"
"process:default",
"notification:default",
"notification:allow-notify",
"notification:allow-request-permission",
"notification:allow-is-permission-granted"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 567 KiB

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more