feat(protection): /api/protection/event aktualisiert device_protection_states
- Backend: /api/protection/event setzt bei Vorhandensein von deviceId (Body oder x-device-id Header) auch device_protection_states. source=mdm -> protectionType=nefilter, sonst vpn. - Native App: sendet deviceId im Body von /api/protection/event. - Magic App: Lock-Profil-Status wird nach lokaler Installation ans Backend gemeldet und Backend-Status neu geladen.
This commit is contained in:
parent
45d7981680
commit
97f8d593a5
@ -8,6 +8,7 @@ import {
|
||||
formatCooldownRemaining,
|
||||
} from '../lib/protection';
|
||||
import { apiFetch } from '../lib/api';
|
||||
import { getDeviceId } from '../lib/deviceId';
|
||||
import type { WebContentFilterResult } from '../modules/rebreak-protection';
|
||||
|
||||
const POLL_MS_ACTIVE_COOLDOWN = 5_000;
|
||||
@ -210,7 +211,7 @@ export function useProtectionState(): UseProtectionStateReturn {
|
||||
return () => sub?.remove();
|
||||
}, [fetchState]);
|
||||
|
||||
// Report protection-state transitions to the coverage log.
|
||||
// Report protection-state transitions to the coverage log and per-device state.
|
||||
// Fires only on genuine active↔inactive flips; deduped via ref.
|
||||
useEffect(() => {
|
||||
if (state === null) return;
|
||||
@ -218,7 +219,14 @@ export function useProtectionState(): UseProtectionStateReturn {
|
||||
if (lastReportedActiveRef.current === active) return;
|
||||
lastReportedActiveRef.current = active;
|
||||
const source = resolveEventSource(state);
|
||||
apiFetch('/api/protection/event', { method: 'POST', body: { active, source } }).catch(() => {});
|
||||
getDeviceId()
|
||||
.then((deviceId) => {
|
||||
apiFetch('/api/protection/event', {
|
||||
method: 'POST',
|
||||
body: { active, source, deviceId },
|
||||
}).catch(() => {});
|
||||
})
|
||||
.catch(() => {});
|
||||
}, [state]);
|
||||
|
||||
// ─── Public Actions ────────────────────────────────────────────────
|
||||
|
||||
@ -1,21 +1,37 @@
|
||||
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' }
|
||||
* 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).
|
||||
*/
|
||||
@ -33,5 +49,23 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
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 };
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user