@flusys/nestjs-notification 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +998 -0
- package/adapters/index.d.ts +1 -0
- package/adapters/notification.adapter.d.ts +10 -0
- package/cjs/adapters/index.js +18 -0
- package/cjs/adapters/notification.adapter.js +79 -0
- package/cjs/config/index.js +18 -0
- package/cjs/config/notification.constants.js +11 -0
- package/cjs/controllers/index.js +18 -0
- package/cjs/controllers/notification.controller.js +278 -0
- package/cjs/docs/index.js +18 -0
- package/cjs/docs/notification-swagger.config.js +61 -0
- package/cjs/dtos/index.js +18 -0
- package/cjs/dtos/notification.dto.js +244 -0
- package/cjs/entities/index.js +48 -0
- package/cjs/entities/notification-with-company.entity.js +57 -0
- package/cjs/entities/notification.entity.js +111 -0
- package/cjs/gateways/index.js +18 -0
- package/cjs/gateways/notification.gateway.js +226 -0
- package/cjs/index.js +28 -0
- package/cjs/interfaces/index.js +19 -0
- package/cjs/interfaces/notification-module.interface.js +4 -0
- package/cjs/interfaces/notification.interface.js +4 -0
- package/cjs/modules/index.js +18 -0
- package/cjs/modules/notification.module.js +139 -0
- package/cjs/services/index.js +21 -0
- package/cjs/services/notification-config.service.js +72 -0
- package/cjs/services/notification-datasource.provider.js +174 -0
- package/cjs/services/notification-helper.service.js +186 -0
- package/cjs/services/notification.service.js +220 -0
- package/config/index.d.ts +1 -0
- package/config/notification.constants.d.ts +1 -0
- package/controllers/index.d.ts +1 -0
- package/controllers/notification.controller.d.ts +26 -0
- package/docs/index.d.ts +1 -0
- package/docs/notification-swagger.config.d.ts +2 -0
- package/dtos/index.d.ts +1 -0
- package/dtos/notification.dto.d.ts +47 -0
- package/entities/index.d.ts +8 -0
- package/entities/notification-with-company.entity.d.ts +4 -0
- package/entities/notification.entity.d.ts +10 -0
- package/fesm/adapters/index.js +1 -0
- package/fesm/adapters/notification.adapter.js +69 -0
- package/fesm/config/index.js +1 -0
- package/fesm/config/notification.constants.js +1 -0
- package/fesm/controllers/index.js +1 -0
- package/fesm/controllers/notification.controller.js +268 -0
- package/fesm/docs/index.js +1 -0
- package/fesm/docs/notification-swagger.config.js +51 -0
- package/fesm/dtos/index.js +1 -0
- package/fesm/dtos/notification.dto.js +211 -0
- package/fesm/entities/index.js +14 -0
- package/fesm/entities/notification-with-company.entity.js +47 -0
- package/fesm/entities/notification.entity.js +101 -0
- package/fesm/gateways/index.js +1 -0
- package/fesm/gateways/notification.gateway.js +175 -0
- package/fesm/index.js +20 -0
- package/fesm/interfaces/index.js +2 -0
- package/fesm/interfaces/notification-module.interface.js +1 -0
- package/fesm/interfaces/notification.interface.js +1 -0
- package/fesm/modules/index.js +1 -0
- package/fesm/modules/notification.module.js +129 -0
- package/fesm/services/index.js +4 -0
- package/fesm/services/notification-config.service.js +62 -0
- package/fesm/services/notification-datasource.provider.js +123 -0
- package/fesm/services/notification-helper.service.js +176 -0
- package/fesm/services/notification.service.js +210 -0
- package/gateways/index.d.ts +1 -0
- package/gateways/notification.gateway.d.ts +21 -0
- package/index.d.ts +10 -0
- package/interfaces/index.d.ts +2 -0
- package/interfaces/notification-module.interface.d.ts +20 -0
- package/interfaces/notification.interface.d.ts +19 -0
- package/modules/index.d.ts +1 -0
- package/modules/notification.module.d.ts +8 -0
- package/package.json +98 -0
- package/services/index.d.ts +4 -0
- package/services/notification-config.service.d.ts +13 -0
- package/services/notification-datasource.provider.d.ts +23 -0
- package/services/notification-helper.service.d.ts +44 -0
- package/services/notification.service.d.ts +35 -0
package/README.md
ADDED
|
@@ -0,0 +1,998 @@
|
|
|
1
|
+
# Notification Package Guide
|
|
2
|
+
|
|
3
|
+
> **Package:** `@flusys/nestjs-notification`
|
|
4
|
+
> **Version:** 3.0.1
|
|
5
|
+
> **Type:** Real-time push notifications with WebSocket support, multi-tenant, and company isolation
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Overview](#overview)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Constants](#constants)
|
|
12
|
+
- [Module Setup](#module-setup)
|
|
13
|
+
- [Entities](#entities)
|
|
14
|
+
- [Interfaces](#interfaces)
|
|
15
|
+
- [DTOs](#dtos)
|
|
16
|
+
- [Services](#services)
|
|
17
|
+
- [WebSocket Gateway](#websocket-gateway)
|
|
18
|
+
- [Adapter Pattern](#adapter-pattern)
|
|
19
|
+
- [API Endpoints](#api-endpoints)
|
|
20
|
+
- [Swagger Documentation](#swagger-documentation)
|
|
21
|
+
- [Permissions](#permissions)
|
|
22
|
+
- [Multi-Tenant Support](#multi-tenant-support)
|
|
23
|
+
- [Integration with Other Modules](#integration-with-other-modules)
|
|
24
|
+
- [Best Practices](#best-practices)
|
|
25
|
+
- [API Reference](#api-reference)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Overview
|
|
30
|
+
|
|
31
|
+
`@flusys/nestjs-notification` provides:
|
|
32
|
+
|
|
33
|
+
- **Single User Notifications** - Send to individual users
|
|
34
|
+
- **Bulk Notifications** - Send to multiple users simultaneously
|
|
35
|
+
- **Company Broadcasts** - Send to all users in a company
|
|
36
|
+
- **Real-time Delivery** - WebSocket support via Socket.io
|
|
37
|
+
- **Read Tracking** - Mark notifications as read with timestamps
|
|
38
|
+
- **Unread Count** - Get user's unread notification count
|
|
39
|
+
- **Multi-Tenant Support** - Company-scoped notifications
|
|
40
|
+
|
|
41
|
+
### Package Hierarchy
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
@flusys/nestjs-core <- Foundation
|
|
45
|
+
↓
|
|
46
|
+
@flusys/nestjs-shared <- Shared utilities (guards, DTOs, NOTIFICATION_ADAPTER token)
|
|
47
|
+
↓
|
|
48
|
+
@flusys/nestjs-notification <- Notification system (THIS PACKAGE)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm install @flusys/nestjs-notification
|
|
57
|
+
|
|
58
|
+
# WebSocket support (Socket.IO)
|
|
59
|
+
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Constants
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// Injection Token
|
|
68
|
+
export const NOTIFICATION_MODULE_OPTIONS = 'NOTIFICATION_MODULE_OPTIONS';
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Module Setup
|
|
74
|
+
|
|
75
|
+
### Module Options Interface
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
interface INotificationModuleConfig extends IDataSourceServiceOptions {
|
|
79
|
+
enableRealtime?: boolean; // WebSocket support (default: true)
|
|
80
|
+
jwtSecret?: string; // For WebSocket authentication
|
|
81
|
+
defaultDatabaseConfig?: IDatabaseConfig;
|
|
82
|
+
tenantDefaultDatabaseConfig?: IDatabaseConfig;
|
|
83
|
+
tenants?: ITenantDatabaseConfig[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
interface NotificationModuleOptions extends IDynamicModuleConfig {
|
|
87
|
+
bootstrapAppConfig?: IBootstrapAppConfig;
|
|
88
|
+
config?: INotificationModuleConfig;
|
|
89
|
+
global?: boolean;
|
|
90
|
+
includeController?: boolean;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Basic Setup
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { NotificationModule } from '@flusys/nestjs-notification';
|
|
98
|
+
|
|
99
|
+
@Module({
|
|
100
|
+
imports: [
|
|
101
|
+
NotificationModule.forRoot({
|
|
102
|
+
global: true,
|
|
103
|
+
includeController: true,
|
|
104
|
+
bootstrapAppConfig: {
|
|
105
|
+
databaseMode: 'single',
|
|
106
|
+
enableCompanyFeature: false,
|
|
107
|
+
},
|
|
108
|
+
config: {
|
|
109
|
+
enableRealtime: true,
|
|
110
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
111
|
+
defaultDatabaseConfig: {
|
|
112
|
+
type: 'postgres',
|
|
113
|
+
host: 'localhost',
|
|
114
|
+
port: 5432,
|
|
115
|
+
username: 'user',
|
|
116
|
+
password: 'password',
|
|
117
|
+
database: 'myapp',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
}),
|
|
121
|
+
],
|
|
122
|
+
})
|
|
123
|
+
export class AppModule {}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### With Company Feature
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
NotificationModule.forRoot({
|
|
130
|
+
global: true,
|
|
131
|
+
includeController: true,
|
|
132
|
+
bootstrapAppConfig: {
|
|
133
|
+
databaseMode: 'single',
|
|
134
|
+
enableCompanyFeature: true,
|
|
135
|
+
},
|
|
136
|
+
config: {
|
|
137
|
+
enableRealtime: true,
|
|
138
|
+
jwtSecret: process.env.JWT_SECRET,
|
|
139
|
+
defaultDatabaseConfig: { ... },
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Async Configuration
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
NotificationModule.forRootAsync({
|
|
148
|
+
global: true,
|
|
149
|
+
includeController: true,
|
|
150
|
+
bootstrapAppConfig: {
|
|
151
|
+
databaseMode: 'single',
|
|
152
|
+
enableCompanyFeature: true,
|
|
153
|
+
},
|
|
154
|
+
imports: [ConfigModule],
|
|
155
|
+
useFactory: async (configService: ConfigService) => ({
|
|
156
|
+
enableRealtime: true,
|
|
157
|
+
jwtSecret: configService.get('JWT_SECRET'),
|
|
158
|
+
defaultDatabaseConfig: configService.getDatabaseConfig(),
|
|
159
|
+
}),
|
|
160
|
+
inject: [ConfigService],
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Module Import Order
|
|
165
|
+
|
|
166
|
+
**Critical:** NotificationModule must be imported BEFORE AuthModule so `NOTIFICATION_ADAPTER` is available:
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
@Module({
|
|
170
|
+
imports: [
|
|
171
|
+
// Must be first!
|
|
172
|
+
NotificationModule.forRoot(getNotificationModuleOptions()),
|
|
173
|
+
// Then other modules...
|
|
174
|
+
AuthModule.forRoot(...),
|
|
175
|
+
],
|
|
176
|
+
})
|
|
177
|
+
export class AppModule {}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Entities
|
|
183
|
+
|
|
184
|
+
### Entity Groups
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// Core entities (no company feature)
|
|
188
|
+
export const NotificationCoreEntities = [Notification];
|
|
189
|
+
|
|
190
|
+
// Company-specific entities
|
|
191
|
+
export const NotificationCompanyEntities = [NotificationWithCompany];
|
|
192
|
+
|
|
193
|
+
// Helper function
|
|
194
|
+
export function getNotificationEntitiesByConfig(enableCompanyFeature: boolean): any[] {
|
|
195
|
+
return enableCompanyFeature ? NotificationCompanyEntities : NotificationCoreEntities;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Base type alias
|
|
199
|
+
export type NotificationBase = Notification | NotificationWithCompany;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Notification Entity
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
@Entity({ name: 'notification' })
|
|
206
|
+
@Index(['userId', 'isRead'])
|
|
207
|
+
@Index(['userId', 'createdAt'])
|
|
208
|
+
export class Notification extends Identity {
|
|
209
|
+
@Column({ name: 'user_id' })
|
|
210
|
+
@Index()
|
|
211
|
+
userId: string;
|
|
212
|
+
|
|
213
|
+
@Column({ length: 50, default: 'info' })
|
|
214
|
+
type: string;
|
|
215
|
+
|
|
216
|
+
@Column({ length: 255 })
|
|
217
|
+
title: string;
|
|
218
|
+
|
|
219
|
+
@Column({ type: 'text', nullable: true })
|
|
220
|
+
message: string | null;
|
|
221
|
+
|
|
222
|
+
@Column({ type: 'json', nullable: true })
|
|
223
|
+
data: Record<string, unknown> | null;
|
|
224
|
+
|
|
225
|
+
@Column({ name: 'is_read', default: false })
|
|
226
|
+
@Index()
|
|
227
|
+
isRead: boolean;
|
|
228
|
+
|
|
229
|
+
@Column({ name: 'read_at', type: 'timestamp', nullable: true })
|
|
230
|
+
readAt: Date | null;
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### NotificationWithCompany Entity
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
@Entity({ name: 'notification' })
|
|
238
|
+
@Index(['companyId', 'userId', 'isRead'])
|
|
239
|
+
export class NotificationWithCompany extends Notification {
|
|
240
|
+
@Column({ name: 'company_id', nullable: true })
|
|
241
|
+
companyId: string | null;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Database Indexes
|
|
246
|
+
|
|
247
|
+
| Index | Columns | Purpose |
|
|
248
|
+
|-------|---------|---------|
|
|
249
|
+
| Single | `user_id` | User lookups |
|
|
250
|
+
| Single | `is_read` | Unread filtering |
|
|
251
|
+
| Composite | `user_id, created_at` | Sorted user notifications |
|
|
252
|
+
| Composite | `user_id, is_read` | Common filter queries |
|
|
253
|
+
| Composite | `company_id, user_id, is_read` | Company filtering (with company feature) |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Interfaces
|
|
258
|
+
|
|
259
|
+
### INotification
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
interface INotification extends IIdentity {
|
|
263
|
+
userId: string;
|
|
264
|
+
type: string;
|
|
265
|
+
title: string;
|
|
266
|
+
message: string | null;
|
|
267
|
+
data: Record<string, unknown> | null;
|
|
268
|
+
isRead: boolean;
|
|
269
|
+
readAt: Date | null;
|
|
270
|
+
companyId?: string | null;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### INotificationPayload
|
|
275
|
+
|
|
276
|
+
WebSocket event payload:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
interface INotificationPayload {
|
|
280
|
+
id: string;
|
|
281
|
+
type: string;
|
|
282
|
+
title: string;
|
|
283
|
+
message: string | null;
|
|
284
|
+
data: Record<string, unknown> | null;
|
|
285
|
+
createdAt: Date;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
## DTOs
|
|
292
|
+
|
|
293
|
+
### Input DTOs
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
class CreateNotificationDto {
|
|
297
|
+
@IsUUID()
|
|
298
|
+
userId: string;
|
|
299
|
+
|
|
300
|
+
@IsString()
|
|
301
|
+
@MaxLength(255)
|
|
302
|
+
title: string;
|
|
303
|
+
|
|
304
|
+
@IsOptional()
|
|
305
|
+
@IsString()
|
|
306
|
+
message?: string;
|
|
307
|
+
|
|
308
|
+
@IsOptional()
|
|
309
|
+
@IsEnum(NotificationType)
|
|
310
|
+
type?: NotificationType;
|
|
311
|
+
|
|
312
|
+
@IsOptional()
|
|
313
|
+
@IsObject()
|
|
314
|
+
data?: Record<string, unknown>;
|
|
315
|
+
|
|
316
|
+
@IsOptional()
|
|
317
|
+
@IsUUID()
|
|
318
|
+
companyId?: string;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
class UpdateNotificationDto extends PartialType(CreateNotificationDto) {
|
|
322
|
+
@IsUUID()
|
|
323
|
+
id: string;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
class SendNotificationDto {
|
|
327
|
+
@IsUUID()
|
|
328
|
+
userId: string;
|
|
329
|
+
|
|
330
|
+
@IsString()
|
|
331
|
+
@MaxLength(255)
|
|
332
|
+
title: string;
|
|
333
|
+
|
|
334
|
+
@IsOptional()
|
|
335
|
+
@IsString()
|
|
336
|
+
message?: string;
|
|
337
|
+
|
|
338
|
+
@IsOptional()
|
|
339
|
+
@IsEnum(NotificationType)
|
|
340
|
+
type?: NotificationType;
|
|
341
|
+
|
|
342
|
+
@IsOptional()
|
|
343
|
+
@IsObject()
|
|
344
|
+
data?: Record<string, unknown>;
|
|
345
|
+
|
|
346
|
+
@IsOptional()
|
|
347
|
+
@IsUUID()
|
|
348
|
+
companyId?: string;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
class SendBulkNotificationDto {
|
|
352
|
+
@IsArray()
|
|
353
|
+
@IsUUID('4', { each: true })
|
|
354
|
+
userIds: string[];
|
|
355
|
+
|
|
356
|
+
@IsString()
|
|
357
|
+
@MaxLength(255)
|
|
358
|
+
title: string;
|
|
359
|
+
|
|
360
|
+
@IsOptional()
|
|
361
|
+
@IsString()
|
|
362
|
+
message?: string;
|
|
363
|
+
|
|
364
|
+
@IsOptional()
|
|
365
|
+
@IsEnum(NotificationType)
|
|
366
|
+
type?: NotificationType;
|
|
367
|
+
|
|
368
|
+
@IsOptional()
|
|
369
|
+
@IsObject()
|
|
370
|
+
data?: Record<string, unknown>;
|
|
371
|
+
|
|
372
|
+
@IsOptional()
|
|
373
|
+
@IsUUID()
|
|
374
|
+
companyId?: string;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
class MarkReadDto {
|
|
378
|
+
@IsUUID()
|
|
379
|
+
id: string;
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Output DTOs
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
class NotificationResponseDto {
|
|
387
|
+
id: string;
|
|
388
|
+
userId: string;
|
|
389
|
+
type: string;
|
|
390
|
+
title: string;
|
|
391
|
+
message: string | null;
|
|
392
|
+
data: Record<string, unknown> | null;
|
|
393
|
+
isRead: boolean;
|
|
394
|
+
readAt: Date | null;
|
|
395
|
+
companyId?: string | null;
|
|
396
|
+
createdAt: Date;
|
|
397
|
+
updatedAt: Date;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
class UnreadCountResponseDto {
|
|
401
|
+
count: number;
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## Services
|
|
408
|
+
|
|
409
|
+
### NotificationService
|
|
410
|
+
|
|
411
|
+
Main CRUD service. Extends `RequestScopedApiService`.
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
415
|
+
export class NotificationService extends RequestScopedApiService<
|
|
416
|
+
CreateNotificationDto,
|
|
417
|
+
UpdateNotificationDto,
|
|
418
|
+
INotification,
|
|
419
|
+
NotificationBase
|
|
420
|
+
> {
|
|
421
|
+
// Mark single notification as read
|
|
422
|
+
async markAsRead(id: string, user: ILoggedUserInfo): Promise<NotificationBase>;
|
|
423
|
+
|
|
424
|
+
// Mark all user's unread as read (returns count)
|
|
425
|
+
async markAllAsRead(user: ILoggedUserInfo): Promise<number>;
|
|
426
|
+
|
|
427
|
+
// Count unread notifications
|
|
428
|
+
async getUnreadCount(user: ILoggedUserInfo): Promise<number>;
|
|
429
|
+
}
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
**Automatic Features:**
|
|
433
|
+
- User scoping (users can only see own notifications)
|
|
434
|
+
- Company filtering (when enabled)
|
|
435
|
+
- WebSocket events on insert/read
|
|
436
|
+
- Cache clearing after modifications
|
|
437
|
+
- Soft delete support
|
|
438
|
+
|
|
439
|
+
### NotificationConfigService
|
|
440
|
+
|
|
441
|
+
Configuration service:
|
|
442
|
+
|
|
443
|
+
```typescript
|
|
444
|
+
@Injectable()
|
|
445
|
+
export class NotificationConfigService implements IModuleConfigService {
|
|
446
|
+
isCompanyFeatureEnabled(): boolean;
|
|
447
|
+
getDatabaseMode(): 'single' | 'multi-tenant';
|
|
448
|
+
isMultiTenant(): boolean;
|
|
449
|
+
isRealtimeEnabled(): boolean;
|
|
450
|
+
getJwtSecret(): string | undefined;
|
|
451
|
+
getOptions(): NotificationModuleOptions;
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
### NotificationHelperService
|
|
456
|
+
|
|
457
|
+
Direct notification sending (bypasses CRUD layer):
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
@Injectable()
|
|
461
|
+
export class NotificationHelperService {
|
|
462
|
+
// Send to single user
|
|
463
|
+
async send(options: SendNotificationOptions): Promise<NotificationBase>;
|
|
464
|
+
|
|
465
|
+
// Send to multiple users
|
|
466
|
+
async sendToMany(options: SendBulkNotificationOptions): Promise<NotificationBase[]>;
|
|
467
|
+
|
|
468
|
+
// Broadcast to company via WebSocket
|
|
469
|
+
async sendToCompany(
|
|
470
|
+
companyId: string,
|
|
471
|
+
title: string,
|
|
472
|
+
message?: string,
|
|
473
|
+
type?: NotificationType,
|
|
474
|
+
data?: Record<string, unknown>,
|
|
475
|
+
): Promise<void>;
|
|
476
|
+
|
|
477
|
+
// Check if user is connected
|
|
478
|
+
isUserOnline(userId: string): boolean;
|
|
479
|
+
|
|
480
|
+
// Get total connected users
|
|
481
|
+
getOnlineUserCount(): number;
|
|
482
|
+
}
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### NotificationDataSourceProvider
|
|
486
|
+
|
|
487
|
+
Dynamic datasource loading per request:
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
491
|
+
export class NotificationDataSourceProvider extends MultiTenantDataSourceService {
|
|
492
|
+
async getNotificationEntities(enableCompanyFeature?: boolean): Promise<any[]>;
|
|
493
|
+
async createDataSourceFromConfig(config: IDatabaseConfig): Promise<DataSource>;
|
|
494
|
+
async getSingleDataSource(): Promise<DataSource>;
|
|
495
|
+
async getOrCreateTenantConnection(tenant: ITenantDatabaseConfig): Promise<DataSource>;
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
---
|
|
500
|
+
|
|
501
|
+
## WebSocket Gateway
|
|
502
|
+
|
|
503
|
+
### Gateway Configuration
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
@WebSocketGateway({
|
|
507
|
+
namespace: '/notifications',
|
|
508
|
+
cors: { origin: '*', credentials: true },
|
|
509
|
+
})
|
|
510
|
+
export class NotificationGateway {
|
|
511
|
+
// Emit to all user's sockets (regardless of company)
|
|
512
|
+
emitToUser(userId: string, event: string, data: any): void;
|
|
513
|
+
|
|
514
|
+
// Emit to user's sockets only within a specific company context
|
|
515
|
+
emitToUserInCompany(userId: string, companyId: string | null | undefined, event: string, data: any): void;
|
|
516
|
+
|
|
517
|
+
// Emit to all sockets in a company room
|
|
518
|
+
emitToCompany(companyId: string, event: string, data: any): void;
|
|
519
|
+
|
|
520
|
+
// Broadcast to all connected clients
|
|
521
|
+
broadcast(event: string, data: any): void;
|
|
522
|
+
|
|
523
|
+
// Check if user is connected
|
|
524
|
+
isUserOnline(userId: string): boolean;
|
|
525
|
+
|
|
526
|
+
// Get total connected clients
|
|
527
|
+
getOnlineUserCount(): number;
|
|
528
|
+
}
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### Multi-Company Session Handling
|
|
532
|
+
|
|
533
|
+
When a user logs in with **different companies on different devices**, the gateway correctly isolates notifications:
|
|
534
|
+
|
|
535
|
+
| Device | Company Context | Socket Rooms |
|
|
536
|
+
|--------|-----------------|--------------|
|
|
537
|
+
| Device 1 | Company A | `user:{userId}`, `company:{companyA.id}` |
|
|
538
|
+
| Device 2 | Company B | `user:{userId}`, `company:{companyB.id}` |
|
|
539
|
+
|
|
540
|
+
**Notification Delivery:**
|
|
541
|
+
|
|
542
|
+
| Notification Type | Target Method | Device 1 (Company A) | Device 2 (Company B) |
|
|
543
|
+
|-------------------|---------------|----------------------|----------------------|
|
|
544
|
+
| User notification (no companyId) | `emitToUser()` | Receives | Receives |
|
|
545
|
+
| Company A notification | `emitToUserInCompany()` | Receives | Does NOT receive |
|
|
546
|
+
| Company B notification | `emitToUserInCompany()` | Does NOT receive | Receives |
|
|
547
|
+
| Company-wide broadcast | `emitToCompany()` | Receives (if Company A) | Receives (if Company B) |
|
|
548
|
+
|
|
549
|
+
This ensures that notifications are only delivered to devices connected with the matching company context, preventing confusion when users work with multiple companies.
|
|
550
|
+
|
|
551
|
+
### Client Connection
|
|
552
|
+
|
|
553
|
+
```javascript
|
|
554
|
+
// Client-side connection
|
|
555
|
+
const socket = io('ws://localhost:2002/notifications', {
|
|
556
|
+
auth: { token: 'jwt-token' },
|
|
557
|
+
// OR
|
|
558
|
+
query: { token: 'jwt-token' },
|
|
559
|
+
// OR
|
|
560
|
+
extraHeaders: { authorization: 'Bearer jwt-token' },
|
|
561
|
+
});
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
### WebSocket Events
|
|
565
|
+
|
|
566
|
+
**Server → Client:**
|
|
567
|
+
|
|
568
|
+
| Event | Payload | Trigger |
|
|
569
|
+
|-------|---------|---------|
|
|
570
|
+
| `notification:new` | `INotificationPayload` | New notification created |
|
|
571
|
+
| `notification:read` | `{ id: string }` | Single notification marked read |
|
|
572
|
+
| `notification:read-all` | `{}` | All notifications marked read |
|
|
573
|
+
|
|
574
|
+
### Room Structure
|
|
575
|
+
|
|
576
|
+
Users automatically join:
|
|
577
|
+
- `user:{userId}` - User-specific room
|
|
578
|
+
- `company:{companyId}` - Company room (if company feature enabled)
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
## Adapter Pattern
|
|
583
|
+
|
|
584
|
+
### Interface (from nestjs-shared)
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
interface INotificationAdapter {
|
|
588
|
+
send(options: NotificationSendOptions): Promise<void>;
|
|
589
|
+
sendToMany(options: NotificationBulkSendOptions): Promise<void>;
|
|
590
|
+
broadcastToCompany?(companyId, title, message, type, data): Promise<void>;
|
|
591
|
+
isUserOnline?(userId: string): boolean;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
interface NotificationSendOptions {
|
|
595
|
+
userId: string;
|
|
596
|
+
title: string;
|
|
597
|
+
message?: string;
|
|
598
|
+
type?: NotificationType;
|
|
599
|
+
data?: Record<string, unknown>;
|
|
600
|
+
companyId?: string;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export const NOTIFICATION_ADAPTER = 'NOTIFICATION_ADAPTER';
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Usage in Other Modules
|
|
607
|
+
|
|
608
|
+
```typescript
|
|
609
|
+
import { NOTIFICATION_ADAPTER, INotificationAdapter } from '@flusys/nestjs-shared';
|
|
610
|
+
|
|
611
|
+
@Injectable()
|
|
612
|
+
export class SomeService {
|
|
613
|
+
constructor(
|
|
614
|
+
@Inject(NOTIFICATION_ADAPTER)
|
|
615
|
+
private readonly notificationAdapter: INotificationAdapter,
|
|
616
|
+
) {}
|
|
617
|
+
|
|
618
|
+
async someAction() {
|
|
619
|
+
// Send notification
|
|
620
|
+
await this.notificationAdapter.send({
|
|
621
|
+
userId: 'user-uuid',
|
|
622
|
+
title: 'Action Completed',
|
|
623
|
+
type: NotificationType.SUCCESS,
|
|
624
|
+
data: { actionId: '123' },
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
---
|
|
631
|
+
|
|
632
|
+
## API Endpoints
|
|
633
|
+
|
|
634
|
+
All endpoints use POST (RPC pattern) and require JWT authentication.
|
|
635
|
+
|
|
636
|
+
### User Endpoints
|
|
637
|
+
|
|
638
|
+
| Endpoint | Permission | Description |
|
|
639
|
+
|----------|-----------|-------------|
|
|
640
|
+
| `POST /notification/get-all` | JWT | Get paginated notifications |
|
|
641
|
+
| `POST /notification/get/:id` | JWT | Get single notification |
|
|
642
|
+
| `POST /notification/mark-read` | JWT | Mark notification as read |
|
|
643
|
+
| `POST /notification/mark-all-read` | JWT | Mark all as read |
|
|
644
|
+
| `POST /notification/unread-count` | JWT | Get unread count |
|
|
645
|
+
|
|
646
|
+
### Admin Endpoints
|
|
647
|
+
|
|
648
|
+
| Endpoint | Permission | Description |
|
|
649
|
+
|----------|-----------|-------------|
|
|
650
|
+
| `POST /notification/send` | `notification.create` | Send to single user |
|
|
651
|
+
| `POST /notification/send-bulk` | `notification.create` | Send to multiple users |
|
|
652
|
+
| `POST /notification/insert` | `notification.create` | Direct CRUD insert |
|
|
653
|
+
| `POST /notification/update` | `notification.update` | Direct CRUD update |
|
|
654
|
+
| `POST /notification/delete` | `notification.delete` | Direct CRUD delete |
|
|
655
|
+
|
|
656
|
+
### Example Requests
|
|
657
|
+
|
|
658
|
+
**Mark as Read:**
|
|
659
|
+
|
|
660
|
+
```bash
|
|
661
|
+
curl -X POST http://localhost:2002/notification/mark-read \
|
|
662
|
+
-H "Authorization: Bearer <token>" \
|
|
663
|
+
-H "Content-Type: application/json" \
|
|
664
|
+
-d '{ "id": "notification-uuid" }'
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
**Send Notification:**
|
|
668
|
+
|
|
669
|
+
```bash
|
|
670
|
+
curl -X POST http://localhost:2002/notification/send \
|
|
671
|
+
-H "Authorization: Bearer <token>" \
|
|
672
|
+
-H "Content-Type: application/json" \
|
|
673
|
+
-d '{
|
|
674
|
+
"userId": "user-uuid",
|
|
675
|
+
"title": "Order Confirmed",
|
|
676
|
+
"message": "Your order #12345 has been confirmed",
|
|
677
|
+
"type": "success",
|
|
678
|
+
"data": { "orderId": "12345" }
|
|
679
|
+
}'
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
**Send Bulk:**
|
|
683
|
+
|
|
684
|
+
```bash
|
|
685
|
+
curl -X POST http://localhost:2002/notification/send-bulk \
|
|
686
|
+
-H "Authorization: Bearer <token>" \
|
|
687
|
+
-H "Content-Type: application/json" \
|
|
688
|
+
-d '{
|
|
689
|
+
"userIds": ["uuid1", "uuid2", "uuid3"],
|
|
690
|
+
"title": "System Maintenance",
|
|
691
|
+
"message": "Scheduled maintenance at 10 PM",
|
|
692
|
+
"type": "warning"
|
|
693
|
+
}'
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**Get Unread Count:**
|
|
697
|
+
|
|
698
|
+
```bash
|
|
699
|
+
curl -X POST http://localhost:2002/notification/unread-count \
|
|
700
|
+
-H "Authorization: Bearer <token>"
|
|
701
|
+
|
|
702
|
+
# Response: { "success": true, "data": { "count": 5 } }
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## Swagger Documentation
|
|
708
|
+
|
|
709
|
+
### Setup Swagger for Notification Module
|
|
710
|
+
|
|
711
|
+
```typescript
|
|
712
|
+
import { notificationSwaggerConfig } from '@flusys/nestjs-notification';
|
|
713
|
+
import { setupSwaggerDocs } from '@flusys/nestjs-core/docs';
|
|
714
|
+
|
|
715
|
+
// In your bootstrap function
|
|
716
|
+
setupSwaggerDocs(app, notificationSwaggerConfig(bootstrapAppConfig));
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
### Swagger Access
|
|
720
|
+
|
|
721
|
+
- **URL:** `http://localhost:2002/api/docs/notification`
|
|
722
|
+
- **Features:** Bearer auth, auto-generated schemas
|
|
723
|
+
- **Company Feature:** When disabled, `companyId` fields are hidden from schemas
|
|
724
|
+
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
## Permissions
|
|
728
|
+
|
|
729
|
+
```typescript
|
|
730
|
+
export const NOTIFICATION_PERMISSIONS = {
|
|
731
|
+
CREATE: 'notification.create',
|
|
732
|
+
READ: 'notification.read',
|
|
733
|
+
UPDATE: 'notification.update',
|
|
734
|
+
DELETE: 'notification.delete',
|
|
735
|
+
} as const;
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
**Endpoint Security:**
|
|
739
|
+
|
|
740
|
+
| Endpoint | Permission | Access |
|
|
741
|
+
|----------|-----------|--------|
|
|
742
|
+
| `get-all`, `get/:id` | JWT only | Own notifications only |
|
|
743
|
+
| `mark-read`, `mark-all-read`, `unread-count` | JWT only | Own notifications only |
|
|
744
|
+
| `send`, `send-bulk` | `notification.create` | Admin/privileged users |
|
|
745
|
+
| CRUD endpoints | Respective permissions | Admin |
|
|
746
|
+
|
|
747
|
+
---
|
|
748
|
+
|
|
749
|
+
## Multi-Tenant Support
|
|
750
|
+
|
|
751
|
+
### Company Isolation
|
|
752
|
+
|
|
753
|
+
When `enableCompanyFeature: true`:
|
|
754
|
+
- Notifications are company-scoped
|
|
755
|
+
- Users can only see notifications for their company
|
|
756
|
+
- WebSocket broadcasts to company rooms
|
|
757
|
+
|
|
758
|
+
### Company Filtering
|
|
759
|
+
|
|
760
|
+
Services automatically filter by `companyId`:
|
|
761
|
+
|
|
762
|
+
```typescript
|
|
763
|
+
// In NotificationService
|
|
764
|
+
if (this.notificationConfig.isCompanyFeatureEnabled() && user?.companyId) {
|
|
765
|
+
query.andWhere('notification.companyId = :companyId', {
|
|
766
|
+
companyId: user.companyId,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### Dynamic Entity Selection
|
|
772
|
+
|
|
773
|
+
```typescript
|
|
774
|
+
protected resolveEntity(): EntityTarget<NotificationBase> {
|
|
775
|
+
return this.notificationConfig.isCompanyFeatureEnabled()
|
|
776
|
+
? NotificationWithCompany
|
|
777
|
+
: Notification;
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
---
|
|
782
|
+
|
|
783
|
+
## Integration with Other Modules
|
|
784
|
+
|
|
785
|
+
### Auth Module Integration
|
|
786
|
+
|
|
787
|
+
The Auth module can send login notifications via the adapter:
|
|
788
|
+
|
|
789
|
+
```typescript
|
|
790
|
+
// In AppModule providers (app level)
|
|
791
|
+
{
|
|
792
|
+
provide: AUTH_NOTIFICATION_PROVIDER,
|
|
793
|
+
useExisting: NotificationAdapter,
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// In AuthenticationService
|
|
797
|
+
@Optional() @Inject(NOTIFICATION_ADAPTER)
|
|
798
|
+
private readonly notificationAdapter?: INotificationAdapter;
|
|
799
|
+
|
|
800
|
+
async onLoginSuccess(userId: string, companyId?: string): Promise<void> {
|
|
801
|
+
if (!this.notificationAdapter) return;
|
|
802
|
+
|
|
803
|
+
await this.notificationAdapter.send({
|
|
804
|
+
userId,
|
|
805
|
+
title: 'Login successful',
|
|
806
|
+
message: 'You have successfully logged in to your account.',
|
|
807
|
+
type: NotificationType.INFO,
|
|
808
|
+
companyId,
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
```
|
|
812
|
+
|
|
813
|
+
### Module Registration Order
|
|
814
|
+
|
|
815
|
+
**IMPORTANT:** NotificationModule must be registered BEFORE AuthModule:
|
|
816
|
+
|
|
817
|
+
```typescript
|
|
818
|
+
@Module({
|
|
819
|
+
imports: [
|
|
820
|
+
// NotificationModule FIRST - provides NOTIFICATION_ADAPTER
|
|
821
|
+
NotificationModule.forRootAsync(getNotificationModuleAsyncOptions()),
|
|
822
|
+
|
|
823
|
+
// AuthModule SECOND - consumes NOTIFICATION_ADAPTER
|
|
824
|
+
AuthModule.forRootAsync(getAuthModuleAsyncOptions()),
|
|
825
|
+
|
|
826
|
+
// Other modules...
|
|
827
|
+
],
|
|
828
|
+
})
|
|
829
|
+
export class AppModule {}
|
|
830
|
+
```
|
|
831
|
+
|
|
832
|
+
---
|
|
833
|
+
|
|
834
|
+
## Best Practices
|
|
835
|
+
|
|
836
|
+
### 1. Use Adapter for Cross-Module Notifications
|
|
837
|
+
|
|
838
|
+
```typescript
|
|
839
|
+
// In any module - use adapter interface
|
|
840
|
+
await this.notificationAdapter.send({
|
|
841
|
+
userId: user.id,
|
|
842
|
+
title: 'Email Sent',
|
|
843
|
+
type: NotificationType.SUCCESS,
|
|
844
|
+
});
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
### 2. Include Meaningful Data
|
|
848
|
+
|
|
849
|
+
```typescript
|
|
850
|
+
await notificationService.insert({
|
|
851
|
+
userId: user.id,
|
|
852
|
+
title: 'Order Shipped',
|
|
853
|
+
message: 'Your order #12345 is on its way',
|
|
854
|
+
type: NotificationType.INFO,
|
|
855
|
+
data: {
|
|
856
|
+
orderId: '12345',
|
|
857
|
+
trackingNumber: 'ABC123',
|
|
858
|
+
estimatedDelivery: '2026-03-01',
|
|
859
|
+
},
|
|
860
|
+
}, user);
|
|
861
|
+
```
|
|
862
|
+
|
|
863
|
+
### 3. Use Appropriate Notification Types
|
|
864
|
+
|
|
865
|
+
| Type | Use For |
|
|
866
|
+
|------|---------|
|
|
867
|
+
| `INFO` | General information, updates |
|
|
868
|
+
| `SUCCESS` | Successful operations |
|
|
869
|
+
| `WARNING` | Important notices, upcoming deadlines |
|
|
870
|
+
| `ERROR` | Failed operations, errors |
|
|
871
|
+
|
|
872
|
+
### 4. Handle WebSocket Reconnection
|
|
873
|
+
|
|
874
|
+
Client should handle reconnection:
|
|
875
|
+
|
|
876
|
+
```javascript
|
|
877
|
+
socket.on('disconnect', () => {
|
|
878
|
+
// Optionally reload unread count on reconnect
|
|
879
|
+
});
|
|
880
|
+
|
|
881
|
+
socket.on('connect', () => {
|
|
882
|
+
// Refresh notifications after reconnect
|
|
883
|
+
});
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
---
|
|
887
|
+
|
|
888
|
+
## API Reference
|
|
889
|
+
|
|
890
|
+
### Main Exports
|
|
891
|
+
|
|
892
|
+
```typescript
|
|
893
|
+
// Module
|
|
894
|
+
import { NotificationModule } from '@flusys/nestjs-notification';
|
|
895
|
+
|
|
896
|
+
// Services
|
|
897
|
+
import {
|
|
898
|
+
NotificationService,
|
|
899
|
+
NotificationConfigService,
|
|
900
|
+
NotificationHelperService,
|
|
901
|
+
NotificationDataSourceProvider,
|
|
902
|
+
} from '@flusys/nestjs-notification/services';
|
|
903
|
+
|
|
904
|
+
// Entities
|
|
905
|
+
import {
|
|
906
|
+
Notification,
|
|
907
|
+
NotificationBase,
|
|
908
|
+
NotificationWithCompany,
|
|
909
|
+
NotificationCoreEntities,
|
|
910
|
+
NotificationCompanyEntities,
|
|
911
|
+
getNotificationEntitiesByConfig,
|
|
912
|
+
} from '@flusys/nestjs-notification/entities';
|
|
913
|
+
|
|
914
|
+
// DTOs
|
|
915
|
+
import {
|
|
916
|
+
CreateNotificationDto,
|
|
917
|
+
UpdateNotificationDto,
|
|
918
|
+
SendNotificationDto,
|
|
919
|
+
SendBulkNotificationDto,
|
|
920
|
+
MarkReadDto,
|
|
921
|
+
NotificationResponseDto,
|
|
922
|
+
UnreadCountResponseDto,
|
|
923
|
+
} from '@flusys/nestjs-notification/dtos';
|
|
924
|
+
|
|
925
|
+
// Interfaces
|
|
926
|
+
import {
|
|
927
|
+
INotification,
|
|
928
|
+
INotificationPayload,
|
|
929
|
+
INotificationModuleConfig,
|
|
930
|
+
NotificationModuleOptions,
|
|
931
|
+
NotificationModuleAsyncOptions,
|
|
932
|
+
NotificationOptionsFactory,
|
|
933
|
+
} from '@flusys/nestjs-notification/interfaces';
|
|
934
|
+
|
|
935
|
+
// Gateway
|
|
936
|
+
import { NotificationGateway } from '@flusys/nestjs-notification/gateways';
|
|
937
|
+
|
|
938
|
+
// Adapter
|
|
939
|
+
import { NotificationAdapter } from '@flusys/nestjs-notification/adapters';
|
|
940
|
+
|
|
941
|
+
// Controller
|
|
942
|
+
import { NotificationController } from '@flusys/nestjs-notification/controllers';
|
|
943
|
+
|
|
944
|
+
// Constants
|
|
945
|
+
import { NOTIFICATION_MODULE_OPTIONS } from '@flusys/nestjs-notification/config';
|
|
946
|
+
|
|
947
|
+
// Swagger Config
|
|
948
|
+
import { notificationSwaggerConfig } from '@flusys/nestjs-notification/docs';
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
### Service Methods Reference
|
|
952
|
+
|
|
953
|
+
#### NotificationService
|
|
954
|
+
|
|
955
|
+
```typescript
|
|
956
|
+
class NotificationService {
|
|
957
|
+
// Standard CRUD (inherited from RequestScopedApiService)
|
|
958
|
+
insert(dto, user): Promise<NotificationBase>;
|
|
959
|
+
getById(id, user): Promise<NotificationBase | null>;
|
|
960
|
+
getAll(filterDto, user): Promise<{ list: NotificationBase[]; total: number }>;
|
|
961
|
+
update(dto, user): Promise<NotificationBase>;
|
|
962
|
+
delete(id, user): Promise<boolean>;
|
|
963
|
+
|
|
964
|
+
// Custom methods
|
|
965
|
+
markAsRead(id, user): Promise<NotificationBase>;
|
|
966
|
+
markAllAsRead(user): Promise<number>;
|
|
967
|
+
getUnreadCount(user): Promise<number>;
|
|
968
|
+
}
|
|
969
|
+
```
|
|
970
|
+
|
|
971
|
+
#### NotificationHelperService
|
|
972
|
+
|
|
973
|
+
```typescript
|
|
974
|
+
class NotificationHelperService {
|
|
975
|
+
send(options: SendNotificationOptions): Promise<NotificationBase>;
|
|
976
|
+
sendToMany(options: SendBulkNotificationOptions): Promise<NotificationBase[]>;
|
|
977
|
+
sendToCompany(companyId, title, message?, type?, data?): Promise<void>;
|
|
978
|
+
isUserOnline(userId: string): boolean;
|
|
979
|
+
getOnlineUserCount(): number;
|
|
980
|
+
}
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
#### NotificationGateway
|
|
984
|
+
|
|
985
|
+
```typescript
|
|
986
|
+
class NotificationGateway {
|
|
987
|
+
emitToUser(userId, event, data): void;
|
|
988
|
+
emitToCompany(companyId, event, data): void;
|
|
989
|
+
broadcast(event, data): void;
|
|
990
|
+
isUserOnline(userId): boolean;
|
|
991
|
+
getOnlineUserCount(): number;
|
|
992
|
+
}
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
---
|
|
996
|
+
|
|
997
|
+
**Last Updated:** 2026-02-26
|
|
998
|
+
**Version:** 3.0.0
|