/** * POST /api/calls/ring * * Triggert einen Push an den Callee bei einem eingehenden Voice-Call. * Wird vom Caller direkt nach dem Supabase-Realtime-Broadcast aufgerufen * (fire-and-forget). Der Push deckt den Background-/Locked-Screen-Fall ab; * Foreground wird weiter via Realtime gehandhabt. * * Body: { peerId: string, callId: string } * * Kein VoIPPushKit/CallKit (Phase 2). Regulärer APNs/FCM Alert-Push mit * priority=high + channelId="calls". */ import { requireUser } from "../../utils/auth"; import { usePrisma } from "../../utils/prisma"; import { sendCallRingPush } from "../../services/push"; export default defineEventHandler(async (event) => { const user = await requireUser(event); const body = await readBody<{ peerId?: string; callId?: string }>(event); const peerId = body?.peerId?.trim(); const callId = body?.callId?.trim(); if (!peerId || !callId) { throw createError({ statusCode: 400, statusMessage: "peerId_and_callId_required" }); } if (peerId === user.id) { throw createError({ statusCode: 400, statusMessage: "cannot_ring_self" }); } const db = usePrisma(); const me = await db.profile.findUnique({ where: { id: user.id }, select: { id: true, nickname: true, username: true, avatar: true }, }); if (!me) { throw createError({ statusCode: 404, statusMessage: "caller_profile_not_found" }); } const callerName = me.nickname || me.username || "Jemand"; console.log(`[ring] from=${me.id.slice(0,8)} (${callerName}) → to=${peerId.slice(0,8)} callId=${callId}`); // Fire-and-forget — auch wenn der Push fehlschlägt soll der Caller // keine Verzögerung sehen. Der Realtime-Ring läuft parallel. void sendCallRingPush({ receiverId: peerId, callerName, callerId: me.id, callerNickname: me.nickname || me.username || "", callerAvatar: me.avatar, callId, }); return { ok: true }; });