feat(desktop): adiciona Raven Service e corrige UAC

- Implementa Windows Service (raven-service) para operacoes privilegiadas
- Comunicacao via Named Pipes sem necessidade de UAC adicional
- Adiciona single-instance para evitar multiplos icones na bandeja
- Corrige todos os warnings do clippy (rustdesk, lib, usb_control, agent)
- Remove fallback de elevacao para evitar UAC desnecessario
- USB Policy e RustDesk provisioning agora usam o servico quando disponivel

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
esdrasrenan 2025-12-15 02:30:43 -03:00
parent caa6c53b2b
commit c4664ab1c7
16 changed files with 4209 additions and 143 deletions

View file

@ -1,5 +1,3 @@
#![cfg(target_os = "windows")]
use crate::RustdeskProvisioningResult;
use chrono::{Local, Utc};
use once_cell::sync::Lazy;
@ -30,7 +28,9 @@ const LOCAL_SERVICE_CONFIG: &str = r"C:\\Windows\\ServiceProfiles\\LocalService\
const LOCAL_SYSTEM_CONFIG: &str = r"C:\\Windows\\System32\\config\\systemprofile\\AppData\\Roaming\\RustDesk\\config";
const APP_IDENTIFIER: &str = "br.com.esdrasrenan.sistemadechamados";
const MACHINE_STORE_FILENAME: &str = "machine-agent.json";
#[allow(dead_code)]
const ACL_FLAG_FILENAME: &str = "rustdesk_acl_unlocked.flag";
#[allow(dead_code)]
const RUSTDESK_ACL_STORE_KEY: &str = "rustdeskAclUnlockedAt";
const SECURITY_VERIFICATION_VALUE: &str = "use-permanent-password";
const SECURITY_APPROVE_MODE_VALUE: &str = "password";
@ -85,11 +85,11 @@ fn define_custom_id_from_machine(exe_path: &Path, machine_id: Option<&str>) -> O
}) {
match set_custom_id(exe_path, value) {
Ok(custom) => {
log_event(&format!("ID determinístico definido: {custom}"));
log_event(format!("ID determinístico definido: {custom}"));
Some(custom)
}
Err(error) => {
log_event(&format!("Falha ao definir ID determinístico: {error}"));
log_event(format!("Falha ao definir ID determinístico: {error}"));
None
}
}
@ -107,7 +107,7 @@ pub fn ensure_rustdesk(
log_event("Iniciando preparo do RustDesk");
if let Err(error) = ensure_service_profiles_writable_preflight() {
log_event(&format!(
log_event(format!(
"Aviso: não foi possível preparar ACL dos perfis do serviço ({error}). Continuando mesmo assim; o serviço pode não aplicar a senha."
));
}
@ -116,7 +116,7 @@ pub fn ensure_rustdesk(
// Isso preserva o ID quando o Raven é reinstalado mas o RustDesk permanece
let preserved_remote_id = read_remote_id_from_profiles();
if let Some(ref id) = preserved_remote_id {
log_event(&format!("ID existente preservado antes da limpeza: {}", id));
log_event(format!("ID existente preservado antes da limpeza: {}", id));
}
let exe_path = detect_executable_path();
@ -129,7 +129,7 @@ pub fn ensure_rustdesk(
match stop_rustdesk_processes() {
Ok(_) => log_event("Instâncias existentes do RustDesk encerradas"),
Err(error) => log_event(&format!(
Err(error) => log_event(format!(
"Aviso: não foi possível parar completamente o RustDesk antes da reprovisionamento ({error})"
)),
}
@ -139,7 +139,7 @@ pub fn ensure_rustdesk(
if freshly_installed {
match purge_existing_rustdesk_profiles() {
Ok(_) => log_event("Configurações antigas do RustDesk limpas (instalação fresca)"),
Err(error) => log_event(&format!(
Err(error) => log_event(format!(
"Aviso: não foi possível limpar completamente os perfis existentes do RustDesk ({error})"
)),
}
@ -152,19 +152,19 @@ pub fn ensure_rustdesk(
if trimmed.is_empty() { None } else { Some(trimmed) }
}) {
if let Err(error) = run_with_args(&exe_path, &["--config", value]) {
log_event(&format!("Falha ao aplicar configuração inline: {error}"));
log_event(format!("Falha ao aplicar configuração inline: {error}"));
} else {
log_event("Configuração aplicada via --config");
}
} else {
let config_path = write_config_files()?;
log_event(&format!(
log_event(format!(
"Arquivo de configuração atualizado em {}",
config_path.display()
));
if let Err(error) = apply_config(&exe_path, &config_path) {
log_event(&format!("Falha ao aplicar configuração via CLI: {error}"));
log_event(format!("Falha ao aplicar configuração via CLI: {error}"));
} else {
log_event("Configuração aplicada via CLI");
}
@ -176,7 +176,7 @@ pub fn ensure_rustdesk(
.unwrap_or_else(|| DEFAULT_PASSWORD.to_string());
if let Err(error) = set_password(&exe_path, &password) {
log_event(&format!("Falha ao definir senha padrão: {error}"));
log_event(format!("Falha ao definir senha padrão: {error}"));
} else {
log_event("Senha padrão definida com sucesso");
log_event("Aplicando senha nos perfis do RustDesk");
@ -185,21 +185,21 @@ pub fn ensure_rustdesk(
log_event("Senha e flags de segurança gravadas em todos os perfis do RustDesk");
log_password_replication(&password);
}
Err(error) => log_event(&format!("Falha ao persistir senha nos perfis: {error}")),
Err(error) => log_event(format!("Falha ao persistir senha nos perfis: {error}")),
}
match propagate_password_profile() {
Ok(_) => log_event("Perfil base propagado para ProgramData e perfis de serviço"),
Err(error) => log_event(&format!("Falha ao copiar perfil de senha: {error}")),
Err(error) => log_event(format!("Falha ao copiar perfil de senha: {error}")),
}
match replicate_password_artifacts() {
Ok(_) => log_event("Artefatos de senha replicados para o serviço do RustDesk"),
Err(error) => log_event(&format!("Falha ao replicar artefatos de senha: {error}")),
Err(error) => log_event(format!("Falha ao replicar artefatos de senha: {error}")),
}
if let Err(error) = enforce_security_flags() {
log_event(&format!("Falha ao reforçar configuração de senha permanente: {error}"));
log_event(format!("Falha ao reforçar configuração de senha permanente: {error}"));
}
}
@ -207,7 +207,7 @@ pub fn ensure_rustdesk(
// Isso garante que reinstalar o Raven nao muda o ID do RustDesk
let custom_id = if let Some(ref existing_id) = preserved_remote_id {
if !freshly_installed {
log_event(&format!("Reutilizando ID existente do RustDesk: {}", existing_id));
log_event(format!("Reutilizando ID existente do RustDesk: {}", existing_id));
Some(existing_id.clone())
} else {
// Instalacao fresca - define novo ID baseado no machine_id
@ -219,7 +219,7 @@ pub fn ensure_rustdesk(
};
if let Err(error) = ensure_service_running(&exe_path) {
log_event(&format!("Falha ao reiniciar serviço do RustDesk: {error}"));
log_event(format!("Falha ao reiniciar serviço do RustDesk: {error}"));
} else {
log_event("Serviço RustDesk reiniciado/run ativo");
}
@ -227,10 +227,10 @@ pub fn ensure_rustdesk(
let reported_id = match query_id_with_retries(&exe_path, 5) {
Ok(value) => value,
Err(error) => {
log_event(&format!("Falha ao obter ID após múltiplas tentativas: {error}"));
log_event(format!("Falha ao obter ID após múltiplas tentativas: {error}"));
match read_remote_id_from_profiles().or_else(|| custom_id.clone()) {
Some(value) => {
log_event(&format!("ID obtido via arquivos de perfil: {value}"));
log_event(format!("ID obtido via arquivos de perfil: {value}"));
value
}
None => return Err(error),
@ -242,7 +242,7 @@ pub fn ensure_rustdesk(
if let Some(expected) = custom_id.as_ref() {
if expected != &reported_id {
log_event(&format!(
log_event(format!(
"ID retornado difere do determinístico ({expected}) -> reaplicando ID determinístico"
));
@ -252,25 +252,25 @@ pub fn ensure_rustdesk(
Ok(_) => match query_id_with_retries(&exe_path, 3) {
Ok(rechecked) => {
if &rechecked == expected {
log_event(&format!("ID determinístico aplicado com sucesso: {rechecked}"));
log_event(format!("ID determinístico aplicado com sucesso: {rechecked}"));
final_id = rechecked;
enforced = true;
} else {
log_event(&format!(
log_event(format!(
"ID ainda difere após reaplicação (esperado {expected}, reportado {rechecked}); usando ID reportado"
));
final_id = rechecked;
}
}
Err(error) => {
log_event(&format!(
log_event(format!(
"Falha ao consultar ID após reaplicação: {error}; usando ID reportado ({reported_id})"
));
final_id = reported_id.clone();
}
},
Err(error) => {
log_event(&format!(
log_event(format!(
"Falha ao reaplicar ID determinístico ({expected}): {error}; usando ID reportado ({reported_id})"
));
final_id = reported_id.clone();
@ -308,7 +308,7 @@ pub fn ensure_rustdesk(
"lastError": serde_json::Value::Null
});
if let Err(error) = upsert_machine_store_value("rustdesk", rustdesk_data) {
log_event(&format!("Aviso: falha ao salvar dados do RustDesk no store: {error}"));
log_event(format!("Aviso: falha ao salvar dados do RustDesk no store: {error}"));
} else {
log_event("Dados do RustDesk salvos no machine-agent.json");
}
@ -316,7 +316,7 @@ pub fn ensure_rustdesk(
// Sincroniza com o backend imediatamente apos provisionar
// O Rust faz o HTTP direto, sem passar pelo CSP do webview
if let Err(error) = sync_remote_access_with_backend(&result) {
log_event(&format!("Aviso: falha ao sincronizar com backend: {error}"));
log_event(format!("Aviso: falha ao sincronizar com backend: {error}"));
} else {
log_event("Acesso remoto sincronizado com backend");
// Atualiza lastSyncedAt no store
@ -330,13 +330,13 @@ pub fn ensure_rustdesk(
"lastError": serde_json::Value::Null
});
if let Err(e) = upsert_machine_store_value("rustdesk", synced_data) {
log_event(&format!("Aviso: falha ao atualizar lastSyncedAt: {e}"));
log_event(format!("Aviso: falha ao atualizar lastSyncedAt: {e}"));
} else {
log_event("lastSyncedAt atualizado com sucesso");
}
}
log_event(&format!("Provisionamento concluído. ID final: {final_id}. Versão: {:?}", version));
log_event(format!("Provisionamento concluído. ID final: {final_id}. Versão: {:?}", version));
Ok(result)
}
@ -403,7 +403,7 @@ fn write_config_files() -> Result<PathBuf, RustdeskError> {
let config_contents = build_config_contents();
let main_path = program_data_config_dir().join("RustDesk2.toml");
write_file(&main_path, &config_contents)?;
log_event(&format!(
log_event(format!(
"Config principal gravada em {}",
main_path.display()
));
@ -412,7 +412,7 @@ fn write_config_files() -> Result<PathBuf, RustdeskError> {
for service_dir in service_profile_dirs() {
let service_profile = service_dir.join("RustDesk2.toml");
if let Err(error) = write_file(&service_profile, &config_contents) {
log_event(&format!(
log_event(format!(
"Falha ao gravar config no perfil do serviço ({}): {error}",
service_profile.display()
));
@ -421,7 +421,7 @@ fn write_config_files() -> Result<PathBuf, RustdeskError> {
if let Some(appdata_path) = user_appdata_config_path("RustDesk2.toml") {
if let Err(error) = write_file(&appdata_path, &config_contents) {
log_event(&format!(
log_event(format!(
"Falha ao atualizar config no AppData do usuário: {error}"
));
}
@ -516,7 +516,7 @@ fn ensure_service_running(exe_path: &Path) -> Result<(), RustdeskError> {
ensure_service_installed(exe_path)?;
if let Err(error) = configure_service_startup() {
log_event(&format!(
log_event(format!(
"Aviso: não foi possível reforçar autostart/recuperação do serviço RustDesk: {error}"
));
}
@ -553,7 +553,7 @@ fn ensure_service_running(exe_path: &Path) -> Result<(), RustdeskError> {
let _ = run_with_args(exe_path, &["--install-service"]);
let _ = run_sc(&["config", SERVICE_NAME, &format!("start= {}", "auto")]);
if let Err(error) = start_sequence() {
log_event(&format!(
log_event(format!(
"Falha ao subir o serviço RustDesk mesmo após reinstalação: {error}"
));
}
@ -631,8 +631,8 @@ fn remove_rustdesk_autorun_artifacts() {
for path in startup_paths {
if path.exists() {
match fs::remove_file(&path) {
Ok(_) => log_event(&format!("Atalho de inicialização do RustDesk removido: {}", path.display())),
Err(error) => log_event(&format!(
Ok(_) => log_event(format!("Atalho de inicialização do RustDesk removido: {}", path.display())),
Err(error) => log_event(format!(
"Falha ao remover atalho de inicialização do RustDesk ({}): {}",
path.display(),
error
@ -650,7 +650,7 @@ fn remove_rustdesk_autorun_artifacts() {
.status();
if let Ok(code) = status {
if code.success() {
log_event(&format!("Entrada de auto-run RustDesk removida de {}", reg_path));
log_event(format!("Entrada de auto-run RustDesk removida de {}", reg_path));
}
}
}
@ -658,7 +658,7 @@ fn remove_rustdesk_autorun_artifacts() {
fn stop_rustdesk_processes() -> Result<(), RustdeskError> {
if let Err(error) = try_stop_service() {
log_event(&format!(
log_event(format!(
"Não foi possível parar o serviço RustDesk antes da sincronização: {error}"
));
}
@ -774,12 +774,12 @@ fn ensure_remote_id_files(id: &str) {
for dir in remote_id_directories() {
let path = dir.join("RustDesk_local.toml");
match write_remote_id_value(&path, id) {
Ok(_) => log_event(&format!(
Ok(_) => log_event(format!(
"remote_id atualizado para {} em {}",
id,
path.display()
)),
Err(error) => log_event(&format!(
Err(error) => log_event(format!(
"Falha ao atualizar remote_id em {}: {error}",
path.display()
)),
@ -821,7 +821,7 @@ fn ensure_password_files(secret: &str) -> Result<(), String> {
if let Err(error) = write_toml_kv(&password_path, "password", secret) {
errors.push(format!("{} -> {}", password_path.display(), error));
} else {
log_event(&format!(
log_event(format!(
"Senha escrita via fallback em {}",
password_path.display()
));
@ -829,12 +829,12 @@ fn ensure_password_files(secret: &str) -> Result<(), String> {
let local_path = dir.join("RustDesk_local.toml");
if let Err(error) = write_toml_kv(&local_path, "verification-method", SECURITY_VERIFICATION_VALUE) {
log_event(&format!(
log_event(format!(
"Falha ao ajustar verification-method em {}: {error}",
local_path.display()
));
} else {
log_event(&format!(
log_event(format!(
"verification-method atualizado para {} em {}",
SECURITY_VERIFICATION_VALUE,
local_path.display()
@ -843,19 +843,19 @@ fn ensure_password_files(secret: &str) -> Result<(), String> {
let rustdesk2_path = dir.join("RustDesk2.toml");
if let Err(error) = enforce_security_in_rustdesk2(&rustdesk2_path) {
log_event(&format!(
log_event(format!(
"Falha ao ajustar flags no RustDesk2.toml em {}: {error}",
rustdesk2_path.display()
));
}
if let Err(error) = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE) {
log_event(&format!(
log_event(format!(
"Falha ao ajustar approve-mode em {}: {error}",
local_path.display()
));
} else {
log_event(&format!(
log_event(format!(
"approve-mode atualizado para {} em {}",
SECURITY_APPROVE_MODE_VALUE,
local_path.display()
@ -877,7 +877,7 @@ fn enforce_security_flags() -> Result<(), String> {
if let Err(error) = write_toml_kv(&local_path, "verification-method", SECURITY_VERIFICATION_VALUE) {
errors.push(format!("{} -> {}", local_path.display(), error));
} else {
log_event(&format!(
log_event(format!(
"verification-method atualizado para {} em {}",
SECURITY_VERIFICATION_VALUE,
local_path.display()
@ -887,7 +887,7 @@ fn enforce_security_flags() -> Result<(), String> {
if let Err(error) = write_toml_kv(&local_path, "approve-mode", SECURITY_APPROVE_MODE_VALUE) {
errors.push(format!("{} -> {}", local_path.display(), error));
} else {
log_event(&format!(
log_event(format!(
"approve-mode atualizado para {} em {}",
SECURITY_APPROVE_MODE_VALUE,
local_path.display()
@ -921,7 +921,7 @@ fn propagate_password_profile() -> io::Result<bool> {
if !src_path.exists() {
continue;
}
log_event(&format!(
log_event(format!(
"Copiando {} para ProgramData/serviços",
src_path.display()
));
@ -929,7 +929,7 @@ fn propagate_password_profile() -> io::Result<bool> {
for dest_root in propagation_destinations() {
let target_path = dest_root.join(filename);
copy_overwrite(&src_path, &target_path)?;
log_event(&format!(
log_event(format!(
"{} propagado para {}",
filename,
target_path.display()
@ -969,7 +969,7 @@ fn replicate_password_artifacts() -> io::Result<()> {
let target_path = dest.join(name);
copy_overwrite(&source_path, &target_path)?;
log_event(&format!(
log_event(format!(
"Artefato de senha {name} replicado para {}",
target_path.display()
));
@ -981,13 +981,11 @@ fn replicate_password_artifacts() -> io::Result<()> {
fn purge_existing_rustdesk_profiles() -> Result<(), String> {
let mut errors = Vec::new();
let mut cleaned_any = false;
for dir in remote_id_directories() {
match purge_config_dir(&dir) {
Ok(true) => {
cleaned_any = true;
log_event(&format!(
log_event(format!(
"Perfis antigos removidos em {}",
dir.display()
));
@ -997,9 +995,7 @@ fn purge_existing_rustdesk_profiles() -> Result<(), String> {
}
}
if cleaned_any {
Ok(())
} else if errors.is_empty() {
if errors.is_empty() {
Ok(())
} else {
Err(errors.join(" | "))
@ -1030,6 +1026,7 @@ fn purge_config_dir(dir: &Path) -> Result<bool, io::Error> {
Ok(removed)
}
#[allow(dead_code)]
fn run_powershell_elevated(script: &str) -> Result<(), String> {
let temp_dir = env::temp_dir();
let payload = temp_dir.join("raven_payload.ps1");
@ -1077,6 +1074,7 @@ exit $process.ExitCode
Err(format!("elevated ps exit {:?}", status.code()))
}
#[allow(dead_code)]
fn fix_profile_acl(target: &Path) -> Result<(), String> {
let target_str = target.display().to_string();
let transcript = env::temp_dir().join("raven_acl_ps.log");
@ -1111,7 +1109,7 @@ try {{
let result = run_powershell_elevated(&script);
if result.is_err() {
if let Ok(content) = fs::read_to_string(&transcript) {
log_event(&format!(
log_event(format!(
"ACL transcript para {}:\n{}",
target.display(), content
));
@ -1122,6 +1120,9 @@ try {{
}
fn ensure_service_profiles_writable_preflight() -> Result<(), String> {
// Verificamos se os diretorios de perfil sao graváveis
// Se nao forem, apenas logamos aviso - o Raven Service deve lidar com isso
// Nao usamos elevacao para evitar UAC adicional
let mut blocked_dirs = Vec::new();
for dir in service_profile_dirs() {
if !can_write_dir(&dir) {
@ -1133,53 +1134,46 @@ fn ensure_service_profiles_writable_preflight() -> Result<(), String> {
return Ok(());
}
if has_acl_unlock_flag() {
log_event("Perfis do serviço voltaram a bloquear escrita; reaplicando correção de ACL");
} else {
log_event("Executando ajuste inicial de ACL dos perfis do serviço (requer UAC)");
}
// Apenas logamos aviso - o serviço RavenService deve lidar com permissões
log_event(format!(
"Aviso: alguns perfis de serviço não são graváveis: {:?}. O Raven Service deve configurar permissões.",
blocked_dirs.iter().map(|d| d.display().to_string()).collect::<Vec<_>>()
));
let mut last_error: Option<String> = None;
for dir in blocked_dirs.iter() {
log_event(&format!(
"Tentando corrigir ACL via UAC (preflight) em {}...",
dir.display()
));
if let Err(error) = fix_profile_acl(dir) {
last_error = Some(error);
continue;
}
if can_write_dir(dir) {
log_event(&format!(
"ACL ajustada com sucesso em {}",
dir.display()
));
} else {
last_error = Some(format!(
"continua sem permissão para {} mesmo após preflight",
dir.display()
));
}
}
if blocked_dirs.iter().all(|dir| can_write_dir(dir)) {
mark_acl_unlock_flag();
Ok(())
} else {
Err(last_error.unwrap_or_else(|| "nenhum perfil de serviço acessível".into()))
}
// Retornamos Ok para não bloquear o fluxo
// O Raven Service, rodando como LocalSystem, pode gravar nesses diretórios
Ok(())
}
fn stop_service_elevated() -> Result<(), String> {
let script = r#"
$ErrorActionPreference='Stop'
$service = Get-Service -Name 'RustDesk' -ErrorAction SilentlyContinue
if ($service -and $service.Status -ne 'Stopped') {
Stop-Service -Name 'RustDesk' -Force -ErrorAction Stop
$service.WaitForStatus('Stopped','00:00:10')
}
"#;
run_powershell_elevated(script)
// Tentamos parar o serviço RustDesk sem elevação
// Se falhar, apenas logamos aviso - o Raven Service pode lidar com isso
// Não usamos elevação para evitar UAC adicional
let output = Command::new("sc")
.args(["stop", "RustDesk"])
.output();
match output {
Ok(result) => {
if result.status.success() {
// Aguarda um pouco para o serviço parar
std::thread::sleep(std::time::Duration::from_secs(2));
Ok(())
} else {
let stderr = String::from_utf8_lossy(&result.stderr);
log_event(format!(
"Aviso: não foi possível parar o serviço RustDesk sem elevação: {}",
stderr.trim()
));
// Retornamos Ok para não bloquear - o serviço pode estar já parado
Ok(())
}
}
Err(e) => {
log_event(format!("Aviso: falha ao executar sc stop RustDesk: {e}"));
Ok(())
}
}
}
fn can_write_dir(dir: &Path) -> bool {
@ -1339,21 +1333,21 @@ fn log_password_replication(secret: &str) {
fn log_password_match(path: &Path, secret: &str) {
match read_password_from_file(path) {
Some(value) if value == secret => {
log_event(&format!(
log_event(format!(
"Senha confirmada em {} ({})",
path.display(),
mask_secret(&value)
));
}
Some(value) => {
log_event(&format!(
log_event(format!(
"Aviso: senha divergente ({}) em {}",
mask_secret(&value),
path.display()
));
}
None => {
log_event(&format!(
log_event(format!(
"Aviso: chave 'password' não encontrada em {}",
path.display()
));
@ -1469,21 +1463,24 @@ fn write_machine_store_object(map: JsonMap<String, JsonValue>) -> Result<(), Str
}
fn upsert_machine_store_value(key: &str, value: JsonValue) -> Result<(), String> {
let mut map = read_machine_store_object().unwrap_or_else(JsonMap::new);
let mut map = read_machine_store_object().unwrap_or_default();
map.insert(key.to_string(), value);
write_machine_store_object(map)
}
#[allow(dead_code)]
fn machine_store_key_exists(key: &str) -> bool {
read_machine_store_object()
.map(|map| map.contains_key(key))
.unwrap_or(false)
}
#[allow(dead_code)]
fn acl_flag_file_path() -> Option<PathBuf> {
raven_appdata_root().map(|dir| dir.join(ACL_FLAG_FILENAME))
}
#[allow(dead_code)]
fn has_acl_unlock_flag() -> bool {
if let Some(flag) = acl_flag_file_path() {
if flag.exists() {
@ -1493,6 +1490,7 @@ fn has_acl_unlock_flag() -> bool {
machine_store_key_exists(RUSTDESK_ACL_STORE_KEY)
}
#[allow(dead_code)]
fn mark_acl_unlock_flag() {
let timestamp = Utc::now().timestamp_millis();
if let Some(flag_path) = acl_flag_file_path() {
@ -1500,7 +1498,7 @@ fn mark_acl_unlock_flag() {
let _ = fs::create_dir_all(parent);
}
if let Err(error) = fs::write(&flag_path, timestamp.to_string()) {
log_event(&format!(
log_event(format!(
"Falha ao gravar flag de ACL em {}: {error}",
flag_path.display()
));
@ -1508,7 +1506,7 @@ fn mark_acl_unlock_flag() {
}
if let Err(error) = upsert_machine_store_value(RUSTDESK_ACL_STORE_KEY, JsonValue::from(timestamp)) {
log_event(&format!(
log_event(format!(
"Falha ao registrar flag de ACL no machine-agent: {error}"
));
}
@ -1547,7 +1545,7 @@ fn sync_remote_access_with_backend(result: &crate::RustdeskProvisioningResult) -
.and_then(|v| v.as_str())
.unwrap_or("https://tickets.esdrasrenan.com.br");
log_event(&format!("Sincronizando com backend: {} (machineId: {})", api_base_url, machine_id));
log_event(format!("Sincronizando com backend: {} (machineId: {})", api_base_url, machine_id));
// Monta payload conforme schema esperado pelo backend
// Schema: { machineToken, provider, identifier, password?, url?, username?, notes? }
@ -1575,13 +1573,13 @@ fn sync_remote_access_with_backend(result: &crate::RustdeskProvisioningResult) -
.send()?;
if response.status().is_success() {
log_event(&format!("Sync com backend OK: status {}", response.status()));
log_event(format!("Sync com backend OK: status {}", response.status()));
Ok(())
} else {
let status = response.status();
let body = response.text().unwrap_or_default();
let body_preview = if body.len() > 200 { &body[..200] } else { &body };
log_event(&format!("Sync com backend falhou: {} - {}", status, body_preview));
log_event(format!("Sync com backend falhou: {} - {}", status, body_preview));
Err(RustdeskError::CommandFailed {
command: "sync_remote_access".to_string(),
status: Some(status.as_u16() as i32)