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)
This commit is contained in:
Esdras Renan 2025-10-14 15:09:54 -03:00
parent 682d39db70
commit 087170e321
5 changed files with 48 additions and 9 deletions

View file

@ -37,12 +37,21 @@
1) Coleta perfil (hostname/OS/MAC/seriais/métricas). 1) Coleta perfil (hostname/OS/MAC/seriais/métricas).
2) Provisiona via `POST /api/machines/register` com `MACHINE_PROVISIONING_SECRET`, solicitando o **perfil de acesso** (Colaborador ou Gestor) e os dados do usuário associado. O backend garante a vinculação única da máquina ao colaborador ou gestor informado. 2) Provisiona via `POST /api/machines/register` com `MACHINE_PROVISIONING_SECRET`, solicitando o **perfil de acesso** (Colaborador ou Gestor) e os dados do usuário associado. O backend garante a vinculação única da máquina ao colaborador ou gestor informado.
3) Envia heartbeats a cada 5 min para `/api/machines/heartbeat` com inventário básico + estendido (discos, GPUs, serviços, softwares). 3) Envia heartbeats a cada 5 min para `/api/machines/heartbeat` com inventário básico + estendido (discos, GPUs, serviços, softwares).
4) Abre `APP_URL/machines/handshake?token=...&redirect=...` para autenticar a sessão: colaboradores são direcionados ao portal (`/portal`), gestores ao painel completo (`/dashboard`). 4) Abre `APP_URL/machines/handshake?token=...&redirect=...` para autenticar a sessão: colaboradores são direcionados ao portal (`/portal`), gestores ao painel completo (`/dashboard`). A rota de handshake é pública no middleware para permitir a criação da sessão sem login prévio.
- Segurança: token salvo no cofre do SO (Keyring). Store guarda apenas metadados não sensíveis. - 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). - Endpoint extra: `POST /api/machines/inventory` (atualiza inventário por token ou provisioningSecret).
- Atualizações automáticas: o plugin `@tauri-apps/plugin-updater` verifica `latest.json` nos releases do GitHub. Publicar uma nova release com manifestos atualiza os clientes sem reinstalação manual. - Atualizações automáticas: o plugin `@tauri-apps/plugin-updater` verifica `latest.json` nos releases do GitHub. Publicar uma nova release com manifestos atualiza os clientes sem reinstalação manual.
- Ajustes administrativos: em **Admin ▸ Máquinas** é possível vincular ou alterar o perfil (colaborador/gestor) e e-mail associado através do botão “Ajustar acesso”. - Ajustes administrativos: em **Admin ▸ Máquinas** é possível vincular ou alterar o perfil (colaborador/gestor) e e-mail associado através do botão “Ajustar acesso”.
### Sessão "machine" no frontend
- Ao autenticar como `machine`, o frontend consulta `/api/machines/session` e popula `machineContext` (assignedUserId, email, name, persona).
- O Portal usa `machineContext.assignedUserId` como `viewerId` ao abrir chamados, permitindo que o colaborador/gestor abra tickets pelo desktop.
- Na UI interna, o menu do usuário (canto inferior do sidebar) oculta o botão "Encerrar sessão" quando a sessão é de máquina.
### Sinalizador de desktop (opcional futuro)
- Podemos adicionar um cookie (ex.: `desktop_shell=1`) no handshake para diferenciar acessos do app desktop de acessos web convencionais.
- Esse cookie permitiria customizações de UI específicas (ex.: ocultar "Sair" apenas no desktop) sem depender de heurísticas do ambiente.
## Desenvolvimento local — boas práticas (atualizado) ## 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`. - 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`. - Convex em DEV: rode `pnpm convex:dev` e aponte o front para `http://127.0.0.1:3210` via `NEXT_PUBLIC_CONVEX_URL`.

View file

@ -28,7 +28,25 @@ VITE_API_BASE_URL=
- Build executável (bundle): - Build executável (bundle):
- `pnpm -C apps/desktop tauri build` - `pnpm -C 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`).
Build rápido e leve em dev:
```bash
pnpm -C 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'
pnpm -C apps/desktop tauri build --bundles nsis
```
## Prérequisitos Tauri ## Prérequisitos Tauri
- Rust toolchain instalado. - Rust toolchain instalado.

