108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import { computed, watchEffect, 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] ?? "").replace(/[^a-z0-9]/g, "");
|
|
}
|
|
|
|
export function useDeviceStatus(
|
|
devices: Ref<MagicDeviceInfo[]>,
|
|
localHostname: Ref<string | null>,
|
|
iphone: Ref<IphoneDeviceState | null>,
|
|
currentDeviceId?: Ref<string | null>,
|
|
) {
|
|
function isCurrentDevice(d: MagicDeviceInfo): boolean {
|
|
if (currentDeviceId?.value) {
|
|
return d.deviceId === currentDeviceId.value;
|
|
}
|
|
if (!localHostname.value) return false;
|
|
const local = normalizeHostname(localHostname.value);
|
|
return normalizeHostname(d.hostname) === local;
|
|
}
|
|
|
|
const currentBackendDevice = computed<ComputedDevice | null>(() => {
|
|
const found = devices.value.find(isCurrentDevice);
|
|
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<ComputedDevice[]>(() => {
|
|
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,
|
|
};
|
|
});
|
|
|
|
// DEBUG: log current device matching details
|
|
watchEffect(() => {
|
|
const ids = devices.value.map((d) => ({ deviceId: d.deviceId, hostname: d.hostname, platform: d.model ?? d.hostname }));
|
|
console.log("[useDeviceStatus] local deviceId:", currentDeviceId?.value ?? "(none)", "hostname:", localHostname.value ?? "(none)", "backend ids:", ids, "matched:", currentBackendDevice.value?.deviceId ?? "(none)");
|
|
});
|
|
|
|
return {
|
|
currentBackendDevice,
|
|
otherDevices,
|
|
iosStars,
|
|
};
|
|
}
|