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

102 lines
2.9 KiB
TypeScript

import { requireUser } from "../../utils/auth";
import { getActiveCooldown, resolveCooldown } from "../../db/cooldown";
import { signCooldownToken } from "../../utils/cooldownToken";
import { usePrisma } from "../../utils/prisma";
/** GET /api/cooldown/status — Current cooldown state for the authenticated user. */
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
// ─── MDM-Guard: wenn User via MDM verwaltet wird, kann er Schutz nie selbst
// deaktivieren — Cooldown-Flow ist komplett irrelevant. Früh zurückkehren.
const db = usePrisma();
const profile = await db.profile.findUnique({
where: { id: user.id },
select: { mdmManaged: true },
});
if (profile?.mdmManaged) {
return {
success: true,
data: {
active: false,
remainingSeconds: 0,
cooldownEndsAt: null,
canDisableProtection: false,
reason: "mdm_managed",
message: "MDM-Mode: Schutz nur via Apple Configurator oder Trustee deaktivierbar",
token: null,
},
};
}
const cooldown = await getActiveCooldown(user.id);
const now = new Date();
if (!cooldown) {
// No cooldown ever started (or all were cancelled/resolved).
return {
success: true,
data: {
active: false,
remainingSeconds: 0,
cooldownEndsAt: null,
canDisableProtection: true,
token: null, // no cooldown row to bind to; app may proceed freely
},
};
}
const expired = now >= cooldown.cooldownEndsAt;
if (expired) {
// Auto-resolve so we don't re-check next time.
await resolveCooldown(cooldown.id);
// Anti-Auto-Reactivation: User hat den 24h-Cooldown durchgehalten + jetzt
// wird der Schutz abgeschaltet. Markiere Profile damit /api/protection/state
// protectionShouldBeActive=false zurückgibt → Frontend macht keine Auto-
// Reactivation (Sucht-Recovery-Pattern: einfach an, schwer aus, sehr schwer
// wieder zurück an Auto-Magic). User muss explizit reaktivieren.
const db = usePrisma();
await db.profile.update({
where: { id: user.id },
data: { protectionDisabledAt: new Date() },
}).catch(() => {});
const token = await signCooldownToken(
user.id,
cooldown.tokenJti,
cooldown.cooldownEndsAt,
);
return {
success: true,
data: {
active: false,
remainingSeconds: 0,
cooldownEndsAt: cooldown.cooldownEndsAt.toISOString(),
canDisableProtection: true,
token,
},
};
}
// Still counting down.
const remainingSeconds = Math.max(
0,
Math.floor((cooldown.cooldownEndsAt.getTime() - now.getTime()) / 1000),
);
return {
success: true,
data: {
active: true,
remainingSeconds,
cooldownEndsAt: cooldown.cooldownEndsAt.toISOString(),
canDisableProtection: false,
token: null,
},
};
});