import { getHeader } from "h3"; import { requireUser } from "../../utils/auth"; import { appendProtectionEventDeduped, type ProtectionSource, } from "../../db/protectionStateLog"; import { upsertDeviceProtectionState } from "../../db/device-protection"; import type { ProtectionType } from "../../db/device-protection"; const VALID_SOURCES: ProtectionSource[] = ["vpn", "mdm", "client"]; function sourceToProtectionType(source: ProtectionSource): ProtectionType { if (source === "mdm") return "nefilter"; return "vpn"; } /** * POST /api/protection/event * * Body: { * active: boolean, * source: 'vpn' | 'mdm' | 'client', * deviceId?: string // optional, falls bekannt (z.B. native App) * } * * Called from the native app (useProtectionState / lib/protection) when the * combined protection state transitions on↔off. The client deduplicates * locally (only fires on real transitions); the server deduplicates again * against the last DB row for the user. * * Side-effect: if deviceId is provided (body or x-device-id header), the * per-device protection state (device_protection_states) is also updated so * that MDM / Magic-App views see the current nefilter/vpn status. * * Returns { success: true, written: true } if a new row was written, * { success: true, written: false } if deduplicated (state unchanged). */ export default defineEventHandler(async (event) => { const user = await requireUser(event); const body = await readBody(event); if (typeof body?.active !== "boolean") { throw createError({ statusCode: 400, message: "active (boolean) required" }); } const source: ProtectionSource = VALID_SOURCES.includes(body.source) ? (body.source as ProtectionSource) : "client"; const row = await appendProtectionEventDeduped(user.id, body.active, source); const deviceId = body?.deviceId ?? getHeader(event, "x-device-id") ?? null; if (deviceId && typeof deviceId === "string") { try { await upsertDeviceProtectionState( user.id, deviceId, "ios", sourceToProtectionType(source), body.active, new Date(), `protection event via ${source}`, "native-app", ); } catch (err: any) { console.error("[protection/event] device_protection_states upsert failed:", err?.message ?? err); } } return { success: true, written: row !== null }; });