@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.
Files changed (80) hide show
  1. package/README.md +998 -0
  2. package/adapters/index.d.ts +1 -0
  3. package/adapters/notification.adapter.d.ts +10 -0
  4. package/cjs/adapters/index.js +18 -0
  5. package/cjs/adapters/notification.adapter.js +79 -0
  6. package/cjs/config/index.js +18 -0
  7. package/cjs/config/notification.constants.js +11 -0
  8. package/cjs/controllers/index.js +18 -0
  9. package/cjs/controllers/notification.controller.js +278 -0
  10. package/cjs/docs/index.js +18 -0
  11. package/cjs/docs/notification-swagger.config.js +61 -0
  12. package/cjs/dtos/index.js +18 -0
  13. package/cjs/dtos/notification.dto.js +244 -0
  14. package/cjs/entities/index.js +48 -0
  15. package/cjs/entities/notification-with-company.entity.js +57 -0
  16. package/cjs/entities/notification.entity.js +111 -0
  17. package/cjs/gateways/index.js +18 -0
  18. package/cjs/gateways/notification.gateway.js +226 -0
  19. package/cjs/index.js +28 -0
  20. package/cjs/interfaces/index.js +19 -0
  21. package/cjs/interfaces/notification-module.interface.js +4 -0
  22. package/cjs/interfaces/notification.interface.js +4 -0
  23. package/cjs/modules/index.js +18 -0
  24. package/cjs/modules/notification.module.js +139 -0
  25. package/cjs/services/index.js +21 -0
  26. package/cjs/services/notification-config.service.js +72 -0
  27. package/cjs/services/notification-datasource.provider.js +174 -0
  28. package/cjs/services/notification-helper.service.js +186 -0
  29. package/cjs/services/notification.service.js +220 -0
  30. package/config/index.d.ts +1 -0
  31. package/config/notification.constants.d.ts +1 -0
  32. package/controllers/index.d.ts +1 -0
  33. package/controllers/notification.controller.d.ts +26 -0
  34. package/docs/index.d.ts +1 -0
  35. package/docs/notification-swagger.config.d.ts +2 -0
  36. package/dtos/index.d.ts +1 -0
  37. package/dtos/notification.dto.d.ts +47 -0
  38. package/entities/index.d.ts +8 -0
  39. package/entities/notification-with-company.entity.d.ts +4 -0
  40. package/entities/notification.entity.d.ts +10 -0
  41. package/fesm/adapters/index.js +1 -0
  42. package/fesm/adapters/notification.adapter.js +69 -0
  43. package/fesm/config/index.js +1 -0
  44. package/fesm/config/notification.constants.js +1 -0
  45. package/fesm/controllers/index.js +1 -0
  46. package/fesm/controllers/notification.controller.js +268 -0
  47. package/fesm/docs/index.js +1 -0
  48. package/fesm/docs/notification-swagger.config.js +51 -0
  49. package/fesm/dtos/index.js +1 -0
  50. package/fesm/dtos/notification.dto.js +211 -0
  51. package/fesm/entities/index.js +14 -0
  52. package/fesm/entities/notification-with-company.entity.js +47 -0
  53. package/fesm/entities/notification.entity.js +101 -0
  54. package/fesm/gateways/index.js +1 -0
  55. package/fesm/gateways/notification.gateway.js +175 -0
  56. package/fesm/index.js +20 -0
  57. package/fesm/interfaces/index.js +2 -0
  58. package/fesm/interfaces/notification-module.interface.js +1 -0
  59. package/fesm/interfaces/notification.interface.js +1 -0
  60. package/fesm/modules/index.js +1 -0
  61. package/fesm/modules/notification.module.js +129 -0
  62. package/fesm/services/index.js +4 -0
  63. package/fesm/services/notification-config.service.js +62 -0
  64. package/fesm/services/notification-datasource.provider.js +123 -0
  65. package/fesm/services/notification-helper.service.js +176 -0
  66. package/fesm/services/notification.service.js +210 -0
  67. package/gateways/index.d.ts +1 -0
  68. package/gateways/notification.gateway.d.ts +21 -0
  69. package/index.d.ts +10 -0
  70. package/interfaces/index.d.ts +2 -0
  71. package/interfaces/notification-module.interface.d.ts +20 -0
  72. package/interfaces/notification.interface.d.ts +19 -0
  73. package/modules/index.d.ts +1 -0
  74. package/modules/notification.module.d.ts +8 -0
  75. package/package.json +98 -0
  76. package/services/index.d.ts +4 -0
  77. package/services/notification-config.service.d.ts +13 -0
  78. package/services/notification-datasource.provider.d.ts +23 -0
  79. package/services/notification-helper.service.d.ts +44 -0
  80. 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