@flusys/nestjs-notification 5.1.2 → 5.1.3
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/adapters/notification.adapter.d.ts +3 -4
- package/cjs/adapters/notification.adapter.js +2 -19
- package/cjs/modules/notification.module.js +2 -6
- package/cjs/services/notification-config.service.js +0 -3
- package/cjs/services/notification-datasource.provider.js +0 -45
- package/cjs/services/notification-helper.service.js +44 -83
- package/cjs/services/notification.service.js +10 -19
- package/controllers/notification.controller.d.ts +6 -6
- package/fesm/adapters/notification.adapter.js +3 -20
- package/fesm/modules/notification.module.js +2 -6
- package/fesm/services/notification-config.service.js +0 -3
- package/fesm/services/notification-datasource.provider.js +1 -46
- package/fesm/services/notification-helper.service.js +44 -83
- package/fesm/services/notification.service.js +11 -20
- package/interfaces/notification-module.interface.d.ts +0 -1
- package/package.json +15 -15
- package/services/notification-config.service.d.ts +0 -1
- package/services/notification-datasource.provider.d.ts +0 -2
- package/services/notification-helper.service.d.ts +10 -27
- package/services/notification.service.d.ts +4 -6
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { INotificationAdapter,
|
|
1
|
+
import { INotificationAdapter, INotificationBulkSendOptions, INotificationSendOptions } from '@flusys/nestjs-shared';
|
|
2
2
|
import { NotificationHelperService } from '../services/notification-helper.service';
|
|
3
3
|
export declare class NotificationAdapter implements INotificationAdapter {
|
|
4
4
|
private readonly notificationHelper;
|
|
5
5
|
constructor(notificationHelper: NotificationHelperService);
|
|
6
|
-
send(options:
|
|
7
|
-
sendToMany(options:
|
|
8
|
-
broadcastToCompany(companyId: string, title: string, message?: string, type?: NotificationType, data?: Record<string, unknown>): Promise<void>;
|
|
6
|
+
send(options: INotificationSendOptions): Promise<void>;
|
|
7
|
+
sendToMany(options: INotificationBulkSendOptions): Promise<void>;
|
|
9
8
|
isUserOnline(userId: string): boolean;
|
|
10
9
|
}
|
|
@@ -39,27 +39,10 @@ function _ts_param(paramIndex, decorator) {
|
|
|
39
39
|
}
|
|
40
40
|
let NotificationAdapter = class NotificationAdapter {
|
|
41
41
|
async send(options) {
|
|
42
|
-
await this.notificationHelper.send(
|
|
43
|
-
userId: options.userId,
|
|
44
|
-
title: options.title,
|
|
45
|
-
message: options.message,
|
|
46
|
-
type: options.type,
|
|
47
|
-
data: options.data,
|
|
48
|
-
companyId: options.companyId
|
|
49
|
-
});
|
|
42
|
+
await this.notificationHelper.send(options);
|
|
50
43
|
}
|
|
51
44
|
async sendToMany(options) {
|
|
52
|
-
await this.notificationHelper.sendToMany(
|
|
53
|
-
userIds: options.userIds,
|
|
54
|
-
title: options.title,
|
|
55
|
-
message: options.message,
|
|
56
|
-
type: options.type,
|
|
57
|
-
data: options.data,
|
|
58
|
-
companyId: options.companyId
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
async broadcastToCompany(companyId, title, message, type, data) {
|
|
62
|
-
await this.notificationHelper.sendToCompany(companyId, title, message, type, data);
|
|
45
|
+
await this.notificationHelper.sendToMany(options);
|
|
63
46
|
}
|
|
64
47
|
isUserOnline(userId) {
|
|
65
48
|
return this.notificationHelper.isUserOnline(userId);
|
|
@@ -37,10 +37,6 @@ const ADAPTER_PROVIDER = {
|
|
|
37
37
|
};
|
|
38
38
|
let NotificationModule = class NotificationModule {
|
|
39
39
|
static forRoot(options) {
|
|
40
|
-
const enableRealtime = options.config?.enableRealtime !== false;
|
|
41
|
-
const gatewayProviders = enableRealtime ? [
|
|
42
|
-
_gateways.NotificationGateway
|
|
43
|
-
] : [];
|
|
44
40
|
return {
|
|
45
41
|
module: NotificationModule,
|
|
46
42
|
global: options.global ?? false,
|
|
@@ -51,13 +47,13 @@ let NotificationModule = class NotificationModule {
|
|
|
51
47
|
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
52
48
|
providers: [
|
|
53
49
|
...this.buildProviders(options),
|
|
54
|
-
|
|
50
|
+
_gateways.NotificationGateway,
|
|
55
51
|
_adapters.NotificationAdapter,
|
|
56
52
|
ADAPTER_PROVIDER
|
|
57
53
|
],
|
|
58
54
|
exports: [
|
|
59
55
|
...EXPORTED_SERVICES,
|
|
60
|
-
|
|
56
|
+
_gateways.NotificationGateway,
|
|
61
57
|
_interfaces.NOTIFICATION_ADAPTER
|
|
62
58
|
]
|
|
63
59
|
};
|
|
@@ -48,9 +48,6 @@ let NotificationConfigService = class NotificationConfigService {
|
|
|
48
48
|
isMultiTenant() {
|
|
49
49
|
return this.getDatabaseMode() === 'multi-tenant';
|
|
50
50
|
}
|
|
51
|
-
isRealtimeEnabled() {
|
|
52
|
-
return this.options.config?.enableRealtime !== false;
|
|
53
|
-
}
|
|
54
51
|
getJwtSecret() {
|
|
55
52
|
return this.options.config?.jwtSecret;
|
|
56
53
|
}
|
|
@@ -9,7 +9,6 @@ Object.defineProperty(exports, "NotificationDataSourceProvider", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _modules = require("@flusys/nestjs-shared/modules");
|
|
12
|
-
const _constants = require("@flusys/nestjs-shared/constants");
|
|
13
12
|
const _common = require("@nestjs/common");
|
|
14
13
|
const _core = require("@nestjs/core");
|
|
15
14
|
const _express = require("express");
|
|
@@ -109,50 +108,6 @@ let NotificationDataSourceProvider = class NotificationDataSourceProvider extend
|
|
|
109
108
|
const entities = await this.getNotificationEntities(enableCompanyFeature);
|
|
110
109
|
return super.createDataSourceFromConfig(config, entities);
|
|
111
110
|
}
|
|
112
|
-
async getSingleDataSource() {
|
|
113
|
-
if (NotificationDataSourceProvider.singleDataSource?.isInitialized) {
|
|
114
|
-
return NotificationDataSourceProvider.singleDataSource;
|
|
115
|
-
}
|
|
116
|
-
if (NotificationDataSourceProvider.singleConnectionLock) {
|
|
117
|
-
return NotificationDataSourceProvider.singleConnectionLock;
|
|
118
|
-
}
|
|
119
|
-
const config = this.getDefaultDatabaseConfig();
|
|
120
|
-
if (!config) {
|
|
121
|
-
throw new _common.InternalServerErrorException({
|
|
122
|
-
message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
|
|
123
|
-
messageKey: _constants.SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
127
|
-
NotificationDataSourceProvider.singleConnectionLock = connectionPromise;
|
|
128
|
-
try {
|
|
129
|
-
const dataSource = await connectionPromise;
|
|
130
|
-
NotificationDataSourceProvider.singleDataSource = dataSource;
|
|
131
|
-
return dataSource;
|
|
132
|
-
} finally{
|
|
133
|
-
NotificationDataSourceProvider.singleConnectionLock = null;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
async getOrCreateTenantConnection(tenant) {
|
|
137
|
-
const existing = NotificationDataSourceProvider.tenantConnections.get(tenant.id);
|
|
138
|
-
if (existing?.isInitialized) {
|
|
139
|
-
return existing;
|
|
140
|
-
}
|
|
141
|
-
const pendingConnection = NotificationDataSourceProvider.connectionLocks.get(tenant.id);
|
|
142
|
-
if (pendingConnection) {
|
|
143
|
-
return pendingConnection;
|
|
144
|
-
}
|
|
145
|
-
const config = this.buildTenantDatabaseConfig(tenant);
|
|
146
|
-
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
147
|
-
NotificationDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
|
|
148
|
-
try {
|
|
149
|
-
const dataSource = await connectionPromise;
|
|
150
|
-
NotificationDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
|
|
151
|
-
return dataSource;
|
|
152
|
-
} finally{
|
|
153
|
-
NotificationDataSourceProvider.connectionLocks.delete(tenant.id);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
111
|
constructor(configService, request){
|
|
157
112
|
super(NotificationDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), this.configService = configService;
|
|
158
113
|
}
|
|
@@ -8,15 +8,15 @@ Object.defineProperty(exports, "NotificationHelperService", {
|
|
|
8
8
|
return NotificationHelperService;
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
|
-
const _common = require("@nestjs/common");
|
|
12
|
-
const _classes = require("@flusys/nestjs-shared/classes");
|
|
13
11
|
const _nestjsshared = require("@flusys/nestjs-shared");
|
|
12
|
+
const _classes = require("@flusys/nestjs-shared/classes");
|
|
13
|
+
const _enums = require("@flusys/nestjs-shared/enums");
|
|
14
14
|
const _modules = require("@flusys/nestjs-shared/modules");
|
|
15
|
+
const _common = require("@nestjs/common");
|
|
16
|
+
const _entities = require("../entities");
|
|
17
|
+
const _gateways = require("../gateways");
|
|
15
18
|
const _notificationconfigservice = require("./notification-config.service");
|
|
16
19
|
const _notificationdatasourceprovider = require("./notification-datasource.provider");
|
|
17
|
-
const _gateways = require("../gateways");
|
|
18
|
-
const _entities = require("../entities");
|
|
19
|
-
const _enums = require("@flusys/nestjs-shared/enums");
|
|
20
20
|
function _define_property(obj, key, value) {
|
|
21
21
|
if (key in obj) {
|
|
22
22
|
Object.defineProperty(obj, key, {
|
|
@@ -45,90 +45,68 @@ function _ts_param(paramIndex, decorator) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
let NotificationHelperService = class NotificationHelperService {
|
|
48
|
-
async getDataSource() {
|
|
49
|
-
if (!this.dataSource) {
|
|
50
|
-
this.dataSource = await this.dataSourceProvider.getDataSource();
|
|
51
|
-
}
|
|
52
|
-
return this.dataSource;
|
|
53
|
-
}
|
|
54
|
-
async getRepository() {
|
|
55
|
-
const ds = await this.getDataSource();
|
|
56
|
-
const EntityClass = this.configService.isCompanyFeatureEnabled() ? _entities.NotificationWithCompany : _entities.Notification;
|
|
57
|
-
return ds.getRepository(EntityClass);
|
|
58
|
-
}
|
|
59
48
|
async send(options) {
|
|
60
|
-
const { userId, title, message = null, type = _enums.NotificationType.INFO, data = null, companyId } = options;
|
|
61
49
|
const repo = await this.getRepository();
|
|
62
|
-
const entity =
|
|
63
|
-
userId,
|
|
64
|
-
type,
|
|
65
|
-
title,
|
|
66
|
-
message,
|
|
67
|
-
data,
|
|
68
|
-
isRead: false
|
|
69
|
-
};
|
|
70
|
-
if (this.configService.isCompanyFeatureEnabled() && companyId) {
|
|
71
|
-
entity.companyId = companyId;
|
|
72
|
-
}
|
|
50
|
+
const entity = this.buildEntity(options.userId, options);
|
|
73
51
|
const saved = await repo.save(entity);
|
|
74
52
|
await this.clearCache();
|
|
75
|
-
this.
|
|
53
|
+
this.emitToUser(saved, options.realtime !== false);
|
|
76
54
|
return saved;
|
|
77
55
|
}
|
|
78
56
|
async sendToMany(options) {
|
|
79
|
-
const { userIds, title, message = null, type = _enums.NotificationType.INFO, data = null, companyId } = options;
|
|
80
57
|
const repo = await this.getRepository();
|
|
81
|
-
const entities = userIds.map((userId)=>(
|
|
82
|
-
userId,
|
|
83
|
-
type,
|
|
84
|
-
title,
|
|
85
|
-
message,
|
|
86
|
-
data,
|
|
87
|
-
isRead: false,
|
|
88
|
-
...this.configService.isCompanyFeatureEnabled() && companyId ? {
|
|
89
|
-
companyId
|
|
90
|
-
} : {}
|
|
91
|
-
}));
|
|
58
|
+
const entities = options.userIds.map((userId)=>this.buildEntity(userId, options));
|
|
92
59
|
const saved = await repo.save(entities);
|
|
93
60
|
await this.clearCache();
|
|
94
|
-
|
|
95
|
-
|
|
61
|
+
if (options.realtime !== false) {
|
|
62
|
+
setImmediate(()=>{
|
|
63
|
+
for (const notification of saved){
|
|
64
|
+
this.emitToUser(notification, true);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
96
67
|
}
|
|
97
68
|
return saved;
|
|
98
69
|
}
|
|
99
|
-
async sendToCompany(companyId, title, message, type = _enums.NotificationType.INFO, data) {
|
|
100
|
-
if (!this.configService.isCompanyFeatureEnabled()) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (this.configService.isRealtimeEnabled() && this.gateway) {
|
|
104
|
-
const payload = {
|
|
105
|
-
id: `broadcast-${Date.now()}`,
|
|
106
|
-
type,
|
|
107
|
-
title,
|
|
108
|
-
message: message ?? null,
|
|
109
|
-
data: data ?? null,
|
|
110
|
-
createdAt: new Date()
|
|
111
|
-
};
|
|
112
|
-
this.gateway.emitToCompany(companyId, 'notification:new', payload);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
70
|
isUserOnline(userId) {
|
|
116
71
|
return this.gateway?.isUserOnline(userId) ?? false;
|
|
117
72
|
}
|
|
118
73
|
getOnlineUserCount() {
|
|
119
74
|
return this.gateway?.getOnlineUserCount() ?? 0;
|
|
120
75
|
}
|
|
121
|
-
|
|
122
|
-
if (!this.
|
|
76
|
+
async getDataSource() {
|
|
77
|
+
if (!this.dataSource) {
|
|
78
|
+
this.dataSource = await this.dataSourceProvider.getDataSource();
|
|
79
|
+
}
|
|
80
|
+
return this.dataSource;
|
|
81
|
+
}
|
|
82
|
+
async getRepository() {
|
|
83
|
+
const ds = await this.getDataSource();
|
|
84
|
+
const EntityClass = this.configService.isCompanyFeatureEnabled() ? _entities.NotificationWithCompany : _entities.Notification;
|
|
85
|
+
return ds.getRepository(EntityClass);
|
|
86
|
+
}
|
|
87
|
+
buildEntity(userId, content) {
|
|
88
|
+
const entity = {
|
|
89
|
+
userId,
|
|
90
|
+
type: content.type ?? _enums.NotificationType.INFO,
|
|
91
|
+
title: content.title,
|
|
92
|
+
message: content.message ?? null,
|
|
93
|
+
data: content.data ?? null,
|
|
94
|
+
isRead: false
|
|
95
|
+
};
|
|
96
|
+
if (this.configService.isCompanyFeatureEnabled() && content.companyId) {
|
|
97
|
+
entity.companyId = content.companyId;
|
|
98
|
+
}
|
|
99
|
+
return entity;
|
|
100
|
+
}
|
|
101
|
+
emitToUser(notification, realtime) {
|
|
102
|
+
if (!realtime || !this.gateway) {
|
|
123
103
|
return;
|
|
124
104
|
}
|
|
125
105
|
const ext = notification;
|
|
126
106
|
const payload = this.toPayload(notification);
|
|
127
107
|
if (this.configService.isCompanyFeatureEnabled() && ext.companyId) {
|
|
128
|
-
// Company feature enabled - emit only to sockets in matching company context
|
|
129
108
|
this.gateway.emitToUserInCompany(notification.userId, ext.companyId, 'notification:new', payload);
|
|
130
109
|
} else {
|
|
131
|
-
// No company feature or no companyId - emit to all user's sockets
|
|
132
110
|
this.gateway.emitToUser(notification.userId, 'notification:new', payload);
|
|
133
111
|
}
|
|
134
112
|
}
|
|
@@ -143,7 +121,7 @@ let NotificationHelperService = class NotificationHelperService {
|
|
|
143
121
|
};
|
|
144
122
|
}
|
|
145
123
|
async clearCache() {
|
|
146
|
-
await this.utilsService.clearCache(
|
|
124
|
+
await this.utilsService.clearCache('notification', this.cacheManager);
|
|
147
125
|
}
|
|
148
126
|
constructor(cacheManager, utilsService, configService, dataSourceProvider, gateway){
|
|
149
127
|
_define_property(this, "cacheManager", void 0);
|
|
@@ -151,14 +129,12 @@ let NotificationHelperService = class NotificationHelperService {
|
|
|
151
129
|
_define_property(this, "configService", void 0);
|
|
152
130
|
_define_property(this, "dataSourceProvider", void 0);
|
|
153
131
|
_define_property(this, "gateway", void 0);
|
|
154
|
-
_define_property(this, "entityName", void 0);
|
|
155
132
|
_define_property(this, "dataSource", void 0);
|
|
156
133
|
this.cacheManager = cacheManager;
|
|
157
134
|
this.utilsService = utilsService;
|
|
158
135
|
this.configService = configService;
|
|
159
136
|
this.dataSourceProvider = dataSourceProvider;
|
|
160
137
|
this.gateway = gateway;
|
|
161
|
-
this.entityName = 'notification';
|
|
162
138
|
this.dataSource = null;
|
|
163
139
|
}
|
|
164
140
|
};
|
|
@@ -169,7 +145,7 @@ _ts_decorate([
|
|
|
169
145
|
}),
|
|
170
146
|
_ts_metadata("design:type", Function),
|
|
171
147
|
_ts_metadata("design:paramtypes", [
|
|
172
|
-
typeof
|
|
148
|
+
typeof _nestjsshared.INotificationSendOptions === "undefined" ? Object : _nestjsshared.INotificationSendOptions
|
|
173
149
|
]),
|
|
174
150
|
_ts_metadata("design:returntype", Promise)
|
|
175
151
|
], NotificationHelperService.prototype, "send", null);
|
|
@@ -180,25 +156,10 @@ _ts_decorate([
|
|
|
180
156
|
}),
|
|
181
157
|
_ts_metadata("design:type", Function),
|
|
182
158
|
_ts_metadata("design:paramtypes", [
|
|
183
|
-
typeof
|
|
159
|
+
typeof _nestjsshared.INotificationBulkSendOptions === "undefined" ? Object : _nestjsshared.INotificationBulkSendOptions
|
|
184
160
|
]),
|
|
185
161
|
_ts_metadata("design:returntype", Promise)
|
|
186
162
|
], NotificationHelperService.prototype, "sendToMany", null);
|
|
187
|
-
_ts_decorate([
|
|
188
|
-
(0, _nestjsshared.LogAction)({
|
|
189
|
-
action: 'notification.sendToCompany',
|
|
190
|
-
module: 'notification'
|
|
191
|
-
}),
|
|
192
|
-
_ts_metadata("design:type", Function),
|
|
193
|
-
_ts_metadata("design:paramtypes", [
|
|
194
|
-
String,
|
|
195
|
-
String,
|
|
196
|
-
Object,
|
|
197
|
-
Object,
|
|
198
|
-
Object
|
|
199
|
-
]),
|
|
200
|
-
_ts_metadata("design:returntype", Promise)
|
|
201
|
-
], NotificationHelperService.prototype, "sendToCompany", null);
|
|
202
163
|
NotificationHelperService = _ts_decorate([
|
|
203
164
|
(0, _common.Injectable)(),
|
|
204
165
|
_ts_param(0, (0, _common.Inject)('CACHE_INSTANCE')),
|
|
@@ -46,13 +46,10 @@ function _ts_param(paramIndex, decorator) {
|
|
|
46
46
|
decorator(target, key, paramIndex);
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
let NotificationService = class NotificationService extends _classes.
|
|
49
|
+
let NotificationService = class NotificationService extends _classes.ApiService {
|
|
50
50
|
resolveEntity() {
|
|
51
51
|
return this.notificationConfig.isCompanyFeatureEnabled() ? _entities.NotificationWithCompany : _entities.Notification;
|
|
52
52
|
}
|
|
53
|
-
getDataSourceProvider() {
|
|
54
|
-
return this.dataSourceProvider;
|
|
55
|
-
}
|
|
56
53
|
async convertSingleDtoToEntity(dto, user) {
|
|
57
54
|
let entity = {};
|
|
58
55
|
if ('id' in dto && dto.id) {
|
|
@@ -125,11 +122,11 @@ let NotificationService = class NotificationService extends _classes.RequestScop
|
|
|
125
122
|
async afterInsertOperation(entities, _user, _qr) {
|
|
126
123
|
for (const entity of entities){
|
|
127
124
|
const ext = entity;
|
|
128
|
-
this.
|
|
125
|
+
this.emitToUser(entity.userId, ext.companyId, 'notification:new', this.toPayload(entity));
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
async markAsRead(id, user) {
|
|
132
|
-
await this.
|
|
129
|
+
await this.ensureDataSourceRepository();
|
|
133
130
|
const notification = await this.repository.findOne({
|
|
134
131
|
where: {
|
|
135
132
|
id,
|
|
@@ -153,13 +150,13 @@ let NotificationService = class NotificationService extends _classes.RequestScop
|
|
|
153
150
|
]);
|
|
154
151
|
// Emit to user's sockets in the same company context
|
|
155
152
|
const ext = notification;
|
|
156
|
-
this.
|
|
153
|
+
this.emitToUser(user.id, ext.companyId ?? user.companyId, 'notification:read', {
|
|
157
154
|
id
|
|
158
155
|
});
|
|
159
156
|
return notification;
|
|
160
157
|
}
|
|
161
158
|
async markAllAsRead(user) {
|
|
162
|
-
await this.
|
|
159
|
+
await this.ensureDataSourceRepository();
|
|
163
160
|
const where = {
|
|
164
161
|
userId: user.id,
|
|
165
162
|
isRead: false
|
|
@@ -173,11 +170,11 @@ let NotificationService = class NotificationService extends _classes.RequestScop
|
|
|
173
170
|
});
|
|
174
171
|
await this.clearCacheForAll();
|
|
175
172
|
// Emit to user's sockets in the current company context
|
|
176
|
-
this.
|
|
173
|
+
this.emitToUser(user.id, user.companyId, 'notification:read-all', {});
|
|
177
174
|
return result.affected ?? 0;
|
|
178
175
|
}
|
|
179
176
|
async getUnreadCount(user) {
|
|
180
|
-
await this.
|
|
177
|
+
await this.ensureDataSourceRepository();
|
|
181
178
|
const where = {
|
|
182
179
|
userId: user.id,
|
|
183
180
|
isRead: false
|
|
@@ -199,24 +196,18 @@ let NotificationService = class NotificationService extends _classes.RequestScop
|
|
|
199
196
|
createdAt: entity.createdAt
|
|
200
197
|
};
|
|
201
198
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
* If company feature is enabled and companyId is provided, only sockets
|
|
205
|
-
* connected with that company will receive the event.
|
|
206
|
-
*/ emitToUserInCompanyIfRealtime(userId, companyId, event, payload) {
|
|
207
|
-
if (!this.notificationConfig.isRealtimeEnabled() || !this.gateway) {
|
|
199
|
+
emitToUser(userId, companyId, event, payload) {
|
|
200
|
+
if (!this.gateway) {
|
|
208
201
|
return;
|
|
209
202
|
}
|
|
210
203
|
if (this.notificationConfig.isCompanyFeatureEnabled() && companyId) {
|
|
211
|
-
// Company feature enabled - emit only to sockets in matching company context
|
|
212
204
|
this.gateway.emitToUserInCompany(userId, companyId, event, payload);
|
|
213
205
|
} else {
|
|
214
|
-
// No company feature or no companyId - emit to all user's sockets
|
|
215
206
|
this.gateway.emitToUser(userId, event, payload);
|
|
216
207
|
}
|
|
217
208
|
}
|
|
218
209
|
constructor(cacheManager, utilsService, notificationConfig, dataSourceProvider, gateway){
|
|
219
|
-
super('notification',
|
|
210
|
+
super('notification', cacheManager, utilsService, NotificationService.name, false, 'notification', undefined, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "notificationConfig", void 0), _define_property(this, "gateway", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.notificationConfig = notificationConfig, this.gateway = gateway;
|
|
220
211
|
}
|
|
221
212
|
};
|
|
222
213
|
_ts_decorate([
|
|
@@ -4,19 +4,19 @@ 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
|
|
7
|
+
readonly enabledEndpoints: import("@flusys/nestjs-shared").ApiEndpoint[] | "all";
|
|
8
8
|
service: NotificationService;
|
|
9
|
-
isEnabled(endpoint: import("@flusys/nestjs-shared
|
|
9
|
+
isEnabled(endpoint: import("@flusys/nestjs-shared").ApiEndpoint): boolean;
|
|
10
10
|
insert(addDto: CreateNotificationDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
|
|
11
11
|
insertMany(addDto: CreateNotificationDto[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<NotificationResponseDto>>;
|
|
12
|
-
getById(id: string, body: import("@flusys/nestjs-shared
|
|
13
|
-
getByIds(body: import("@flusys/nestjs-shared
|
|
12
|
+
getById(id: string, body: import("@flusys/nestjs-shared").GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
|
|
13
|
+
getByIds(body: import("@flusys/nestjs-shared").GetByIdsDto, user: ILoggedUserInfo | null): Promise<import("@flusys/nestjs-shared").ListResponseDto<NotificationResponseDto>>;
|
|
14
14
|
update(updateDto: UpdateNotificationDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
|
|
15
15
|
updateMany(updateDtos: UpdateNotificationDto[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<NotificationResponseDto>>;
|
|
16
16
|
bulkUpsert(dtos: (CreateNotificationDto | UpdateNotificationDto)[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<NotificationResponseDto>>;
|
|
17
17
|
getByFilter(filter: Record<string, any>, user: ILoggedUserInfo | null): Promise<SingleResponseDto<NotificationResponseDto>>;
|
|
18
|
-
getAll(filterAndPaginationDto: import("@flusys/nestjs-shared
|
|
19
|
-
delete(deleteDto: import("@flusys/nestjs-shared
|
|
18
|
+
getAll(filterAndPaginationDto: import("@flusys/nestjs-shared").FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<import("@flusys/nestjs-shared").ListResponseDto<NotificationResponseDto>>;
|
|
19
|
+
delete(deleteDto: import("@flusys/nestjs-shared").DeleteDto, user: ILoggedUserInfo | null): Promise<MessageResponseDto>;
|
|
20
20
|
};
|
|
21
21
|
export declare class NotificationController extends NotificationController_base {
|
|
22
22
|
service: NotificationService;
|
|
@@ -25,31 +25,14 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
29
29
|
import { NotificationHelperService } from '../services/notification-helper.service';
|
|
30
30
|
export class NotificationAdapter {
|
|
31
31
|
async send(options) {
|
|
32
|
-
await this.notificationHelper.send(
|
|
33
|
-
userId: options.userId,
|
|
34
|
-
title: options.title,
|
|
35
|
-
message: options.message,
|
|
36
|
-
type: options.type,
|
|
37
|
-
data: options.data,
|
|
38
|
-
companyId: options.companyId
|
|
39
|
-
});
|
|
32
|
+
await this.notificationHelper.send(options);
|
|
40
33
|
}
|
|
41
34
|
async sendToMany(options) {
|
|
42
|
-
await this.notificationHelper.sendToMany(
|
|
43
|
-
userIds: options.userIds,
|
|
44
|
-
title: options.title,
|
|
45
|
-
message: options.message,
|
|
46
|
-
type: options.type,
|
|
47
|
-
data: options.data,
|
|
48
|
-
companyId: options.companyId
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
async broadcastToCompany(companyId, title, message, type, data) {
|
|
52
|
-
await this.notificationHelper.sendToCompany(companyId, title, message, type, data);
|
|
35
|
+
await this.notificationHelper.sendToMany(options);
|
|
53
36
|
}
|
|
54
37
|
isUserOnline(userId) {
|
|
55
38
|
return this.notificationHelper.isUserOnline(userId);
|
|
@@ -27,10 +27,6 @@ const ADAPTER_PROVIDER = {
|
|
|
27
27
|
};
|
|
28
28
|
export class NotificationModule {
|
|
29
29
|
static forRoot(options) {
|
|
30
|
-
const enableRealtime = options.config?.enableRealtime !== false;
|
|
31
|
-
const gatewayProviders = enableRealtime ? [
|
|
32
|
-
NotificationGateway
|
|
33
|
-
] : [];
|
|
34
30
|
return {
|
|
35
31
|
module: NotificationModule,
|
|
36
32
|
global: options.global ?? false,
|
|
@@ -41,13 +37,13 @@ export class NotificationModule {
|
|
|
41
37
|
controllers: options.includeController !== false ? CONTROLLERS : [],
|
|
42
38
|
providers: [
|
|
43
39
|
...this.buildProviders(options),
|
|
44
|
-
|
|
40
|
+
NotificationGateway,
|
|
45
41
|
NotificationAdapter,
|
|
46
42
|
ADAPTER_PROVIDER
|
|
47
43
|
],
|
|
48
44
|
exports: [
|
|
49
45
|
...EXPORTED_SERVICES,
|
|
50
|
-
|
|
46
|
+
NotificationGateway,
|
|
51
47
|
NOTIFICATION_ADAPTER
|
|
52
48
|
]
|
|
53
49
|
};
|
|
@@ -38,9 +38,6 @@ export class NotificationConfigService {
|
|
|
38
38
|
isMultiTenant() {
|
|
39
39
|
return this.getDatabaseMode() === 'multi-tenant';
|
|
40
40
|
}
|
|
41
|
-
isRealtimeEnabled() {
|
|
42
|
-
return this.options.config?.enableRealtime !== false;
|
|
43
|
-
}
|
|
44
41
|
getJwtSecret() {
|
|
45
42
|
return this.options.config?.jwtSecret;
|
|
46
43
|
}
|
|
@@ -26,8 +26,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
|
|
29
|
-
import {
|
|
30
|
-
import { Inject, Injectable, InternalServerErrorException, Optional, Scope } from '@nestjs/common';
|
|
29
|
+
import { Inject, Injectable, Optional, Scope } from '@nestjs/common';
|
|
31
30
|
import { REQUEST } from '@nestjs/core';
|
|
32
31
|
import { Request } from 'express';
|
|
33
32
|
import { NotificationConfigService } from './notification-config.service';
|
|
@@ -58,50 +57,6 @@ export class NotificationDataSourceProvider extends MultiTenantDataSourceService
|
|
|
58
57
|
const entities = await this.getNotificationEntities(enableCompanyFeature);
|
|
59
58
|
return super.createDataSourceFromConfig(config, entities);
|
|
60
59
|
}
|
|
61
|
-
async getSingleDataSource() {
|
|
62
|
-
if (NotificationDataSourceProvider.singleDataSource?.isInitialized) {
|
|
63
|
-
return NotificationDataSourceProvider.singleDataSource;
|
|
64
|
-
}
|
|
65
|
-
if (NotificationDataSourceProvider.singleConnectionLock) {
|
|
66
|
-
return NotificationDataSourceProvider.singleConnectionLock;
|
|
67
|
-
}
|
|
68
|
-
const config = this.getDefaultDatabaseConfig();
|
|
69
|
-
if (!config) {
|
|
70
|
-
throw new InternalServerErrorException({
|
|
71
|
-
message: 'No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.',
|
|
72
|
-
messageKey: SYSTEM_MESSAGES.DATABASE_CONFIG_NOT_AVAILABLE
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
76
|
-
NotificationDataSourceProvider.singleConnectionLock = connectionPromise;
|
|
77
|
-
try {
|
|
78
|
-
const dataSource = await connectionPromise;
|
|
79
|
-
NotificationDataSourceProvider.singleDataSource = dataSource;
|
|
80
|
-
return dataSource;
|
|
81
|
-
} finally{
|
|
82
|
-
NotificationDataSourceProvider.singleConnectionLock = null;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
async getOrCreateTenantConnection(tenant) {
|
|
86
|
-
const existing = NotificationDataSourceProvider.tenantConnections.get(tenant.id);
|
|
87
|
-
if (existing?.isInitialized) {
|
|
88
|
-
return existing;
|
|
89
|
-
}
|
|
90
|
-
const pendingConnection = NotificationDataSourceProvider.connectionLocks.get(tenant.id);
|
|
91
|
-
if (pendingConnection) {
|
|
92
|
-
return pendingConnection;
|
|
93
|
-
}
|
|
94
|
-
const config = this.buildTenantDatabaseConfig(tenant);
|
|
95
|
-
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
96
|
-
NotificationDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
|
|
97
|
-
try {
|
|
98
|
-
const dataSource = await connectionPromise;
|
|
99
|
-
NotificationDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
|
|
100
|
-
return dataSource;
|
|
101
|
-
} finally{
|
|
102
|
-
NotificationDataSourceProvider.connectionLocks.delete(tenant.id);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
60
|
constructor(configService, request){
|
|
106
61
|
super(NotificationDataSourceProvider.buildParentOptions(configService.getOptions()), request), _define_property(this, "configService", void 0), this.configService = configService;
|
|
107
62
|
}
|
|
@@ -25,100 +25,78 @@ function _ts_param(paramIndex, decorator) {
|
|
|
25
25
|
decorator(target, key, paramIndex);
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
import {
|
|
28
|
+
import { INotificationBulkSendOptions, INotificationSendOptions, LogAction } from '@flusys/nestjs-shared';
|
|
29
29
|
import { HybridCache } from '@flusys/nestjs-shared/classes';
|
|
30
|
-
import {
|
|
30
|
+
import { NotificationType } from '@flusys/nestjs-shared/enums';
|
|
31
31
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
32
|
+
import { Inject, Injectable, Optional } from '@nestjs/common';
|
|
33
|
+
import { Notification, NotificationWithCompany } from '../entities';
|
|
34
|
+
import { NotificationGateway } from '../gateways';
|
|
32
35
|
import { NotificationConfigService } from './notification-config.service';
|
|
33
36
|
import { NotificationDataSourceProvider } from './notification-datasource.provider';
|
|
34
|
-
import { NotificationGateway } from '../gateways';
|
|
35
|
-
import { Notification, NotificationWithCompany } from '../entities';
|
|
36
|
-
import { NotificationType } from '@flusys/nestjs-shared/enums';
|
|
37
37
|
export class NotificationHelperService {
|
|
38
|
-
async getDataSource() {
|
|
39
|
-
if (!this.dataSource) {
|
|
40
|
-
this.dataSource = await this.dataSourceProvider.getDataSource();
|
|
41
|
-
}
|
|
42
|
-
return this.dataSource;
|
|
43
|
-
}
|
|
44
|
-
async getRepository() {
|
|
45
|
-
const ds = await this.getDataSource();
|
|
46
|
-
const EntityClass = this.configService.isCompanyFeatureEnabled() ? NotificationWithCompany : Notification;
|
|
47
|
-
return ds.getRepository(EntityClass);
|
|
48
|
-
}
|
|
49
38
|
async send(options) {
|
|
50
|
-
const { userId, title, message = null, type = NotificationType.INFO, data = null, companyId } = options;
|
|
51
39
|
const repo = await this.getRepository();
|
|
52
|
-
const entity =
|
|
53
|
-
userId,
|
|
54
|
-
type,
|
|
55
|
-
title,
|
|
56
|
-
message,
|
|
57
|
-
data,
|
|
58
|
-
isRead: false
|
|
59
|
-
};
|
|
60
|
-
if (this.configService.isCompanyFeatureEnabled() && companyId) {
|
|
61
|
-
entity.companyId = companyId;
|
|
62
|
-
}
|
|
40
|
+
const entity = this.buildEntity(options.userId, options);
|
|
63
41
|
const saved = await repo.save(entity);
|
|
64
42
|
await this.clearCache();
|
|
65
|
-
this.
|
|
43
|
+
this.emitToUser(saved, options.realtime !== false);
|
|
66
44
|
return saved;
|
|
67
45
|
}
|
|
68
46
|
async sendToMany(options) {
|
|
69
|
-
const { userIds, title, message = null, type = NotificationType.INFO, data = null, companyId } = options;
|
|
70
47
|
const repo = await this.getRepository();
|
|
71
|
-
const entities = userIds.map((userId)=>(
|
|
72
|
-
userId,
|
|
73
|
-
type,
|
|
74
|
-
title,
|
|
75
|
-
message,
|
|
76
|
-
data,
|
|
77
|
-
isRead: false,
|
|
78
|
-
...this.configService.isCompanyFeatureEnabled() && companyId ? {
|
|
79
|
-
companyId
|
|
80
|
-
} : {}
|
|
81
|
-
}));
|
|
48
|
+
const entities = options.userIds.map((userId)=>this.buildEntity(userId, options));
|
|
82
49
|
const saved = await repo.save(entities);
|
|
83
50
|
await this.clearCache();
|
|
84
|
-
|
|
85
|
-
|
|
51
|
+
if (options.realtime !== false) {
|
|
52
|
+
setImmediate(()=>{
|
|
53
|
+
for (const notification of saved){
|
|
54
|
+
this.emitToUser(notification, true);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
86
57
|
}
|
|
87
58
|
return saved;
|
|
88
59
|
}
|
|
89
|
-
async sendToCompany(companyId, title, message, type = NotificationType.INFO, data) {
|
|
90
|
-
if (!this.configService.isCompanyFeatureEnabled()) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
if (this.configService.isRealtimeEnabled() && this.gateway) {
|
|
94
|
-
const payload = {
|
|
95
|
-
id: `broadcast-${Date.now()}`,
|
|
96
|
-
type,
|
|
97
|
-
title,
|
|
98
|
-
message: message ?? null,
|
|
99
|
-
data: data ?? null,
|
|
100
|
-
createdAt: new Date()
|
|
101
|
-
};
|
|
102
|
-
this.gateway.emitToCompany(companyId, 'notification:new', payload);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
60
|
isUserOnline(userId) {
|
|
106
61
|
return this.gateway?.isUserOnline(userId) ?? false;
|
|
107
62
|
}
|
|
108
63
|
getOnlineUserCount() {
|
|
109
64
|
return this.gateway?.getOnlineUserCount() ?? 0;
|
|
110
65
|
}
|
|
111
|
-
|
|
112
|
-
if (!this.
|
|
66
|
+
async getDataSource() {
|
|
67
|
+
if (!this.dataSource) {
|
|
68
|
+
this.dataSource = await this.dataSourceProvider.getDataSource();
|
|
69
|
+
}
|
|
70
|
+
return this.dataSource;
|
|
71
|
+
}
|
|
72
|
+
async getRepository() {
|
|
73
|
+
const ds = await this.getDataSource();
|
|
74
|
+
const EntityClass = this.configService.isCompanyFeatureEnabled() ? NotificationWithCompany : Notification;
|
|
75
|
+
return ds.getRepository(EntityClass);
|
|
76
|
+
}
|
|
77
|
+
buildEntity(userId, content) {
|
|
78
|
+
const entity = {
|
|
79
|
+
userId,
|
|
80
|
+
type: content.type ?? NotificationType.INFO,
|
|
81
|
+
title: content.title,
|
|
82
|
+
message: content.message ?? null,
|
|
83
|
+
data: content.data ?? null,
|
|
84
|
+
isRead: false
|
|
85
|
+
};
|
|
86
|
+
if (this.configService.isCompanyFeatureEnabled() && content.companyId) {
|
|
87
|
+
entity.companyId = content.companyId;
|
|
88
|
+
}
|
|
89
|
+
return entity;
|
|
90
|
+
}
|
|
91
|
+
emitToUser(notification, realtime) {
|
|
92
|
+
if (!realtime || !this.gateway) {
|
|
113
93
|
return;
|
|
114
94
|
}
|
|
115
95
|
const ext = notification;
|
|
116
96
|
const payload = this.toPayload(notification);
|
|
117
97
|
if (this.configService.isCompanyFeatureEnabled() && ext.companyId) {
|
|
118
|
-
// Company feature enabled - emit only to sockets in matching company context
|
|
119
98
|
this.gateway.emitToUserInCompany(notification.userId, ext.companyId, 'notification:new', payload);
|
|
120
99
|
} else {
|
|
121
|
-
// No company feature or no companyId - emit to all user's sockets
|
|
122
100
|
this.gateway.emitToUser(notification.userId, 'notification:new', payload);
|
|
123
101
|
}
|
|
124
102
|
}
|
|
@@ -133,7 +111,7 @@ export class NotificationHelperService {
|
|
|
133
111
|
};
|
|
134
112
|
}
|
|
135
113
|
async clearCache() {
|
|
136
|
-
await this.utilsService.clearCache(
|
|
114
|
+
await this.utilsService.clearCache('notification', this.cacheManager);
|
|
137
115
|
}
|
|
138
116
|
constructor(cacheManager, utilsService, configService, dataSourceProvider, gateway){
|
|
139
117
|
_define_property(this, "cacheManager", void 0);
|
|
@@ -141,14 +119,12 @@ export class NotificationHelperService {
|
|
|
141
119
|
_define_property(this, "configService", void 0);
|
|
142
120
|
_define_property(this, "dataSourceProvider", void 0);
|
|
143
121
|
_define_property(this, "gateway", void 0);
|
|
144
|
-
_define_property(this, "entityName", void 0);
|
|
145
122
|
_define_property(this, "dataSource", void 0);
|
|
146
123
|
this.cacheManager = cacheManager;
|
|
147
124
|
this.utilsService = utilsService;
|
|
148
125
|
this.configService = configService;
|
|
149
126
|
this.dataSourceProvider = dataSourceProvider;
|
|
150
127
|
this.gateway = gateway;
|
|
151
|
-
this.entityName = 'notification';
|
|
152
128
|
this.dataSource = null;
|
|
153
129
|
}
|
|
154
130
|
}
|
|
@@ -159,7 +135,7 @@ _ts_decorate([
|
|
|
159
135
|
}),
|
|
160
136
|
_ts_metadata("design:type", Function),
|
|
161
137
|
_ts_metadata("design:paramtypes", [
|
|
162
|
-
typeof
|
|
138
|
+
typeof INotificationSendOptions === "undefined" ? Object : INotificationSendOptions
|
|
163
139
|
]),
|
|
164
140
|
_ts_metadata("design:returntype", Promise)
|
|
165
141
|
], NotificationHelperService.prototype, "send", null);
|
|
@@ -170,25 +146,10 @@ _ts_decorate([
|
|
|
170
146
|
}),
|
|
171
147
|
_ts_metadata("design:type", Function),
|
|
172
148
|
_ts_metadata("design:paramtypes", [
|
|
173
|
-
typeof
|
|
149
|
+
typeof INotificationBulkSendOptions === "undefined" ? Object : INotificationBulkSendOptions
|
|
174
150
|
]),
|
|
175
151
|
_ts_metadata("design:returntype", Promise)
|
|
176
152
|
], NotificationHelperService.prototype, "sendToMany", null);
|
|
177
|
-
_ts_decorate([
|
|
178
|
-
LogAction({
|
|
179
|
-
action: 'notification.sendToCompany',
|
|
180
|
-
module: 'notification'
|
|
181
|
-
}),
|
|
182
|
-
_ts_metadata("design:type", Function),
|
|
183
|
-
_ts_metadata("design:paramtypes", [
|
|
184
|
-
String,
|
|
185
|
-
String,
|
|
186
|
-
Object,
|
|
187
|
-
Object,
|
|
188
|
-
Object
|
|
189
|
-
]),
|
|
190
|
-
_ts_metadata("design:returntype", Promise)
|
|
191
|
-
], NotificationHelperService.prototype, "sendToCompany", null);
|
|
192
153
|
NotificationHelperService = _ts_decorate([
|
|
193
154
|
Injectable(),
|
|
194
155
|
_ts_param(0, Inject('CACHE_INSTANCE')),
|
|
@@ -26,7 +26,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
28
|
import { LogAction } from '@flusys/nestjs-shared';
|
|
29
|
-
import { HybridCache,
|
|
29
|
+
import { HybridCache, ApiService } from '@flusys/nestjs-shared/classes';
|
|
30
30
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
31
31
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
32
32
|
import { applyCompanyFilter } from '@flusys/nestjs-shared/utils';
|
|
@@ -36,13 +36,10 @@ import { Notification, NotificationWithCompany } from '../entities';
|
|
|
36
36
|
import { NotificationGateway } from '../gateways';
|
|
37
37
|
import { NotificationConfigService } from './notification-config.service';
|
|
38
38
|
import { NotificationDataSourceProvider } from './notification-datasource.provider';
|
|
39
|
-
export class NotificationService extends
|
|
39
|
+
export class NotificationService extends ApiService {
|
|
40
40
|
resolveEntity() {
|
|
41
41
|
return this.notificationConfig.isCompanyFeatureEnabled() ? NotificationWithCompany : Notification;
|
|
42
42
|
}
|
|
43
|
-
getDataSourceProvider() {
|
|
44
|
-
return this.dataSourceProvider;
|
|
45
|
-
}
|
|
46
43
|
async convertSingleDtoToEntity(dto, user) {
|
|
47
44
|
let entity = {};
|
|
48
45
|
if ('id' in dto && dto.id) {
|
|
@@ -115,11 +112,11 @@ export class NotificationService extends RequestScopedApiService {
|
|
|
115
112
|
async afterInsertOperation(entities, _user, _qr) {
|
|
116
113
|
for (const entity of entities){
|
|
117
114
|
const ext = entity;
|
|
118
|
-
this.
|
|
115
|
+
this.emitToUser(entity.userId, ext.companyId, 'notification:new', this.toPayload(entity));
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
118
|
async markAsRead(id, user) {
|
|
122
|
-
await this.
|
|
119
|
+
await this.ensureDataSourceRepository();
|
|
123
120
|
const notification = await this.repository.findOne({
|
|
124
121
|
where: {
|
|
125
122
|
id,
|
|
@@ -143,13 +140,13 @@ export class NotificationService extends RequestScopedApiService {
|
|
|
143
140
|
]);
|
|
144
141
|
// Emit to user's sockets in the same company context
|
|
145
142
|
const ext = notification;
|
|
146
|
-
this.
|
|
143
|
+
this.emitToUser(user.id, ext.companyId ?? user.companyId, 'notification:read', {
|
|
147
144
|
id
|
|
148
145
|
});
|
|
149
146
|
return notification;
|
|
150
147
|
}
|
|
151
148
|
async markAllAsRead(user) {
|
|
152
|
-
await this.
|
|
149
|
+
await this.ensureDataSourceRepository();
|
|
153
150
|
const where = {
|
|
154
151
|
userId: user.id,
|
|
155
152
|
isRead: false
|
|
@@ -163,11 +160,11 @@ export class NotificationService extends RequestScopedApiService {
|
|
|
163
160
|
});
|
|
164
161
|
await this.clearCacheForAll();
|
|
165
162
|
// Emit to user's sockets in the current company context
|
|
166
|
-
this.
|
|
163
|
+
this.emitToUser(user.id, user.companyId, 'notification:read-all', {});
|
|
167
164
|
return result.affected ?? 0;
|
|
168
165
|
}
|
|
169
166
|
async getUnreadCount(user) {
|
|
170
|
-
await this.
|
|
167
|
+
await this.ensureDataSourceRepository();
|
|
171
168
|
const where = {
|
|
172
169
|
userId: user.id,
|
|
173
170
|
isRead: false
|
|
@@ -189,24 +186,18 @@ export class NotificationService extends RequestScopedApiService {
|
|
|
189
186
|
createdAt: entity.createdAt
|
|
190
187
|
};
|
|
191
188
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
* If company feature is enabled and companyId is provided, only sockets
|
|
195
|
-
* connected with that company will receive the event.
|
|
196
|
-
*/ emitToUserInCompanyIfRealtime(userId, companyId, event, payload) {
|
|
197
|
-
if (!this.notificationConfig.isRealtimeEnabled() || !this.gateway) {
|
|
189
|
+
emitToUser(userId, companyId, event, payload) {
|
|
190
|
+
if (!this.gateway) {
|
|
198
191
|
return;
|
|
199
192
|
}
|
|
200
193
|
if (this.notificationConfig.isCompanyFeatureEnabled() && companyId) {
|
|
201
|
-
// Company feature enabled - emit only to sockets in matching company context
|
|
202
194
|
this.gateway.emitToUserInCompany(userId, companyId, event, payload);
|
|
203
195
|
} else {
|
|
204
|
-
// No company feature or no companyId - emit to all user's sockets
|
|
205
196
|
this.gateway.emitToUser(userId, event, payload);
|
|
206
197
|
}
|
|
207
198
|
}
|
|
208
199
|
constructor(cacheManager, utilsService, notificationConfig, dataSourceProvider, gateway){
|
|
209
|
-
super('notification',
|
|
200
|
+
super('notification', cacheManager, utilsService, NotificationService.name, false, 'notification', undefined, dataSourceProvider), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "notificationConfig", void 0), _define_property(this, "gateway", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.notificationConfig = notificationConfig, this.gateway = gateway;
|
|
210
201
|
}
|
|
211
202
|
}
|
|
212
203
|
_ts_decorate([
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { IBootstrapAppConfig, IDataSourceServiceOptions, IDynamicModuleConfig, IModuleOptionsFactory } from '@flusys/nestjs-core';
|
|
2
2
|
import { ModuleMetadata, Type } from '@nestjs/common';
|
|
3
3
|
export interface INotificationModuleConfig extends IDataSourceServiceOptions {
|
|
4
|
-
enableRealtime?: boolean;
|
|
5
4
|
jwtSecret?: string;
|
|
6
5
|
}
|
|
7
6
|
export interface NotificationModuleOptions extends IDynamicModuleConfig {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flusys/nestjs-notification",
|
|
3
|
-
"version": "5.1.
|
|
3
|
+
"version": "5.1.3",
|
|
4
4
|
"description": "Notification module with real-time Socket.IO support",
|
|
5
5
|
"main": "cjs/index.js",
|
|
6
6
|
"module": "fesm/index.js",
|
|
@@ -77,23 +77,23 @@
|
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
"peerDependencies": {
|
|
80
|
-
"@nestjs/common": "^
|
|
81
|
-
"@nestjs/config": "^
|
|
82
|
-
"@nestjs/core": "^
|
|
83
|
-
"@nestjs/platform-express": "^
|
|
84
|
-
"@nestjs/swagger": "^
|
|
85
|
-
"@nestjs/typeorm": "^
|
|
86
|
-
"@nestjs/websockets": "^
|
|
87
|
-
"@nestjs/platform-socket.io": "^
|
|
88
|
-
"class-transformer": "^0.5.
|
|
89
|
-
"class-validator": "^0.14.0",
|
|
90
|
-
"socket.io": "^4.
|
|
80
|
+
"@nestjs/common": "^11.0.0",
|
|
81
|
+
"@nestjs/config": "^4.0.0",
|
|
82
|
+
"@nestjs/core": "^11.0.0",
|
|
83
|
+
"@nestjs/platform-express": "^11.0.0",
|
|
84
|
+
"@nestjs/swagger": "^11.0.0",
|
|
85
|
+
"@nestjs/typeorm": "^11.0.0",
|
|
86
|
+
"@nestjs/websockets": "^11.0.0",
|
|
87
|
+
"@nestjs/platform-socket.io": "^11.0.0",
|
|
88
|
+
"class-transformer": "^0.5.1",
|
|
89
|
+
"class-validator": "^0.14.0 || ^0.15.0",
|
|
90
|
+
"socket.io": "^4.8.0",
|
|
91
91
|
"typeorm": "^0.3.0",
|
|
92
|
-
"express": "^4.18.0"
|
|
92
|
+
"express": "^4.18.0 || ^5.0.0"
|
|
93
93
|
},
|
|
94
94
|
"dependencies": {
|
|
95
|
-
"@flusys/nestjs-core": "5.1.
|
|
96
|
-
"@flusys/nestjs-shared": "5.1.
|
|
95
|
+
"@flusys/nestjs-core": "5.1.3",
|
|
96
|
+
"@flusys/nestjs-shared": "5.1.3",
|
|
97
97
|
"jsonwebtoken": "^9.0.0"
|
|
98
98
|
}
|
|
99
99
|
}
|
|
@@ -7,7 +7,6 @@ export declare class NotificationConfigService implements IModuleConfigService {
|
|
|
7
7
|
isCompanyFeatureEnabled(): boolean;
|
|
8
8
|
getDatabaseMode(): DatabaseMode;
|
|
9
9
|
isMultiTenant(): boolean;
|
|
10
|
-
isRealtimeEnabled(): boolean;
|
|
11
10
|
getJwtSecret(): string | undefined;
|
|
12
11
|
getOptions(): NotificationModuleOptions;
|
|
13
12
|
}
|
|
@@ -16,6 +16,4 @@ export declare class NotificationDataSourceProvider extends MultiTenantDataSourc
|
|
|
16
16
|
getEnableCompanyFeatureForTenant(tenant?: ITenantDatabaseConfig): boolean;
|
|
17
17
|
getNotificationEntities(enableCompanyFeature?: boolean): Promise<any[]>;
|
|
18
18
|
protected createDataSourceFromConfig(config: IDatabaseConfig): Promise<DataSource>;
|
|
19
|
-
protected getSingleDataSource(): Promise<DataSource>;
|
|
20
|
-
protected getOrCreateTenantConnection(tenant: ITenantDatabaseConfig): Promise<DataSource>;
|
|
21
19
|
}
|
|
@@ -1,43 +1,26 @@
|
|
|
1
|
+
import { INotificationBulkSendOptions, INotificationSendOptions } from '@flusys/nestjs-shared';
|
|
1
2
|
import { HybridCache } from '@flusys/nestjs-shared/classes';
|
|
2
3
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
4
|
+
import { NotificationBase } from '../entities';
|
|
5
|
+
import { NotificationGateway } from '../gateways';
|
|
3
6
|
import { NotificationConfigService } from './notification-config.service';
|
|
4
7
|
import { NotificationDataSourceProvider } from './notification-datasource.provider';
|
|
5
|
-
import { NotificationGateway } from '../gateways';
|
|
6
|
-
import { NotificationBase } from '../entities';
|
|
7
|
-
import { NotificationType } from '@flusys/nestjs-shared/enums';
|
|
8
|
-
export interface SendNotificationOptions {
|
|
9
|
-
userId: string;
|
|
10
|
-
title: string;
|
|
11
|
-
message?: string | null;
|
|
12
|
-
type?: NotificationType | string;
|
|
13
|
-
data?: Record<string, unknown> | null;
|
|
14
|
-
companyId?: string | null;
|
|
15
|
-
}
|
|
16
|
-
export interface SendBulkNotificationOptions {
|
|
17
|
-
userIds: string[];
|
|
18
|
-
title: string;
|
|
19
|
-
message?: string | null;
|
|
20
|
-
type?: NotificationType | string;
|
|
21
|
-
data?: Record<string, unknown> | null;
|
|
22
|
-
companyId?: string | null;
|
|
23
|
-
}
|
|
24
8
|
export declare class NotificationHelperService {
|
|
25
9
|
private readonly cacheManager;
|
|
26
10
|
private readonly utilsService;
|
|
27
11
|
private readonly configService;
|
|
28
12
|
private readonly dataSourceProvider;
|
|
29
13
|
private readonly gateway?;
|
|
30
|
-
private readonly entityName;
|
|
31
14
|
private dataSource;
|
|
32
|
-
constructor(cacheManager: HybridCache, utilsService: UtilsService, configService: NotificationConfigService, dataSourceProvider: NotificationDataSourceProvider, gateway?: NotificationGateway);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
send(options: SendNotificationOptions): Promise<NotificationBase>;
|
|
36
|
-
sendToMany(options: SendBulkNotificationOptions): Promise<NotificationBase[]>;
|
|
37
|
-
sendToCompany(companyId: string, title: string, message?: string | null, type?: NotificationType | string, data?: Record<string, unknown> | null): Promise<void>;
|
|
15
|
+
constructor(cacheManager: HybridCache, utilsService: UtilsService, configService: NotificationConfigService, dataSourceProvider: NotificationDataSourceProvider, gateway?: NotificationGateway | undefined);
|
|
16
|
+
send(options: INotificationSendOptions): Promise<NotificationBase>;
|
|
17
|
+
sendToMany(options: INotificationBulkSendOptions): Promise<NotificationBase[]>;
|
|
38
18
|
isUserOnline(userId: string): boolean;
|
|
39
19
|
getOnlineUserCount(): number;
|
|
40
|
-
private
|
|
20
|
+
private getDataSource;
|
|
21
|
+
private getRepository;
|
|
22
|
+
private buildEntity;
|
|
23
|
+
private emitToUser;
|
|
41
24
|
private toPayload;
|
|
42
25
|
private clearCache;
|
|
43
26
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { HybridCache,
|
|
1
|
+
import { HybridCache, ApiService } from '@flusys/nestjs-shared/classes';
|
|
2
2
|
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
|
|
3
3
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
4
4
|
import { EntityTarget, QueryRunner, SelectQueryBuilder } from 'typeorm';
|
|
@@ -8,15 +8,13 @@ import { NotificationGateway } from '../gateways';
|
|
|
8
8
|
import { INotification } from '../interfaces';
|
|
9
9
|
import { NotificationConfigService } from './notification-config.service';
|
|
10
10
|
import { NotificationDataSourceProvider } from './notification-datasource.provider';
|
|
11
|
-
export declare class NotificationService extends
|
|
11
|
+
export declare class NotificationService extends ApiService<CreateNotificationDto, UpdateNotificationDto, INotification, NotificationBase> {
|
|
12
12
|
protected cacheManager: HybridCache;
|
|
13
13
|
protected utilsService: UtilsService;
|
|
14
14
|
private readonly notificationConfig;
|
|
15
|
-
private readonly dataSourceProvider;
|
|
16
15
|
private readonly gateway?;
|
|
17
|
-
constructor(cacheManager: HybridCache, utilsService: UtilsService, notificationConfig: NotificationConfigService, dataSourceProvider: NotificationDataSourceProvider, gateway?: NotificationGateway);
|
|
16
|
+
constructor(cacheManager: HybridCache, utilsService: UtilsService, notificationConfig: NotificationConfigService, dataSourceProvider: NotificationDataSourceProvider, gateway?: NotificationGateway | undefined);
|
|
18
17
|
protected resolveEntity(): EntityTarget<NotificationBase>;
|
|
19
|
-
protected getDataSourceProvider(): NotificationDataSourceProvider;
|
|
20
18
|
convertSingleDtoToEntity(dto: CreateNotificationDto | UpdateNotificationDto, user: ILoggedUserInfo | null): Promise<NotificationBase>;
|
|
21
19
|
protected getFilterQuery(query: SelectQueryBuilder<NotificationBase>, filter: Record<string, unknown>, _user: ILoggedUserInfo | null): Promise<{
|
|
22
20
|
query: SelectQueryBuilder<NotificationBase>;
|
|
@@ -31,5 +29,5 @@ export declare class NotificationService extends RequestScopedApiService<CreateN
|
|
|
31
29
|
markAllAsRead(user: ILoggedUserInfo): Promise<number>;
|
|
32
30
|
getUnreadCount(user: ILoggedUserInfo): Promise<number>;
|
|
33
31
|
private toPayload;
|
|
34
|
-
private
|
|
32
|
+
private emitToUser;
|
|
35
33
|
}
|