rebreak-monorepo/backend/server/plugins/mdm-health-cron.ts
chahinebrini e20b21e0ef
Some checks failed
Build ReBreak Magic Windows / NSIS Installer (x64) (push) Waiting to run
ci/woodpecker/push/woodpecker Pipeline failed
Deploy Staging / Build backend (Nitro) (push) Has been cancelled
Deploy Staging / Deploy zu Hetzner (push) Has been cancelled
feat(mdm): healthcheck sends ProfileList, disables nefilter when lock profile missing; cfgutil fallback in Magic App
2026-06-18 14:21:45 +02:00

144 lines
4.1 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,
getLatestProfileListResult,
getMdmEnrollmentStatusesByUdids,
enqueueProfileListCommand,
isLockProfileInstalled,
markNefilterInactiveIfLockProfileMissing,
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;
let profileListEnqueued = 0;
let nefilterDisabled = 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) {
try {
await updateUserDeviceMdmHealth(device.id, status);
updated++;
} catch (err: any) {
consola.error(
`[mdm-health-cron] Failed to update device ${device.id}: ${err?.message ?? err}`,
);
}
} else {
unchanged++;
}
// ── Lock-profile check via ProfileList ───────────────────────────────
if (status.enrolled && device.mdmId) {
try {
const profileList = await getLatestProfileListResult(device.mdmId);
if (profileList) {
const missing = !isLockProfileInstalled(profileList.result);
if (missing) {
await markNefilterInactiveIfLockProfileMissing(
device.userId,
device.deviceId,
profileList.result,
profileList.updatedAt,
);
nefilterDisabled++;
}
}
} catch (err: any) {
consola.error(
`[mdm-health-cron] ProfileList parse failed for ${device.mdmId}: ${err?.message ?? err}`,
);
}
try {
await enqueueProfileListCommand(device.mdmId);
profileListEnqueued++;
} catch (err: any) {
consola.error(
`[mdm-health-cron] Failed to enqueue ProfileList for ${device.mdmId}: ${err?.message ?? err}`,
);
}
}
}
consola.success(
`[mdm-health-cron] Checked ${devices.length} devices in ${Date.now() - start}ms (${updated} updated, ${unchanged} unchanged, ${profileListEnqueued} ProfileList enqueued, ${nefilterDisabled} nefilter disabled)`,
);
} 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();
}