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.
Files changed (96) hide show
  1. package/dist/app.module.js +2 -0
  2. package/dist/app.module.js.map +1 -1
  3. package/dist/module/communication/controller/communication.controller.d.ts +4 -1
  4. package/dist/module/communication/controller/communication.controller.js +23 -3
  5. package/dist/module/communication/controller/communication.controller.js.map +1 -1
  6. package/dist/module/communication/entity/communication-config.entity.js +1 -1
  7. package/dist/module/communication/entity/communication-config.entity.js.map +1 -1
  8. package/dist/module/communication/entity/communication-hub.entity.js +1 -1
  9. package/dist/module/communication/entity/communication-hub.entity.js.map +1 -1
  10. package/dist/module/communication/service/communication.service.d.ts +5 -1
  11. package/dist/module/communication/service/communication.service.js +13 -3
  12. package/dist/module/communication/service/communication.service.js.map +1 -1
  13. package/dist/module/communication/strategies/email/gmail-api.strategy.d.ts +3 -0
  14. package/dist/module/communication/strategies/email/gmail-api.strategy.js +33 -12
  15. package/dist/module/communication/strategies/email/gmail-api.strategy.js.map +1 -1
  16. package/dist/module/communication/strategies/gmail.strategy.d.ts +3 -0
  17. package/dist/module/communication/strategies/gmail.strategy.js +17 -7
  18. package/dist/module/communication/strategies/gmail.strategy.js.map +1 -1
  19. package/dist/module/listmaster/service/list-master.service.js +0 -7
  20. package/dist/module/listmaster/service/list-master.service.js.map +1 -1
  21. package/dist/module/meta/service/entity-service-impl.service.js +0 -1
  22. package/dist/module/meta/service/entity-service-impl.service.js.map +1 -1
  23. package/dist/module/notification/controller/notification.controller.d.ts +17 -0
  24. package/dist/module/notification/controller/notification.controller.js +48 -0
  25. package/dist/module/notification/controller/notification.controller.js.map +1 -0
  26. package/dist/module/notification/controller/otp.controller.d.ts +2 -1
  27. package/dist/module/notification/controller/otp.controller.js +2 -2
  28. package/dist/module/notification/controller/otp.controller.js.map +1 -1
  29. package/dist/module/notification/entity/notification.entity.d.ts +8 -0
  30. package/dist/module/notification/entity/notification.entity.js +41 -0
  31. package/dist/module/notification/entity/notification.entity.js.map +1 -0
  32. package/dist/module/notification/firebase-admin.config.d.ts +2 -0
  33. package/dist/module/notification/firebase-admin.config.js +10 -0
  34. package/dist/module/notification/firebase-admin.config.js.map +1 -0
  35. package/dist/module/notification/firebase-admin.json +13 -0
  36. package/dist/module/notification/notification.module.js +7 -4
  37. package/dist/module/notification/notification.module.js.map +1 -1
  38. package/dist/module/notification/service/firebase.service.d.ts +11 -0
  39. package/dist/module/notification/service/firebase.service.js +40 -0
  40. package/dist/module/notification/service/firebase.service.js.map +1 -0
  41. package/dist/module/notification/service/otp.service.d.ts +2 -1
  42. package/dist/module/notification/service/otp.service.js +8 -3
  43. package/dist/module/notification/service/otp.service.js.map +1 -1
  44. package/dist/module/user/controller/login.controller.js +16 -4
  45. package/dist/module/user/controller/login.controller.js.map +1 -1
  46. package/dist/module/user/controller/user.controller.d.ts +1 -9
  47. package/dist/module/user/controller/user.controller.js +10 -4
  48. package/dist/module/user/controller/user.controller.js.map +1 -1
  49. package/dist/module/user/entity/user.entity.d.ts +1 -0
  50. package/dist/module/user/entity/user.entity.js +4 -0
  51. package/dist/module/user/entity/user.entity.js.map +1 -1
  52. package/dist/module/user/service/login.service.d.ts +17 -3
  53. package/dist/module/user/service/login.service.js +14 -5
  54. package/dist/module/user/service/login.service.js.map +1 -1
  55. package/dist/module/user/service/user.service.d.ts +7 -10
  56. package/dist/module/user/service/user.service.js +23 -8
  57. package/dist/module/user/service/user.service.js.map +1 -1
  58. package/dist/module/workflow/entity/task-data.entity.d.ts +2 -0
  59. package/dist/module/workflow/entity/task-data.entity.js +9 -1
  60. package/dist/module/workflow/entity/task-data.entity.js.map +1 -1
  61. package/dist/module/workflow/repository/task.repository.js +6 -1
  62. package/dist/module/workflow/repository/task.repository.js.map +1 -1
  63. package/dist/module/workflow/service/task.service.d.ts +4 -1
  64. package/dist/module/workflow/service/task.service.js +46 -2
  65. package/dist/module/workflow/service/task.service.js.map +1 -1
  66. package/dist/module/workflow/workflow.module.js +2 -0
  67. package/dist/module/workflow/workflow.module.js.map +1 -1
  68. package/dist/tsconfig.build.tsbuildinfo +1 -1
  69. package/package.json +4 -1
  70. package/src/app.module.ts +2 -0
  71. package/src/module/communication/controller/communication.controller.ts +19 -1
  72. package/src/module/communication/entity/communication-config.entity.ts +1 -1
  73. package/src/module/communication/entity/communication-hub.entity.ts +1 -1
  74. package/src/module/communication/service/communication.service.ts +21 -2
  75. package/src/module/communication/strategies/email/gmail-api.strategy.ts +37 -13
  76. package/src/module/communication/strategies/gmail.strategy.ts +15 -9
  77. package/src/module/listmaster/service/list-master.service.ts +0 -12
  78. package/src/module/meta/service/entity-service-impl.service.ts +0 -1
  79. package/src/module/notification/controller/notification.controller.ts +25 -0
  80. package/src/module/notification/controller/otp.controller.ts +3 -2
  81. package/src/module/notification/entity/notification.entity.ts +20 -0
  82. package/src/module/notification/firebase-admin.config.ts +8 -0
  83. package/src/module/notification/firebase-admin.json +13 -0
  84. package/src/module/notification/notification.module.ts +7 -4
  85. package/src/module/notification/service/firebase.service.ts +31 -0
  86. package/src/module/notification/service/otp.service.ts +9 -3
  87. package/src/module/user/controller/login.controller.ts +26 -11
  88. package/src/module/user/controller/user.controller.ts +7 -2
  89. package/src/module/user/entity/user.entity.ts +3 -0
  90. package/src/module/user/service/login.service.ts +24 -8
  91. package/src/module/user/service/user.service.ts +32 -6
  92. package/src/module/workflow/entity/task-data.entity.ts +7 -1
  93. package/src/module/workflow/repository/task.repository.ts +10 -2
  94. package/src/module/workflow/service/task.service.ts +56 -0
  95. package/src/module/workflow/workflow.module.ts +2 -0
  96. package/tsconfig.json +5 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rez_core",
