chore: move notifications backend to proper DI

The send() dispatcher (services/notificationService.ts) and the in-app store
(services/inAppNotifications.ts) fold together into the DI-native
NotificationsService over DatabaseService + RealtimeService; the prefs
matrix, transports, channel registry and inAppNotificationActions stay plain
infra modules. A 1-export notifications.bridge covers scheduler, legacy
adminService/memories and the six lazy fire-and-forget sends; AdminController
and the plugin RPC host (24th factory dep) inject. The 5-tool registrar +
notifications-in-app resource move to notifications.mcp.ts. Tests move with
case IDs preserved (NSVC-*, INOTIF-*, + NSVC-020 bridge pin); ~18 suites
repoint path mocks/warm-ups; the module e2e goes DI-native.

Also carries the verified-quirk fixes that would normally trail as a
separate fix(server) commit (file-overlap with the fold): toUtcIso on all
three created_at paths (INOTIF-013/014), respond claims the response CAS
before running the action handler with release-on-failure (INOTIF-011/012),
synology's fire-and-forget send gains .catch, the dispatch-failure log
unwraps Error messages (NSVC-021), and the MCP resource description drops
its false 'unread first' claim.
This commit is contained in:
jubnl
2026-08-02 11:36:06 +02:00
parent c814653f4e
commit d203d8cccd
54 changed files with 1257 additions and 968 deletions
+4 -3
View File
@@ -9,7 +9,7 @@ import { CurrentUser } from '../auth/current-user.decorator';
import { getClientIp } from '../audit/client-ip';
import { logInfo } from '../audit/audit-log.logger';
import { AuditService } from '../audit/audit.service';
import { send as sendNotification } from '../../services/notificationService';
import { NotificationsService } from '../notifications/notifications.service';
import type { User } from '../../types';
/** Throw the legacy {error,status} envelope when a service call reports failure. */
@@ -38,6 +38,7 @@ export class AdminController {
private readonly admin: AdminService,
private readonly pluginRuntime: PluginRuntimeService,
private readonly audit: AuditService,
private readonly notifications: NotificationsService,
) {}
// ── Users ──
@@ -353,14 +354,14 @@ export class AdminController {
throw new NotFoundException();
}
try {
await sendNotification({
await this.notifications.send({
event: body.event ?? 'trip_reminder',
actorId: user.id,
scope: body.scope ?? 'user',
targetId: body.targetId ?? user.id,
params: { actor: user.email, ...(body.params ?? {}) },
inApp: body.inApp,
} as unknown as Parameters<typeof sendNotification>[0]);
} as unknown as Parameters<NotificationsService['send']>[0]);
return { success: true };
} catch (err) {
throw new HttpException({ error: err instanceof Error ? err.message : String(err) }, 400);