@flusys/nestjs-notification 4.0.2 → 4.1.0

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 CHANGED
@@ -1,99 +1,92 @@
1
- # Notification Package Guide
1
+ # @flusys/nestjs-notification
2
2
 
3
- > **Package:** `@flusys/nestjs-notification`
4
- > **Version:** 4.0.2
5
- > **Type:** Real-time push notifications with WebSocket support, multi-tenant, and company isolation
3
+ > Real-time notification system for NestJS — WebSocket via Socket.IO, persistent PostgreSQL storage, read/unread tracking, company broadcasting, and a cross-module adapter for dependency-free integration.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@flusys/nestjs-notification.svg)](https://www.npmjs.com/package/@flusys/nestjs-notification)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![NestJS](https://img.shields.io/badge/NestJS-11.x-red.svg)](https://nestjs.com/)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/)
9
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18.x-green.svg)](https://nodejs.org/)
10
+
11
+ ---
6
12
 
7
13
  ## Table of Contents
8
14
 
9
15
  - [Overview](#overview)
16
+ - [Features](#features)
17
+ - [Compatibility](#compatibility)
10
18
  - [Installation](#installation)
11
- - [Constants](#constants)
12
- - [Module Setup](#module-setup)
13
- - [Entities](#entities)
14
- - [Interfaces](#interfaces)
15
- - [DTOs](#dtos)
16
- - [Services](#services)
19
+ - [Quick Start](#quick-start)
20
+ - [Module Registration](#module-registration)
21
+ - [forRoot (Sync)](#forroot-sync)
22
+ - [forRootAsync (Factory)](#forrootasync-factory)
23
+ - [Configuration Reference](#configuration-reference)
24
+ - [Feature Toggles](#feature-toggles)
17
25
  - [WebSocket Gateway](#websocket-gateway)
18
- - [Adapter Pattern](#adapter-pattern)
26
+ - [Client Connection](#client-connection)
27
+ - [WebSocket Events](#websocket-events)
19
28
  - [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)
29
+ - [Entities](#entities)
30
+ - [Cross-Module Adapter (NOTIFICATION_ADAPTER)](#cross-module-adapter-notification_adapter)
31
+ - [Exported Services](#exported-services)
32
+ - [Sending Notifications Programmatically](#sending-notifications-programmatically)
33
+ - [Frontend Integration](#frontend-integration)
34
+ - [Troubleshooting](#troubleshooting)
35
+ - [License](#license)
26
36
 
27
37
  ---
28
38
 
29
39
  ## Overview
30
40
 
31
- `@flusys/nestjs-notification` provides:
41
+ `@flusys/nestjs-notification` delivers notifications both in real-time (Socket.IO WebSocket) and persistently (PostgreSQL). Unread notifications survive disconnections and are delivered on the next connection. The `NOTIFICATION_ADAPTER` allows auth, email, and other modules to send notifications without importing this package directly.
32
42
 
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
43
+ ---
40
44
 
41
- ### Package Hierarchy
45
+ ## Features
42
46
 
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
- ```
47
+ - **Persistent storage** — Notifications stored in PostgreSQL with read/unread tracking
48
+ - **Real-time WebSocket** — Socket.IO gateway at `/notifications` namespace
49
+ - **JWT authentication on WebSocket** — Token verified on connection handshake
50
+ - **Read tracking** — `isRead` + `readAt` timestamp per notification
51
+ - **Mark as read** — Single notification or bulk "mark all as read"
52
+ - **Unread count** — Fast count query for notification badge
53
+ - **Company scoping** — Filter by company in multi-company setups
54
+ - **Broadcast to company** — Emit to all users in a company Socket.IO room
55
+ - **Online presence** — Check if a user is currently connected
56
+ - **Cross-module adapter** — `NOTIFICATION_ADAPTER` for sending from any module
57
+ - **Configurable realtime** — Disable Socket.IO gateway when not needed
50
58
 
51
59
  ---
52
60
 
53
- ## Installation
61
+ ## Compatibility
54
62
 
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
- ```
63
+ | Package | Version |
64
+ |---------|---------|
65
+ | `@flusys/nestjs-core` | `^4.0.0` |
66
+ | `@flusys/nestjs-shared` | `^4.0.0` |
67
+ | `@nestjs/websockets` | `^11.0.0` |
68
+ | `@nestjs/platform-socket.io` | `^11.0.0` |
69
+ | `socket.io` | `^4.0.0` |
70
+ | `jsonwebtoken` | `^9.0.0` |
71
+ | Node.js | `>= 18.x` |
61
72
 
62
73
  ---
63
74
 
64
- ## Constants
75
+ ## Installation
65
76
 
66
- ```typescript
67
- // Injection Token
68
- export const NOTIFICATION_MODULE_OPTIONS = 'NOTIFICATION_MODULE_OPTIONS';
77
+ ```bash
78
+ npm install @flusys/nestjs-notification @flusys/nestjs-shared @flusys/nestjs-core
79
+ npm install @nestjs/platform-socket.io socket.io
69
80
  ```
70
81
 
71
82
  ---
72
83
 
73
- ## Module Setup
84
+ ## Quick Start
74
85
 
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
86
+ ### With Realtime Enabled
95
87
 
96
88
  ```typescript
89
+ import { Module } from '@nestjs/common';
97
90
  import { NotificationModule } from '@flusys/nestjs-notification';
98
91
 
99
92
  @Module({
@@ -110,11 +103,11 @@ import { NotificationModule } from '@flusys/nestjs-notification';
110
103
  jwtSecret: process.env.JWT_SECRET,
111
104
  defaultDatabaseConfig: {
112
105
  type: 'postgres',
113
- host: 'localhost',
114
- port: 5432,
115
- username: 'user',
116
- password: 'password',
117
- database: 'myapp',
106
+ host: process.env.DB_HOST,
107
+ port: Number(process.env.DB_PORT ?? 5432),
108
+ username: process.env.DB_USER,
109
+ password: process.env.DB_PASSWORD,
110
+ database: process.env.DB_NAME,
118
111
  },
119
112
  },
120
113
  }),
@@ -123,27 +116,64 @@ import { NotificationModule } from '@flusys/nestjs-notification';
123
116
  export class AppModule {}
124
117
  ```
125
118
 
126
- ### With Company Feature
119
+ ### Using NOTIFICATION_ADAPTER in Another Module
120
+
121
+ ```typescript
122
+ // In auth.service.ts — sends a notification after login
123
+ import { NOTIFICATION_ADAPTER } from '@flusys/nestjs-shared/interfaces';
124
+ import { INotificationAdapter } from '@flusys/nestjs-shared/interfaces';
125
+
126
+ @Injectable()
127
+ export class AuthenticationService {
128
+ constructor(
129
+ @Optional() @Inject(NOTIFICATION_ADAPTER)
130
+ private readonly notificationAdapter?: INotificationAdapter,
131
+ ) {}
132
+
133
+ async afterLogin(userId: string): Promise<void> {
134
+ await this.notificationAdapter?.send({
135
+ userId,
136
+ title: 'New Login',
137
+ message: 'Your account was accessed from a new device.',
138
+ type: 'warning',
139
+ });
140
+ }
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Module Registration
147
+
148
+ ### forRoot (Sync)
127
149
 
128
150
  ```typescript
129
151
  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
- });
152
+ global?: boolean;
153
+ includeController?: boolean; // Default: true
154
+ bootstrapAppConfig?: {
155
+ databaseMode: 'single' | 'multi-tenant';
156
+ enableCompanyFeature: boolean;
157
+ };
158
+ config?: {
159
+ enableRealtime?: boolean; // Default: true — registers Socket.IO gateway
160
+ jwtSecret?: string; // Required for WebSocket JWT authentication
161
+ defaultDatabaseConfig?: IDatabaseConfig;
162
+ tenants?: ITenantDatabaseConfig[];
163
+ };
164
+ })
142
165
  ```
143
166
 
144
- ### Async Configuration
167
+ **Behavior:**
168
+ - `enableRealtime: false` → `NotificationGateway` is NOT registered (no WebSocket)
169
+ - `includeController: false` → No HTTP endpoints registered
170
+ - `global: true` → Exports available application-wide
171
+
172
+ ### forRootAsync (Factory)
145
173
 
146
174
  ```typescript
175
+ import { ConfigService } from '@nestjs/config';
176
+
147
177
  NotificationModule.forRootAsync({
148
178
  global: true,
149
179
  includeController: true,
@@ -152,476 +182,258 @@ NotificationModule.forRootAsync({
152
182
  enableCompanyFeature: true,
153
183
  },
154
184
  imports: [ConfigModule],
155
- useFactory: async (configService: ConfigService) => ({
185
+ useFactory: (configService: ConfigService) => ({
156
186
  enableRealtime: true,
157
187
  jwtSecret: configService.get('JWT_SECRET'),
158
- defaultDatabaseConfig: configService.getDatabaseConfig(),
188
+ defaultDatabaseConfig: {
189
+ type: 'postgres',
190
+ host: configService.get('DB_HOST'),
191
+ port: configService.get<number>('DB_PORT'),
192
+ username: configService.get('DB_USER'),
193
+ password: configService.get('DB_PASSWORD'),
194
+ database: configService.get('DB_NAME'),
195
+ },
159
196
  }),
160
197
  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
198
  })
177
- export class AppModule {}
178
199
  ```
179
200
 
180
- ---
201
+ **Exported services:**
202
+ - `NotificationConfigService`
203
+ - `NotificationDataSourceProvider`
204
+ - `NotificationService`
205
+ - `NotificationHelperService`
206
+ - `NotificationGateway` *(when `enableRealtime: true`)*
207
+ - `NOTIFICATION_ADAPTER`
181
208
 
182
- ## Entities
209
+ ---
183
210
 
184
- ### Entity Groups
211
+ ## Configuration Reference
185
212
 
186
213
  ```typescript
187
- // Core entities (no company feature)
188
- export const NotificationCoreEntities = [Notification];
189
-
190
- // Company-specific entities
191
- export const NotificationCompanyEntities = [NotificationWithCompany];
214
+ interface INotificationModuleConfig extends IDataSourceServiceOptions {
215
+ /** Enable Socket.IO WebSocket gateway (default: true) */
216
+ enableRealtime?: boolean;
192
217
 
193
- // Helper function
194
- export function getNotificationEntitiesByConfig(enableCompanyFeature: boolean): any[] {
195
- return enableCompanyFeature ? NotificationCompanyEntities : NotificationCoreEntities;
218
+ /** JWT secret for WebSocket connection authentication */
219
+ jwtSecret?: string;
196
220
  }
197
-
198
- // Base type alias
199
- export type NotificationBase = Notification | NotificationWithCompany;
200
221
  ```
201
222
 
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;
223
+ ---
221
224
 
222
- @Column({ type: 'json', nullable: true })
223
- data: Record<string, unknown> | null;
225
+ ## Feature Toggles
224
226
 
225
- @Column({ name: 'is_read', default: false })
226
- @Index()
227
- isRead: boolean;
227
+ | Feature | Config | Default | Effect |
228
+ |---------|--------|---------|--------|
229
+ | WebSocket gateway | `enableRealtime: true` | `true` | Registers `NotificationGateway` on `/notifications` namespace |
230
+ | Company scoping | `enableCompanyFeature: true` | `false` | Uses `NotificationWithCompany` entity; adds company room broadcasting |
231
+ | Multi-tenant | `databaseMode: 'multi-tenant'` | `'single'` | Per-tenant DataSource connections |
228
232
 
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
- ```
233
+ ---
244
234
 
245
- ### Database Indexes
235
+ ## WebSocket Gateway
246
236
 
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) |
237
+ The gateway runs on the `/notifications` Socket.IO namespace. JWT authentication is required on connection.
254
238
 
255
- ---
239
+ ### Client Connection
256
240
 
257
- ## Interfaces
241
+ **Browser (JavaScript):**
258
242
 
259
- ### INotification
243
+ ```javascript
244
+ import { io } from 'socket.io-client';
260
245
 
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
- ```
246
+ const socket = io('http://localhost:2002/notifications', {
247
+ auth: { token: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...' },
248
+ transports: ['websocket'],
249
+ });
273
250
 
274
- ### INotificationPayload
251
+ socket.on('connect', () => {
252
+ console.log('Connected to notification gateway');
253
+ });
275
254
 
276
- WebSocket event payload:
255
+ socket.on('notification', (data) => {
256
+ console.log('New notification:', data);
257
+ // { id, title, message, type, data, createdAt }
258
+ });
277
259
 
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
- }
260
+ socket.on('disconnect', () => {
261
+ console.log('Disconnected');
262
+ });
287
263
  ```
288
264
 
289
- ---
290
-
291
- ## DTOs
292
-
293
- ### Input DTOs
265
+ **Angular service:**
294
266
 
295
267
  ```typescript
296
- class CreateNotificationDto {
297
- @IsUUID()
298
- userId: string;
299
-
300
- @IsString()
301
- @MaxLength(255)
302
- title: string;
268
+ import { io, Socket } from 'socket.io-client';
303
269
 
304
- @IsOptional()
305
- @IsString()
306
- message?: string;
270
+ @Injectable({ providedIn: 'root' })
271
+ export class NotificationSocketService {
272
+ private socket: Socket;
307
273
 
308
- @IsOptional()
309
- @IsEnum(NotificationType)
310
- type?: NotificationType;
274
+ connect(token: string): void {
275
+ this.socket = io(`${environment.apiUrl}/notifications`, {
276
+ auth: { token: `Bearer ${token}` },
277
+ });
311
278
 
312
- @IsOptional()
313
- @IsObject()
314
- data?: Record<string, unknown>;
279
+ this.socket.on('notification', (data) => {
280
+ this.handleNewNotification(data);
281
+ });
282
+ }
315
283
 
316
- @IsOptional()
317
- @IsUUID()
318
- companyId?: string;
284
+ disconnect(): void {
285
+ this.socket?.disconnect();
286
+ }
319
287
  }
288
+ ```
320
289
 
321
- class UpdateNotificationDto extends PartialType(CreateNotificationDto) {
322
- @IsUUID()
323
- id: string;
324
- }
290
+ ### WebSocket Events
325
291
 
326
- class SendNotificationDto {
327
- @IsUUID()
328
- userId: string;
292
+ **Server → Client (emitted by the gateway):**
329
293
 
330
- @IsString()
331
- @MaxLength(255)
332
- title: string;
294
+ | Event | Payload | Description |
295
+ |-------|---------|-------------|
296
+ | `notification` | `INotificationPayload` | New notification delivered in real-time |
297
+ | `notification:count` | `{ count: number }` | Updated unread count |
333
298
 
334
- @IsOptional()
335
- @IsString()
336
- message?: string;
299
+ **Client → Server (handled by the gateway):**
337
300
 
338
- @IsOptional()
339
- @IsEnum(NotificationType)
340
- type?: NotificationType;
301
+ | Event | Payload | Description |
302
+ |-------|---------|-------------|
303
+ | `join:company` | `{ companyId: string }` | Join a company room for broadcast notifications |
304
+ | `leave:company` | `{ companyId: string }` | Leave a company room |
305
+ | `ping` | — | Keepalive |
341
306
 
342
- @IsOptional()
343
- @IsObject()
344
- data?: Record<string, unknown>;
307
+ ---
345
308
 
346
- @IsOptional()
347
- @IsUUID()
348
- companyId?: string;
349
- }
309
+ ## API Endpoints
350
310
 
351
- class SendBulkNotificationDto {
352
- @IsArray()
353
- @IsUUID('4', { each: true })
354
- userIds: string[];
311
+ All endpoints use **POST** and require JWT authentication.
355
312
 
356
- @IsString()
357
- @MaxLength(255)
358
- title: string;
313
+ ### Notifications — `POST /notification/*`
359
314
 
360
- @IsOptional()
361
- @IsString()
362
- message?: string;
315
+ | Endpoint | Auth | Description |
316
+ |----------|------|-------------|
317
+ | `POST /notification/get-all` | JWT | Get current user's notifications |
318
+ | `POST /notification/get-unread-count` | JWT | Get unread notification count |
319
+ | `POST /notification/mark-read` | JWT | Mark a single notification as read |
320
+ | `POST /notification/mark-all-read` | JWT | Mark all notifications as read |
321
+ | `POST /notification/delete` | JWT | Delete a notification |
322
+ | `POST /notification/get/:id` | JWT | Get a notification by ID |
363
323
 
364
- @IsOptional()
365
- @IsEnum(NotificationType)
366
- type?: NotificationType;
324
+ ### Admin — `POST /notification/admin/*`
367
325
 
368
- @IsOptional()
369
- @IsObject()
370
- data?: Record<string, unknown>;
326
+ | Endpoint | Permission | Description |
327
+ |----------|-----------|-------------|
328
+ | `POST /notification/admin/send` | `notification.create` | Send notification to specific user(s) |
329
+ | `POST /notification/admin/broadcast` | `notification.create` | Broadcast to all users in a company |
330
+ | `POST /notification/admin/get-all` | `notification.read` | List all notifications (all users) |
371
331
 
372
- @IsOptional()
373
- @IsUUID()
374
- companyId?: string;
375
- }
332
+ **Send notification request:**
376
333
 
377
- class MarkReadDto {
378
- @IsUUID()
379
- id: string;
334
+ ```json
335
+ POST /notification/admin/send
336
+ {
337
+ "userId": "uuid",
338
+ "title": "Order Shipped",
339
+ "message": "Your order #12345 has been shipped.",
340
+ "type": "success",
341
+ "data": { "orderId": "12345", "trackingUrl": "https://..." }
380
342
  }
381
343
  ```
382
344
 
383
- ### Output DTOs
345
+ **Broadcast to company:**
384
346
 
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;
347
+ ```json
348
+ POST /notification/admin/broadcast
349
+ {
350
+ "companyId": "uuid",
351
+ "title": "System Maintenance",
352
+ "message": "The system will be down for maintenance at 2:00 AM.",
353
+ "type": "warning"
402
354
  }
403
355
  ```
404
356
 
405
357
  ---
406
358
 
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
- ```
359
+ ## Entities
454
360
 
455
- ### NotificationHelperService
361
+ ### Core Entities
456
362
 
457
- Direct notification sending (bypasses CRUD layer):
363
+ | Entity | Table | Description |
364
+ |--------|-------|-------------|
365
+ | `Notification` | `notification` | User notification with type, title, message, JSON data, isRead, readAt |
458
366
 
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;
367
+ **Indexes:** `(userId, isRead)`, `(userId, createdAt)` for fast unread queries.
479
368
 
480
- // Get total connected users
481
- getOnlineUserCount(): number;
482
- }
483
- ```
369
+ ### Company Feature Entities (`enableCompanyFeature: true`)
484
370
 
485
- ### NotificationDataSourceProvider
371
+ | Entity | Table | Description |
372
+ |--------|-------|-------------|
373
+ | `NotificationWithCompany` | `notification` | Same + `companyId` column |
486
374
 
487
- Dynamic datasource loading per request:
375
+ **Additional index:** `(companyId, userId, isRead)`
488
376
 
489
377
  ```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
378
+ import { NotificationModule } from '@flusys/nestjs-notification';
504
379
 
505
- ```typescript
506
- @WebSocketGateway({
507
- namespace: '/notifications',
508
- cors: { origin: '*', credentials: true },
380
+ TypeOrmModule.forRoot({
381
+ entities: [
382
+ ...NotificationModule.getEntities({ enableCompanyFeature: true }),
383
+ ],
509
384
  })
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
385
  ```
530
386
 
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
387
+ ---
575
388
 
576
- Users automatically join:
577
- - `user:{userId}` - User-specific room
578
- - `company:{companyId}` - Company room (if company feature enabled)
389
+ ## Cross-Module Adapter (NOTIFICATION_ADAPTER)
579
390
 
580
- ---
391
+ The `NOTIFICATION_ADAPTER` token is defined in `@flusys/nestjs-shared` so any module can send notifications without importing `@flusys/nestjs-notification`.
581
392
 
582
- ## Adapter Pattern
393
+ When `NotificationModule` is registered in the app, it provides the implementation. When it is not registered, `@Optional()` prevents injection errors.
583
394
 
584
- ### Interface (from nestjs-shared)
395
+ **INotificationAdapter interface (from `@flusys/nestjs-shared`):**
585
396
 
586
397
  ```typescript
587
398
  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;
399
+ send(data: INotificationSendData): Promise<void>;
400
+ sendBulk(data: INotificationSendData[]): Promise<void>;
401
+ broadcast(companyId: string, data: Omit<INotificationSendData, 'userId'>): Promise<void>;
402
+ isUserOnline(userId: string): boolean;
592
403
  }
593
404
 
594
- interface NotificationSendOptions {
405
+ interface INotificationSendData {
595
406
  userId: string;
596
407
  title: string;
597
- message?: string;
598
- type?: NotificationType;
599
- data?: Record<string, unknown>;
408
+ message: string;
409
+ type?: 'info' | 'success' | 'warning' | 'error';
410
+ data?: Record<string, any>;
600
411
  companyId?: string;
601
412
  }
602
-
603
- export const NOTIFICATION_ADAPTER = 'NOTIFICATION_ADAPTER';
604
413
  ```
605
414
 
606
- ### Usage in Other Modules
415
+ **Usage pattern:**
607
416
 
608
417
  ```typescript
609
- import { NOTIFICATION_ADAPTER, INotificationAdapter } from '@flusys/nestjs-shared';
418
+ import { NOTIFICATION_ADAPTER, INotificationAdapter } from '@flusys/nestjs-shared/interfaces';
610
419
 
611
420
  @Injectable()
612
- export class SomeService {
421
+ export class OrderService {
613
422
  constructor(
614
- @Inject(NOTIFICATION_ADAPTER)
615
- private readonly notificationAdapter: INotificationAdapter,
423
+ @Optional() @Inject(NOTIFICATION_ADAPTER)
424
+ private readonly notifications?: INotificationAdapter,
616
425
  ) {}
617
426
 
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' },
427
+ async shipOrder(orderId: string, userId: string): Promise<void> {
428
+ // ... ship order logic
429
+
430
+ // Send notification if adapter is available
431
+ await this.notifications?.send({
432
+ userId,
433
+ title: 'Order Shipped',
434
+ message: `Your order has been shipped.`,
435
+ type: 'success',
436
+ data: { orderId },
625
437
  });
626
438
  }
627
439
  }
@@ -629,370 +441,130 @@ export class SomeService {
629
441
 
630
442
  ---
631
443
 
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:**
444
+ ## Exported Services
683
445
 
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
- ```
446
+ | Service | Description |
447
+ |---------|-------------|
448
+ | `NotificationService` | Notification CRUD (REQUEST-scoped) |
449
+ | `NotificationHelperService` | Singleton service for programmatic sending |
450
+ | `NotificationGateway` | Socket.IO gateway *(when `enableRealtime: true`)* |
451
+ | `NotificationConfigService` | Runtime config and feature flags |
452
+ | `NotificationDataSourceProvider` | Dynamic DataSource per request |
453
+ | `NOTIFICATION_ADAPTER` | Cross-module adapter implementation |
704
454
 
705
455
  ---
706
456
 
707
- ## Swagger Documentation
457
+ ## Sending Notifications Programmatically
708
458
 
709
- ### Setup Swagger for Notification Module
459
+ Use `NotificationHelperService` (singleton) within the same application:
710
460
 
711
461
  ```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
462
+ import { NotificationHelperService } from '@flusys/nestjs-notification';
724
463
 
725
- ---
464
+ @Injectable()
465
+ export class PaymentService {
466
+ constructor(
467
+ @Inject(NotificationHelperService)
468
+ private readonly notificationHelper: NotificationHelperService,
469
+ ) {}
726
470
 
727
- ## Permissions
471
+ async afterPayment(userId: string, amount: number): Promise<void> {
472
+ await this.notificationHelper.send({
473
+ userId,
474
+ title: 'Payment Received',
475
+ message: `We received your payment of $${amount}.`,
476
+ type: 'success',
477
+ data: { amount },
478
+ });
479
+ }
728
480
 
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;
481
+ async notifyCompany(companyId: string): Promise<void> {
482
+ await this.notificationHelper.broadcast(companyId, {
483
+ title: 'Company Announcement',
484
+ message: 'Check your email for important updates.',
485
+ type: 'info',
486
+ });
487
+ }
488
+ }
736
489
  ```
737
490
 
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
491
  ---
748
492
 
749
- ## Multi-Tenant Support
750
-
751
- ### Company Isolation
493
+ ## Frontend Integration
752
494
 
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`:
495
+ ### Unread Badge Count
761
496
 
762
497
  ```typescript
763
- // In NotificationService
764
- if (this.notificationConfig.isCompanyFeatureEnabled() && user?.companyId) {
765
- query.andWhere('notification.companyId = :companyId', {
766
- companyId: user.companyId,
767
- });
498
+ // Poll for unread count (or update via WebSocket event 'notification:count')
499
+ async getUnreadCount(): Promise<number> {
500
+ const response = await this.http.post('/notification/get-unread-count', {}).toPromise();
501
+ return response.data.count;
768
502
  }
769
503
  ```
770
504
 
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:
505
+ ### Notification List
788
506
 
789
507
  ```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
- });
508
+ async getNotifications(page = 1, pageSize = 20) {
509
+ return this.http.post('/notification/get-all', { page, pageSize }).toPromise();
810
510
  }
811
511
  ```
812
512
 
813
- ### Module Registration Order
814
-
815
- **IMPORTANT:** NotificationModule must be registered BEFORE AuthModule:
513
+ ### Mark as Read
816
514
 
817
515
  ```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()),
516
+ // Single
517
+ await this.http.post('/notification/mark-read', { id: notificationId }).toPromise();
825
518
 
826
- // Other modules...
827
- ],
828
- })
829
- export class AppModule {}
519
+ // All
520
+ await this.http.post('/notification/mark-all-read', {}).toPromise();
830
521
  ```
831
522
 
832
523
  ---
833
524
 
834
- ## Best Practices
525
+ ## Troubleshooting
835
526
 
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
- ```
527
+ **WebSocket connection rejected (401)**
846
528
 
847
- ### 2. Include Meaningful Data
529
+ The JWT token in the `auth.token` field is invalid or expired. Ensure you pass `Bearer <token>` format:
848
530
 
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);
531
+ ```javascript
532
+ auth: { token: `Bearer ${accessToken}` }
861
533
  ```
862
534
 
863
- ### 3. Use Appropriate Notification Types
535
+ ---
864
536
 
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 |
537
+ **Notifications delivered via REST but not real-time**
871
538
 
872
- ### 4. Handle WebSocket Reconnection
539
+ Check that `enableRealtime: true` is set in the config and `@nestjs/platform-socket.io` is installed. Also verify that the client is connecting to the correct namespace (`/notifications`).
873
540
 
874
- Client should handle reconnection:
541
+ ---
875
542
 
876
- ```javascript
877
- socket.on('disconnect', () => {
878
- // Optionally reload unread count on reconnect
879
- });
543
+ **Company broadcast not reaching all users**
880
544
 
881
- socket.on('connect', () => {
882
- // Refresh notifications after reconnect
883
- });
884
- ```
545
+ Users must join the company room via the `join:company` WebSocket event after connecting. If `enableCompanyFeature: false`, company rooms are not created.
885
546
 
886
547
  ---
887
548
 
888
- ## API Reference
889
-
890
- ### Main Exports
549
+ **`NOTIFICATION_ADAPTER` is `undefined`**
891
550
 
892
- ```typescript
893
- // Module
894
- import { NotificationModule } from '@flusys/nestjs-notification';
551
+ `NotificationModule` is not registered. Use `@Optional() @Inject(NOTIFICATION_ADAPTER)` so the injection doesn't throw when the module is absent.
895
552
 
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
553
+ ---
952
554
 
953
- #### NotificationService
555
+ **`No metadata for entity`**
954
556
 
557
+ Register entities in `TypeOrmModule`:
955
558
  ```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
- }
559
+ entities: [...NotificationModule.getEntities({ enableCompanyFeature: true })]
969
560
  ```
970
561
 
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
- ```
562
+ ---
982
563
 
983
- #### NotificationGateway
564
+ ## License
984
565
 
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
- ```
566
+ MIT © FLUSYS
994
567
 
995
568
  ---
996
569
 
997
- **Last Updated:** 2026-02-26
998
- **Version:** 3.0.0
570
+ > Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
@@ -4,12 +4,17 @@ import { CreateNotificationDto, MarkReadDto, NotificationResponseDto, SendBulkNo
4
4
  import { NotificationHelperService } from '../services/notification-helper.service';
5
5
  import { NotificationService } from '../services/notification.service';
6
6
  declare const NotificationController_base: abstract new (service: NotificationService) => {
7
+ readonly enabledEndpoints: import("@flusys/nestjs-shared/classes").ApiEndpoint[] | "all";
7
8
  service: NotificationService;
9
+ isEnabled(endpoint: import("@flusys/nestjs-shared/classes").ApiEndpoint): boolean;
8
10
  insert(addDto: CreateNotificationDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
9
11
  insertMany(addDto: CreateNotificationDto[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<NotificationResponseDto>>;
10
12
  getById(id: string, body: import("@flusys/nestjs-shared/dtos").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
13
+ getByIds(body: import("@flusys/nestjs-shared/dtos").GetByIdsDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared/dtos").ListResponseDto<NotificationResponseDto>>;
11
14
  update(updateDto: UpdateNotificationDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
12
15
  updateMany(updateDtos: UpdateNotificationDto[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<NotificationResponseDto>>;
16
+ bulkUpsert(dtos: (CreateNotificationDto | UpdateNotificationDto)[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<NotificationResponseDto>>;
17
+ getByFilter(filter: Record<string, any>, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
13
18
  getAll(filterAndPaginationDto: import("@flusys/nestjs-shared/dtos").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<import("@flusys/nestjs-shared/dtos").ListResponseDto<NotificationResponseDto>>;
14
19
  delete(deleteDto: import("@flusys/nestjs-shared/dtos").DeleteDto, user: ILoggedUserInfo | null): Promise<MessageResponseDto>;
15
20
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flusys/nestjs-notification",
3
- "version": "4.0.2",
3
+ "version": "4.1.0",
4
4
  "description": "Notification module with real-time Socket.IO support",
5
5
  "main": "cjs/index.js",
6
6
  "module": "fesm/index.js",
@@ -92,8 +92,8 @@
92
92
  "express": "^4.18.0"
93
93
  },
94
94
  "dependencies": {
95
- "@flusys/nestjs-core": "4.0.2",
96
- "@flusys/nestjs-shared": "4.0.2",
95
+ "@flusys/nestjs-core": "4.1.0",
96
+ "@flusys/nestjs-shared": "4.1.0",
97
97
  "jsonwebtoken": "^9.0.0"
98
98
  }
99
99
  }