3
- "version": "2.2.166",
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: 'communication_config' })
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: 'communication_hub' })
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('communication_config', 'config', 'config.id = hub.config_id')
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: { level_id: levelId, level_type: levelType },
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 (!this.validateConfig(config)) {
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 { accessToken, subject, html, cc, bcc } = config;
22
+ const { subject, html, cc, bcc } = config;
21
23
 
22
24
  console.log('---- Using HTTP Gmail API ----');
23
- console.log('accessToken:', accessToken ? 'Present' : 'Missing');
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
- // Direct HTTP call to Gmail API
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 ${accessToken}`,
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 HTTP API, we just need accessToken
115
- return config.accessToken && typeof config.accessToken === 'string';
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 { clientId, clientSecret, refreshToken } = config;
128
+ const { refreshToken } = config;
121
129
 
122
- if (!clientId || !clientSecret || !refreshToken) {
123
- throw new Error('Missing OAuth credentials for token refresh');
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/json',
155
+ 'Content-Type': 'application/x-www-form-urlencoded',
137
156
  },
138
157
  },
139
158
  );
140
159
 
141
- return response.data.access_token || '';
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
- // Create a minimal OAuth2 client just for token management
25
- const oauth2Client = new google.auth.OAuth2();
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.message);
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
- return !!(
97
- config.clientId &&
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
- body: {
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(body);
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,8 @@
1
+ import * as admin from 'firebase-admin';
2
+ import * as serviceAccount from './firebase-admin.json';
3
+
4
+ admin.initializeApp({
5
+ credential: admin.credential.cert(serviceAccount as admin.ServiceAccount),
6
+ });
7
+
8
+ export const firebaseAdmin = admin;
@@ -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(body: {
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 } = body;
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(identifier, undefined, true, subdomain);
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 { email_id, password, appcode, organization_id, subdomain } = body;
32
- const result = await this.loginService.login(
31
+ const {
33
32
  email_id,
34
33
  password,
35
- false,
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(email, name);
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('email_id') email: string) {
33
- return await this.userService.checkEmailExists(email);
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
  }
@@ -53,4 +53,7 @@ export class UserData extends BaseEntity {
53
53
 
54
54
  @Column({ nullable: true })
55
55
  date: Date;
56
+
57
+ @Column({ type: 'varchar', nullable: true })
58
+ fcm_token: string;
56
59
  }