fix(protection): cooldown-elapse must set protectionDisabledAt in state endpoint
Bug: nach Cooldown-Ablauf reaktivierte sich der Schutz automatisch. Root-Cause: Race zwischen zwei Endpoints die abgelaufene Cooldowns auflösen. /api/cooldown/status setzt korrekt profile.protectionDisabledAt. /api/ protection/state — alle 5s während Cooldown gepollt — machte beim Ablauf NUR resolveCooldown(), ohne protectionDisabledAt. state.get gewann das Race fast immer → protectionDisabledAt blieb null → protectionShouldBeActive=true → Frontend-Bypass-Detection reaktivierte den Schutz. Fix: state.get.ts setzt im expired-Branch ebenfalls protectionDisabledAt + cooldownJustResolved-Flag.
This commit is contained in:
parent
c32eeeb070
commit
34005803da
@ -1,6 +1,7 @@
|
|||||||
import { requireUser } from "../../utils/auth";
|
import { requireUser } from "../../utils/auth";
|
||||||
import { getActiveCooldown, resolveCooldown } from "../../db/cooldown";
|
import { getActiveCooldown, resolveCooldown } from "../../db/cooldown";
|
||||||
import { getProfile } from "../../db/profile";
|
import { getProfile } from "../../db/profile";
|
||||||
|
import { usePrisma } from "../../utils/prisma";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GET /api/protection/state
|
* GET /api/protection/state
|
||||||
@ -18,11 +19,26 @@ export default defineEventHandler(async (event) => {
|
|||||||
let active = false;
|
let active = false;
|
||||||
let remainingSeconds = 0;
|
let remainingSeconds = 0;
|
||||||
let cooldownEndsAt: string | null = null;
|
let cooldownEndsAt: string | null = null;
|
||||||
|
// True wenn dieser Request gerade einen abgelaufenen Cooldown resolved hat.
|
||||||
|
let cooldownJustResolved = false;
|
||||||
|
|
||||||
if (cooldown) {
|
if (cooldown) {
|
||||||
const expired = now >= cooldown.cooldownEndsAt;
|
const expired = now >= cooldown.cooldownEndsAt;
|
||||||
if (expired) {
|
if (expired) {
|
||||||
await resolveCooldown(cooldown.id);
|
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
|
// After resolve: no active cooldown
|
||||||
} else {
|
} else {
|
||||||
active = true;
|
active = true;
|
||||||
@ -42,7 +58,8 @@ export default defineEventHandler(async (event) => {
|
|||||||
// (protectionDisabledAt gesetzt) → Frontend macht KEINE Auto-Reactivation,
|
// (protectionDisabledAt gesetzt) → Frontend macht KEINE Auto-Reactivation,
|
||||||
// User muss explizit re-aktivieren via /api/protection/mark-active.
|
// User muss explizit re-aktivieren via /api/protection/mark-active.
|
||||||
// - true sonst (Normal-Zustand: Schutz sollte laufen)
|
// - true sonst (Normal-Zustand: Schutz sollte laufen)
|
||||||
const protectionShouldBeActive = !active && profile?.protectionDisabledAt === null;
|
const protectionShouldBeActive =
|
||||||
|
!active && profile?.protectionDisabledAt === null && !cooldownJustResolved;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user