App desktop (Tauri), GitHub Actions de CI/release, elevação automática
- Move a UI de crates/sabia-cli/src/web/ para apps/gui/ui/: fonte única servida tanto pelo `sabia ui` quanto pelo app desktop. - sabia-core::engine::Engine centraliza sessões de scan/monitor/update — usado pelo servidor local da CLI e pelo app Tauri, sem duplicar lógica. - apps/gui/src-tauri: app desktop Tauri 2, mesma interface, comandos IPC espelhando a API HTTP. Pede elevação de administrador/root automaticamente ao abrir (modo monitor e captura crua exigem): manifesto requireAdministrator no Windows (UAC antes mesmo do app abrir), pkexec no Linux e osascript no macOS, com reinício automático e aviso + botão de nova tentativa na interface se for negada. - Ícone gerado a partir do logo (apps/gui/src-tauri/icons/source.png). - .github/workflows/ci.yml: fmt, clippy, testes (Linux/Windows/macOS), auditoria de dependências, compilação do app desktop. - .github/workflows/release.yml: ao empurrar uma tag vX.Y.Z, builda a CLI (Linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64) e o app desktop pras mesmas plataformas, publica tudo numa release do GitHub com checksums SHA-256. - sabia-core::update: corrige o nome do asset esperado (casava pelo nome fixo, mas o release empacota com a versão no nome) e a extração (tar.gz no Linux/macOS, zip no Windows). - LICENSE-MIT, LICENSE-APACHE, CHANGELOG.md com a entrada 0.1.0. - cargo fmt + clippy (-D warnings) limpos em todo o workspace.
This commit is contained in:
1 parent
fd3ef5240e
commit
df27974359
29 files changed
+1268
-191
No files matched your search
@@ -0,0 +1,139 @@
|
||||
//! Ponte única entre o núcleo e as duas interfaces (servidor local da CLI e
|
||||
//! o app desktop Tauri): ambos chamam exatamente os mesmos métodos, então
|
||||
//! qualquer coisa que funcione em um funciona idêntico no outro.
|
||||
|
||||
use crate::audit::AuditLog;
|
||||
use crate::error::Result;
|
||||
use crate::iface::{self, Interface};
|
||||
use crate::netlink;
|
||||
use crate::scan::{AccessPoint, ScanTable};
|
||||
use crate::update::{self, UpdateStatus};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
struct Session {
|
||||
table: Arc<Mutex<ScanTable>>,
|
||||
running: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
pub struct Engine {
|
||||
sessions: Mutex<HashMap<String, Session>>,
|
||||
audit_path: PathBuf,
|
||||
}
|
||||
|
||||
impl Default for Engine {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
sessions: Mutex::new(HashMap::new()),
|
||||
audit_path: PathBuf::from(".sabia-audit.log"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn version(&self) -> &'static str {
|
||||
env!("CARGO_PKG_VERSION")
|
||||
}
|
||||
|
||||
pub fn list_ifaces(&self) -> Result<Vec<Interface>> {
|
||||
iface::list_wireless()
|
||||
}
|
||||
|
||||
pub fn set_monitor(&self, name: &str, enable: bool) -> Result<()> {
|
||||
netlink::set_monitor_mode(name, enable)?;
|
||||
if let Ok(mut log) = AuditLog::open(&self.audit_path) {
|
||||
let _ = log.record("monitor_mode", name, &format!("{{\"enable\":{enable}}}"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn start_scan(&self, name: &str) -> Result<()> {
|
||||
use crate::capture::RawCapture;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
let mut sessions = self.sessions.lock().unwrap();
|
||||
if sessions.contains_key(name) {
|
||||
return Ok(());
|
||||
}
|
||||
let table = Arc::new(Mutex::new(ScanTable::new()));
|
||||
let running = Arc::new(AtomicBool::new(true));
|
||||
|
||||
let t = table.clone();
|
||||
let r = running.clone();
|
||||
let iface_name = name.to_string();
|
||||
thread::spawn(move || {
|
||||
let cap = match RawCapture::open(&iface_name) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("falha ao abrir captura em {iface_name}: {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let _ = cap.set_read_timeout(Duration::from_millis(500));
|
||||
let channels = netlink::supported_channels(&iface_name).unwrap_or_default();
|
||||
let mut idx = 0usize;
|
||||
let mut last_hop = std::time::Instant::now();
|
||||
let mut buf = [0u8; 65535];
|
||||
while r.load(Ordering::SeqCst) {
|
||||
if !channels.is_empty() && last_hop.elapsed() > Duration::from_millis(300) {
|
||||
idx = (idx + 1) % channels.len();
|
||||
let _ = netlink::set_channel(&iface_name, channels[idx]);
|
||||
last_hop = std::time::Instant::now();
|
||||
}
|
||||
if let Ok(Some(n)) = cap.recv(&mut buf) {
|
||||
let mut table = t.lock().unwrap();
|
||||
table.ingest(&buf[..n]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if let Ok(mut log) = AuditLog::open(&self.audit_path) {
|
||||
let _ = log.record("scan_start", name, "{}");
|
||||
}
|
||||
sessions.insert(name.to_string(), Session { table, running });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Captura crua (AF_PACKET) só existe no Linux (ver `crate::capture`).
|
||||
/// Em macOS/Windows o app compila e roda, mas a captura ainda não tem
|
||||
/// backend nativo — próximo passo natural do projeto.
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
pub fn start_scan(&self, _name: &str) -> Result<()> {
|
||||
Err(crate::error::SabiaError::UnsupportedPlatform)
|
||||
}
|
||||
|
||||
pub fn stop_scan(&self, name: &str) {
|
||||
let mut sessions = self.sessions.lock().unwrap();
|
||||
if let Some(s) = sessions.remove(name) {
|
||||
s.running.store(false, Ordering::SeqCst);
|
||||
if let Ok(mut log) = AuditLog::open(&self.audit_path) {
|
||||
let _ = log.record("scan_stop", name, "{}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan_results(&self, name: &str) -> Vec<AccessPoint> {
|
||||
let sessions = self.sessions.lock().unwrap();
|
||||
match sessions.get(name) {
|
||||
Some(s) => s.table.lock().unwrap().access_points(),
|
||||
None => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_check(&self) -> Result<UpdateStatus> {
|
||||
update::check(self.version())
|
||||
}
|
||||
|
||||
pub fn update_apply(&self) -> Result<String> {
|
||||
let exe = std::env::current_exe()?;
|
||||
update::apply(&exe)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user