diff --git a/apps/rebreak-magic/app/composables/useDeviceStatus.ts b/apps/rebreak-magic/app/composables/useDeviceStatus.ts new file mode 100644 index 0000000..a12c1fe --- /dev/null +++ b/apps/rebreak-magic/app/composables/useDeviceStatus.ts @@ -0,0 +1,93 @@ +import { computed, type Ref } from "vue"; +import type { MagicDeviceInfo, IphoneDeviceState } from "./useTauri"; + +export type DeviceStatus = "active" | "cooldown" | "revoked" | "pending" | "unprotected"; + +export interface ComputedDevice { + deviceId: string; + name: string; + platform: "mac" | "windows" | "ios" | "android" | "unknown"; + model: string | null; + osVersion: string | null; + status: DeviceStatus; + isCurrent: boolean; + cooldownUntil: string | null; + lastSeenAt: string | null; + enrolledAt: string | null; +} + +const ENROLLMENT_PROFILE_ID = "org.rebreak.mdm.enrollment"; +const LOCK_PROFILE_ID = "org.rebreak.protection.contentfilter.sideload"; +const APP_BUNDLE_ID = "org.rebreak.app"; + +function normalizePlatform(value: string | null | undefined): ComputedDevice["platform"] { + const n = (value ?? "").toLowerCase(); + if (n.startsWith("mac") || n === "darwin") return "mac"; + if (n.startsWith("ios") || n.startsWith("iphone") || n.startsWith("ipad")) return "ios"; + if (n.startsWith("android")) return "android"; + if (n.startsWith("win")) return "windows"; + return "unknown"; +} + +function normalizeHostname(value: string): string { + return (value.toLowerCase().split(".")[0] ?? "").trim(); +} + +export function useDeviceStatus( + devices: Ref, + localHostname: Ref, + iphone: Ref, +) { + const currentBackendDevice = computed(() => { + if (!localHostname.value) return null; + const local = normalizeHostname(localHostname.value); + const found = devices.value.find((d) => normalizeHostname(d.hostname) === local); + if (!found) return null; + return { + deviceId: found.deviceId, + name: found.model ?? found.hostname, + platform: normalizePlatform(found.model ?? found.hostname), + model: found.model, + osVersion: found.osVersion, + status: found.status, + isCurrent: true, + cooldownUntil: found.cooldownUntil, + lastSeenAt: found.lastSeenAt, + enrolledAt: found.magicEnrolledAt, + }; + }); + + const otherDevices = computed(() => { + const currentId = currentBackendDevice.value?.deviceId; + return devices.value + .filter((d) => d.deviceId !== currentId) + .map((d) => ({ + deviceId: d.deviceId, + name: d.model ?? d.hostname, + platform: normalizePlatform(d.model ?? d.hostname), + model: d.model, + osVersion: d.osVersion, + status: d.status, + isCurrent: false, + cooldownUntil: d.cooldownUntil, + lastSeenAt: d.lastSeenAt, + enrolledAt: d.magicEnrolledAt, + })); + }); + + const iosStars = computed(() => { + if (!iphone.value) return null; + return { + enrollment: iphone.value.installedProfileIDs?.includes(ENROLLMENT_PROFILE_ID) ?? false, + sideload: iphone.value.installedProfileIDs?.includes(LOCK_PROFILE_ID) ?? false, + app: iphone.value.installedAppBundleIDs?.includes(APP_BUNDLE_ID) ?? false, + isSupervised: iphone.value.isSupervised, + }; + }); + + return { + currentBackendDevice, + otherDevices, + iosStars, + }; +}