New global push/ module mirroring mail/ and files/storage/: - PushProvider abstraction; default LogPushProvider (no delivery, logs), PUSH_PROVIDER=fcm switches to FcmPushProvider — Firebase Cloud Messaging HTTP v1, authenticated by a service-account JWT exchanged for an OAuth token (no extra dependency; jsonwebtoken does the signing). Prunes tokens FCM reports as invalid. - DeviceToken model (token + platform, bound to a User or GuestAccount), migration + added to the sync log. - POST /api/push/register + /unregister (any of the three token kinds). - PushService.notifyChannel() resolves a channel's readable audience (DIREKT participants / LT / Gemeinde members + guests / whole KC for broadcast), looks up their device tokens (minus the sender), sends. - ChatService.sendMessage() fires it best-effort after persisting. New env: PUSH_PROVIDER, FCM_PROJECT_ID (default konfi-castle-app), GOOGLE_APPLICATION_CREDENTIALS. Verified against local Postgres: register a token, send a Gemeinde-group chat message from another member -> log-push logs "would push ... to 1 device". Real FCM send needs the service-account JSON. npm test 56. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
30 lines
1.1 KiB
TypeScript
30 lines
1.1 KiB
TypeScript
import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common';
|
|
import { AuthGuard } from '@nestjs/passport';
|
|
import { PushService } from './push.service';
|
|
import { RegisterDeviceDto, UnregisterDeviceDto } from './dto/register-device.dto';
|
|
// Import the util directly (not via chat.service) to keep the module graph acyclic.
|
|
import { resolveChatCaller } from '../chat/caller.util';
|
|
import { AuthenticatedRequest } from '../auth/authenticated-request';
|
|
import { GuestJwtPayload } from '../auth/guest-auth.service';
|
|
|
|
type PushRequest = AuthenticatedRequest & {
|
|
user?: AuthenticatedRequest['user'] | GuestJwtPayload;
|
|
};
|
|
|
|
@Controller('push')
|
|
export class PushController {
|
|
constructor(private readonly push: PushService) {}
|
|
|
|
@Post('register')
|
|
@UseGuards(AuthGuard(['authentik', 'team', 'guest']))
|
|
register(@Body() dto: RegisterDeviceDto, @Req() req: PushRequest) {
|
|
return this.push.register(dto.token, dto.platform, resolveChatCaller(req.user!));
|
|
}
|
|
|
|
@Post('unregister')
|
|
@UseGuards(AuthGuard(['authentik', 'team', 'guest']))
|
|
unregister(@Body() dto: UnregisterDeviceDto) {
|
|
return this.push.unregister(dto.token);
|
|
}
|
|
}
|