rez_core 2.2.166 → 2.2.169
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/dist/app.module.js +2 -0
- package/dist/app.module.js.map +1 -1
- package/dist/module/communication/controller/communication.controller.d.ts +4 -1
- package/dist/module/communication/controller/communication.controller.js +23 -3
- package/dist/module/communication/controller/communication.controller.js.map +1 -1
- package/dist/module/communication/entity/communication-config.entity.js +1 -1
- package/dist/module/communication/entity/communication-config.entity.js.map +1 -1
- package/dist/module/communication/entity/communication-hub.entity.js +1 -1
- package/dist/module/communication/entity/communication-hub.entity.js.map +1 -1
- package/dist/module/communication/service/communication.service.d.ts +5 -1
- package/dist/module/communication/service/communication.service.js +13 -3
- package/dist/module/communication/service/communication.service.js.map +1 -1
- package/dist/module/communication/strategies/email/gmail-api.strategy.d.ts +3 -0
- package/dist/module/communication/strategies/email/gmail-api.strategy.js +33 -12
- package/dist/module/communication/strategies/email/gmail-api.strategy.js.map +1 -1
- package/dist/module/communication/strategies/gmail.strategy.d.ts +3 -0
- package/dist/module/communication/strategies/gmail.strategy.js +17 -7
- package/dist/module/communication/strategies/gmail.strategy.js.map +1 -1
- package/dist/module/listmaster/service/list-master.service.js +0 -7
- package/dist/module/listmaster/service/list-master.service.js.map +1 -1
- package/dist/module/meta/service/entity-service-impl.service.js +0 -1
- package/dist/module/meta/service/entity-service-impl.service.js.map +1 -1
- package/dist/module/notification/controller/notification.controller.d.ts +17 -0
- package/dist/module/notification/controller/notification.controller.js +48 -0
- package/dist/module/notification/controller/notification.controller.js.map +1 -0
- package/dist/module/notification/controller/otp.controller.d.ts +2 -1
- package/dist/module/notification/controller/otp.controller.js +2 -2
- package/dist/module/notification/controller/otp.controller.js.map +1 -1
- package/dist/module/notification/entity/notification.entity.d.ts +8 -0
- package/dist/module/notification/entity/notification.entity.js +41 -0
- package/dist/module/notification/entity/notification.entity.js.map +1 -0
- package/dist/module/notification/firebase-admin.config.d.ts +2 -0
- package/dist/module/notification/firebase-admin.config.js +10 -0
- package/dist/module/notification/firebase-admin.config.js.map +1 -0
- package/dist/module/notification/firebase-admin.json +13 -0
- package/dist/module/notification/notification.module.js +7 -4
- package/dist/module/notification/notification.module.js.map +1 -1
- package/dist/module/notification/service/firebase.service.d.ts +11 -0
- package/dist/module/notification/service/firebase.service.js +40 -0
- package/dist/module/notification/service/firebase.service.js.map +1 -0
- package/dist/module/notification/service/otp.service.d.ts +2 -1
- package/dist/module/notification/service/otp.service.js +8 -3
- package/dist/module/notification/service/otp.service.js.map +1 -1
- package/dist/module/user/controller/login.controller.js +16 -4
- package/dist/module/user/controller/login.controller.js.map +1 -1
- package/dist/module/user/controller/user.controller.d.ts +1 -9
- package/dist/module/user/controller/user.controller.js +10 -4
- package/dist/module/user/controller/user.controller.js.map +1 -1
- package/dist/module/user/entity/user.entity.d.ts +1 -0
- package/dist/module/user/entity/user.entity.js +4 -0
- package/dist/module/user/entity/user.entity.js.map +1 -1
- package/dist/module/user/service/login.service.d.ts +17 -3
- package/dist/module/user/service/login.service.js +14 -5
- package/dist/module/user/service/login.service.js.map +1 -1
- package/dist/module/user/service/user.service.d.ts +7 -10
- package/dist/module/user/service/user.service.js +23 -8
- package/dist/module/user/service/user.service.js.map +1 -1
- package/dist/module/workflow/entity/task-data.entity.d.ts +2 -0
- package/dist/module/workflow/entity/task-data.entity.js +9 -1
- package/dist/module/workflow/entity/task-data.entity.js.map +1 -1
- package/dist/module/workflow/repository/task.repository.js +6 -1
- package/dist/module/workflow/repository/task.repository.js.map +1 -1
- package/dist/module/workflow/service/task.service.d.ts +4 -1
- package/dist/module/workflow/service/task.service.js +46 -2
- package/dist/module/workflow/service/task.service.js.map +1 -1
- package/dist/module/workflow/workflow.module.js +2 -0
- package/dist/module/workflow/workflow.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +4 -1
- package/src/app.module.ts +2 -0
- package/src/module/communication/controller/communication.controller.ts +19 -1
- package/src/module/communication/entity/communication-config.entity.ts +1 -1
- package/src/module/communication/entity/communication-hub.entity.ts +1 -1
- package/src/module/communication/service/communication.service.ts +21 -2
- package/src/module/communication/strategies/email/gmail-api.strategy.ts +37 -13
- package/src/module/communication/strategies/gmail.strategy.ts +15 -9
- package/src/module/listmaster/service/list-master.service.ts +0 -12
- package/src/module/meta/service/entity-service-impl.service.ts +0 -1
- package/src/module/notification/controller/notification.controller.ts +25 -0
- package/src/module/notification/controller/otp.controller.ts +3 -2
- package/src/module/notification/entity/notification.entity.ts +20 -0
- package/src/module/notification/firebase-admin.config.ts +8 -0
- package/src/module/notification/firebase-admin.json +13 -0
- package/src/module/notification/notification.module.ts +7 -4
- package/src/module/notification/service/firebase.service.ts +31 -0
- package/src/module/notification/service/otp.service.ts +9 -3
- package/src/module/user/controller/login.controller.ts +26 -11
- package/src/module/user/controller/user.controller.ts +7 -2
- package/src/module/user/entity/user.entity.ts +3 -0
- package/src/module/user/service/login.service.ts +24 -8
- package/src/module/user/service/user.service.ts +32 -6
- package/src/module/workflow/entity/task-data.entity.ts +7 -1
- package/src/module/workflow/repository/task.repository.ts +10 -2
- package/src/module/workflow/service/task.service.ts +56 -0
- package/src/module/workflow/workflow.module.ts +2 -0
- package/tsconfig.json +5 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rez_core",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.169",
|
|
4
4
|
"description": "",
|
|
5
5
|
"author": "",
|
|
6
6
|
"private": false,
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"@nestjs/jwt": "^11.0.0",
|
|
32
32
|
"@nestjs/passport": "^11.0.5",
|
|
33
33
|
"@nestjs/platform-express": "^11.0.20",
|
|
34
|
+
"@nestjs/schedule": "^6.0.0",
|
|
34
35
|
"@nestjs/swagger": "^11.0.3",
|
|
35
36
|
"@nestjs/typeorm": "^11.0.0",
|
|
36
37
|
"250218_nodejs_core": "file:",
|
|
@@ -43,6 +44,7 @@
|
|
|
43
44
|
"crypto-js": "^4.2.0",
|
|
44
45
|
"dotenv": "^16.4.7",
|
|
45
46
|
"exceljs": "^4.4.0",
|
|
47
|
+
"firebase-admin": "^13.5.0",
|
|
46
48
|
"googleapis": "^159.0.0",
|
|
47
49
|
"handlebars": "^4.7.8",
|
|
48
50
|
"ics": "^3.8.1",
|
|
@@ -71,6 +73,7 @@
|
|
|
71
73
|
"@nestjs/testing": "^11.0.1",
|
|
72
74
|
"@swc/cli": "^0.6.0",
|
|
73
75
|
"@swc/core": "^1.10.7",
|
|
76
|
+
"@types/cron": "^2.0.1",
|
|
74
77
|
"@types/express": "^5.0.0",
|
|
75
78
|
"@types/jest": "^29.5.14",
|
|
76
79
|
"@types/multer": "^1.4.12",
|
package/src/app.module.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { WorkflowModule } from './module/workflow/workflow.module';
|
|
|
17
17
|
import { IcsMeetingModule } from './module/ics/ics.module';
|
|
18
18
|
import { DashboardModule } from './module/dashboard/dashboard.module';
|
|
19
19
|
import { CommunicationModule } from './module/communication/communication.module';
|
|
20
|
+
import { ScheduleModule } from '@nestjs/schedule';
|
|
20
21
|
|
|
21
22
|
@Module({
|
|
22
23
|
imports: [
|
|
@@ -38,6 +39,7 @@ import { CommunicationModule } from './module/communication/communication.module
|
|
|
38
39
|
IcsMeetingModule,
|
|
39
40
|
DashboardModule,
|
|
40
41
|
CommunicationModule,
|
|
42
|
+
ScheduleModule.forRoot(),
|
|
41
43
|
],
|
|
42
44
|
})
|
|
43
45
|
export class AppModule {}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
ParseIntPipe,
|
|
9
9
|
HttpStatus,
|
|
10
10
|
HttpCode,
|
|
11
|
+
Query,
|
|
11
12
|
} from '@nestjs/common';
|
|
12
13
|
import { CommunicationService } from '../service/communication.service';
|
|
13
14
|
import {
|
|
@@ -84,8 +85,15 @@ export class CommunicationController {
|
|
|
84
85
|
async getLevelConfigs(
|
|
85
86
|
@Param('id', ParseIntPipe) levelId: number,
|
|
86
87
|
@Param('type') levelType: string,
|
|
88
|
+
@Query('communication_config_type') configType?: 'WA' | 'SMS' | 'EMAIL' | 'TELEPHONE',
|
|
89
|
+
@Query('service') service?: 'API' | 'THIRD_PARTY' | 'SMTP',
|
|
90
|
+
@Query('provider') provider?: string,
|
|
87
91
|
) {
|
|
88
|
-
return this.communicationService.getLevelConfigs(levelId, levelType
|
|
92
|
+
return this.communicationService.getLevelConfigs(levelId, levelType, {
|
|
93
|
+
communication_config_type: configType,
|
|
94
|
+
service,
|
|
95
|
+
provider,
|
|
96
|
+
});
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
@Put('config/:id/status')
|
|
@@ -100,6 +108,16 @@ export class CommunicationController {
|
|
|
100
108
|
return { message: 'Status updated successfully' };
|
|
101
109
|
}
|
|
102
110
|
|
|
111
|
+
@Post('config/:id/update')
|
|
112
|
+
@HttpCode(HttpStatus.OK)
|
|
113
|
+
async activateConfig(
|
|
114
|
+
@Param('id', ParseIntPipe) hubId: number,
|
|
115
|
+
@Param('status', ParseIntPipe) status: number,
|
|
116
|
+
) {
|
|
117
|
+
await this.communicationService.updateConfigStatus(hubId, status);
|
|
118
|
+
return { message: 'Configuration activated successfully' };
|
|
119
|
+
}
|
|
120
|
+
|
|
103
121
|
@Post('gmail/oauth/init')
|
|
104
122
|
async initGmailOAuth(@Body() initDto: GmailOAuthInitDto) {
|
|
105
123
|
return this.communicationService.initGmailOAuth(
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from 'typeorm';
|
|
8
8
|
import { IsNotEmpty, IsObject } from 'class-validator';
|
|
9
9
|
|
|
10
|
-
@Entity({ name: '
|
|
10
|
+
@Entity({ name: 'cr_communication_config' })
|
|
11
11
|
export class CommunicationConfig {
|
|
12
12
|
@PrimaryGeneratedColumn({ name: 'id', type: 'int' })
|
|
13
13
|
id: number;
|
|
@@ -15,7 +15,7 @@ export enum CommunicationConfigType {
|
|
|
15
15
|
TELEPHONE = 'TELEPHONE',
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
@Entity({ name: '
|
|
18
|
+
@Entity({ name: 'cr_communication_hub' })
|
|
19
19
|
@Index('idx_level_status', ['level_id', 'level_type', 'status'])
|
|
20
20
|
@Index('idx_config_type', ['communication_config_type'])
|
|
21
21
|
@Index('idx_service_provider', ['service', 'provider'])
|
|
@@ -146,7 +146,7 @@ export class CommunicationService {
|
|
|
146
146
|
): Promise<CommunicationHubWithConfig[]> {
|
|
147
147
|
let query = this.hubRepository
|
|
148
148
|
.createQueryBuilder('hub')
|
|
149
|
-
.leftJoin('
|
|
149
|
+
.leftJoin('cr_communication_config', 'config', 'config.id = hub.config_id')
|
|
150
150
|
.select([
|
|
151
151
|
'hub.id as hub_id',
|
|
152
152
|
'hub.level_id as hub_level_id',
|
|
@@ -322,9 +322,28 @@ export class CommunicationService {
|
|
|
322
322
|
async getLevelConfigs(
|
|
323
323
|
levelId: number,
|
|
324
324
|
levelType: string,
|
|
325
|
+
filters?: {
|
|
326
|
+
communication_config_type?: 'WA' | 'SMS' | 'EMAIL' | 'TELEPHONE';
|
|
327
|
+
service?: 'API' | 'THIRD_PARTY' | 'SMTP';
|
|
328
|
+
provider?: string;
|
|
329
|
+
},
|
|
325
330
|
): Promise<CommunicationHub[]> {
|
|
331
|
+
const where: any = { level_id: levelId, level_type: levelType };
|
|
332
|
+
|
|
333
|
+
if (filters?.communication_config_type) {
|
|
334
|
+
where.communication_config_type = filters.communication_config_type;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (filters?.service) {
|
|
338
|
+
where.service = filters.service;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (filters?.provider) {
|
|
342
|
+
where.provider = filters.provider;
|
|
343
|
+
}
|
|
344
|
+
|
|
326
345
|
return this.hubRepository.find({
|
|
327
|
-
where
|
|
346
|
+
where,
|
|
328
347
|
order: { created_at: 'DESC' },
|
|
329
348
|
});
|
|
330
349
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
2
3
|
import axios from 'axios';
|
|
3
4
|
import {
|
|
4
5
|
CommunicationStrategy,
|
|
@@ -7,20 +8,26 @@ import {
|
|
|
7
8
|
|
|
8
9
|
@Injectable()
|
|
9
10
|
export class GmailApiStrategy implements CommunicationStrategy {
|
|
11
|
+
constructor(private readonly configService: ConfigService) {}
|
|
10
12
|
async sendMessage(
|
|
11
13
|
to: string | string[],
|
|
12
14
|
message: string,
|
|
13
15
|
config: any,
|
|
14
16
|
): Promise<CommunicationResult> {
|
|
15
17
|
try {
|
|
16
|
-
if (!
|
|
17
|
-
throw new Error('Invalid Gmail API configuration');
|
|
18
|
+
if (!config || !config.refreshToken) {
|
|
19
|
+
throw new Error('Invalid Gmail API configuration - missing refresh token');
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
const {
|
|
22
|
+
const { subject, html, cc, bcc } = config;
|
|
21
23
|
|
|
22
24
|
console.log('---- Using HTTP Gmail API ----');
|
|
23
|
-
console.log('
|
|
25
|
+
console.log('Always refreshing token before send...');
|
|
26
|
+
|
|
27
|
+
// ALWAYS refresh token first - no expiration checking
|
|
28
|
+
const freshAccessToken = await this.refreshAccessToken(config);
|
|
29
|
+
|
|
30
|
+
console.log('---- Fresh token obtained, preparing email ----');
|
|
24
31
|
|
|
25
32
|
const toRecipients = Array.isArray(to) ? to.join(', ') : to;
|
|
26
33
|
const ccRecipients = cc
|
|
@@ -63,7 +70,7 @@ export class GmailApiStrategy implements CommunicationStrategy {
|
|
|
63
70
|
.replace(/\//g, '_')
|
|
64
71
|
.replace(/=+$/, '');
|
|
65
72
|
|
|
66
|
-
//
|
|
73
|
+
// Send with fresh token
|
|
67
74
|
const response = await axios.post(
|
|
68
75
|
'https://gmail.googleapis.com/gmail/v1/users/me/messages/send',
|
|
69
76
|
{
|
|
@@ -71,7 +78,7 @@ export class GmailApiStrategy implements CommunicationStrategy {
|
|
|
71
78
|
},
|
|
72
79
|
{
|
|
73
80
|
headers: {
|
|
74
|
-
Authorization: `Bearer ${
|
|
81
|
+
Authorization: `Bearer ${freshAccessToken}`,
|
|
75
82
|
'Content-Type': 'application/json',
|
|
76
83
|
},
|
|
77
84
|
timeout: 30000, // 30 second timeout
|
|
@@ -88,6 +95,7 @@ export class GmailApiStrategy implements CommunicationStrategy {
|
|
|
88
95
|
provider: 'gmail',
|
|
89
96
|
service: 'API',
|
|
90
97
|
timestamp: new Date(),
|
|
98
|
+
refreshedToken: freshAccessToken, // Always return the fresh token for database update
|
|
91
99
|
};
|
|
92
100
|
} catch (error) {
|
|
93
101
|
console.error('---- Gmail HTTP API Error ----');
|
|
@@ -111,18 +119,29 @@ export class GmailApiStrategy implements CommunicationStrategy {
|
|
|
111
119
|
validateConfig(config: any): boolean {
|
|
112
120
|
if (!config) return false;
|
|
113
121
|
|
|
114
|
-
// For
|
|
115
|
-
return config.
|
|
122
|
+
// For always-refresh strategy, we only need a refresh token
|
|
123
|
+
return config.refreshToken && typeof config.refreshToken === 'string';
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
async refreshAccessToken(config: any): Promise<string> {
|
|
119
127
|
try {
|
|
120
|
-
const {
|
|
128
|
+
const { refreshToken } = config;
|
|
121
129
|
|
|
122
|
-
if (!
|
|
123
|
-
throw new Error('Missing
|
|
130
|
+
if (!refreshToken) {
|
|
131
|
+
throw new Error('Missing refresh token for token refresh');
|
|
124
132
|
}
|
|
125
133
|
|
|
134
|
+
// Get system OAuth credentials
|
|
135
|
+
const clientId = this.configService.get<string>('CLIENT_ID');
|
|
136
|
+
const clientSecret = this.configService.get<string>('CLIENT_SECRET');
|
|
137
|
+
|
|
138
|
+
if (!clientId || !clientSecret) {
|
|
139
|
+
throw new Error('Gmail OAuth system credentials not configured');
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log('---- Refreshing token with system credentials ----');
|
|
143
|
+
|
|
144
|
+
// Use Google's OAuth endpoint to refresh token with system credentials
|
|
126
145
|
const response = await axios.post(
|
|
127
146
|
'https://oauth2.googleapis.com/token',
|
|
128
147
|
{
|
|
@@ -133,12 +152,17 @@ export class GmailApiStrategy implements CommunicationStrategy {
|
|
|
133
152
|
},
|
|
134
153
|
{
|
|
135
154
|
headers: {
|
|
136
|
-
'Content-Type': 'application/
|
|
155
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
137
156
|
},
|
|
138
157
|
},
|
|
139
158
|
);
|
|
140
159
|
|
|
141
|
-
|
|
160
|
+
if (!response.data.access_token) {
|
|
161
|
+
throw new Error('No access token received from refresh');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log('---- Token refreshed successfully ----');
|
|
165
|
+
return response.data.access_token;
|
|
142
166
|
} catch (error) {
|
|
143
167
|
console.error(
|
|
144
168
|
'Failed to refresh access token:',
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { ConfigService } from '@nestjs/config';
|
|
2
3
|
import { google } from 'googleapis';
|
|
3
4
|
import {
|
|
4
5
|
CommunicationStrategy,
|
|
@@ -7,6 +8,7 @@ import {
|
|
|
7
8
|
|
|
8
9
|
@Injectable()
|
|
9
10
|
export class GmailStrategy implements CommunicationStrategy {
|
|
11
|
+
constructor(private readonly configService: ConfigService) {}
|
|
10
12
|
async sendMessage(
|
|
11
13
|
to: string,
|
|
12
14
|
message: string,
|
|
@@ -21,8 +23,16 @@ export class GmailStrategy implements CommunicationStrategy {
|
|
|
21
23
|
throw new Error('Missing required Gmail credentials: refreshToken and email are required');
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
//
|
|
25
|
-
const
|
|
26
|
+
// Get system OAuth credentials
|
|
27
|
+
const systemClientId = this.configService.get<string>('CLIENT_ID');
|
|
28
|
+
const systemClientSecret = this.configService.get<string>('CLIENT_SECRET');
|
|
29
|
+
|
|
30
|
+
if (!systemClientId || !systemClientSecret) {
|
|
31
|
+
throw new Error('Gmail OAuth system credentials not configured');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Create OAuth2 client with system credentials
|
|
35
|
+
const oauth2Client = new google.auth.OAuth2(systemClientId, systemClientSecret);
|
|
26
36
|
|
|
27
37
|
oauth2Client.setCredentials({
|
|
28
38
|
refresh_token: refreshToken,
|
|
@@ -75,7 +85,7 @@ export class GmailStrategy implements CommunicationStrategy {
|
|
|
75
85
|
refreshedToken: config.accessToken !== accessToken ? config.accessToken : undefined,
|
|
76
86
|
};
|
|
77
87
|
} catch (error) {
|
|
78
|
-
console.log('Gmail strategy error:', error
|
|
88
|
+
console.log('Gmail strategy error:', error);
|
|
79
89
|
console.log('Config validation:', JSON.stringify({
|
|
80
90
|
hasRefreshToken: !!config.refreshToken,
|
|
81
91
|
hasAccessToken: !!config.accessToken,
|
|
@@ -93,11 +103,7 @@ export class GmailStrategy implements CommunicationStrategy {
|
|
|
93
103
|
}
|
|
94
104
|
|
|
95
105
|
validateConfig(config: any): boolean {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
config.clientSecret &&
|
|
99
|
-
config.refreshToken &&
|
|
100
|
-
config.email
|
|
101
|
-
);
|
|
106
|
+
// Since we use system credentials, we only need refreshToken and email from config
|
|
107
|
+
return !!(config.refreshToken && config.email);
|
|
102
108
|
}
|
|
103
109
|
}
|
|
@@ -325,18 +325,6 @@ export class ListMasterService {
|
|
|
325
325
|
);
|
|
326
326
|
}
|
|
327
327
|
|
|
328
|
-
// Check for duplicate code
|
|
329
|
-
const codeExists = await this.listMasterRepo.findOneByCondition({
|
|
330
|
-
code,
|
|
331
|
-
organization_id: loggedInUser.organization_id,
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
if (codeExists) {
|
|
335
|
-
throw new BadRequestException(
|
|
336
|
-
'A List Master with the same code already exists',
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
|
|
340
328
|
const createdListMaster = await this.entityServiceImpl.createEntity(
|
|
341
329
|
entityData,
|
|
342
330
|
loggedInUser,
|
|
@@ -318,7 +318,6 @@ export class EntityServiceImpl implements EntityService<BaseEntity> {
|
|
|
318
318
|
if (!entityData.enterprise_id)
|
|
319
319
|
entityData.enterprise_id = loggedInUser.enterprise_id;
|
|
320
320
|
}
|
|
321
|
-
console.log(entityData, 'WWWWWWWWW');
|
|
322
321
|
await repoService.update(entityData.id, entityData);
|
|
323
322
|
return await repoService.findOne({ where: { id: entityData.id } });
|
|
324
323
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Body, Controller, Post } from '@nestjs/common';
|
|
2
|
+
import { NotificationsService } from '../service/firebase.service';
|
|
3
|
+
|
|
4
|
+
@Controller('notifications')
|
|
5
|
+
export class NotificationsController {
|
|
6
|
+
constructor(private readonly notificationsService: NotificationsService) {}
|
|
7
|
+
|
|
8
|
+
// Store token from frontend
|
|
9
|
+
@Post('register-token')
|
|
10
|
+
async registerToken(@Body() body: { userId?: string; token: string }) {
|
|
11
|
+
// Optional: associate token with userId if provided
|
|
12
|
+
return this.notificationsService.saveToken(body.userId, body.token);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Post('send')
|
|
16
|
+
async sendNotification(
|
|
17
|
+
@Body() body: { token: string; title: string; message: string },
|
|
18
|
+
) {
|
|
19
|
+
return this.notificationsService.sendToDevice(
|
|
20
|
+
body.token,
|
|
21
|
+
body.title,
|
|
22
|
+
body.message,
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -45,14 +45,15 @@ export class OtpController {
|
|
|
45
45
|
@HttpCode(HttpStatus.OK)
|
|
46
46
|
async verifyOtp(
|
|
47
47
|
@Body()
|
|
48
|
-
|
|
48
|
+
data: {
|
|
49
49
|
otp: string;
|
|
50
50
|
otp_id: string;
|
|
51
51
|
identifier: string;
|
|
52
52
|
subdomain: string;
|
|
53
|
+
fcm_token: string;
|
|
53
54
|
},
|
|
54
55
|
) {
|
|
55
|
-
return await this.otpService.verifyOtp(
|
|
56
|
+
return await this.otpService.verifyOtp(data);
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
@Post('send-mail')
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseEntity } from 'src/module/meta/entity/base-entity.entity';
|
|
2
|
+
import { Column, Entity } from 'typeorm';
|
|
3
|
+
|
|
4
|
+
@Entity({ name: 'cr_notification' })
|
|
5
|
+
export class NotificationData extends BaseEntity {
|
|
6
|
+
@Column({ name: 'user_id', type: 'int', nullable: true })
|
|
7
|
+
user_id: number | null;
|
|
8
|
+
|
|
9
|
+
@Column({ name: 'event_type', type: 'varchar', length: 255, nullable: true })
|
|
10
|
+
event_type: string | null;
|
|
11
|
+
|
|
12
|
+
@Column({ name: 'message', type: 'text', nullable: true })
|
|
13
|
+
message: string | null;
|
|
14
|
+
|
|
15
|
+
@Column({ nullable: true })
|
|
16
|
+
mapped_entity_id: number;
|
|
17
|
+
|
|
18
|
+
@Column({ nullable: true })
|
|
19
|
+
mapped_entity_type: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "service_account",
|
|
3
|
+
"project_id": "sample-d7855",
|
|
4
|
+
"private_key_id": "753237e3e7d64a87155b916deda9f3d1cb9cdd52",
|
|
5
|
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQChrgvD+0M3u8EU\ntm3xV27RGuYRSjIpZIRi6I3tH0BTwpOde4DnwZWUQnTEbg/wgBGvFtGS7T4UXaef\ngkhTSRqPhIqEaIVRXf34NOhYTAOxBcvDaG9skQbOzhH9gz8cEG1HyDRCWqmxgpCy\nt0jUig5Ta72t0zSaQKc/H6AhwWF8Yiz+KnwGR837FvKDM+ZN28B21sRNcOciDXHf\nEeh4H4GjBQguM+5aMGFRACclIFztwxwTBXCJ2r/AMxSjCntzOCHimgG6dOtlW/KS\neZqunZjxvbmHZpj3N1sGRJQFHFjRLQSgCdMzwOclJ82AImcOYLJ3w1kKTmts6JlW\nVYvj6jzpAgMBAAECggEAA9xhIYv1ffY41SLKG1JILiwjRFRuQDjLRIVjJj9xzM4/\nsioJ2P7xcHnpyesK7GZShcjYtlZ9/gSChTeaPIrpXHYAzMqmLFw/PVceGUWvbOHD\njOYIiyIndHbNTS1+K1BlG6q3PMJnfPquM+ZWkeS+FNXk0KrTfb9/fu1laIjyqAKy\na8w7IwNkk4iBkKxRc74204TxQsgUpooROuoP9JfoVVL90R1EDFJWnrciTcqB77SS\nAFsPbOY9kZ5WOmUp6ZzkQD434fRRCUis7WdicV4OU0VEQmLX57UxzxNTOZzw8Adn\nnn7nmFXfcF5+ODX1htkTSn84p+qP4CR+a99/4rLqgQKBgQDbbsySP4I0KBhXT+xQ\n8iHpTo8lV71R1t4gboBIQXIHkmlrI9Qy/oOe3yViATp54oH727W/vWm4OEomzpl4\nNrs27HrI65XZcYyRCU6mLEaDx2ZjYYKHLMwx0DdQ9MPn+n1JusadiRN671ZZ3smb\nbtMLqeslmoN43SPDiG2CSNOycQKBgQC8n3RViNpHZJDUQoLxHZ7rpfIa/0v3qpjd\nI3nDxDH8udu2ipsVjqXmGJTBY+NQcWhuvuhYTzH5plnSuD3tBrPnA3VoN7FFDd29\nx+5K60acjgbu0sa9yO4aSAYD7sjkfkxVAKhzIgNWlAzl2zluakq5r3+uGQ+FVvJh\nmo5FxSj9+QKBgBkaBqrgOvvObmJmkSj9WeW/h96Et/KJuuVI3sHlQq8dD5QjCB5B\nQTtGWZdpfo/82lO+YX8qotJhFhJ0Zdf7otT4nl8nm//A3oyk3OtjezmN8OeDexQN\nitpT8FABf/ukivqJNDlHOgRBJsanFrcCKYBXEsA9eba0zWeLzsMto5HxAoGBAIaH\nW5udzcDZkwownd+GdtAvPSvQJchwnjIqmS/tAJH1pSTeWpnXca9YnNAJhBjdqdRC\nyMgjQ8uAv9OwoEorW6hKKTS5c++CYkJ3FBfPEj+adItlPWYipt+Luu6XIiUFhz+h\nBoOHIMZhNYnC/4UmvkENUI1FRnKdfqXqa8qLQh9RAoGBAMxOm2fMUKzLPY8xqp0j\nWmo3ZYMkRRi/BFqVn1F+aFGx0ahW8GVT70qy30rhiNJAkl2QnPnllQ6rD/CBc3UT\n3kJLwTc5BWXP1nKxEOuu33xB5wk4mIzyItFf/eYhYFzsWMpJo2gaKE3iNNUKUP0W\nG2coAWXT2iRSS6OHh9B1bRnQ\n-----END PRIVATE KEY-----\n",
|
|
6
|
+
"client_email": "firebase-adminsdk-fbsvc@sample-d7855.iam.gserviceaccount.com",
|
|
7
|
+
"client_id": "115995668082397151669",
|
|
8
|
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
9
|
+
"token_uri": "https://oauth2.googleapis.com/token",
|
|
10
|
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
|
11
|
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40sample-d7855.iam.gserviceaccount.com",
|
|
12
|
+
"universe_domain": "googleapis.com"
|
|
13
|
+
}
|
|
@@ -12,10 +12,13 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
|
12
12
|
import { AuthModule } from '../auth/auth.module';
|
|
13
13
|
import { UserModule } from '../user/user.module';
|
|
14
14
|
import { IcsMeetingModule } from '../ics/ics.module';
|
|
15
|
+
import { NotificationData } from './entity/notification.entity';
|
|
16
|
+
import { NotificationsController } from './controller/notification.controller';
|
|
17
|
+
import { NotificationsService } from './service/firebase.service';
|
|
15
18
|
|
|
16
19
|
@Module({
|
|
17
20
|
imports: [
|
|
18
|
-
TypeOrmModule.forFeature([Otp]),
|
|
21
|
+
TypeOrmModule.forFeature([Otp, NotificationData]),
|
|
19
22
|
MailerModule.forRootAsync({
|
|
20
23
|
imports: [ConfigModule],
|
|
21
24
|
inject: [ConfigService],
|
|
@@ -45,8 +48,8 @@ import { IcsMeetingModule } from '../ics/ics.module';
|
|
|
45
48
|
UserModule,
|
|
46
49
|
IcsMeetingModule,
|
|
47
50
|
],
|
|
48
|
-
providers: [OtpService, OtpRepository, EmailService],
|
|
49
|
-
exports: [OtpService, EmailService],
|
|
50
|
-
controllers: [OtpController],
|
|
51
|
+
providers: [OtpService, OtpRepository, EmailService, NotificationsService],
|
|
52
|
+
exports: [OtpService, EmailService, NotificationsService],
|
|
53
|
+
controllers: [OtpController, NotificationsController],
|
|
51
54
|
})
|
|
52
55
|
export class NotificationModule {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import { firebaseAdmin } from '../firebase-admin.config';
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class NotificationsService {
|
|
6
|
+
private tokens: Map<string, string> = new Map(); // store in memory for now
|
|
7
|
+
|
|
8
|
+
async saveToken(userId: string | undefined, token: string) {
|
|
9
|
+
if (userId) {
|
|
10
|
+
this.tokens.set(userId, token);
|
|
11
|
+
}
|
|
12
|
+
return { success: true, token };
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async sendToDevice(token: string, title: string, body: string) {
|
|
16
|
+
const message = {
|
|
17
|
+
token,
|
|
18
|
+
notification: { title, body },
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return firebaseAdmin.messaging().send(message);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Helper: send to a registered user by userId
|
|
25
|
+
async sendToUser(userId: string, title: string, body: string) {
|
|
26
|
+
const token = this.tokens.get(userId);
|
|
27
|
+
if (!token) return { error: 'No token found for user' };
|
|
28
|
+
|
|
29
|
+
return this.sendToDevice(token, title, body);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -53,13 +53,14 @@ export class OtpService {
|
|
|
53
53
|
return await this.otpRepository.save(otp);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
async verifyOtp(
|
|
56
|
+
async verifyOtp(data: {
|
|
57
57
|
otp: string;
|
|
58
58
|
otp_id: string;
|
|
59
59
|
identifier: string;
|
|
60
60
|
subdomain: string;
|
|
61
|
+
fcm_token: string;
|
|
61
62
|
}) {
|
|
62
|
-
const { otp, otp_id, identifier, subdomain } =
|
|
63
|
+
const { otp, otp_id, identifier, subdomain, fcm_token } = data;
|
|
63
64
|
const verifyOTPResponse = { isValid: false };
|
|
64
65
|
|
|
65
66
|
// Find OTP entity by ID
|
|
@@ -98,7 +99,12 @@ export class OtpService {
|
|
|
98
99
|
}
|
|
99
100
|
|
|
100
101
|
// Proceed to login
|
|
101
|
-
return this.loginService.login(
|
|
102
|
+
return this.loginService.login({
|
|
103
|
+
email: data.identifier,
|
|
104
|
+
is_otp: true,
|
|
105
|
+
subdomain: data.subdomain,
|
|
106
|
+
fcm_token: data.fcm_token,
|
|
107
|
+
});
|
|
102
108
|
}
|
|
103
109
|
|
|
104
110
|
async findByOtpId(otpId: string) {
|
|
@@ -28,13 +28,23 @@ export class LoginController {
|
|
|
28
28
|
|
|
29
29
|
@Post('login')
|
|
30
30
|
async login(@Body() body, @Res() res: Response) {
|
|
31
|
-
const {
|
|
32
|
-
const result = await this.loginService.login(
|
|
31
|
+
const {
|
|
33
32
|
email_id,
|
|
34
33
|
password,
|
|
35
|
-
|
|
34
|
+
appcode,
|
|
35
|
+
organization_id,
|
|
36
36
|
subdomain,
|
|
37
|
-
|
|
37
|
+
fcm_token,
|
|
38
|
+
} = body;
|
|
39
|
+
|
|
40
|
+
const result = await this.loginService.login({
|
|
41
|
+
email: email_id,
|
|
42
|
+
password,
|
|
43
|
+
subdomain,
|
|
44
|
+
fcm_token,
|
|
45
|
+
is_otp: false, // default since you had it before
|
|
46
|
+
});
|
|
47
|
+
|
|
38
48
|
return res.status(HttpStatus.OK).json(result);
|
|
39
49
|
}
|
|
40
50
|
@Post('form-login')
|
|
@@ -67,9 +77,12 @@ export class LoginController {
|
|
|
67
77
|
async googleAuthRedirect(@Req() req: any, @Res() res: Response) {
|
|
68
78
|
const {
|
|
69
79
|
email,
|
|
80
|
+
password,
|
|
70
81
|
name,
|
|
71
82
|
accessToken: googleAccessToken,
|
|
72
83
|
refreshToken: googleRefreshToken,
|
|
84
|
+
subdomain,
|
|
85
|
+
fcm_token,
|
|
73
86
|
} = req.user;
|
|
74
87
|
const { state } = req.query;
|
|
75
88
|
|
|
@@ -92,18 +105,20 @@ export class LoginController {
|
|
|
92
105
|
actualState,
|
|
93
106
|
);
|
|
94
107
|
|
|
95
|
-
return res.redirect(
|
|
96
|
-
`${this.configService.get('BASE_URL')}`,
|
|
97
|
-
);
|
|
108
|
+
return res.redirect(`${this.configService.get('BASE_URL')}`);
|
|
98
109
|
} catch (error) {
|
|
99
|
-
return res.redirect(
|
|
100
|
-
`${this.configService.get('BASE_URL')}`,
|
|
101
|
-
);
|
|
110
|
+
return res.redirect(`${this.configService.get('BASE_URL')}`);
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
// Original login flow
|
|
106
|
-
const data = await this.loginService.loginWithGoogle(
|
|
115
|
+
const data = await this.loginService.loginWithGoogle({
|
|
116
|
+
email,
|
|
117
|
+
password,
|
|
118
|
+
name,
|
|
119
|
+
subdomain,
|
|
120
|
+
fcm_token,
|
|
121
|
+
});
|
|
107
122
|
|
|
108
123
|
if (!('accessToken' in data) || !data.accessToken)
|
|
109
124
|
return res.redirect(
|
|
@@ -29,7 +29,12 @@ export class UserController {
|
|
|
29
29
|
|
|
30
30
|
@Post('check-email')
|
|
31
31
|
@HttpCode(HttpStatus.OK)
|
|
32
|
-
async checkEmail(@Body(
|
|
33
|
-
|
|
32
|
+
async checkEmail(@Body() body, @Res() res: Response) {
|
|
33
|
+
const { email, subdomain } = body;
|
|
34
|
+
const result = await this.userService.checkEmailExists({
|
|
35
|
+
email,
|
|
36
|
+
subdomain,
|
|
37
|
+
});
|
|
38
|
+
return res.status(HttpStatus.OK).json(result);
|
|
34
39
|
}
|
|
35
40
|
}
|