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.
133 lines
3.7 KiB
Vue
133 lines
3.7 KiB
Vue
<template>
|
|
<div class="min-h-screen flex flex-col items-center justify-center bg-gray-50 p-6">
|
|
<div class="max-w-md w-full space-y-6">
|
|
<div class="text-center">
|
|
<div class="w-16 h-16 mx-auto bg-[var(--rebreak-primary)] rounded-xl flex items-center justify-center mb-4">
|
|
<UIcon name="i-heroicons-qr-code" class="w-8 h-8 text-white" />
|
|
</div>
|
|
<h1 class="text-2xl font-bold text-gray-900">
|
|
Schutz-Profil installieren
|
|
</h1>
|
|
<p class="text-gray-600 mt-2">
|
|
Scanne den QR-Code mit deinem iPhone und installiere das Schutz-Profil.
|
|
</p>
|
|
</div>
|
|
|
|
<UCard class="text-center">
|
|
<div class="space-y-4">
|
|
<UButton
|
|
v-if="!serverInfo"
|
|
size="lg"
|
|
color="primary"
|
|
block
|
|
:loading="starting"
|
|
@click="startServer"
|
|
>
|
|
QR-Code generieren
|
|
</UButton>
|
|
|
|
<div v-else class="space-y-4">
|
|
<div class="bg-white p-4 rounded-lg inline-block">
|
|
<img :src="qrCodeDataUrl" alt="QR Code" class="w-48 h-48">
|
|
</div>
|
|
<p class="text-sm text-gray-500 break-all">
|
|
{{ serverInfo.url }}
|
|
</p>
|
|
<UButton
|
|
size="sm"
|
|
color="neutral"
|
|
variant="ghost"
|
|
@click="stopServer"
|
|
>
|
|
Server stoppen
|
|
</UButton>
|
|
</div>
|
|
|
|
<p v-if="error" class="text-sm text-red-600">
|
|
{{ error }}
|
|
</p>
|
|
</div>
|
|
</UCard>
|
|
|
|
<div class="bg-blue-50 text-blue-800 text-sm p-4 rounded-lg">
|
|
<p class="font-semibold mb-1">So geht's:</p>
|
|
<ol class="list-decimal list-inside space-y-1">
|
|
<li>iPhone-Kamera öffnen und QR-Code scannen</li>
|
|
<li>Link in Safari öffnen</li>
|
|
<li>„Einstellungen" → „Profil installieren" tippen</li>
|
|
<li>Geräte-Passcode eingeben und bestätigen</li>
|
|
</ol>
|
|
</div>
|
|
|
|
<div class="flex justify-between">
|
|
<UButton to="/supervise" variant="ghost" color="neutral">
|
|
Zurück
|
|
</UButton>
|
|
<UButton to="/enroll" variant="solid" color="primary" :disabled="!serverInfo">
|
|
Weiter
|
|
</UButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, watch, onUnmounted } from "vue";
|
|
import QRCode from "qrcode";
|
|
import { useTauri, type LocalServerInfo } from "~/composables/useTauri";
|
|
import { useMagicSession } from "~/composables/useMagicState";
|
|
|
|
const { startLocalProfileServer, stopLocalProfileServer, downloadProfile } = useTauri();
|
|
const session = useMagicSession();
|
|
|
|
const starting = ref(false);
|
|
const serverInfo = ref<LocalServerInfo | null>(null);
|
|
const qrCodeDataUrl = ref("");
|
|
const error = ref<string | null>(null);
|
|
|
|
watch(
|
|
serverInfo,
|
|
async (info) => {
|
|
if (info) {
|
|
qrCodeDataUrl.value = await QRCode.toDataURL(info.qr_payload, {
|
|
width: 192,
|
|
margin: 2,
|
|
});
|
|
} else {
|
|
qrCodeDataUrl.value = "";
|
|
}
|
|
},
|
|
{ immediate: true },
|
|
);
|
|
|
|
onUnmounted(async () => {
|
|
if (serverInfo.value) {
|
|
await stopLocalProfileServer();
|
|
}
|
|
});
|
|
|
|
async function startServer() {
|
|
starting.value = true;
|
|
error.value = null;
|
|
|
|
try {
|
|
if (!session.value?.profileUrl) {
|
|
error.value = "Kein Profil verfügbar. Bitte zuerst das iPhone koppeln.";
|
|
return;
|
|
}
|
|
|
|
const profilePath = await downloadProfile(session.value.profileUrl);
|
|
serverInfo.value = await startLocalProfileServer(profilePath);
|
|
} catch (e: any) {
|
|
error.value = e?.message ?? "QR-Code konnte nicht erzeugt werden";
|
|
} finally {
|
|
starting.value = false;
|
|
}
|
|
}
|
|
|
|
async function stopServer() {
|
|
await stopLocalProfileServer();
|
|
serverInfo.value = null;
|
|
}
|
|
</script>
|