diff --git a/backend/server/api/magic/devices/[deviceId]/cancel-cooldown.post.ts b/backend/server/api/magic/devices/[deviceId]/cancel-cooldown.post.ts new file mode 100644 index 0000000..40f9bfc --- /dev/null +++ b/backend/server/api/magic/devices/[deviceId]/cancel-cooldown.post.ts @@ -0,0 +1,30 @@ +/** + * POST /api/magic/devices/:deviceId/cancel-cooldown + * + * Removes an active Magic device cooldown immediately. + */ +export default defineEventHandler(async (event) => { + const user = await requireUser(event); + const deviceId = getRouterParam(event, "deviceId"); + + if (!deviceId) { + throw createError({ statusCode: 400, message: "deviceId required" }); + } + + const db = usePrisma(); + const device = await db.userDevice.findUnique({ + where: { userId_deviceId: { userId: user.id, deviceId } }, + select: { id: true, magicEnrolledAt: true, magicRevokedAt: true }, + }); + + if (!device || !device.magicEnrolledAt || device.magicRevokedAt) { + throw createError({ statusCode: 404, message: "Magic-Binding nicht gefunden" }); + } + + await db.userDevice.update({ + where: { id: device.id }, + data: { magicCooldownUntil: null }, + }); + + return { success: true, data: { cooldownUntil: null } }; +}); diff --git a/backend/server/api/magic/devices/[deviceId]/cooldown.post.ts b/backend/server/api/magic/devices/[deviceId]/cooldown.post.ts new file mode 100644 index 0000000..2c535f7 --- /dev/null +++ b/backend/server/api/magic/devices/[deviceId]/cooldown.post.ts @@ -0,0 +1,51 @@ +/** + * POST /api/magic/devices/:deviceId/cooldown + * + * Sets a temporary cooldown on a Magic device binding until the given + * duration (in minutes, 1–1440) elapses. + */ +export default defineEventHandler(async (event) => { + const user = await requireUser(event); + const deviceId = getRouterParam(event, "deviceId"); + + if (!deviceId) { + throw createError({ statusCode: 400, message: "deviceId required" }); + } + + const body = await readBody(event); + const raw = body?.durationMinutes; + if ( + typeof raw !== "number" || + !Number.isFinite(raw) || + !Number.isInteger(raw) || + raw < 1 || + raw > 1440 + ) { + throw createError({ + statusCode: 400, + message: "durationMinutes must be an integer between 1 and 1440", + }); + } + const durationMinutes = raw; + const cooldownUntil = new Date(Date.now() + durationMinutes * 60 * 1000); + + const db = usePrisma(); + const device = await db.userDevice.findUnique({ + where: { userId_deviceId: { userId: user.id, deviceId } }, + select: { id: true, magicEnrolledAt: true, magicRevokedAt: true }, + }); + + if (!device || !device.magicEnrolledAt || device.magicRevokedAt) { + throw createError({ statusCode: 404, message: "Magic-Binding nicht gefunden" }); + } + + await db.userDevice.update({ + where: { id: device.id }, + data: { magicCooldownUntil: cooldownUntil }, + }); + + return { + success: true, + data: { cooldownUntil: cooldownUntil.toISOString() }, + }; +});