diff --git a/.gitignore b/.gitignore index 25396ba..8646f08 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,9 @@ Thumbs.db # Claude Code agent state (lokale Definitionen, nicht versioniert) .claude/ +# Kimi Code / Sixth plugin state +.sixth/ + # xgit binary (generated) xgit diff --git a/apps/rebreak-magic/app/components/IosDeviceCard.vue b/apps/rebreak-magic/app/components/IosDeviceCard.vue index 0a95b4b..4ba6329 100644 --- a/apps/rebreak-magic/app/components/IosDeviceCard.vue +++ b/apps/rebreak-magic/app/components/IosDeviceCard.vue @@ -195,18 +195,60 @@

Lock-Profil

+ +
+ + 1. Server starten + + + + 2. QR-Code scannen + + + + 3. Prüfen + +
+ + +
+
+ Lock-Profil QR-Code +
+

+ Scanne den Code mit der iPhone-Kamera und installiere das Lock-Profil. +

+ + Installation prüfen + +
+
- Lock-Profil wird per MDM auf das iPhone gepusht … + Lock-Profil-Server wird gestartet …
- ✓ Lock-Profil-Installation initiiert. Das Gerät aktualisiert den Schutz in Kürze. + ✓ Lock-Profil installiert. Das Gerät aktualisiert den Schutz in Kürze.
✗ {{ lockError || "Lock-Profil-Installation fehlgeschlagen" }} @@ -283,7 +325,6 @@ const { stopLocalProfileServer, getInstalledProfiles, mdmPush, - mdmInstallLockProfile, } = useTauri(); const enrollmentPhase = ref<"idle" | "loading" | "waiting" | "checking" | "success" | "error">("idle"); @@ -292,9 +333,10 @@ const enrollmentQrUrl = ref(""); const enrollmentError = ref(null); const enrollmentLogs = ref([]); -const lockPhase = ref<"idle" | "loading" | "success" | "error">("idle"); +const lockPhase = ref<"idle" | "loading" | "waiting" | "checking" | "success" | "error">("idle"); const lockError = ref(null); const lockLogs = ref([]); +const lockQrUrl = ref(""); const LOCK_PROFILE_PATH = "/Users/chahinebrini/mono/rebreak-monorepo/ops/mdm/profiles/rebreak-content-filter-sideload.mobileconfig"; @@ -780,6 +822,15 @@ async function checkInlineEnrollment() { } await refreshMdmStatus(); + + // After successful enrollment, automatically continue to lock profile if needed. + if (!localLock.value) { + enrollmentLogs.value.push("→ Enrollment abgeschlossen. Starte Lock-Profil …"); + closeInlineEnrollment(); + await startInlineLockProfile(); + return; + } + enrollmentPhase.value = "success"; } catch (e: any) { enrollmentError.value = e?.message ?? "Prüfung fehlgeschlagen"; @@ -797,29 +848,60 @@ function closeInlineEnrollment() { } async function startInlineLockProfile() { - if (!props.iphone?.udid) return; - lockPhase.value = "loading"; lockError.value = null; lockLogs.value = []; + lockQrUrl.value = ""; try { - lockLogs.value.push("→ Installiere Lock-Profil per MDM …"); - const result = await mdmInstallLockProfile(props.iphone.udid, LOCK_PROFILE_PATH); - lockLogs.value.push(`✓ Command UUID: ${result.command_uuid}`); + lockLogs.value.push("→ Starte lokalen Server für Lock-Profil …"); + const serverInfo = await startLocalProfileServer(LOCK_PROFILE_PATH); + lockLogs.value.push(`✓ Server gestartet: ${serverInfo.url}`); + lockQrUrl.value = await QRCode.toDataURL(serverInfo.qr_payload, { + width: 192, + margin: 2, + }); + + lockPhase.value = "waiting"; + } catch (e: any) { + lockError.value = e?.message ?? "Lock-Profil-Server konnte nicht gestartet werden"; + lockLogs.value.push(`✗ ${lockError.value}`); + lockPhase.value = "error"; + } +} + +async function checkInlineLockProfile() { + if (!props.iphone) return; + + lockPhase.value = "checking"; + lockError.value = null; + + try { + const ids = await getInstalledProfiles(); + props.iphone.installedProfileIDs = ids; + + if (!ids.includes(LOCK_PROFILE_ID)) { + lockError.value = "Lock-Profil noch nicht installiert. Bitte QR-Code scannen und Profil installieren."; + lockPhase.value = "error"; + return; + } + + lockLogs.value.push("✓ Lock-Profil erkannt"); await refreshMdmStatus(); lockPhase.value = "success"; } catch (e: any) { - lockError.value = e?.message ?? "Lock-Profil-Installation fehlgeschlagen"; + lockError.value = e?.message ?? "Prüfung fehlgeschlagen"; lockLogs.value.push(`✗ ${lockError.value}`); lockPhase.value = "error"; } } function closeInlineLockProfile() { + stopLocalProfileServer(); lockPhase.value = "idle"; lockError.value = null; lockLogs.value = []; + lockQrUrl.value = ""; } diff --git a/apps/rebreak-magic/src-tauri/src/server/local_http.rs b/apps/rebreak-magic/src-tauri/src/server/local_http.rs index 4da1802..fc8b4c0 100644 --- a/apps/rebreak-magic/src-tauri/src/server/local_http.rs +++ b/apps/rebreak-magic/src-tauri/src/server/local_http.rs @@ -3,12 +3,9 @@ use serde::{Deserialize, Serialize}; use std::fs; use std::net::SocketAddr; use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; use tiny_http::{Response, Server}; -static SERVER_RUNNING: AtomicBool = AtomicBool::new(false); - #[derive(Debug, Clone, Serialize, Deserialize)] pub struct LocalServerInfo { pub url: String, @@ -17,10 +14,6 @@ pub struct LocalServerInfo { #[tauri::command] pub fn start_local_profile_server(profile_path: String) -> AppResult { - if SERVER_RUNNING.load(Ordering::SeqCst) { - return Err(AppError::new("Local server is already running")); - } - let path = PathBuf::from(profile_path); if !path.exists() { return Err(AppError::new(format!( @@ -48,8 +41,6 @@ pub fn start_local_profile_server(profile_path: String) -> AppResult AppResult AppResult AppResult<()> { // tiny_http does not support graceful shutdown out of the box. - // In a real implementation, store the server handle and close it. - SERVER_RUNNING.store(false, Ordering::SeqCst); + // Old server threads keep running, but each new profile gets a fresh port. Ok(()) }