import { randomInt } from "crypto"; import { requireUser } from "../../../utils/auth"; /** * POST /api/magic/pair/create * * Native-App ruft auf (Supabase-Auth). Generiert einen 6-stelligen numerischen * Code mit 10min Lebenszeit. Mac-App tauscht den Code via /pair/redeem gegen * einen MagicSession-Token. * * Returns: { code: "482913", expiresAt: ISO, expiresInSeconds: 600 } */ const CODE_TTL_MS = 10 * 60 * 1000; // 10 Minuten export default defineEventHandler(async (event) => { const user = await requireUser(event); const db = usePrisma(); // Alte unbenutzte Codes des Users invalidieren (max 1 aktiv pro User) await db.magicPairingCode.deleteMany({ where: { userId: user.id, redeemedAt: null, }, }); // Generiere unique 6-digit Code (sehr unwahrscheinlich dass dieselbe // Zahl gleichzeitig aktiv ist, aber wir retry-en sicherheitshalber). let code: string | null = null; let attempts = 0; while (attempts < 5 && code === null) { const candidate = String(randomInt(0, 1_000_000)).padStart(6, "0"); const exists = await db.magicPairingCode.findUnique({ where: { code: candidate }, select: { id: true, expiresAt: true, redeemedAt: true }, }); if ( !exists || exists.redeemedAt !== null || exists.expiresAt < new Date() ) { // Falls expired/redeemed: löschen damit Unique-Constraint frei wird if (exists) { await db.magicPairingCode .delete({ where: { id: exists.id } }) .catch(() => {}); } code = candidate; } attempts++; } if (!code) { throw createError({ statusCode: 500, message: "Konnte keinen freien Pairing-Code generieren", }); } const expiresAt = new Date(Date.now() + CODE_TTL_MS); await db.magicPairingCode.create({ data: { userId: user.id, code, expiresAt, }, }); return { success: true, data: { code, expiresAt: expiresAt.toISOString(), expiresInSeconds: Math.floor(CODE_TTL_MS / 1000), }, }; });