- 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)
68 lines
2.2 KiB
TypeScript
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 } };
|
|
});
|