- backend/api/magic/register: explicit import of MAGIC_DEVICE_LIMIT and createAdGuardClient (Nitro auto-import was missing them → ReferenceError → HTTP 500 on /api/magic/register) - mac-app: default backendBaseUrl falls back to staging.rebreak.org (app.rebreak.org serves wrong TLS cert) - native MagicSheet: fallback download/dmg URLs point to staging - native settings: Magic sheet capped at detents=[0.85] so AppHeader stays visible - bundles all in-flight Magic feature work (pair create/redeem, device endpoints, schema, adguard utils, mac-app, locales)
79 lines
2.0 KiB
TypeScript
79 lines
2.0 KiB
TypeScript
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),
|
|
},
|
|
};
|
|
});
|