Include recent Magic app work: Tauri native shell, iOS device detection via supervise-magic sidecar, MDM client, local HTTP server, new pages (detect, enroll, supervise, sideload, pair, preflight, configure, done), and updated device section/status UI.
319 lines
10 KiB
Rust
319 lines
10 KiB
Rust
mod backend;
|
|
mod config;
|
|
mod error;
|
|
mod ios_device;
|
|
mod mdm;
|
|
mod platform;
|
|
mod server;
|
|
mod sidecar;
|
|
|
|
use backend::api::{
|
|
MagicApiClient, MagicDeviceInfo, MdmStatusByUdidData, MdmStatusData, RedeemPairingResponse,
|
|
RegisterDeviceResponse, ReleaseResponse, UserProfile,
|
|
};
|
|
use config::{AppConfig, DesktopProtectionState, MagicSession};
|
|
use error::AppResult;
|
|
use std::process::Command;
|
|
|
|
pub fn run() {
|
|
tauri::Builder::default()
|
|
.plugin(tauri_plugin_shell::init())
|
|
.plugin(tauri_plugin_fs::init())
|
|
.plugin(tauri_plugin_http::init())
|
|
.plugin(tauri_plugin_os::init())
|
|
.invoke_handler(tauri::generate_handler![
|
|
platform::get_platform,
|
|
server::local_http::start_local_profile_server,
|
|
server::local_http::stop_local_profile_server,
|
|
sidecar::supervise_magic::run_supervise_magic,
|
|
ios_device::detect_iphone_state,
|
|
ios_device::get_supervise_status,
|
|
ios_device::get_installed_profiles,
|
|
ios_device::get_installed_apps,
|
|
ios_device::install_profile,
|
|
ios_device::download_and_patch_enrollment_profile,
|
|
mdm::mdm_ping,
|
|
mdm::mdm_push,
|
|
mdm::mdm_install_app,
|
|
mdm::mdm_set_supervised_mode,
|
|
mdm::mdm_take_management,
|
|
mdm::mdm_install_lock_profile,
|
|
redeem_pairing_code,
|
|
register_device,
|
|
get_stored_session,
|
|
get_magic_devices,
|
|
get_magic_status,
|
|
request_release,
|
|
cancel_release,
|
|
start_cooldown,
|
|
cancel_cooldown,
|
|
logout_magic,
|
|
download_profile,
|
|
activate_protection,
|
|
fetch_me,
|
|
get_mdm_status,
|
|
get_mdm_status_by_udid,
|
|
link_mdm_device,
|
|
report_device_protection_state,
|
|
get_desktop_protection_status,
|
|
set_desktop_protection_status,
|
|
get_hostname,
|
|
get_hardware_id,
|
|
get_device_id,
|
|
])
|
|
.run(tauri::generate_context!())
|
|
.expect("error while running tauri application");
|
|
}
|
|
|
|
fn hostname() -> AppResult<String> {
|
|
let output = Command::new("hostname")
|
|
.output()
|
|
.map_err(|e| error::AppError::new(format!("Failed to get hostname: {}", e)))?;
|
|
let mut hostname = String::from_utf8_lossy(&output.stdout).to_string();
|
|
hostname = hostname.trim().to_string();
|
|
if hostname.is_empty() {
|
|
hostname = "ReBreak Magic Device".to_string();
|
|
}
|
|
Ok(hostname)
|
|
}
|
|
|
|
fn get_or_init_hardware_id(config: &mut AppConfig) -> AppResult<String> {
|
|
if let Some(id) = config.hardware_id.clone() {
|
|
return Ok(id);
|
|
}
|
|
let id = platform::get_hardware_id()?;
|
|
config.set_hardware_id(id.clone())?;
|
|
Ok(id)
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn get_hardware_id() -> AppResult<String> {
|
|
let mut config = AppConfig::load();
|
|
get_or_init_hardware_id(&mut config)
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn get_device_id() -> AppResult<Option<String>> {
|
|
Ok(AppConfig::load().device_id)
|
|
}
|
|
|
|
fn require_session() -> AppResult<MagicSession> {
|
|
AppConfig::load_magic_session()?.ok_or_else(|| {
|
|
error::AppError::new("Keine Magic-Session gefunden. Bitte neu paaren.")
|
|
})
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn redeem_pairing_code(code: String, label: Option<String>) -> AppResult<RedeemPairingResponse> {
|
|
let trimmed = code.trim().to_string();
|
|
if !trimmed.chars().all(|c| c.is_ascii_digit()) || trimmed.len() != 6 {
|
|
return Err(error::AppError::new("Der Code besteht aus 6 Ziffern.".to_string()));
|
|
}
|
|
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
let response = client
|
|
.redeem_pairing_code(&trimmed, label.as_deref())
|
|
.await?;
|
|
|
|
let label = label.unwrap_or_else(|| hostname().unwrap_or_else(|_| "ReBreak Magic".to_string()));
|
|
let session = MagicSession::new(response.token.clone(), response.session_id.clone(), Some(label));
|
|
AppConfig::save_magic_session(&session)?;
|
|
|
|
Ok(response)
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn register_device(
|
|
model: Option<String>,
|
|
os_version: Option<String>,
|
|
) -> AppResult<RegisterDeviceResponse> {
|
|
let session = require_session()?;
|
|
let mut config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
let hostname = hostname()?;
|
|
|
|
// Hardware-ID ist der stabile Identifikator; deviceId wird vom Backend zurückgeliefert.
|
|
let hardware_id = get_or_init_hardware_id(&mut config)?;
|
|
let response = client
|
|
.register_device(
|
|
&session.access_token,
|
|
config.device_id.as_deref(),
|
|
Some(&hardware_id),
|
|
&hostname,
|
|
model.as_deref(),
|
|
os_version.as_deref(),
|
|
)
|
|
.await?;
|
|
|
|
config.set_device_id(response.device_id.clone())?;
|
|
Ok(response)
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn get_stored_session() -> AppResult<Option<MagicSession>> {
|
|
AppConfig::load_magic_session()
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn get_magic_devices() -> AppResult<Vec<MagicDeviceInfo>> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.list_devices(&session.access_token).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn get_magic_status(dns_token: String) -> AppResult<bool> {
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.get_status(&dns_token).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn request_release(device_id: String) -> AppResult<ReleaseResponse> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.request_release(&session.access_token, &device_id).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn cancel_release(device_id: String) -> AppResult<()> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.cancel_release(&session.access_token, &device_id).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn start_cooldown(device_id: String, duration_minutes: u32) -> AppResult<serde_json::Value> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
let result = client.start_cooldown(&session.access_token, &device_id, duration_minutes).await?;
|
|
Ok(serde_json::json!({ "cooldownUntil": result.cooldown_until }))
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn cancel_cooldown(device_id: String) -> AppResult<()> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.cancel_cooldown(&session.access_token, &device_id).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn logout_magic() -> AppResult<()> {
|
|
AppConfig::clear_magic_session()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn activate_protection(profile_path: String) -> AppResult<()> {
|
|
platform::activate_protection(&profile_path)
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn fetch_me() -> AppResult<UserProfile> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.fetch_me(&session.access_token).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn get_mdm_status(device_id: String) -> AppResult<MdmStatusData> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.get_mdm_status(&session.access_token, &device_id).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn link_mdm_device(device_id: String, mdm_id: String) -> AppResult<()> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.link_mdm_device(&session.access_token, &device_id, &mdm_id).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn report_device_protection_state(
|
|
device_id: String,
|
|
platform: String,
|
|
protection_type: String,
|
|
active: bool,
|
|
reason: Option<String>,
|
|
) -> AppResult<()> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client
|
|
.report_device_protection_state(
|
|
&session.access_token,
|
|
&device_id,
|
|
&platform,
|
|
&protection_type,
|
|
active,
|
|
reason.as_deref(),
|
|
)
|
|
.await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn get_mdm_status_by_udid(udid: String) -> AppResult<MdmStatusByUdidData> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
client.get_mdm_status_by_udid(&session.access_token, &udid).await
|
|
}
|
|
|
|
#[tauri::command]
|
|
async fn download_profile(profile_url: String) -> AppResult<String> {
|
|
let session = require_session()?;
|
|
let config = AppConfig::load();
|
|
let client = MagicApiClient::new(&config);
|
|
|
|
// Try to extract dns_token from profileUrl, fallback to direct token lookup
|
|
let dns_token = extract_dns_token(&profile_url)
|
|
.or_else(|| Some(session.access_token.clone()))
|
|
.ok_or_else(|| error::AppError::new("Kein DNS-Token verfügbar".to_string()))?;
|
|
|
|
let bytes = client.download_profile(&dns_token).await?;
|
|
|
|
let config_dir = AppConfig::config_dir()?;
|
|
std::fs::create_dir_all(&config_dir)?;
|
|
let profile_path = config_dir.join("rebreak-iphone-protect.mobileconfig");
|
|
std::fs::write(&profile_path, bytes)?;
|
|
|
|
Ok(profile_path.to_string_lossy().to_string())
|
|
}
|
|
|
|
fn extract_dns_token(profile_url: &str) -> Option<String> {
|
|
profile_url.split("token=").nth(1).map(|s| s.split('&').next().unwrap_or(s).to_string())
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn get_desktop_protection_status() -> AppResult<Option<DesktopProtectionState>> {
|
|
AppConfig::load_desktop_protection()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn get_hostname() -> AppResult<String> {
|
|
hostname()
|
|
}
|
|
|
|
#[tauri::command]
|
|
fn set_desktop_protection_status(active: bool, platform: String) -> AppResult<()> {
|
|
if active {
|
|
let state = DesktopProtectionState {
|
|
active,
|
|
platform,
|
|
activated_at: chrono::Utc::now(),
|
|
};
|
|
AppConfig::save_desktop_protection(&state)
|
|
} else {
|
|
AppConfig::clear_desktop_protection()
|
|
}
|
|
}
|