107 lines
2.7 KiB
TypeScript
107 lines
2.7 KiB
TypeScript
/**
|
|
* MDM Healthcheck Cron
|
|
*
|
|
* Läuft alle 5 Minuten. Prüft für alle mit NanoMDM verknüpften iOS-Geräte
|
|
* den aktuellen Enrollment-/Supervision-Status und spiegelt ihn auf UserDevice.
|
|
*/
|
|
import { consola } from "consola";
|
|
import {
|
|
getLinkedUserDevices,
|
|
getMdmEnrollmentStatusesByUdids,
|
|
updateUserDeviceMdmHealth,
|
|
type MdmEnrollmentStatus,
|
|
} from "../db/mdm";
|
|
|
|
const FIVE_MINUTES = 5 * 60 * 1000;
|
|
const INITIAL_DELAY_MS = 30 * 1000;
|
|
|
|
let running = false;
|
|
|
|
export default defineNitroPlugin((nitro) => {
|
|
if (import.meta.dev) {
|
|
consola.info("[mdm-health-cron] Skipping cron in dev mode");
|
|
return;
|
|
}
|
|
|
|
consola.info("[mdm-health-cron] Starting (5min interval)");
|
|
|
|
const initialTimer = setTimeout(() => {
|
|
runMdmHealthCheck().catch(() => {});
|
|
}, INITIAL_DELAY_MS);
|
|
|
|
const interval = setInterval(() => {
|
|
runMdmHealthCheck().catch(() => {});
|
|
}, FIVE_MINUTES);
|
|
|
|
nitro.hooks.hook("close", () => {
|
|
clearTimeout(initialTimer);
|
|
clearInterval(interval);
|
|
});
|
|
});
|
|
|
|
async function runMdmHealthCheck() {
|
|
if (running) {
|
|
consola.info("[mdm-health-cron] Previous run still in progress, skipping");
|
|
return;
|
|
}
|
|
|
|
running = true;
|
|
const start = Date.now();
|
|
|
|
try {
|
|
const devices = await getLinkedUserDevices();
|
|
if (devices.length === 0) {
|
|
consola.info("[mdm-health-cron] No linked iOS devices");
|
|
return;
|
|
}
|
|
|
|
const statuses = await getMdmEnrollmentStatusesByUdids(
|
|
devices.map((d) => d.mdmId).filter((id): id is string => id != null),
|
|
);
|
|
|
|
let updated = 0;
|
|
let unchanged = 0;
|
|
|
|
for (const device of devices) {
|
|
const status: MdmEnrollmentStatus = statuses.get(device.mdmId ?? "") ?? {
|
|
enrolled: false,
|
|
supervised: false,
|
|
lastSeenAt: null,
|
|
};
|
|
|
|
const changed =
|
|
device.mdmEnrolled !== status.enrolled ||
|
|
device.mdmSupervised !== status.supervised ||
|
|
!sameNullableDate(device.mdmLastSeenAt, status.lastSeenAt);
|
|
|
|
if (!changed) {
|
|
unchanged++;
|
|
continue;
|
|
}
|
|
|
|
try {
|
|
await updateUserDeviceMdmHealth(device.id, status);
|
|
updated++;
|
|
} catch (err: any) {
|
|
consola.error(
|
|
`[mdm-health-cron] Failed to update device ${device.id}: ${err?.message ?? err}`,
|
|
);
|
|
}
|
|
}
|
|
|
|
consola.success(
|
|
`[mdm-health-cron] Checked ${devices.length} devices in ${Date.now() - start}ms (${updated} updated, ${unchanged} unchanged)`,
|
|
);
|
|
} catch (err: any) {
|
|
consola.error("[mdm-health-cron] run failed:", err?.message ?? err);
|
|
} finally {
|
|
running = false;
|
|
}
|
|
}
|
|
|
|
function sameNullableDate(a: Date | null, b: Date | null): boolean {
|
|
if (a === null && b === null) return true;
|
|
if (a === null || b === null) return false;
|
|
return a.getTime() === b.getTime();
|
|
}
|