@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
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
import { Column, Entity, Index } from 'typeorm';
|
|
24
|
+
import { Notification } from './notification.entity';
|
|
25
|
+
export class NotificationWithCompany extends Notification {
|
|
26
|
+
constructor(...args){
|
|
27
|
+
super(...args), _define_property(this, "companyId", void 0);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
_ts_decorate([
|
|
31
|
+
Column({
|
|
32
|
+
name: 'company_id',
|
|
33
|
+
type: 'uuid',
|
|
34
|
+
nullable: true
|
|
35
|
+
}),
|
|
36
|
+
_ts_metadata("design:type", Object)
|
|
37
|
+
], NotificationWithCompany.prototype, "companyId", void 0);
|
|
38
|
+
NotificationWithCompany = _ts_decorate([
|
|
39
|
+
Entity({
|
|
40
|
+
name: 'notification'
|
|
41
|
+
}),
|
|
42
|
+
Index([
|
|
43
|
+
'companyId',
|
|
44
|
+
'userId',
|
|
45
|
+
'isRead'
|
|
46
|
+
])
|
|
47
|
+
], NotificationWithCompany);
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
import { Identity } from '@flusys/nestjs-shared/entities';
|
|
24
|
+
import { Column, Entity, Index } from 'typeorm';
|
|
25
|
+
import { NotificationType } from '@flusys/nestjs-shared/enums';
|
|
26
|
+
export class Notification extends Identity {
|
|
27
|
+
constructor(...args){
|
|
28
|
+
super(...args), _define_property(this, "userId", void 0), _define_property(this, "type", void 0), _define_property(this, "title", void 0), _define_property(this, "message", void 0), _define_property(this, "data", void 0), _define_property(this, "isRead", void 0), _define_property(this, "readAt", void 0);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
_ts_decorate([
|
|
32
|
+
Column({
|
|
33
|
+
name: 'user_id',
|
|
34
|
+
type: 'uuid',
|
|
35
|
+
nullable: false
|
|
36
|
+
}),
|
|
37
|
+
Index(),
|
|
38
|
+
_ts_metadata("design:type", String)
|
|
39
|
+
], Notification.prototype, "userId", void 0);
|
|
40
|
+
_ts_decorate([
|
|
41
|
+
Column({
|
|
42
|
+
type: 'varchar',
|
|
43
|
+
length: 50,
|
|
44
|
+
nullable: false,
|
|
45
|
+
default: NotificationType.INFO
|
|
46
|
+
}),
|
|
47
|
+
_ts_metadata("design:type", String)
|
|
48
|
+
], Notification.prototype, "type", void 0);
|
|
49
|
+
_ts_decorate([
|
|
50
|
+
Column({
|
|
51
|
+
type: 'varchar',
|
|
52
|
+
length: 255,
|
|
53
|
+
nullable: false
|
|
54
|
+
}),
|
|
55
|
+
_ts_metadata("design:type", String)
|
|
56
|
+
], Notification.prototype, "title", void 0);
|
|
57
|
+
_ts_decorate([
|
|
58
|
+
Column({
|
|
59
|
+
type: 'text',
|
|
60
|
+
nullable: true
|
|
61
|
+
}),
|
|
62
|
+
_ts_metadata("design:type", Object)
|
|
63
|
+
], Notification.prototype, "message", void 0);
|
|
64
|
+
_ts_decorate([
|
|
65
|
+
Column({
|
|
66
|
+
type: 'simple-json',
|
|
67
|
+
nullable: true
|
|
68
|
+
}),
|
|
69
|
+
_ts_metadata("design:type", Object)
|
|
70
|
+
], Notification.prototype, "data", void 0);
|
|
71
|
+
_ts_decorate([
|
|
72
|
+
Column({
|
|
73
|
+
name: 'is_read',
|
|
74
|
+
type: 'boolean',
|
|
75
|
+
nullable: false,
|
|
76
|
+
default: false
|
|
77
|
+
}),
|
|
78
|
+
Index(),
|
|
79
|
+
_ts_metadata("design:type", Boolean)
|
|
80
|
+
], Notification.prototype, "isRead", void 0);
|
|
81
|
+
_ts_decorate([
|
|
82
|
+
Column({
|
|
83
|
+
name: 'read_at',
|
|
84
|
+
type: 'timestamp',
|
|
85
|
+
nullable: true
|
|
86
|
+
}),
|
|
87
|
+
_ts_metadata("design:type", Object)
|
|
88
|
+
], Notification.prototype, "readAt", void 0);
|
|
89
|
+
Notification = _ts_decorate([
|
|
90
|
+
Entity({
|
|
91
|
+
name: 'notification'
|
|
92
|
+
}),
|
|
93
|
+
Index([
|
|
94
|
+
'userId',
|
|
95
|
+
'isRead'
|
|
96
|
+
]),
|
|
97
|
+
Index([
|
|
98
|
+
'userId',
|
|
99
|
+
'createdAt'
|
|
100
|
+
])
|
|
101
|
+
], Notification);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './notification.gateway';
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { Inject, Logger } from '@nestjs/common';
|
|
29
|
+
import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
|
|
30
|
+
import { Server } from 'socket.io';
|
|
31
|
+
import * as jwt from 'jsonwebtoken';
|
|
32
|
+
import { NotificationConfigService } from '../services/notification-config.service';
|
|
33
|
+
export class NotificationGateway {
|
|
34
|
+
afterInit() {
|
|
35
|
+
this.logger.log('Notification WebSocket Gateway initialized');
|
|
36
|
+
}
|
|
37
|
+
async handleConnection(client) {
|
|
38
|
+
try {
|
|
39
|
+
const token = this.extractToken(client);
|
|
40
|
+
if (!token) {
|
|
41
|
+
this.logger.warn(`Client ${client.id} connection rejected: No token provided`);
|
|
42
|
+
client.disconnect();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const payload = this.verifyToken(token);
|
|
46
|
+
if (!payload) {
|
|
47
|
+
this.logger.warn(`Client ${client.id} connection rejected: Invalid token`);
|
|
48
|
+
client.disconnect();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const userId = payload.id;
|
|
52
|
+
client.data.userId = userId;
|
|
53
|
+
client.data.companyId = payload.companyId;
|
|
54
|
+
if (!this.connectedUsers.has(userId)) {
|
|
55
|
+
this.connectedUsers.set(userId, new Set());
|
|
56
|
+
}
|
|
57
|
+
this.connectedUsers.get(userId).add(client.id);
|
|
58
|
+
client.join(`user:${userId}`);
|
|
59
|
+
if (payload.companyId) {
|
|
60
|
+
client.join(`company:${payload.companyId}`);
|
|
61
|
+
}
|
|
62
|
+
this.logger.log(`Client ${client.id} connected for user ${userId}`);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.logger.error(`Client ${client.id} connection error: ${error}`);
|
|
65
|
+
client.disconnect();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
handleDisconnect(client) {
|
|
69
|
+
const userId = client.data.userId;
|
|
70
|
+
if (userId) {
|
|
71
|
+
const sockets = this.connectedUsers.get(userId);
|
|
72
|
+
sockets?.delete(client.id);
|
|
73
|
+
if (sockets?.size === 0) {
|
|
74
|
+
this.connectedUsers.delete(userId);
|
|
75
|
+
}
|
|
76
|
+
this.logger.log(`Client ${client.id} disconnected for user ${userId}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
emitToUser(userId, event, data) {
|
|
80
|
+
this.server.to(`user:${userId}`).emit(event, data);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Emit to a specific user only on sockets connected with a specific company.
|
|
84
|
+
* This is crucial for multi-company scenarios where a user may be logged in
|
|
85
|
+
* with different companies on different devices.
|
|
86
|
+
*
|
|
87
|
+
* @param userId - The user ID
|
|
88
|
+
* @param companyId - The company ID (if null/undefined, emits to all user's sockets)
|
|
89
|
+
* @param event - The event name
|
|
90
|
+
* @param data - The payload
|
|
91
|
+
*/ emitToUserInCompany(userId, companyId, event, data) {
|
|
92
|
+
if (!companyId) {
|
|
93
|
+
// No company context - emit to all user's sockets
|
|
94
|
+
this.emitToUser(userId, event, data);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Get all sockets in the user's room
|
|
98
|
+
const userRoom = `user:${userId}`;
|
|
99
|
+
const sockets = this.server.sockets.adapter.rooms.get(userRoom);
|
|
100
|
+
if (!sockets) {
|
|
101
|
+
this.logger.debug(`No sockets found for user ${userId}`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Emit only to sockets that match the company context
|
|
105
|
+
for (const socketId of sockets){
|
|
106
|
+
const socket = this.server.sockets.sockets.get(socketId);
|
|
107
|
+
if (socket && socket.data.companyId === companyId) {
|
|
108
|
+
socket.emit(event, data);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
emitToCompany(companyId, event, data) {
|
|
113
|
+
this.server.to(`company:${companyId}`).emit(event, data);
|
|
114
|
+
}
|
|
115
|
+
broadcast(event, data) {
|
|
116
|
+
this.server.emit(event, data);
|
|
117
|
+
}
|
|
118
|
+
isUserOnline(userId) {
|
|
119
|
+
return this.connectedUsers.has(userId);
|
|
120
|
+
}
|
|
121
|
+
getOnlineUserCount() {
|
|
122
|
+
return this.connectedUsers.size;
|
|
123
|
+
}
|
|
124
|
+
extractToken(client) {
|
|
125
|
+
const authToken = client.handshake.auth?.token;
|
|
126
|
+
if (authToken) return authToken;
|
|
127
|
+
const queryToken = client.handshake.query?.token;
|
|
128
|
+
if (typeof queryToken === 'string') return queryToken;
|
|
129
|
+
const authHeader = client.handshake.headers?.authorization;
|
|
130
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
131
|
+
return authHeader.substring(7);
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
verifyToken(token) {
|
|
136
|
+
try {
|
|
137
|
+
const secret = this.configService.getJwtSecret();
|
|
138
|
+
if (!secret) {
|
|
139
|
+
this.logger.error('No JWT secret configured for WebSocket authentication');
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return jwt.verify(token, secret);
|
|
143
|
+
} catch (error) {
|
|
144
|
+
this.logger.warn(`Token verification failed: ${error}`);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
constructor(configService){
|
|
149
|
+
_define_property(this, "configService", void 0);
|
|
150
|
+
_define_property(this, "logger", void 0);
|
|
151
|
+
_define_property(this, "connectedUsers", void 0);
|
|
152
|
+
_define_property(this, "server", void 0);
|
|
153
|
+
this.configService = configService;
|
|
154
|
+
this.logger = new Logger(NotificationGateway.name);
|
|
155
|
+
this.connectedUsers = new Map();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
_ts_decorate([
|
|
159
|
+
WebSocketServer(),
|
|
160
|
+
_ts_metadata("design:type", typeof Server === "undefined" ? Object : Server)
|
|
161
|
+
], NotificationGateway.prototype, "server", void 0);
|
|
162
|
+
NotificationGateway = _ts_decorate([
|
|
163
|
+
WebSocketGateway({
|
|
164
|
+
namespace: '/notifications',
|
|
165
|
+
cors: {
|
|
166
|
+
origin: '*',
|
|
167
|
+
credentials: true
|
|
168
|
+
}
|
|
169
|
+
}),
|
|
170
|
+
_ts_param(0, Inject(NotificationConfigService)),
|
|
171
|
+
_ts_metadata("design:type", Function),
|
|
172
|
+
_ts_metadata("design:paramtypes", [
|
|
173
|
+
typeof NotificationConfigService === "undefined" ? Object : NotificationConfigService
|
|
174
|
+
])
|
|
175
|
+
], NotificationGateway);
|
package/fesm/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Adapters
|
|
2
|
+
export * from './adapters';
|
|
3
|
+
// Config
|
|
4
|
+
export * from './config';
|
|
5
|
+
// Controllers
|
|
6
|
+
export * from './controllers';
|
|
7
|
+
// Docs
|
|
8
|
+
export * from './docs';
|
|
9
|
+
// DTOs
|
|
10
|
+
export * from './dtos';
|
|
11
|
+
// Entities
|
|
12
|
+
export * from './entities';
|
|
13
|
+
// Gateways
|
|
14
|
+
export * from './gateways';
|
|
15
|
+
// Interfaces
|
|
16
|
+
export * from './interfaces';
|
|
17
|
+
// Modules
|
|
18
|
+
export * from './modules';
|
|
19
|
+
// Services
|
|
20
|
+
export * from './services';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './notification.module';
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
}
|
|
7
|
+
import { CacheModule, UtilsModule } from '@flusys/nestjs-shared/modules';
|
|
8
|
+
import { NOTIFICATION_ADAPTER } from '@flusys/nestjs-shared/interfaces';
|
|
9
|
+
import { Module } from '@nestjs/common';
|
|
10
|
+
import { NOTIFICATION_MODULE_OPTIONS } from '../config/notification.constants';
|
|
11
|
+
import { NotificationController } from '../controllers';
|
|
12
|
+
import { NotificationAdapter } from '../adapters';
|
|
13
|
+
import { NotificationConfigService, NotificationDataSourceProvider, NotificationService, NotificationHelperService } from '../services';
|
|
14
|
+
import { NotificationGateway } from '../gateways';
|
|
15
|
+
const CONTROLLERS = [
|
|
16
|
+
NotificationController
|
|
17
|
+
];
|
|
18
|
+
const EXPORTED_SERVICES = [
|
|
19
|
+
NotificationConfigService,
|
|
20
|
+
NotificationDataSourceProvider,
|
|
21
|
+
NotificationService,
|
|
22
|
+
NotificationHelperService
|
|
23
|
+
];
|
|
24
|
+
const ADAPTER_PROVIDER = {
|
|
25
|
+
provide: NOTIFICATION_ADAPTER,
|
|
26
|
+
useClass: NotificationAdapter
|
|
27
|
+
};
|
|
28
|
+
export class NotificationModule {
|
|
29
|
+
static forRoot(options) {
|
|
30
|
+
const enableRealtime = options.config?.enableRealtime !== false;
|
|
31
|
+
const gatewayProviders = enableRealtime ? [
|
|
32
|
+
NotificationGateway
|
|
33
|
+
] : [];
|
|
34
|
+
return {
|
|
35
|
+
module: NotificationModule,
|
|
36
|
+
global: options.global ?? false,
|
|
37
|
+
imports: [
|
|
38
|
+
CacheModule,
|
|
39
|
+
UtilsModule
|
|
40
|
+
],
|
|
41
|
+
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
42
|
+
providers: [
|
|
43
|
+
...this.buildProviders(options),
|
|
44
|
+
...gatewayProviders,
|
|
45
|
+
NotificationAdapter,
|
|
46
|
+
ADAPTER_PROVIDER
|
|
47
|
+
],
|
|
48
|
+
exports: [
|
|
49
|
+
...EXPORTED_SERVICES,
|
|
50
|
+
...gatewayProviders,
|
|
51
|
+
NOTIFICATION_ADAPTER
|
|
52
|
+
]
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
static forRootAsync(options) {
|
|
56
|
+
return {
|
|
57
|
+
module: NotificationModule,
|
|
58
|
+
global: options.global ?? false,
|
|
59
|
+
imports: [
|
|
60
|
+
...options.imports || [],
|
|
61
|
+
CacheModule,
|
|
62
|
+
UtilsModule
|
|
63
|
+
],
|
|
64
|
+
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
65
|
+
providers: [
|
|
66
|
+
...this.createAsyncProviders(options),
|
|
67
|
+
...this.buildProviders(),
|
|
68
|
+
NotificationGateway,
|
|
69
|
+
NotificationAdapter,
|
|
70
|
+
ADAPTER_PROVIDER
|
|
71
|
+
],
|
|
72
|
+
exports: [
|
|
73
|
+
...EXPORTED_SERVICES,
|
|
74
|
+
NotificationGateway,
|
|
75
|
+
NOTIFICATION_ADAPTER
|
|
76
|
+
]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
static buildProviders(options) {
|
|
80
|
+
const providers = [
|
|
81
|
+
...EXPORTED_SERVICES
|
|
82
|
+
];
|
|
83
|
+
if (options) {
|
|
84
|
+
providers.unshift({
|
|
85
|
+
provide: NOTIFICATION_MODULE_OPTIONS,
|
|
86
|
+
useValue: options
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return providers;
|
|
90
|
+
}
|
|
91
|
+
static createAsyncProviders(options) {
|
|
92
|
+
if (options.useFactory) {
|
|
93
|
+
return [
|
|
94
|
+
{
|
|
95
|
+
provide: NOTIFICATION_MODULE_OPTIONS,
|
|
96
|
+
useFactory: async (...args)=>({
|
|
97
|
+
...options,
|
|
98
|
+
config: await options.useFactory(...args)
|
|
99
|
+
}),
|
|
100
|
+
inject: options.inject || []
|
|
101
|
+
}
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
const factoryClass = options.useClass || options.useExisting;
|
|
105
|
+
if (!factoryClass) return [];
|
|
106
|
+
const providers = [
|
|
107
|
+
{
|
|
108
|
+
provide: NOTIFICATION_MODULE_OPTIONS,
|
|
109
|
+
useFactory: async (factory)=>({
|
|
110
|
+
...options,
|
|
111
|
+
config: await factory.createNotificationOptions()
|
|
112
|
+
}),
|
|
113
|
+
inject: [
|
|
114
|
+
factoryClass
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
];
|
|
118
|
+
if (options.useClass) {
|
|
119
|
+
providers.push({
|
|
120
|
+
provide: options.useClass,
|
|
121
|
+
useClass: options.useClass
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return providers;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
NotificationModule = _ts_decorate([
|
|
128
|
+
Module({})
|
|
129
|
+
], NotificationModule);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
29
|
+
import { NotificationModuleOptions } from '../interfaces';
|
|
30
|
+
import { NOTIFICATION_MODULE_OPTIONS } from '../config/notification.constants';
|
|
31
|
+
export class NotificationConfigService {
|
|
32
|
+
isCompanyFeatureEnabled() {
|
|
33
|
+
return this.options.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
34
|
+
}
|
|
35
|
+
getDatabaseMode() {
|
|
36
|
+
return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
37
|
+
}
|
|
38
|
+
isMultiTenant() {
|
|
39
|
+
return this.getDatabaseMode() === 'multi-tenant';
|
|
40
|
+
}
|
|
41
|
+
isRealtimeEnabled() {
|
|
42
|
+
return this.options.config?.enableRealtime !== false;
|
|
43
|
+
}
|
|
44
|
+
getJwtSecret() {
|
|
45
|
+
return this.options.config?.jwtSecret;
|
|
46
|
+
}
|
|
47
|
+
getOptions() {
|
|
48
|
+
return this.options;
|
|
49
|
+
}
|
|
50
|
+
constructor(options){
|
|
51
|
+
_define_property(this, "options", void 0);
|
|
52
|
+
this.options = options;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
NotificationConfigService = _ts_decorate([
|
|
56
|
+
Injectable(),
|
|
57
|
+
_ts_param(0, Inject(NOTIFICATION_MODULE_OPTIONS)),
|
|
58
|
+
_ts_metadata("design:type", Function),
|
|
59
|
+
_ts_metadata("design:paramtypes", [
|
|
60
|
+
typeof NotificationModuleOptions === "undefined" ? Object : NotificationModuleOptions
|
|
61
|
+
])
|
|
62
|
+
], NotificationConfigService);
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
function _define_property(obj, key, value) {
|
|
2
|
+
if (key in obj) {
|
|
3
|
+
Object.defineProperty(obj, key, {
|
|
4
|
+
value: value,
|
|
5
|
+
enumerable: true,
|
|
6
|
+
configurable: true,
|
|
7
|
+
writable: true
|
|
8
|
+
});
|
|
9
|
+
} else {
|
|
10
|
+
obj[key] = value;
|
|
11
|
+
}
|
|
12
|
+
return obj;
|
|
13
|
+
}
|
|
14
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
+
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
+
}
|
|
20
|
+
function _ts_metadata(k, v) {
|
|
21
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
+
}
|
|
23
|
+
function _ts_param(paramIndex, decorator) {
|
|
24
|
+
return function(target, key) {
|
|
25
|
+
decorator(target, key, paramIndex);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
|
|
29
|
+
import { Inject, Injectable, InternalServerErrorException, Logger, Optional, Scope } from '@nestjs/common';
|
|
30
|
+
import { REQUEST } from '@nestjs/core';
|
|
31
|
+
import { Request } from 'express';
|
|
32
|
+
import { NotificationConfigService } from './notification-config.service';
|
|
33
|
+
export class NotificationDataSourceProvider extends MultiTenantDataSourceService {
|
|
34
|
+
static buildParentOptions(options) {
|
|
35
|
+
return {
|
|
36
|
+
bootstrapAppConfig: options.bootstrapAppConfig,
|
|
37
|
+
defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
|
|
38
|
+
tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
|
|
39
|
+
tenants: options.config?.tenants
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
getEnableCompanyFeatureForTenant(tenant) {
|
|
43
|
+
return tenant?.enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
|
|
44
|
+
}
|
|
45
|
+
async getNotificationEntities(enableCompanyFeature) {
|
|
46
|
+
const enable = enableCompanyFeature ?? this.configService.isCompanyFeatureEnabled();
|
|
47
|
+
const entities = await import('../entities');
|
|
48
|
+
return enable ? [
|
|
49
|
+
entities.NotificationWithCompany
|
|
50
|
+
] : [
|
|
51
|
+
entities.Notification
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
async createDataSourceFromConfig(config) {
|
|
55
|
+
const currentTenant = this.getCurrentTenant();
|
|
56
|
+
const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
|
|
57
|
+
const entities = await this.getNotificationEntities(enableCompanyFeature);
|
|
58
|
+
return super.createDataSourceFromConfig(config, entities);
|
|
59
|
+
}
|
|
60
|
+
async getSingleDataSource() {
|
|
61
|
+
if (NotificationDataSourceProvider.singleDataSource?.isInitialized) {
|
|
62
|
+
return NotificationDataSourceProvider.singleDataSource;
|
|
63
|
+
}
|
|
64
|
+
if (NotificationDataSourceProvider.singleConnectionLock) {
|
|
65
|
+
return NotificationDataSourceProvider.singleConnectionLock;
|
|
66
|
+
}
|
|
67
|
+
const config = this.getDefaultDatabaseConfig();
|
|
68
|
+
if (!config) {
|
|
69
|
+
throw new InternalServerErrorException('No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.');
|
|
70
|
+
}
|
|
71
|
+
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
72
|
+
NotificationDataSourceProvider.singleConnectionLock = connectionPromise;
|
|
73
|
+
try {
|
|
74
|
+
const dataSource = await connectionPromise;
|
|
75
|
+
NotificationDataSourceProvider.singleDataSource = dataSource;
|
|
76
|
+
return dataSource;
|
|
77
|
+
} finally{
|
|
78
|
+
NotificationDataSourceProvider.singleConnectionLock = null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async getOrCreateTenantConnection(tenant) {
|
|
82
|
+
const existing = NotificationDataSourceProvider.tenantConnections.get(tenant.id);
|
|
83
|
+
if (existing?.isInitialized) {
|
|
84
|
+
return existing;
|
|
85
|
+
}
|
|
86
|
+
const pendingConnection = NotificationDataSourceProvider.connectionLocks.get(tenant.id);
|
|
87
|
+
if (pendingConnection) {
|
|
88
|
+
return pendingConnection;
|
|
89
|
+
}
|
|
90
|
+
const config = this.buildTenantDatabaseConfig(tenant);
|
|
91
|
+
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
92
|
+
NotificationDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
|
|
93
|
+
try {
|
|
94
|
+
const dataSource = await connectionPromise;
|
|
95
|
+
NotificationDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
|
|
96
|
+
return dataSource;
|
|
97
|
+
} finally{
|
|
98
|
+
NotificationDataSourceProvider.connectionLocks.delete(tenant.id);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
constructor(configService, request){
|
|
102
|
+
super(NotificationDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), _define_property(this, "logger", void 0), this.configService = configService, this.logger = new Logger(NotificationDataSourceProvider.name);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
_define_property(NotificationDataSourceProvider, "tenantConnections", new Map());
|
|
106
|
+
_define_property(NotificationDataSourceProvider, "singleDataSource", null);
|
|
107
|
+
_define_property(NotificationDataSourceProvider, "tenantsRegistry", new Map());
|
|
108
|
+
_define_property(NotificationDataSourceProvider, "initialized", false);
|
|
109
|
+
_define_property(NotificationDataSourceProvider, "connectionLocks", new Map());
|
|
110
|
+
_define_property(NotificationDataSourceProvider, "singleConnectionLock", null);
|
|
111
|
+
NotificationDataSourceProvider = _ts_decorate([
|
|
112
|
+
Injectable({
|
|
113
|
+
scope: Scope.REQUEST
|
|
114
|
+
}),
|
|
115
|
+
_ts_param(0, Inject(NotificationConfigService)),
|
|
116
|
+
_ts_param(1, Optional()),
|
|
117
|
+
_ts_param(1, Inject(REQUEST)),
|
|
118
|
+
_ts_metadata("design:type", Function),
|
|
119
|
+
_ts_metadata("design:paramtypes", [
|
|
120
|
+
typeof NotificationConfigService === "undefined" ? Object : NotificationConfigService,
|
|
121
|
+
typeof Request === "undefined" ? Object : Request
|
|
122
|
+
])
|
|
123
|
+
], NotificationDataSourceProvider);
|