60 lines
2.1 KiB
TypeScript
60 lines
2.1 KiB
TypeScript
import { requireUser } from "../../utils/auth";
|
|
import { getActiveCooldown, createCooldown } from "../../db/cooldown";
|
|
import { signCooldownToken, generateJti } from "../../utils/cooldownToken";
|
|
|
|
/** POST /api/cooldown/request — Start a 24h cooldown before protection can be disabled. */
|
|
export default defineEventHandler(async (event) => {
|
|
const user = await requireUser(event);
|
|
const body = await readBody(event).catch(() => ({}));
|
|
|
|
// Reject if a cooldown is already running (not resolved, not cancelled).
|
|
const existing = await getActiveCooldown(user.id);
|
|
if (existing) {
|
|
const now = new Date();
|
|
// If the existing one already expired but wasn't resolved yet, that's fine —
|
|
// it means canDisableProtection is already true. Return 409 so the client
|
|
// calls /status instead.
|
|
throw createError({
|
|
statusCode: 409,
|
|
data: {
|
|
error: "cooldown_already_active",
|
|
existingEndsAt: existing.cooldownEndsAt.toISOString(),
|
|
},
|
|
});
|
|
}
|
|
|
|
// Test-Mode (5min statt 24h) — nur außerhalb von Production aktivierbar.
|
|
// Detection via appUrl statt NODE_ENV, da staging.rebreak.org auch mit
|
|
// NODE_ENV=production läuft (siehe start-staging.sh).
|
|
const config = useRuntimeConfig(event);
|
|
const appUrl = (config.public?.appUrl as string) ?? "";
|
|
const isProductionUrl =
|
|
appUrl.includes("rebreak.org") && !appUrl.includes("staging");
|
|
const isTestMode = body?.testMode === true && !isProductionUrl;
|
|
const cooldownMs = isTestMode ? 40 * 1000 : 24 * 60 * 60 * 1000;
|
|
|
|
const now = new Date();
|
|
const cooldownEndsAt = new Date(now.getTime() + cooldownMs);
|
|
const jti = generateJti();
|
|
|
|
await createCooldown(user.id, jti, cooldownEndsAt, body?.reason);
|
|
|
|
const remainingSeconds = Math.max(
|
|
0,
|
|
Math.floor((cooldownEndsAt.getTime() - Date.now()) / 1000),
|
|
);
|
|
|
|
const token = await signCooldownToken(user.id, jti, cooldownEndsAt);
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
cooldownStartedAt: now.toISOString(),
|
|
cooldownEndsAt: cooldownEndsAt.toISOString(),
|
|
remainingSeconds,
|
|
token,
|
|
testMode: isTestMode,
|
|
},
|
|
};
|
|
});
|