Previously read template via process.cwd() + 'ops/mdm/…' — but pm2
runs the bundled output from /root, not the repo root, so the path
resolved to /root/ops/mdm/… (does not exist) → HTTP 500 'Profile
template not found' after Mac registration.
Switch to Nitro's serverAssets (baseName 'mdm', dir '../ops/mdm')
which is bundled at build-time and read via
useStorage('assets:server'). cwd-independent + survives any deploy
layout change.
87 lines
2.8 KiB
TypeScript
87 lines
2.8 KiB
TypeScript
import { randomUUID } from "crypto";
|
||
import { findMagicDeviceByToken } from "../../db/devices";
|
||
|
||
/**
|
||
* GET /api/magic/profile.mobileconfig?token=<dnsToken>
|
||
*
|
||
* Generiert personalisiertes DNS-Configuration-Profile für macOS.
|
||
* Template: ops/mdm/rebreak-mac-dns-filter.mobileconfig (via Nitro serverAssets
|
||
* unter baseName "mdm" eingebundelt — siehe nitro.config.ts).
|
||
*
|
||
* Ersetzt:
|
||
* - ServerURL: /dns-query → /dns-query/{token}
|
||
* - PayloadUUID: 2× neu generieren (DNSSettings + Profile root)
|
||
* - PayloadIdentifier: unique pro Device
|
||
*
|
||
* TODO: Profile-Signierung via Apple Developer Certificate (Phase 2)
|
||
*/
|
||
export default defineEventHandler(async (event) => {
|
||
const query = getQuery(event);
|
||
const token = query.token as string | undefined;
|
||
|
||
if (!token) {
|
||
throw createError({
|
||
statusCode: 400,
|
||
message: "token query parameter required",
|
||
});
|
||
}
|
||
|
||
// Token in DB suchen (nur aktive, nicht revoked)
|
||
const device = await findMagicDeviceByToken(token);
|
||
if (!device) {
|
||
throw createError({
|
||
statusCode: 404,
|
||
message: "Invalid or revoked DNS token",
|
||
});
|
||
}
|
||
|
||
// Template via Nitro serverAssets lesen (build-time eingebundelt → cwd-unabhängig).
|
||
const storage = useStorage("assets:server");
|
||
const template = (await storage.getItem(
|
||
"mdm/rebreak-mac-dns-filter.mobileconfig",
|
||
)) as string | null;
|
||
|
||
if (!template) {
|
||
console.error(
|
||
"[Magic] Profile template missing in serverAssets (mdm/rebreak-mac-dns-filter.mobileconfig)",
|
||
);
|
||
throw createError({
|
||
statusCode: 500,
|
||
message: "Profile template not found",
|
||
});
|
||
}
|
||
|
||
// ServerURL ersetzen: /dns-query → /dns-query/{token}
|
||
const personalizedProfile = template
|
||
.replace(
|
||
"https://dns.rebreak.org/dns-query",
|
||
`https://dns.rebreak.org/dns-query/${token}`,
|
||
)
|
||
// PayloadUUID neu generieren (2 Stellen im Template)
|
||
.replace("7D2E8B1A-C3D4-4E76-8B23-A4B5C6D7E8F0", randomUUID().toUpperCase())
|
||
.replace("8C3F9A2B-D4E5-4F87-9A12-B5C6D7E8F901", randomUUID().toUpperCase())
|
||
// PayloadIdentifier unique machen (optional, verhindert Konflikt bei Multi-Device)
|
||
.replace(
|
||
"org.rebreak.protection.dns.filter",
|
||
`org.rebreak.protection.dns.filter.${device.deviceId.slice(0, 8)}`,
|
||
)
|
||
.replace(
|
||
"org.rebreak.protection.profile",
|
||
`org.rebreak.protection.profile.${device.deviceId.slice(0, 8)}`,
|
||
);
|
||
|
||
// Response-Headers
|
||
setHeader(event, "Content-Type", "application/x-apple-aspen-config");
|
||
setHeader(
|
||
event,
|
||
"Content-Disposition",
|
||
`attachment; filename="RebreakMagic-${device.deviceId.slice(0, 8)}.mobileconfig"`,
|
||
);
|
||
|
||
// TODO: Profile-Signierung via /usr/bin/security cms -S
|
||
// Requires: Apple Developer Certificate + Private Key in Keychain
|
||
// Siehe: https://developer.apple.com/documentation/devicemanagement/profile-specific_payload_keys
|
||
|
||
return personalizedProfile;
|
||
});
|