diff --git a/apps/desktop/index.html b/apps/desktop/index.html index 00e916c..cde96ca 100644 --- a/apps/desktop/index.html +++ b/apps/desktop/index.html @@ -5,7 +5,7 @@ - Sistema de Chamados — Agente Desktop + Raven — Agente Desktop diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml index c78a41d..5439cc2 100644 --- a/apps/desktop/src-tauri/Cargo.toml +++ b/apps/desktop/src-tauri/Cargo.toml @@ -21,6 +21,8 @@ tauri-build = { version = "2", features = [] } tauri = { version = "2", features = ["wry"] } tauri-plugin-opener = "2" tauri-plugin-store = "2.4" +tauri-plugin-updater = "2" +tauri-plugin-process = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" sysinfo = { version = "0.31", default-features = false, features = ["multithread", "network", "system", "disk"] } diff --git a/apps/desktop/src-tauri/capabilities/default.json b/apps/desktop/src-tauri/capabilities/default.json index 2a2d485..9fe95d8 100644 --- a/apps/desktop/src-tauri/capabilities/default.json +++ b/apps/desktop/src-tauri/capabilities/default.json @@ -11,6 +11,8 @@ "store:allow-set", "store:allow-get", "store:allow-save", - "store:allow-delete" + "store:allow-delete", + "updater:default", + "process:default" ] } diff --git a/apps/desktop/src-tauri/src/agent.rs b/apps/desktop/src-tauri/src/agent.rs index 996a8c6..9fc0e0b 100644 --- a/apps/desktop/src-tauri/src/agent.rs +++ b/apps/desktop/src-tauri/src/agent.rs @@ -469,13 +469,18 @@ fn collect_linux_extended() -> serde_json::Value { #[cfg(target_os = "windows")] fn collect_windows_extended() -> serde_json::Value { use std::process::Command; + use std::os::windows::process::CommandExt; + const CREATE_NO_WINDOW: u32 = 0x08000000; fn ps(cmd: &str) -> Option { let ps_cmd = format!( "$ErrorActionPreference='SilentlyContinue'; {} | ConvertTo-Json -Depth 4 -Compress", cmd ); let out = Command::new("powershell") + .creation_flags(CREATE_NO_WINDOW) .arg("-NoProfile") + .arg("-WindowStyle").arg("Hidden") + .arg("-NoLogo") .arg("-Command") .arg(ps_cmd) .output() @@ -486,23 +491,24 @@ fn collect_windows_extended() -> serde_json::Value { let software = ps(r#"@(Get-ItemProperty 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'; Get-ItemProperty 'HKLM:\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*') | Where-Object { $_.DisplayName } | Select-Object DisplayName, DisplayVersion, Publisher"#) .unwrap_or_else(|| json!([])); - let services = ps("Get-Service | Select-Object Name,Status,DisplayName").unwrap_or_else(|| json!([])); + let services = ps("@(Get-Service | Select-Object Name,Status,DisplayName)").unwrap_or_else(|| json!([])); let defender = ps("Get-MpComputerStatus | Select-Object AMRunningMode,AntivirusEnabled,RealTimeProtectionEnabled,AntispywareEnabled").unwrap_or_else(|| json!({})); let hotfix = ps("Get-HotFix | Select-Object HotFixID,InstalledOn").unwrap_or_else(|| json!([])); // Informações de build/edição e ativação let os_info = ps(r#" $cv = Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'; - $ls = (Get-CimInstance -Query "SELECT LicenseStatus FROM SoftwareLicensingProduct WHERE PartialProductKey IS NOT NULL" | Select-Object -First 1).LicenseStatus; + $ls = Get-CimInstance -Query "SELECT Name, LicenseStatus FROM SoftwareLicensingProduct WHERE PartialProductKey IS NOT NULL" | Where-Object { $_.Name -like 'Windows*' } | Select-Object -First 1; + $lsCode = if ($ls -and $ls.LicenseStatus -ne $null) { [int]$ls.LicenseStatus } else { 0 }; [PSCustomObject]@{ - ProductName = $cv.ProductName - CurrentBuild = $cv.CurrentBuild + ProductName = $cv.ProductName + CurrentBuild = $cv.CurrentBuild CurrentBuildNumber = $cv.CurrentBuildNumber - DisplayVersion = $cv.DisplayVersion - ReleaseId = $cv.ReleaseId - EditionID = $cv.EditionID - LicenseStatus = $ls - IsActivated = ($ls -eq 1) + DisplayVersion = $cv.DisplayVersion + ReleaseId = $cv.ReleaseId + EditionID = $cv.EditionID + LicenseStatus = $lsCode + IsActivated = ($lsCode -eq 1) } "#).unwrap_or_else(|| json!({})); @@ -510,9 +516,9 @@ fn collect_windows_extended() -> serde_json::Value { let cpu = ps("Get-CimInstance Win32_Processor | Select-Object Name,Manufacturer,SocketDesignation,NumberOfCores,NumberOfLogicalProcessors,L2CacheSize,L3CacheSize,MaxClockSpeed").unwrap_or_else(|| json!({})); let baseboard = ps("Get-CimInstance Win32_BaseBoard | Select-Object Product,Manufacturer,SerialNumber,Version").unwrap_or_else(|| json!({})); let bios = ps("Get-CimInstance Win32_BIOS | Select-Object Manufacturer,SMBIOSBIOSVersion,ReleaseDate,Version").unwrap_or_else(|| json!({})); - let memory = ps("Get-CimInstance Win32_PhysicalMemory | Select-Object BankLabel,Capacity,Manufacturer,PartNumber,SerialNumber,ConfiguredClockSpeed,Speed,ConfiguredVoltage").unwrap_or_else(|| json!([])); - let video = ps("Get-CimInstance Win32_VideoController | Select-Object Name,AdapterRAM,DriverVersion,PNPDeviceID").unwrap_or_else(|| json!([])); - let disks = ps("Get-CimInstance Win32_DiskDrive | Select-Object Model,SerialNumber,Size,InterfaceType,MediaType").unwrap_or_else(|| json!([])); + let memory = ps("@(Get-CimInstance Win32_PhysicalMemory | Select-Object BankLabel,Capacity,Manufacturer,PartNumber,SerialNumber,ConfiguredClockSpeed,Speed,ConfiguredVoltage)").unwrap_or_else(|| json!([])); + let video = ps("@(Get-CimInstance Win32_VideoController | Select-Object Name,AdapterRAM,DriverVersion,PNPDeviceID)").unwrap_or_else(|| json!([])); + let disks = ps("@(Get-CimInstance Win32_DiskDrive | Select-Object Model,SerialNumber,Size,InterfaceType,MediaType)").unwrap_or_else(|| json!([])); json!({ "windows": { diff --git a/apps/desktop/src-tauri/src/lib.rs b/apps/desktop/src-tauri/src/lib.rs index d3cf938..a068d3c 100644 --- a/apps/desktop/src-tauri/src/lib.rs +++ b/apps/desktop/src-tauri/src/lib.rs @@ -38,6 +38,8 @@ pub fn run() { .manage(AgentRuntime::new()) .plugin(tauri_plugin_opener::init()) .plugin(StorePluginBuilder::default().build()) + .plugin(tauri_plugin_updater::init()) + .plugin(tauri_plugin_process::init()) .invoke_handler(tauri::generate_handler![ collect_machine_profile, collect_machine_inventory, diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index cfa83ad..ea01eba 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "$schema": "https://schema.tauri.app/config/2", - "productName": "Sistema de Chamados Desktop", + "productName": "Raven", "version": "0.1.0", "identifier": "br.com.esdrasrenan.sistemadechamados", "build": { @@ -13,7 +13,7 @@ "withGlobalTauri": true, "windows": [ { - "title": "Sistema de Chamados", + "title": "Raven", "width": 1100, "height": 720, "resizable": true @@ -23,6 +23,14 @@ "csp": null } }, + "updater": { + "active": true, + "endpoints": [ + "https://github.com/esdrasrenan/sistema-de-chamados/releases/latest/download/latest.json" + ], + "dialog": true, + "pubkey": "REPLACE_WITH_TAURI_PUBLIC_KEY" + }, "bundle": { "active": true, "targets": "all", diff --git a/apps/desktop/src/main.tsx b/apps/desktop/src/main.tsx index 717122c..47f0621 100644 --- a/apps/desktop/src/main.tsx +++ b/apps/desktop/src/main.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from "react" import { createRoot } from "react-dom/client" import { invoke } from "@tauri-apps/api/core" import { Store } from "@tauri-apps/plugin-store" -import { ExternalLink, Eye, EyeOff } from "lucide-react" +import { ExternalLink, Eye, EyeOff, GalleryVerticalEnd, RefreshCw } from "lucide-react" import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs" import { cn } from "./lib/utils" @@ -127,6 +127,7 @@ function App() { const [company, setCompany] = useState("") const [collabEmail, setCollabEmail] = useState("") const [collabName, setCollabName] = useState("") + const [updating, setUpdating] = useState(false) useEffect(() => { (async () => { @@ -241,13 +242,38 @@ function App() { } } + async function checkForUpdates() { + try { + setUpdating(true) + const { check } = await import("@tauri-apps/plugin-updater") + const update = await check() + if (update && (update as any).available) { + // download and install then relaunch + await (update as any).downloadAndInstall() + const { relaunch } = await import("@tauri-apps/plugin-process") + await relaunch() + } else { + alert("Nenhuma atualização disponível.") + } + } catch (error) { + console.error("Falha ao verificar atualizações", error) + alert("Falha ao verificar atualizações.") + } finally { + setUpdating(false) + } + } + return (
-
-

Sistema de Chamados — Agente Desktop

+
+
+ + Sistema de chamados +
+

Agente Desktop

{error ?

{error}

: null} {!token ? (
@@ -263,7 +289,7 @@ function App() {
- setCompany(e.target.value)} /> + setCompany(e.target.value)} />
@@ -360,11 +386,21 @@ function App() { setCollabName(e.target.value)} />
+
+ + +
)}
+
+ Logotipo Rever Tecnologia +
+
) } diff --git a/src/app/api/machines/register/route.ts b/src/app/api/machines/register/route.ts index 206aa7a..d7f6ecb 100644 --- a/src/app/api/machines/register/route.ts +++ b/src/app/api/machines/register/route.ts @@ -103,6 +103,15 @@ export async function POST(request: Request) { } catch (error) { console.error("[machines.register] Falha no provisionamento", error) const details = error instanceof Error ? error.message : String(error) - return jsonWithCors({ error: "Falha ao provisionar máquina", details }, 500, request.headers.get("origin"), CORS_METHODS) + const msg = details.toLowerCase() + // Mapear alguns erros "esperados" para códigos adequados + // - empresa inválida → 404 + // - segredo inválido → 401 + // - demais ConvexError → 400 + const isCompanyNotFound = msg.includes("empresa não encontrada") + const isInvalidSecret = msg.includes("código de provisionamento inválido") + const isConvexError = msg.includes("convexerror") + const status = isCompanyNotFound ? 404 : isInvalidSecret ? 401 : isConvexError ? 400 : 500 + return jsonWithCors({ error: "Falha ao provisionar máquina", details }, status, request.headers.get("origin"), CORS_METHODS) } } diff --git a/src/app/invite/[token]/page.tsx b/src/app/invite/[token]/page.tsx index 23bd23e..6f5f263 100644 --- a/src/app/invite/[token]/page.tsx +++ b/src/app/invite/[token]/page.tsx @@ -29,7 +29,7 @@ export default async function InvitePage({ params }: { params: Promise<{ token: Aceitar convite - Conclua seu cadastro para acessar a plataforma Sistema de chamados. + Conclua seu cadastro para acessar a plataforma Raven. diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3e7afe5..6915eb3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -18,8 +18,8 @@ const jetBrainsMono = JetBrains_Mono({ }) export const metadata: Metadata = { - title: "Sistema de chamados", - description: "Plataforma de chamados da Rever", + title: "Raven", + description: "Plataforma Raven da Rever", icons: { icon: "/rever-8.png", }, diff --git a/src/app/login/login-page-client.tsx b/src/app/login/login-page-client.tsx index 1fd39a4..6757b57 100644 --- a/src/app/login/login-page-client.tsx +++ b/src/app/login/login-page-client.tsx @@ -43,7 +43,7 @@ export function LoginPageClient() {
- Sistema de chamados + Raven
diff --git a/src/app/machines/handshake/route.ts b/src/app/machines/handshake/route.ts index 869457e..d0cf1dc 100644 --- a/src/app/machines/handshake/route.ts +++ b/src/app/machines/handshake/route.ts @@ -23,7 +23,7 @@ const ERROR_TEMPLATE = `

Não foi possível autenticar esta máquina

O token informado é inválido, expirou ou não está mais associado a uma máquina ativa.

Volte ao agente desktop, gere um novo token ou realize o provisionamento novamente.

- Voltar para o Sistema de Chamados + Voltar para o Raven @@ -61,4 +61,3 @@ export async function GET(request: NextRequest) { }) } } - diff --git a/src/components/admin/machines/admin-machines-overview.tsx b/src/components/admin/machines/admin-machines-overview.tsx index 1bdf47d..f969e28 100644 --- a/src/components/admin/machines/admin-machines-overview.tsx +++ b/src/components/admin/machines/admin-machines-overview.tsx @@ -1354,8 +1354,27 @@ function DetailLine({ label, value, classNameValue }: DetailLineProps) { function MetricsGrid({ metrics }: { metrics: MachineMetrics }) { const data = (metrics ?? {}) as Record - const cpu = Number(data.cpuUsage ?? data.cpu ?? data.cpu_percent ?? NaN) - const memory = Number(data.memoryBytes ?? data.memory ?? data.memory_used ?? NaN) + // Compat: aceitar chaves do agente desktop (cpuUsagePercent, memoryUsedBytes, memoryTotalBytes) + const cpu = (() => { + const v = Number( + data.cpuUsage ?? data.cpu ?? data.cpu_percent ?? data.cpuUsagePercent ?? NaN + ) + return v + })() + const memory = (() => { + // valor absoluto em bytes, se disponível + const memBytes = Number( + data.memoryBytes ?? data.memory ?? data.memory_used ?? data.memoryUsedBytes ?? NaN + ) + if (Number.isFinite(memBytes)) return memBytes + // tentar derivar a partir de percentuais do agente + const usedPct = Number(data.memoryUsedPercent ?? NaN) + const totalBytes = Number(data.memoryTotalBytes ?? NaN) + if (Number.isFinite(usedPct) && Number.isFinite(totalBytes)) { + return Math.max(0, Math.min(1, usedPct > 1 ? usedPct / 100 : usedPct)) * totalBytes + } + return NaN + })() const disk = Number(data.diskUsage ?? data.disk ?? NaN) return ( diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index c81fa55..a9eccdd 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -164,7 +164,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { diff --git a/src/components/portal/portal-shell.tsx b/src/components/portal/portal-shell.tsx index 320460f..bcf6c84 100644 --- a/src/components/portal/portal-shell.tsx +++ b/src/components/portal/portal-shell.tsx @@ -59,7 +59,7 @@ export function PortalShell({ children }: PortalShellProps) { Portal do cliente - Sistema de chamados + Raven