rebreak-monorepo/backend/server/api/users/me/push-token.post.ts
chahinebrini 7fae4539ae diag(calls): add VoIP+push-token+ring-target logs; fix /call mount race
- AppDelegate: NSLog for didUpdate token, didInvalidate, didReceiveIncomingPush
- backend/push: log [push-token] register, [call-ring] receiver token-counts +
  expo-push-fanout for android-fallback
- app/call.tsx: 250ms grace window before closeScreen on initial idle (fixes
  'foreground call flashes briefly then disappears' race when dm.tsx
  startCall set() hasn't propagated through useCallStore selector yet)
2026-06-04 20:37:43 +02:00

68 lines
2.2 KiB
TypeScript

/**
* POST /api/users/me/push-token
*
* Client (Expo) ruft das nach `getExpoPushTokenAsync()` auf, um seinen Token
* im Backend zu hinterlegen. Idempotent: bei existierendem Token wird nur
* lastUsedAt + enabled aktualisiert.
*
* Body: { token: string, platform: "ios" | "android", deviceId?: string }
*/
import { requireUser } from "../../../utils/auth";
import { usePrisma } from "../../../utils/prisma";
import { z } from "zod";
const Body = z.object({
token: z.string().min(10).max(200), // ExponentPushToken[xxx]
platform: z.enum(["ios", "android"]),
deviceId: z.string().max(120).optional(),
/// iOS-PushKit-Token (64-char hex) für CallKit-Wake-Pushes. Optional —
/// Client kann später via separatem Call dieselbe Row updaten.
voipToken: z.string().min(32).max(200).optional(),
});
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
const raw = await readBody(event).catch(() => ({}));
const parsed = Body.safeParse(raw);
if (!parsed.success) {
throw createError({
statusCode: 400,
data: { error: "INVALID_BODY", detail: parsed.error.flatten() },
});
}
const { token, platform, deviceId, voipToken } = parsed.data;
const db = usePrisma();
console.log(
`[push-token] register user=${user.id.slice(0,8)} platform=${platform} ` +
`token=${token.slice(0,25)}\u2026 voip=${voipToken ? voipToken.slice(0,16)+'\u2026' : 'none'} ` +
`device=${deviceId ?? 'none'}`,
);
await db.pushToken.upsert({
where: { token },
create: {
userId: user.id,
token,
platform,
deviceId: deviceId ?? null,
voipToken: voipToken ?? null,
enabled: true,
lastUsedAt: new Date(),
},
update: {
userId: user.id, // Token könnte das Device gewechselt haben
platform,
deviceId: deviceId ?? null,
// Wichtig: voipToken nur überschreiben wenn der Client einen mitliefert,
// sonst behalten (separate VoIP-Rotation-Calls könnten ihn schon gesetzt haben).
...(voipToken !== undefined ? { voipToken } : {}),
enabled: true,
lastUsedAt: new Date(),
},
});
return { success: true, data: { ok: true } };
});