File size: 3,119 Bytes
57a889c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { Injectable } from '@nestjs/common';
import type { ChannelTestResult } from '@trek/shared';
import {
  testSmtp,
  testWebhook,
  testNtfy,
  getAdminWebhookUrl,
  getUserWebhookUrl,
  getUserNtfyConfig,
  getAdminNtfyConfig,
} from '../../services/notifications';
import {
  getNotifications,
  getUnreadCount,
  markRead,
  markUnread,
  markAllRead,
  deleteNotification,
  deleteAll,
  respondToBoolean,
} from '../../services/inAppNotifications';
import { getPreferencesMatrix, setPreferences } from '../../services/notificationPreferencesService';

type NtfyConfig = ReturnType<typeof getAdminNtfyConfig>;
type RespondResult = Awaited<ReturnType<typeof respondToBoolean>>;
type PreferencesMatrix = ReturnType<typeof getPreferencesMatrix>;

/**
 * Thin Nest wrapper around the existing notification services. Channel delivery
 * (including the WebSocket push in inAppNotifications) and the preference
 * persistence all stay in the upstream services, so behaviour — including
 * real-time delivery — is unchanged. The webhook/ntfy fallback resolution that
 * the legacy route does inline is exposed here as small accessors so the
 * controller can reproduce it exactly.
 */
@Injectable()
export class NotificationsService {
  getPreferences(userId: number, role: string): PreferencesMatrix {
    return getPreferencesMatrix(userId, role, 'user');
  }

  setPreferences(userId: number, body: Parameters<typeof setPreferences>[1]): void {
    setPreferences(userId, body);
  }

  testSmtp(to: string): Promise<ChannelTestResult> {
    return testSmtp(to);
  }

  testWebhook(url: string): Promise<ChannelTestResult> {
    return testWebhook(url);
  }

  testNtfy(cfg: { topic: string; server?: string | null; token?: string | null }): Promise<ChannelTestResult> {
    return testNtfy(cfg);
  }

  userWebhookUrl(userId: number): string | null {
    return getUserWebhookUrl(userId);
  }

  adminWebhookUrl(): string | null {
    return getAdminWebhookUrl();
  }

  userNtfyConfig(userId: number): NtfyConfig | null {
    return getUserNtfyConfig(userId);
  }

  adminNtfyConfig(): NtfyConfig {
    return getAdminNtfyConfig();
  }

  // Returns the native service shape (NotificationRow[] is a superset of the
  // client-facing InAppListResult contract); the controller surfaces it as-is.
  listInApp(userId: number, options: { limit?: number; offset?: number; unreadOnly?: boolean }) {
    return getNotifications(userId, options);
  }

  unreadCount(userId: number): number {
    return getUnreadCount(userId);
  }

  markRead(id: number, userId: number): boolean {
    return markRead(id, userId);
  }

  markUnread(id: number, userId: number): boolean {
    return markUnread(id, userId);
  }

  markAllRead(userId: number): number {
    return markAllRead(userId);
  }

  deleteOne(id: number, userId: number): boolean {
    return deleteNotification(id, userId);
  }

  deleteAll(userId: number): number {
    return deleteAll(userId);
  }

  respond(id: number, userId: number, response: 'positive' | 'negative'): Promise<RespondResult> {
    return respondToBoolean(id, userId, response);
  }
}