feat: dispositivos e ajustes de csat e relatórios
This commit is contained in:
parent
25d2a9b062
commit
e0ef66555d
86 changed files with 5811 additions and 992 deletions
|
|
@ -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
|
||||
|
|
@ -65,7 +65,7 @@ pnpm -C apps/desktop tauri build --bundles nsis
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@
|
|||
|
||||
## 5. Gerar chaves do updater Tauri
|
||||
|
||||
1. Em qualquer máquina com Node/pnpm (pode ser seu computador local):
|
||||
1. Em qualquer dispositivo com Node/pnpm (pode ser seu computador local):
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm --filter appsdesktop tauri signer generate
|
||||
|
|
@ -267,10 +267,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.
|
||||
|
|
@ -429,7 +429,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 +451,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`. |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use tokio::sync::Notify;
|
|||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum AgentError {
|
||||
#[error("Falha ao obter hostname da máquina")]
|
||||
#[error("Falha ao obter hostname da dispositivo")]
|
||||
Hostname,
|
||||
#[error("Nenhum identificador de hardware disponível (MAC/serial)")]
|
||||
MissingIdentifiers,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ export function DeactivationScreen({ companyName }: { companyName?: string | nul
|
|||
<span className="inline-flex items-center gap-2 rounded-full border border-rose-200 bg-rose-50 px-3 py-1 text-xs font-semibold text-rose-700">
|
||||
<ShieldAlert className="size-4" /> Acesso bloqueado
|
||||
</span>
|
||||
<h1 className="text-2xl font-semibold text-neutral-900">Máquina desativada</h1>
|
||||
<h1 className="text-2xl font-semibold text-neutral-900">Dispositivo desativada</h1>
|
||||
<p className="max-w-md text-sm text-neutral-600">
|
||||
Esta máquina foi desativada temporariamente pelos administradores. Enquanto isso, o acesso ao portal e o
|
||||
Esta dispositivo foi desativada temporariamente pelos administradores. Enquanto isso, o acesso ao portal e o
|
||||
envio de informações ficam indisponíveis.
|
||||
</p>
|
||||
{companyName ? (
|
||||
|
|
|
|||
|
|
@ -273,9 +273,9 @@ function App() {
|
|||
const text = await res.text()
|
||||
const msg = text.toLowerCase()
|
||||
const isInvalid =
|
||||
msg.includes("token de máquina inválido") ||
|
||||
msg.includes("token de máquina revogado") ||
|
||||
msg.includes("token de máquina expirado")
|
||||
msg.includes("token de dispositivo inválido") ||
|
||||
msg.includes("token de dispositivo revogado") ||
|
||||
msg.includes("token de dispositivo expirado")
|
||||
if (isInvalid) {
|
||||
try {
|
||||
await store.delete("token"); await store.delete("config"); await store.save()
|
||||
|
|
@ -293,7 +293,7 @@ function App() {
|
|||
} catch {}
|
||||
} else {
|
||||
// Não limpa token em falhas genéricas (ex.: rede); apenas informa
|
||||
setError("Falha ao validar sessão da máquina. Tente novamente.")
|
||||
setError("Falha ao validar sessão da dispositivo. Tente novamente.")
|
||||
tokenVerifiedRef.current = true
|
||||
setTokenValidationTick((tick) => tick + 1)
|
||||
}
|
||||
|
|
@ -443,12 +443,12 @@ function App() {
|
|||
return
|
||||
}
|
||||
if (!validatedCompany) {
|
||||
setError("Valide o código de provisionamento antes de registrar a máquina.")
|
||||
setError("Valide o código de provisionamento antes de registrar a dispositivo.")
|
||||
return
|
||||
}
|
||||
const normalizedEmail = collabEmail.trim().toLowerCase()
|
||||
if (!normalizedEmail) {
|
||||
setError("Informe o e-mail do colaborador vinculado a esta máquina.")
|
||||
setError("Informe o e-mail do colaborador vinculado a esta dispositivo.")
|
||||
return
|
||||
}
|
||||
if (!emailRegex.current.test(normalizedEmail)) {
|
||||
|
|
@ -575,7 +575,7 @@ function App() {
|
|||
setError(null)
|
||||
}
|
||||
if (!currentActive) {
|
||||
setError("Esta máquina está desativada. Entre em contato com o suporte da Rever para reativar o acesso.")
|
||||
setError("Esta dispositivo está desativada. Entre em contato com o suporte da Rever para reativar o acesso.")
|
||||
setIsLaunchingSystem(false)
|
||||
return
|
||||
}
|
||||
|
|
@ -590,7 +590,7 @@ function App() {
|
|||
: ""
|
||||
setIsMachineActive(false)
|
||||
setIsLaunchingSystem(false)
|
||||
setError(message.length > 0 ? message : "Esta máquina está desativada. Entre em contato com o suporte da Rever.")
|
||||
setError(message.length > 0 ? message : "Esta dispositivo está desativada. Entre em contato com o suporte da Rever.")
|
||||
return
|
||||
}
|
||||
// Se sessão falhar, tenta identificar token inválido/expirado
|
||||
|
|
@ -604,9 +604,9 @@ function App() {
|
|||
const text = await hb.text()
|
||||
const low = text.toLowerCase()
|
||||
const invalid =
|
||||
low.includes("token de máquina inválido") ||
|
||||
low.includes("token de máquina revogado") ||
|
||||
low.includes("token de máquina expirado")
|
||||
low.includes("token de dispositivo inválido") ||
|
||||
low.includes("token de dispositivo revogado") ||
|
||||
low.includes("token de dispositivo expirado")
|
||||
if (invalid) {
|
||||
// Força onboarding
|
||||
await store?.delete("token"); await store?.delete("config"); await store?.save()
|
||||
|
|
@ -616,7 +616,7 @@ function App() {
|
|||
setConfig(null)
|
||||
setStatus(null)
|
||||
setIsMachineActive(true)
|
||||
setError("Sessão expirada. Reprovisione a máquina para continuar.")
|
||||
setError("Sessão expirada. Reprovisione a dispositivo para continuar.")
|
||||
setIsLaunchingSystem(false)
|
||||
const p = await invoke<MachineProfile>("collect_machine_profile")
|
||||
setProfile(p)
|
||||
|
|
@ -787,7 +787,7 @@ function App() {
|
|||
{error ? <p className="mt-3 rounded-md bg-rose-50 p-2 text-sm text-rose-700">{error}</p> : null}
|
||||
{!token ? (
|
||||
<div className="mt-4 space-y-3">
|
||||
<p className="text-sm text-slate-600">Informe os dados para registrar esta máquina.</p>
|
||||
<p className="text-sm text-slate-600">Informe os dados para registrar esta dispositivo.</p>
|
||||
<div className="grid gap-2">
|
||||
<label className="text-sm font-medium">Código de provisionamento</label>
|
||||
<div className="relative">
|
||||
|
|
@ -822,7 +822,7 @@ function App() {
|
|||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-slate-500">
|
||||
Informe o código único fornecido pela equipe para vincular esta máquina a uma empresa.
|
||||
Informe o código único fornecido pela equipe para vincular esta dispositivo a uma empresa.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -832,7 +832,7 @@ function App() {
|
|||
<div className="space-y-1">
|
||||
<span className="block text-sm font-semibold text-emerald-800">{validatedCompany.name}</span>
|
||||
<span className="text-xs text-emerald-700/80">
|
||||
Código reconhecido. Esta máquina será vinculada automaticamente à empresa informada.
|
||||
Código reconhecido. Esta dispositivo será vinculada automaticamente à empresa informada.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -884,7 +884,7 @@ function App() {
|
|||
</div>
|
||||
) : null}
|
||||
<div className="mt-2 flex gap-2">
|
||||
<button disabled={busy || !validatedCompany || !isEmailValid || !collabName.trim() || provisioningCode.trim().length < 32} onClick={register} className="rounded-lg border border-black bg-black px-3 py-2 text-sm font-semibold text-white hover:bg-black/90 disabled:opacity-60">Registrar máquina</button>
|
||||
<button disabled={busy || !validatedCompany || !isEmailValid || !collabName.trim() || provisioningCode.trim().length < 32} onClick={register} className="rounded-lg border border-black bg-black px-3 py-2 text-sm font-semibold text-white hover:bg-black/90 disabled:opacity-60">Registrar dispositivo</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue