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>
This commit is contained in:
parent
b60255fe03
commit
20b63f4ad6
5 changed files with 290 additions and 62 deletions
|
|
@ -1185,12 +1185,17 @@ struct UsbPolicyStatusReport {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
|
async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
|
||||||
|
crate::log_info!("Verificando politica USB pendente...");
|
||||||
|
|
||||||
let url = format!("{}/api/machines/usb-policy?machineToken={}", base_url, token);
|
let url = format!("{}/api/machines/usb-policy?machineToken={}", base_url, token);
|
||||||
|
|
||||||
let response = match HTTP_CLIENT.get(&url).send().await {
|
let response = match HTTP_CLIENT.get(&url).send().await {
|
||||||
Ok(resp) => resp,
|
Ok(resp) => {
|
||||||
|
crate::log_info!("Resposta da verificacao de politica USB: status={}", resp.status());
|
||||||
|
resp
|
||||||
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[agent] Falha ao verificar politica USB: {e}");
|
crate::log_error!("Falha ao verificar politica USB: {e}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -1198,23 +1203,26 @@ async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
|
||||||
let policy_response: UsbPolicyResponse = match response.json().await {
|
let policy_response: UsbPolicyResponse = match response.json().await {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[agent] Falha ao parsear resposta de politica USB: {e}");
|
crate::log_error!("Falha ao parsear resposta de politica USB: {e}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if !policy_response.pending {
|
if !policy_response.pending {
|
||||||
|
crate::log_info!("Nenhuma politica USB pendente");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let policy_str = match policy_response.policy {
|
let policy_str = match policy_response.policy {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => {
|
None => {
|
||||||
eprintln!("[agent] Politica USB pendente mas sem valor de policy");
|
crate::log_warn!("Politica USB pendente mas sem valor de policy");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
crate::log_info!("Politica USB pendente encontrada: {}", policy_str);
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
use crate::usb_control::{apply_usb_policy, get_current_policy, UsbPolicy};
|
use crate::usb_control::{apply_usb_policy, get_current_policy, UsbPolicy};
|
||||||
|
|
@ -1222,7 +1230,7 @@ async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
|
||||||
let policy = match UsbPolicy::from_str(&policy_str) {
|
let policy = match UsbPolicy::from_str(&policy_str) {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
None => {
|
None => {
|
||||||
eprintln!("[agent] Politica USB invalida: {}", policy_str);
|
crate::log_error!("Politica USB invalida: {}", policy_str);
|
||||||
report_usb_policy_status(base_url, token, "FAILED", Some(format!("Politica invalida: {}", policy_str)), None).await;
|
report_usb_policy_status(base_url, token, "FAILED", Some(format!("Politica invalida: {}", policy_str)), None).await;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -1231,30 +1239,44 @@ async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
|
||||||
// Verifica se a politica ja esta aplicada localmente
|
// Verifica se a politica ja esta aplicada localmente
|
||||||
match get_current_policy() {
|
match get_current_policy() {
|
||||||
Ok(current) if current == policy => {
|
Ok(current) if current == policy => {
|
||||||
eprintln!("[agent] Politica USB ja esta aplicada localmente: {}", policy_str);
|
crate::log_info!("Politica USB ja esta aplicada localmente: {}", policy_str);
|
||||||
report_usb_policy_status(base_url, token, "APPLIED", None, Some(policy_str)).await;
|
let reported = report_usb_policy_status(base_url, token, "APPLIED", None, Some(policy_str.clone())).await;
|
||||||
|
if !reported {
|
||||||
|
crate::log_error!("Falha ao reportar politica ja aplicada");
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Ok(current) => {
|
Ok(current) => {
|
||||||
eprintln!("[agent] Politica atual: {:?}, esperada: {:?}", current, policy);
|
crate::log_info!("Politica atual: {:?}, esperada: {:?}", current, policy);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[agent] Nao foi possivel ler politica atual: {e}");
|
crate::log_warn!("Nao foi possivel ler politica atual: {e}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!("[agent] Aplicando politica USB: {}", policy_str);
|
crate::log_info!("Aplicando politica USB: {}", policy_str);
|
||||||
|
|
||||||
// Reporta APPLYING para progress bar real no frontend
|
// Reporta APPLYING para progress bar real no frontend
|
||||||
report_usb_policy_status(base_url, token, "APPLYING", None, None).await;
|
let _ = report_usb_policy_status(base_url, token, "APPLYING", None, None).await;
|
||||||
|
|
||||||
match apply_usb_policy(policy) {
|
match apply_usb_policy(policy) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
eprintln!("[agent] Politica USB aplicada com sucesso: {:?}", result);
|
crate::log_info!("Politica USB aplicada com sucesso: {:?}", result);
|
||||||
report_usb_policy_status(base_url, token, "APPLIED", None, Some(policy_str)).await;
|
let reported = report_usb_policy_status(base_url, token, "APPLIED", None, Some(policy_str.clone())).await;
|
||||||
|
if !reported {
|
||||||
|
crate::log_error!("CRITICO: Politica aplicada mas falha ao reportar ao servidor!");
|
||||||
|
// Agenda retry em background
|
||||||
|
let base_url = base_url.to_string();
|
||||||
|
let token = token.to_string();
|
||||||
|
tokio::spawn(async move {
|
||||||
|
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||||
|
crate::log_info!("Retry agendado: reportando politica USB...");
|
||||||
|
let _ = report_usb_policy_status(&base_url, &token, "APPLIED", None, Some(policy_str)).await;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[agent] Falha ao aplicar politica USB: {e}");
|
crate::log_error!("Falha ao aplicar politica USB: {e}");
|
||||||
report_usb_policy_status(base_url, token, "FAILED", Some(e.to_string()), None).await;
|
report_usb_policy_status(base_url, token, "FAILED", Some(e.to_string()), None).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1262,7 +1284,7 @@ async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
{
|
{
|
||||||
eprintln!("[agent] Controle de USB nao suportado neste sistema operacional");
|
crate::log_warn!("Controle de USB nao suportado neste sistema operacional");
|
||||||
report_usb_policy_status(base_url, token, "FAILED", Some("Sistema operacional nao suportado".to_string()), None).await;
|
report_usb_policy_status(base_url, token, "FAILED", Some("Sistema operacional nao suportado".to_string()), None).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1273,7 +1295,7 @@ async fn report_usb_policy_status(
|
||||||
status: &str,
|
status: &str,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
current_policy: Option<String>,
|
current_policy: Option<String>,
|
||||||
) {
|
) -> bool {
|
||||||
let url = format!("{}/api/machines/usb-policy", base_url);
|
let url = format!("{}/api/machines/usb-policy", base_url);
|
||||||
|
|
||||||
let report = UsbPolicyStatusReport {
|
let report = UsbPolicyStatusReport {
|
||||||
|
|
@ -1283,39 +1305,56 @@ async fn report_usb_policy_status(
|
||||||
current_policy,
|
current_policy,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Retry com backoff exponencial: 2s, 4s, 8s
|
crate::log_info!("Reportando status de politica USB: status={}", status);
|
||||||
let delays = [2, 4, 8];
|
|
||||||
|
// Retry simples: 1 tentativa imediata + 1 retry após 2s
|
||||||
|
let delays = [2];
|
||||||
let mut last_error = None;
|
let mut last_error = None;
|
||||||
|
|
||||||
for (attempt, delay_secs) in delays.iter().enumerate() {
|
for (attempt, delay_secs) in delays.iter().enumerate() {
|
||||||
match HTTP_CLIENT.post(&url).json(&report).send().await {
|
match HTTP_CLIENT.post(&url).json(&report).send().await {
|
||||||
Ok(response) if response.status().is_success() => {
|
|
||||||
if attempt > 0 {
|
|
||||||
eprintln!("[agent] Report de politica USB enviado na tentativa {}", attempt + 1);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Ok(response) => {
|
Ok(response) => {
|
||||||
last_error = Some(format!("HTTP {}", response.status()));
|
let status_code = response.status();
|
||||||
|
if status_code.is_success() {
|
||||||
|
crate::log_info!(
|
||||||
|
"Report de politica USB enviado com sucesso na tentativa {}",
|
||||||
|
attempt + 1
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
let body = response.text().await.unwrap_or_default();
|
||||||
|
last_error = Some(format!("HTTP {} - {}", status_code, body));
|
||||||
|
crate::log_warn!(
|
||||||
|
"Report de politica USB falhou (tentativa {}): HTTP {}",
|
||||||
|
attempt + 1,
|
||||||
|
status_code
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
last_error = Some(e.to_string());
|
last_error = Some(e.to_string());
|
||||||
|
crate::log_warn!(
|
||||||
|
"Report de politica USB falhou (tentativa {}): {}",
|
||||||
|
attempt + 1,
|
||||||
|
e
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if attempt < delays.len() - 1 {
|
if attempt < delays.len() - 1 {
|
||||||
eprintln!(
|
crate::log_info!("Retentando report de politica USB em {}s...", delay_secs);
|
||||||
"[agent] Falha ao reportar politica USB (tentativa {}), retentando em {}s...",
|
|
||||||
attempt + 1,
|
|
||||||
delay_secs
|
|
||||||
);
|
|
||||||
tokio::time::sleep(Duration::from_secs(*delay_secs)).await;
|
tokio::time::sleep(Duration::from_secs(*delay_secs)).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(err) = last_error {
|
if let Some(err) = last_error {
|
||||||
eprintln!("[agent] Falha ao reportar status de politica USB apos 3 tentativas: {err}");
|
crate::log_error!(
|
||||||
|
"Falha ao reportar status de politica USB apos {} tentativas: {err}",
|
||||||
|
delays.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
struct HeartbeatHandle {
|
struct HeartbeatHandle {
|
||||||
|
|
@ -1332,9 +1371,9 @@ impl HeartbeatHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone)]
|
||||||
pub struct AgentRuntime {
|
pub struct AgentRuntime {
|
||||||
inner: Mutex<Option<HeartbeatHandle>>,
|
inner: Arc<Mutex<Option<HeartbeatHandle>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sanitize_base_url(input: &str) -> Result<String, AgentError> {
|
fn sanitize_base_url(input: &str) -> Result<String, AgentError> {
|
||||||
|
|
@ -1348,7 +1387,7 @@ fn sanitize_base_url(input: &str) -> Result<String, AgentError> {
|
||||||
impl AgentRuntime {
|
impl AgentRuntime {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: Mutex::new(None),
|
inner: Arc::new(Mutex::new(None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1381,10 +1420,14 @@ impl AgentRuntime {
|
||||||
let status_clone = status.clone();
|
let status_clone = status.clone();
|
||||||
|
|
||||||
let join_handle = async_runtime::spawn(async move {
|
let join_handle = async_runtime::spawn(async move {
|
||||||
|
crate::log_info!("Loop de agente iniciado");
|
||||||
|
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
post_heartbeat(&base_clone, &token_clone, status_clone.clone()).await
|
post_heartbeat(&base_clone, &token_clone, status_clone.clone()).await
|
||||||
{
|
{
|
||||||
eprintln!("[agent] Falha inicial ao enviar heartbeat: {error}");
|
crate::log_error!("Falha inicial ao enviar heartbeat: {error}");
|
||||||
|
} else {
|
||||||
|
crate::log_info!("Heartbeat inicial enviado com sucesso");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifica politica USB apos heartbeat inicial
|
// Verifica politica USB apos heartbeat inicial
|
||||||
|
|
@ -1399,6 +1442,7 @@ impl AgentRuntime {
|
||||||
// Wait interval
|
// Wait interval
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = stop_signal_clone.notified() => {
|
_ = stop_signal_clone.notified() => {
|
||||||
|
crate::log_info!("Loop de agente encerrado por sinal de parada");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_ = heartbeat_ticker.tick() => {}
|
_ = heartbeat_ticker.tick() => {}
|
||||||
|
|
@ -1411,7 +1455,7 @@ impl AgentRuntime {
|
||||||
if let Err(error) =
|
if let Err(error) =
|
||||||
post_heartbeat(&base_clone, &token_clone, status_clone.clone()).await
|
post_heartbeat(&base_clone, &token_clone, status_clone.clone()).await
|
||||||
{
|
{
|
||||||
eprintln!("[agent] Falha ao enviar heartbeat: {error}");
|
crate::log_error!("Falha ao enviar heartbeat: {error}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verifica politica USB apos cada heartbeat
|
// Verifica politica USB apos cada heartbeat
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,76 @@ use tauri_plugin_store::Builder as StorePluginBuilder;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
#[cfg(target_os = "windows")]
|
use std::sync::OnceLock;
|
||||||
use std::process::Command;
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use tauri::menu::{MenuBuilder, MenuItemBuilder};
|
use tauri::menu::{MenuBuilder, MenuItemBuilder};
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use tauri::tray::TrayIconBuilder;
|
use tauri::tray::TrayIconBuilder;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use winreg::enums::*;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use winreg::RegKey;
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Sistema de Logging para Agente
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
static AGENT_LOG_FILE: OnceLock<std::sync::Mutex<std::fs::File>> = OnceLock::new();
|
||||||
|
|
||||||
|
pub fn init_agent_logging() -> Result<(), String> {
|
||||||
|
let dir = logs_directory()
|
||||||
|
.ok_or("LOCALAPPDATA indisponivel para logging")?;
|
||||||
|
|
||||||
|
std::fs::create_dir_all(&dir)
|
||||||
|
.map_err(|e| format!("Falha ao criar diretorio de logs: {e}"))?;
|
||||||
|
|
||||||
|
let path = dir.join("raven-agent.log");
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&path)
|
||||||
|
.map_err(|e| format!("Falha ao abrir raven-agent.log: {e}"))?;
|
||||||
|
|
||||||
|
let _ = AGENT_LOG_FILE.set(std::sync::Mutex::new(file));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn log_agent(level: &str, message: &str) {
|
||||||
|
let timestamp = Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
let line = format!("[{timestamp}] [{level}] {message}\n");
|
||||||
|
|
||||||
|
// Escreve para stderr (util em dev/debug)
|
||||||
|
eprint!("{line}");
|
||||||
|
|
||||||
|
// Escreve para arquivo
|
||||||
|
if let Some(mutex) = AGENT_LOG_FILE.get() {
|
||||||
|
if let Ok(mut file) = mutex.lock() {
|
||||||
|
let _ = file.write_all(line.as_bytes());
|
||||||
|
let _ = file.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_info {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log_agent("INFO", &format!($($arg)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_error {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log_agent("ERROR", &format!($($arg)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! log_warn {
|
||||||
|
($($arg:tt)*) => {
|
||||||
|
$crate::log_agent("WARN", &format!($($arg)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize)]
|
#[derive(Debug, serde::Serialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
|
|
@ -173,10 +237,28 @@ pub fn run() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
|
// Inicializa sistema de logging primeiro
|
||||||
|
if let Err(e) = init_agent_logging() {
|
||||||
|
eprintln!("[raven] Falha ao inicializar logging: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info!("Raven iniciando...");
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
setup_raven_autostart();
|
setup_raven_autostart();
|
||||||
setup_tray(&app.handle())?;
|
setup_tray(&app.handle())?;
|
||||||
|
|
||||||
|
// Tenta iniciar o agente em background se houver credenciais salvas
|
||||||
|
let app_handle = app.handle().clone();
|
||||||
|
let runtime = app.state::<AgentRuntime>().inner().clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
// Aguarda um pouco para o app estabilizar
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
|
||||||
|
if let Err(e) = try_start_background_agent(&app_handle, runtime).await {
|
||||||
|
log_warn!("Agente nao iniciado em background: {e}");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
@ -198,22 +280,53 @@ pub fn run() {
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
fn setup_raven_autostart() {
|
fn setup_raven_autostart() {
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
let exe_path = match std::env::current_exe() {
|
||||||
let path = exe.display().to_string();
|
Ok(p) => p,
|
||||||
let quoted = format!("\"{}\"", path);
|
Err(e) => {
|
||||||
let _ = Command::new("cmd")
|
log_error!("Falha ao obter caminho do executavel: {e}");
|
||||||
.args([
|
return;
|
||||||
"/C",
|
}
|
||||||
"reg",
|
};
|
||||||
"add",
|
|
||||||
r"HKCU\Software\Microsoft\Windows\CurrentVersion\Run",
|
let path_str = exe_path.display().to_string();
|
||||||
"/v",
|
// Adiciona flag --background para indicar inicio automatico
|
||||||
"Raven",
|
let value = format!("\"{}\" --background", path_str);
|
||||||
"/d",
|
|
||||||
"ed,
|
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
|
||||||
"/f",
|
|
||||||
])
|
match hkcu.open_subkey_with_flags(
|
||||||
.status();
|
r"Software\Microsoft\Windows\CurrentVersion\Run",
|
||||||
|
KEY_SET_VALUE | KEY_READ,
|
||||||
|
) {
|
||||||
|
Ok(key) => {
|
||||||
|
match key.set_value("Raven", &value) {
|
||||||
|
Ok(()) => {
|
||||||
|
log_info!("Auto-start configurado: {value}");
|
||||||
|
|
||||||
|
// Valida que foi salvo corretamente
|
||||||
|
match key.get_value::<String, _>("Raven") {
|
||||||
|
Ok(saved) => {
|
||||||
|
if saved == value {
|
||||||
|
log_info!("Auto-start validado: entrada existe no registro");
|
||||||
|
} else {
|
||||||
|
log_warn!(
|
||||||
|
"Auto-start: valor difere. Esperado: {value}, Salvo: {saved}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log_warn!("Auto-start: nao foi possivel validar entrada: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log_error!("Falha ao definir valor de auto-start no registro: {e}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log_error!("Falha ao abrir chave de registro Run: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,3 +372,65 @@ fn setup_tray(app: &tauri::AppHandle) -> tauri::Result<()> {
|
||||||
builder.build(app)?;
|
builder.build(app)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
async fn try_start_background_agent(
|
||||||
|
app: &tauri::AppHandle,
|
||||||
|
runtime: AgentRuntime,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
log_info!("Verificando credenciais salvas para iniciar agente...");
|
||||||
|
|
||||||
|
let app_data = app
|
||||||
|
.path()
|
||||||
|
.app_local_data_dir()
|
||||||
|
.map_err(|e| format!("Falha ao obter diretorio de dados: {e}"))?;
|
||||||
|
|
||||||
|
let store_path = app_data.join("machine-agent.json");
|
||||||
|
|
||||||
|
if !store_path.exists() {
|
||||||
|
return Err("Nenhuma configuracao encontrada".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ler arquivo JSON diretamente
|
||||||
|
let content = std::fs::read_to_string(&store_path)
|
||||||
|
.map_err(|e| format!("Falha ao ler machine-agent.json: {e}"))?;
|
||||||
|
|
||||||
|
let data: serde_json::Value = serde_json::from_str(&content)
|
||||||
|
.map_err(|e| format!("Falha ao parsear machine-agent.json: {e}"))?;
|
||||||
|
|
||||||
|
let token = data
|
||||||
|
.get("token")
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.filter(|t| !t.is_empty())
|
||||||
|
.ok_or("Token nao encontrado ou vazio")?;
|
||||||
|
|
||||||
|
let config = data.get("config");
|
||||||
|
|
||||||
|
let api_base_url = config
|
||||||
|
.and_then(|c| c.get("apiBaseUrl"))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.unwrap_or("https://tickets.esdrasrenan.com.br");
|
||||||
|
|
||||||
|
let interval = config
|
||||||
|
.and_then(|c| c.get("heartbeatIntervalSec"))
|
||||||
|
.and_then(|v| v.as_u64())
|
||||||
|
.unwrap_or(300);
|
||||||
|
|
||||||
|
log_info!(
|
||||||
|
"Iniciando agente em background: url={}, interval={}s",
|
||||||
|
api_base_url,
|
||||||
|
interval
|
||||||
|
);
|
||||||
|
|
||||||
|
runtime
|
||||||
|
.start_heartbeat(
|
||||||
|
api_base_url.to_string(),
|
||||||
|
token.to_string(),
|
||||||
|
Some("online".to_string()),
|
||||||
|
Some(interval),
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("Falha ao iniciar heartbeat: {e}"))?;
|
||||||
|
|
||||||
|
log_info!("Agente iniciado com sucesso em background");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Raven",
|
"productName": "Raven",
|
||||||
"version": "0.1.9",
|
"version": "0.2.0",
|
||||||
"identifier": "br.com.esdrasrenan.sistemadechamados",
|
"identifier": "br.com.esdrasrenan.sistemadechamados",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "bun run dev",
|
"beforeDevCommand": "bun run dev",
|
||||||
|
|
|
||||||
|
|
@ -2318,10 +2318,19 @@ async function upsertRemoteAccessSnapshotFromHeartbeat(
|
||||||
if (!identifier) return
|
if (!identifier) return
|
||||||
|
|
||||||
const existingEntries = normalizeRemoteAccessList(machine.remoteAccess)
|
const existingEntries = normalizeRemoteAccessList(machine.remoteAccess)
|
||||||
const idx = existingEntries.findIndex(
|
|
||||||
|
// Busca primeiro por provider + identifier (atualização exata)
|
||||||
|
let idx = existingEntries.findIndex(
|
||||||
(entry) => entry.provider.toLowerCase() === provider.toLowerCase() && entry.identifier.toLowerCase() === identifier.toLowerCase()
|
(entry) => entry.provider.toLowerCase() === provider.toLowerCase() && entry.identifier.toLowerCase() === identifier.toLowerCase()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Se não encontrou, busca apenas por provider (substituição - ex: RustDesk ID mudou)
|
||||||
|
if (idx < 0) {
|
||||||
|
idx = existingEntries.findIndex(
|
||||||
|
(entry) => entry.provider.toLowerCase() === provider.toLowerCase()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const entryId = idx >= 0 ? existingEntries[idx].id : createRemoteAccessId()
|
const entryId = idx >= 0 ? existingEntries[idx].id : createRemoteAccessId()
|
||||||
const metadata = {
|
const metadata = {
|
||||||
...(normalized.metadata ?? {}),
|
...(normalized.metadata ?? {}),
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@ const getPolicySchema = z.object({
|
||||||
const reportStatusSchema = z.object({
|
const reportStatusSchema = z.object({
|
||||||
machineToken: z.string().min(1),
|
machineToken: z.string().min(1),
|
||||||
status: z.enum(["PENDING", "APPLYING", "APPLIED", "FAILED"]),
|
status: z.enum(["PENDING", "APPLYING", "APPLIED", "FAILED"]),
|
||||||
error: z.string().optional(),
|
error: z.string().nullable().optional(),
|
||||||
currentPolicy: z.string().optional(),
|
currentPolicy: z.string().nullable().optional(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const CORS_METHODS = "GET, POST, OPTIONS"
|
const CORS_METHODS = "GET, POST, OPTIONS"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue