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(())
}