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,
|
formatCooldownRemaining,
|
||||||
} from '../lib/protection';
|
} from '../lib/protection';
|
||||||
import { apiFetch } from '../lib/api';
|
import { apiFetch } from '../lib/api';
|
||||||
|
import { getDeviceId } from '../lib/deviceId';
|
||||||
import type { WebContentFilterResult } from '../modules/rebreak-protection';
|
import type { WebContentFilterResult } from '../modules/rebreak-protection';
|
||||||
|
|
||||||
const POLL_MS_ACTIVE_COOLDOWN = 5_000;
|
const POLL_MS_ACTIVE_COOLDOWN = 5_000;
|
||||||
@ -210,7 +211,7 @@ export function useProtectionState(): UseProtectionStateReturn {
|
|||||||
return () => sub?.remove();
|
return () => sub?.remove();
|
||||||
}, [fetchState]);
|
}, [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.
|
// Fires only on genuine active↔inactive flips; deduped via ref.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (state === null) return;
|
if (state === null) return;
|
||||||
@ -218,7 +219,14 @@ export function useProtectionState(): UseProtectionStateReturn {
|
|||||||
if (lastReportedActiveRef.current === active) return;
|
if (lastReportedActiveRef.current === active) return;
|
||||||
lastReportedActiveRef.current = active;
|
lastReportedActiveRef.current = active;
|
||||||
const source = resolveEventSource(state);
|
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]);
|
}, [state]);
|
||||||
|
|
||||||
// ─── Public Actions ────────────────────────────────────────────────
|
// ─── Public Actions ────────────────────────────────────────────────
|
||||||
|
|||||||
@ -1,21 +1,37 @@
|
|||||||
|
import { getHeader } from "h3";
|
||||||
import { requireUser } from "../../utils/auth";
|
import { requireUser } from "../../utils/auth";
|
||||||
import {
|
import {
|
||||||
appendProtectionEventDeduped,
|
appendProtectionEventDeduped,
|
||||||
type ProtectionSource,
|
type ProtectionSource,
|
||||||
} from "../../db/protectionStateLog";
|
} from "../../db/protectionStateLog";
|
||||||
|
import { upsertDeviceProtectionState } from "../../db/device-protection";
|
||||||
|
import type { ProtectionType } from "../../db/device-protection";
|
||||||
|
|
||||||
const VALID_SOURCES: ProtectionSource[] = ["vpn", "mdm", "client"];
|
const VALID_SOURCES: ProtectionSource[] = ["vpn", "mdm", "client"];
|
||||||
|
|
||||||
|
function sourceToProtectionType(source: ProtectionSource): ProtectionType {
|
||||||
|
if (source === "mdm") return "nefilter";
|
||||||
|
return "vpn";
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* POST /api/protection/event
|
* 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
|
* Called from the native app (useProtectionState / lib/protection) when the
|
||||||
* combined protection state transitions on↔off. The client deduplicates
|
* combined protection state transitions on↔off. The client deduplicates
|
||||||
* locally (only fires on real transitions); the server deduplicates again
|
* locally (only fires on real transitions); the server deduplicates again
|
||||||
* against the last DB row for the user.
|
* 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,
|
* Returns { success: true, written: true } if a new row was written,
|
||||||
* { success: true, written: false } if deduplicated (state unchanged).
|
* { 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 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 };
|
return { success: true, written: row !== null };
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user