Backend: - ProtectedDevice prisma model + migration add_protected_devices - DB helpers: list/count/get/create/confirm/revoke - mobileconfig.ts utility — XML-escape, unique UUIDs per request - 5 endpoints under /api/devices/* (avoid /api/devices conflict with existing Capacitor UserDevice route by using /api/devices/protected for list) Phase 1: backend ready. DoH-server token-routing comes in phase 2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
46 lines
1.3 KiB
TypeScript
46 lines
1.3 KiB
TypeScript
import { getProtectedDevice } from "../../../db/protectedDevices";
|
|
import {
|
|
generateMacOSDnsProfile,
|
|
labelToSlug,
|
|
} from "../../../utils/mobileconfig";
|
|
|
|
/**
|
|
* GET /api/devices/:id/profile.mobileconfig
|
|
*
|
|
* PUBLIC — der Mac muss ohne Auth-Header zugreifen können.
|
|
* Der dnsToken im Profil IST die Device-Authentifizierung beim DoH-Server.
|
|
*
|
|
* Liefert ein macOS DNS-Over-HTTPS Konfigurationsprofil.
|
|
* Content-Type: application/x-apple-aspen-config
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const id = getRouterParam(event, "id");
|
|
if (!id) throw createError({ statusCode: 400, data: { error: "ID_REQUIRED" } });
|
|
|
|
const device = await getProtectedDevice(id);
|
|
|
|
if (!device || device.status === "revoked") {
|
|
throw createError({ statusCode: 404, data: { error: "DEVICE_NOT_FOUND" } });
|
|
}
|
|
|
|
const plist = generateMacOSDnsProfile({
|
|
deviceId: device.id,
|
|
dnsToken: device.dnsToken,
|
|
label: device.label,
|
|
});
|
|
|
|
const slug = labelToSlug(device.label);
|
|
const filename = `rebreak-${slug || "schutz"}.mobileconfig`;
|
|
|
|
setHeader(event, "Content-Type", "application/x-apple-aspen-config");
|
|
setHeader(
|
|
event,
|
|
"Content-Disposition",
|
|
`attachment; filename="${filename}"`,
|
|
);
|
|
// Kein Caching — Token ist sensitiv
|
|
setHeader(event, "Cache-Control", "no-store");
|
|
|
|
return plist;
|
|
});
|