rebreak-monorepo/backend/server/api/protection/screentime-passcode.get.ts
chahinebrini ab4b9c48e5 feat(ios): Screen Time Passcode als Layer 3 (setup flow)
User generiert 4-stelligen Code in der App, setzt ihn manuell als
Screen Time Passcode → ReBreak speichert ihn auf dem Backend.
Damit kann niemand Screen Time deaktivieren → deny-removal bleibt
aktiv → App nicht deinstallierbar ohne den Passcode.

Backend:
- Profile.screentimePasscode Feld (Migration add_screentime_passcode)
- POST /api/protection/screentime-passcode — Code speichern
- GET /api/protection/screentime-passcode — Code abrufen (nach Cooldown)

iOS UI (blocker.tsx):
- ScreentimePasscodeCard erscheint wenn Layer 1 + 2 aktiv (iOS only)
- Code-Generierung → Einmal-Anzeige → Deep-Link zu Settings → Screen Time
- Bestätigung speichert Code auf Backend, Card zeigt Confirmed-State

Locales: DE/EN/FR/AR screentime_* Keys

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 04:19:43 +02:00

39 lines
1.2 KiB
TypeScript

import { requireUser } from "../../utils/auth";
import { usePrisma } from "../../utils/prisma";
import { getActiveCooldown, resolveCooldown } from "../../db/cooldown";
/**
* GET /api/protection/screentime-passcode
*
* Gibt den Screen-Time-Passcode zurück — aber NUR wenn kein aktiver
* Cooldown läuft (canDisableProtection = true). Das stellt sicher dass
* der Code erst nach der 24h-Wartezeit abrufbar ist.
*
* Gibt { passcode: null } zurück wenn noch kein Code gesetzt wurde
* oder Cooldown noch läuft (kein Error, kein 403 — Client unterscheidet
* anhand passcode=null ob Code fehlt oder gesperrt ist).
*/
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
const db = usePrisma();
const cooldown = await getActiveCooldown(user.id);
if (cooldown) {
const expired = new Date() >= cooldown.cooldownEndsAt;
if (!expired) {
return { success: true, data: { passcode: null, reason: "cooldown_active" } };
}
await resolveCooldown(cooldown.id);
}
const profile = await db.profile.findUnique({
where: { id: user.id },
select: { screentimePasscode: true },
});
return {
success: true,
data: { passcode: profile?.screentimePasscode ?? null },
};
});