All checks were successful
CI / Lint, typecheck, test (push) Successful in 3m26s
CI / Build container images (push) Has been skipped
CD / Build and push images (push) Successful in 3m49s
CD / Deploy to Test (push) Successful in 9s
CD / Smoke tests against Test (push) Successful in 1m12s
CD / Promote to Int (push) Successful in 10s
CI / Auth e2e pack (push) Successful in 5m27s
CI / Import/export fidelity gate (push) Successful in 46s
New notifications table (payload denormalized for join-free rendering; mailed_at already prepares the #95 digests). Generation fans page events out to page and pond watchers, excluding the actors, and re-checks page read permission per watcher at delivery time — a revoked watcher gets nothing. Sources: named version snapshots (api), new comments (api), and the collab server's automatic session-close snapshots — announced over a new pg NOTIFY channel (the reverse of the established api→collab bus) consumed by a dedicated LISTEN client in the api, since the collab server has no permission resolution of its own. API: paginated list (unread first via nulls-first ordering), mark read, mark all read. UI: bell with unread badge in the top bar (30 s polling, no push in v1) and a dropdown whose entries navigate and mark themselves read; comment notifications deep-link with ?comments=1, which now opens the comments panel on load. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EwZ4jR4KFAPvpjWevfUGX1
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
import { Controller, Get, HttpCode, Param, Post, Query, Req } from '@nestjs/common';
|
|
import {
|
|
notificationListQuerySchema,
|
|
type NotificationListQuery,
|
|
type NotificationListView,
|
|
type NotificationView,
|
|
} from '@dorfteich/shared';
|
|
|
|
import { AuthedRequest } from '../auth/auth.guard';
|
|
import { ZodValidationPipe } from '../common/zod-validation.pipe';
|
|
import { AuthenticatedOnly } from '../permissions/permission.decorators';
|
|
import { NotificationsService } from './notifications.service';
|
|
|
|
/** The in-app notification center's API (issue #94). */
|
|
@Controller('notifications')
|
|
export class NotificationsController {
|
|
constructor(private readonly notifications: NotificationsService) {}
|
|
|
|
@Get()
|
|
@AuthenticatedOnly()
|
|
async list(
|
|
@Query(new ZodValidationPipe(notificationListQuerySchema)) query: NotificationListQuery,
|
|
@Req() request: AuthedRequest,
|
|
): Promise<NotificationListView> {
|
|
return this.notifications.list(request.user!, query.page);
|
|
}
|
|
|
|
@Post(':id/read')
|
|
@AuthenticatedOnly()
|
|
async markRead(
|
|
@Param('id') id: string,
|
|
@Req() request: AuthedRequest,
|
|
): Promise<NotificationView> {
|
|
return this.notifications.markRead(request.user!, id);
|
|
}
|
|
|
|
@Post('read-all')
|
|
@HttpCode(204)
|
|
@AuthenticatedOnly()
|
|
async markAllRead(@Req() request: AuthedRequest): Promise<void> {
|
|
await this.notifications.markAllRead(request.user!);
|
|
}
|
|
}
|