feat(backend): add bulk MDM health status helpers
Add MdmEnrollmentStatus, UserDeviceMdmHealthRecord types and helpers: - getLinkedUserDevices() to load iOS devices with NanoMDM UDIDs - getMdmEnrollmentStatusesByUdids() for bulk NanoMDM lookups - updateUserDeviceMdmHealth() to mirror status on UserDevice Also fix PoolConfig option casing: queryTimeout -> query_timeout.
This commit is contained in:
parent
b107262d60
commit
74784fc4da
@ -24,7 +24,7 @@ function useMdmPool(): pg.Pool {
|
|||||||
// NanoMDM queries are point lookups — keep pool small.
|
// NanoMDM queries are point lookups — keep pool small.
|
||||||
max: 5,
|
max: 5,
|
||||||
connectionTimeoutMillis: 5000,
|
connectionTimeoutMillis: 5000,
|
||||||
queryTimeout: 5000,
|
query_timeout: 5000,
|
||||||
});
|
});
|
||||||
|
|
||||||
return _mdmPool;
|
return _mdmPool;
|
||||||
@ -46,6 +46,17 @@ const USER_DEVICE_MDM_SELECT = {
|
|||||||
mdmId: true,
|
mdmId: true,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
const USER_DEVICE_MDM_HEALTH_SELECT = {
|
||||||
|
id: true,
|
||||||
|
userId: true,
|
||||||
|
deviceId: true,
|
||||||
|
platform: true,
|
||||||
|
mdmId: true,
|
||||||
|
mdmEnrolled: true,
|
||||||
|
mdmSupervised: true,
|
||||||
|
mdmLastSeenAt: true,
|
||||||
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a user's iOS device by Capacitor deviceId.
|
* Find a user's iOS device by Capacitor deviceId.
|
||||||
*/
|
*/
|
||||||
@ -90,6 +101,17 @@ export async function clearUserDeviceMdmId(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all iOS devices that have a NanoMDM UDID link.
|
||||||
|
*/
|
||||||
|
export async function getLinkedUserDevices(): Promise<UserDeviceMdmHealthRecord[]> {
|
||||||
|
const db = usePrisma();
|
||||||
|
return db.userDevice.findMany({
|
||||||
|
where: { platform: "ios", mdmId: { not: null } },
|
||||||
|
select: USER_DEVICE_MDM_HEALTH_SELECT,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export interface MdmDeviceStatus {
|
export interface MdmDeviceStatus {
|
||||||
enrolled: boolean;
|
enrolled: boolean;
|
||||||
company: string | null;
|
company: string | null;
|
||||||
@ -99,6 +121,23 @@ export interface MdmDeviceStatus {
|
|||||||
lastAppPushAt: Date | null;
|
lastAppPushAt: Date | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MdmEnrollmentStatus {
|
||||||
|
enrolled: boolean;
|
||||||
|
supervised: boolean;
|
||||||
|
lastSeenAt: Date | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UserDeviceMdmHealthRecord {
|
||||||
|
id: string;
|
||||||
|
userId: string;
|
||||||
|
deviceId: string;
|
||||||
|
platform: string;
|
||||||
|
mdmId: string | null;
|
||||||
|
mdmEnrolled: boolean | null;
|
||||||
|
mdmSupervised: boolean | null;
|
||||||
|
mdmLastSeenAt: Date | null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query NanoMDM Postgres for a device by UDID.
|
* Query NanoMDM Postgres for a device by UDID.
|
||||||
*
|
*
|
||||||
@ -144,3 +183,64 @@ export async function getMdmStatusByUdid(
|
|||||||
lastAppPushAt: row?.last_app_push_at ?? null,
|
lastAppPushAt: row?.last_app_push_at ?? null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk-query NanoMDM for enrollment/supervision/last-seen status.
|
||||||
|
* Returns a map keyed by UDID. Missing devices are omitted.
|
||||||
|
*
|
||||||
|
* Throws if the MDM DB is unreachable — callers should treat this as an
|
||||||
|
* infra/runtime error and not cache a negative result.
|
||||||
|
*/
|
||||||
|
export async function getMdmEnrollmentStatusesByUdids(
|
||||||
|
udids: string[],
|
||||||
|
): Promise<Map<string, MdmEnrollmentStatus>> {
|
||||||
|
if (udids.length === 0) {
|
||||||
|
return new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
const pool = useMdmPool();
|
||||||
|
const result = await pool.query<{
|
||||||
|
udid: string;
|
||||||
|
enrolled: boolean;
|
||||||
|
supervised: boolean;
|
||||||
|
last_seen_at: Date | null;
|
||||||
|
}>(
|
||||||
|
`SELECT
|
||||||
|
d.id AS udid,
|
||||||
|
COALESCE(e.enabled = TRUE, FALSE) AS enrolled,
|
||||||
|
(d.unlock_token IS NOT NULL) AS supervised,
|
||||||
|
e.last_seen_at
|
||||||
|
FROM devices d
|
||||||
|
LEFT JOIN enrollments e ON e.device_id = d.id
|
||||||
|
WHERE d.id = ANY($1::text[])`,
|
||||||
|
[udids],
|
||||||
|
);
|
||||||
|
|
||||||
|
const map = new Map<string, MdmEnrollmentStatus>();
|
||||||
|
for (const row of result.rows) {
|
||||||
|
map.set(row.udid, {
|
||||||
|
enrolled: row.enrolled,
|
||||||
|
supervised: row.supervised,
|
||||||
|
lastSeenAt: row.last_seen_at,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persist mirrored MDM health status on a UserDevice row.
|
||||||
|
*/
|
||||||
|
export async function updateUserDeviceMdmHealth(
|
||||||
|
id: string,
|
||||||
|
status: MdmEnrollmentStatus,
|
||||||
|
): Promise<void> {
|
||||||
|
const db = usePrisma();
|
||||||
|
await db.userDevice.update({
|
||||||
|
where: { id },
|
||||||
|
data: {
|
||||||
|
mdmEnrolled: status.enrolled,
|
||||||
|
mdmSupervised: status.supervised,
|
||||||
|
mdmLastSeenAt: status.lastSeenAt,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user