chahinebrini 8e562c982d feat(backend): MDM-Managed Flag — migration + endpoint + guards
- Prisma migration: users.mdm_managed (Boolean DEFAULT false) + users.mdm_detected_at (DateTime?)
- setMdmManaged() helper in server/db/profile.ts
- POST /api/users/me/mdm-status — App reports MDM status to backend
- cooldown/status + cooldown/request — early-return 400 when mdm_managed
- protection/state — response extended with mdmManaged: boolean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 00:46:44 +02:00

79 lines
2.7 KiB
TypeScript

import { requireUser } from "../../utils/auth";
import { getActiveCooldown, resolveCooldown } from "../../db/cooldown";
import { getProfile } from "../../db/profile";
import { usePrisma } from "../../utils/prisma";
/**
* GET /api/protection/state
* Combined protection + cooldown state polled every 30 s by the app.
*/
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
const [cooldown, profile] = await Promise.all([
getActiveCooldown(user.id),
getProfile(user.id),
]);
const now = new Date();
let active = false;
let remainingSeconds = 0;
let cooldownEndsAt: string | null = null;
// True wenn dieser Request gerade einen abgelaufenen Cooldown resolved hat.
let cooldownJustResolved = false;
if (cooldown) {
const expired = now >= cooldown.cooldownEndsAt;
if (expired) {
await resolveCooldown(cooldown.id);
// Anti-Auto-Reactivation: Cooldown wurde durchgehalten → Schutz bleibt
// jetzt AUS, User muss explizit reaktivieren. MUSS hier passieren —
// dieser Endpoint wird alle 5s während Cooldown gepollt und gewinnt das
// Race gegen /api/cooldown/status fast immer. Ohne dieses Update bliebe
// protectionDisabledAt null → protectionShouldBeActive=true → Frontend-
// Bypass-Detection würde den Schutz automatisch wieder anschalten.
await usePrisma()
.profile.update({
where: { id: user.id },
data: { protectionDisabledAt: new Date() },
})
.catch(() => {});
cooldownJustResolved = true;
// After resolve: no active cooldown
} else {
active = true;
remainingSeconds = Math.max(
0,
Math.floor((cooldown.cooldownEndsAt.getTime() - now.getTime()) / 1000),
);
cooldownEndsAt = cooldown.cooldownEndsAt.toISOString();
}
}
const plan = (profile?.plan ?? "free") as "free" | "pro" | "legend";
const mdmManaged = profile?.mdmManaged ?? false;
// protectionShouldBeActive = "der Schutz sollte gerade auf dem Device laufen"
// - false wenn Cooldown aktiv ist (User darf grad nicht zurück-reaktivieren)
// - false wenn User per Cooldown-Resolve explizit abgeschaltet hat
// (protectionDisabledAt gesetzt) → Frontend macht KEINE Auto-Reactivation,
// User muss explizit re-aktivieren via /api/protection/mark-active.
// - true sonst (Normal-Zustand: Schutz sollte laufen)
const protectionShouldBeActive =
!active && profile?.protectionDisabledAt === null && !cooldownJustResolved;
return {
success: true,
data: {
protectionShouldBeActive,
cooldown: {
active,
remainingSeconds,
cooldownEndsAt,
},
plan,
mdmManaged,
},
};
});