View file

@ -38,16 +38,20 @@
"bundle": { "bundle": {
"active": true, "active": true,
"createUpdaterArtifacts": true, "createUpdaterArtifacts": true,
"targets": [ "targets": ["nsis", "deb", "rpm"],
"deb",
"rpm",
"nsis"
],
"icon": [ "icon": [
"icons/icon.ico", "icons/icon.ico",
"icons/icon.icns", "icons/icon.icns",
"icons/icon.png", "icons/icon.png",
"icons/Raven.png" "icons/Raven.png"
] ],
"windows": {
"nsis": {
"installMode": "perMachine",
"allowToChangeInstallDirectory": true,
"installDirName": "Raven",
"createDesktopShortcut": true
}
}
} }
} }

View file

@ -3,6 +3,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { createRoot } from "react-dom/client" import { createRoot } from "react-dom/client"
import { invoke } from "@tauri-apps/api/core" import { invoke } from "@tauri-apps/api/core"
import { Store } from "@tauri-apps/plugin-store" import { Store } from "@tauri-apps/plugin-store"
import { appLocalDataDir } from "@tauri-apps/api/path"
import { ExternalLink, Eye, EyeOff, GalleryVerticalEnd, Loader2, RefreshCw } from "lucide-react" import { ExternalLink, Eye, EyeOff, GalleryVerticalEnd, Loader2, RefreshCw } from "lucide-react"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs" import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs"
import { cn } from "./lib/utils" import { cn } from "./lib/utils"
@ -103,7 +104,8 @@ const appUrl = normalizeUrl(import.meta.env.VITE_APP_URL, DEFAULT_APP_URL)
const apiBaseUrl = normalizeUrl(import.meta.env.VITE_API_BASE_URL, appUrl) const apiBaseUrl = normalizeUrl(import.meta.env.VITE_API_BASE_URL, appUrl)
async function loadStore(): Promise<Store> { async function loadStore(): Promise<Store> {
return await Store.load(STORE_FILENAME) const dir = await appLocalDataDir()
return await Store.load(STORE_FILENAME, { dir })
} }
async function readToken(store: Store): Promise<string | null> { async function readToken(store: Store): Promise<string | null> {

View file

@ -11,6 +11,12 @@
- Seeds prontos (Better Auth e dados demo Convex). - Seeds prontos (Better Auth e dados demo Convex).
- Seeds Better Auth automáticos: o container do web executa `pnpm auth:seed` após `prisma migrate deploy`, garantindo usuários padrão em toda inicialização (sem resetar senha existente por padrão). - Seeds Better Auth automáticos: o container do web executa `pnpm auth:seed` após `prisma migrate deploy`, garantindo usuários padrão em toda inicialização (sem resetar senha existente por padrão).
### Sessão de máquina (Desktop/Tauri)
- A rota `GET /machines/handshake?token=...&redirect=/portal|/dashboard` é pública no middleware para permitir a criação da sessão "machine" a partir do agente desktop, mesmo sem login prévio.
- Após o handshake, o servidor grava cookies de sessão (Better Auth) e um cookie `machine_ctx` com o vínculo (persona, assignedUser*, etc.). Em seguida, o App carrega `/api/machines/session` para preencher o contexto no cliente (`machineContext`).
- Com esse contexto, o Portal exibe corretamente o nome/email do colaborador/gestor no cabeçalho e permite abrir chamados em nome do usuário vinculado.
- Em sessões de máquina, o botão "Encerrar sessão" no menu do usuário é ocultado por padrão na UI interna.
## Requisitos ## Requisitos
- VPS com Docker/Swarm e Traefik já em execução na rede externa `traefik_public`. - VPS com Docker/Swarm e Traefik já em execução na rede externa `traefik_public`.
- Portainer opcional (para gerenciar a stack “sistema”). - Portainer opcional (para gerenciar a stack “sistema”).