rebreak-monorepo/backend/server/api/users/me/push-token.post.ts
chahinebrini 38df6fc79d feat(chat): push notifications for DMs + rooms
Backend:
- Prisma PushToken model + chat_push_enabled flag on profiles
- Migration 20260530_add_push_tokens (push_tokens table + profile flag)
- Service sendChatPush with expo-server-sdk (auto-disable invalid tokens)
- Fire-and-forget push trigger in sendDirectMessage + createRoomMessage
- API POST /users/me/push-token (upsert) + DELETE (soft-disable)

Client (rebreak-native):
- usePushTokenRegistration hook: permission, getExpoPushTokenAsync,
  Android channel 'chat', POST to backend; idempotent per session
- Notification tap deep-link: dm -> /dm?userId, room -> /room?roomId

Deploy:
- run_quiet spinner for silent altool/xcodebuild/gradle phases
- Release-notes pipeline (--notes flag / NEXT_RELEASE.md / interactive)
  archived to CHANGELOG.md, printed with ASC + Play Console links
- Default version bump ON (--no-bump opt-out), build cleanup
- NEXT_RELEASE.md with push-notification release note
2026-05-30 08:16:45 +02:00

55 lines
1.5 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(),
});
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 } = parsed.data;
const db = usePrisma();
await db.pushToken.upsert({
where: { token },
create: {
userId: user.id,
token,
platform,
deviceId: deviceId ?? null,
enabled: true,
lastUsedAt: new Date(),
},
update: {
userId: user.id, // Token könnte das Device gewechselt haben
platform,
deviceId: deviceId ?? null,
enabled: true,
lastUsedAt: new Date(),
},
});
return { success: true, data: { ok: true } };
});