feat(desktop): add Tauri updater (GitHub Releases), updater UI button, hide PowerShell windows; fix Windows inventory arrays and activation; improve metrics parsing; branding rename to Raven across app; avoid localhost fallback in auth-server; inject APP_URL/AUTH_URL in stack
This commit is contained in:
parent
eb5f39100f
commit
418599ef62
18 changed files with 127 additions and 34 deletions
|
|
@ -5,7 +5,7 @@
|
|||
<meta name="color-scheme" content="light" />
|
||||
<link rel="stylesheet" href="/src/index.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Sistema de Chamados — Agente Desktop</title>
|
||||
<title>Raven — Agente Desktop</title>
|
||||
<script type="module" src="/src/main.tsx" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
|
|
|||
|
|
@ -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"] }
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@
|
|||
"store:allow-set",
|
||||
"store:allow-get",
|
||||
"store:allow-save",
|
||||
"store:allow-delete"
|
||||
"store:allow-delete",
|
||||
"updater:default",
|
||||
"process:default"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<serde_json::Value> {
|
||||
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": {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="min-h-screen grid place-items-center p-6">
|
||||
<div className="w-full max-w-[720px] rounded-2xl border border-slate-200 bg-white p-6 shadow-sm">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<h1 className="text-xl font-semibold">Sistema de Chamados — Agente Desktop</h1>
|
||||
<div className="mb-2 flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2 text-xl font-semibold text-neutral-900">
|
||||
<span className="flex size-6 items-center justify-center rounded-md bg-black text-white"><GalleryVerticalEnd className="size-4" /></span>
|
||||
Sistema de chamados
|
||||
</div>
|
||||
<StatusBadge status={status} />
|
||||
</div>
|
||||
<h1 className="text-base font-semibold text-slate-800">Agente Desktop</h1>
|
||||
{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">
|
||||
|
|
@ -263,7 +289,7 @@ function App() {
|
|||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label className="text-sm font-medium">Empresa (slug opcional)</label>
|
||||
<input className="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm" placeholder="ex.: tenant-atlas" value={company} onChange={(e)=>setCompany(e.target.value)} />
|
||||
<input className="w-full rounded-lg border border-slate-300 px-3 py-2 text-sm" placeholder="ex.: atlas-engenharia" value={company} onChange={(e)=>setCompany(e.target.value)} />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label className="text-sm font-medium">Colaborador (e-mail)</label>
|
||||
|
|
@ -360,11 +386,21 @@ function App() {
|
|||
<label className="label">Nome do colaborador (opcional)</label>
|
||||
<input className="input" placeholder="Nome completo" value={collabName} onChange={(e)=>setCollabName(e.target.value)} />
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<label className="label">Atualizações</label>
|
||||
<button onClick={checkForUpdates} disabled={updating} className={cn("btn btn-outline inline-flex items-center gap-2", updating && "opacity-60")}>
|
||||
<RefreshCw className="size-4" /> Verificar atualizações
|
||||
</button>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-6 flex justify-center">
|
||||
<img src={`${appUrl}/rever-8.png`} alt="Logotipo Rever Tecnologia" width={110} height={110} className="h-[3.45rem] w-auto" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue