@solidstarters/solid-core 1.2.146 → 1.2.148
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/config/common.config.d.ts +10 -0
- package/dist/config/common.config.d.ts.map +1 -1
- package/dist/config/common.config.js +5 -0
- package/dist/config/common.config.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +5 -5
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/jobs/database/api-email-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/api-email-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/api-email-subscriber-database.service.js +6 -3
- package/dist/jobs/database/api-email-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.d.ts +3 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.d.ts.map +1 -1
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.js +6 -3
- package/dist/jobs/database/computed-field-evaluation-subscriber.service.js.map +1 -1
- package/dist/jobs/database/generate-code-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/generate-code-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/generate-code-subscriber-database.service.js +6 -3
- package/dist/jobs/database/generate-code-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/otp-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/otp-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/otp-subscriber-database.service.js +5 -2
- package/dist/jobs/database/otp-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/sms-subscriber-database.service.d.ts +4 -2
- package/dist/jobs/database/sms-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/sms-subscriber-database.service.js +7 -4
- package/dist/jobs/database/sms-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/smtp-email-subscriber-database.service.d.ts +4 -2
- package/dist/jobs/database/smtp-email-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/smtp-email-subscriber-database.service.js +6 -3
- package/dist/jobs/database/smtp-email-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/test-queue-subscriber-database.service.js +6 -3
- package/dist/jobs/database/test-queue-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +5 -2
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
- package/dist/jobs/database/twilio-sms-publisher-database.service.d.ts +11 -0
- package/dist/jobs/database/twilio-sms-publisher-database.service.d.ts.map +1 -0
- package/dist/jobs/database/twilio-sms-publisher-database.service.js +39 -0
- package/dist/jobs/database/twilio-sms-publisher-database.service.js.map +1 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.d.ts +8 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.d.ts.map +1 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.js +10 -0
- package/dist/jobs/database/twilio-sms-queue-database-options.js.map +1 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.d.ts +17 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.d.ts.map +1 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.js +48 -0
- package/dist/jobs/database/twilio-sms-subscriber-database.service.js.map +1 -0
- package/dist/jobs/database/whatsapp-subscriber-database.service.d.ts +3 -1
- package/dist/jobs/database/whatsapp-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/whatsapp-subscriber-database.service.js +6 -3
- package/dist/jobs/database/whatsapp-subscriber-database.service.js.map +1 -1
- package/dist/jobs/sms-subscriber.service.d.ts +1 -1
- package/dist/jobs/sms-subscriber.service.js +1 -1
- package/dist/jobs/sms-subscriber.service.js.map +1 -1
- package/dist/jobs/smtp-email-subscriber.service.d.ts +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts +1 -1
- package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
- package/dist/seeders/module-metadata-seeder.service.js +15 -4
- package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
- package/dist/services/mail/smtp-email.service.d.ts +4 -4
- package/dist/services/mail/smtp-email.service.d.ts.map +1 -1
- package/dist/services/mail/smtp-email.service.js +12 -8
- package/dist/services/mail/smtp-email.service.js.map +1 -1
- package/dist/services/poller.service.d.ts +24 -0
- package/dist/services/poller.service.d.ts.map +1 -0
- package/dist/services/poller.service.js +131 -0
- package/dist/services/poller.service.js.map +1 -0
- package/dist/services/queues/database-subscriber.service.d.ts +4 -1
- package/dist/services/queues/database-subscriber.service.d.ts.map +1 -1
- package/dist/services/queues/database-subscriber.service.js +13 -13
- package/dist/services/queues/database-subscriber.service.js.map +1 -1
- package/dist/services/sms/Msg91BaseSMSService.d.ts +2 -2
- package/dist/services/sms/Msg91BaseSMSService.d.ts.map +1 -1
- package/dist/services/sms/Msg91BaseSMSService.js.map +1 -1
- package/dist/services/sms/Msg91OTPService.d.ts +1 -1
- package/dist/services/sms/Msg91OTPService.d.ts.map +1 -1
- package/dist/services/sms/Msg91OTPService.js.map +1 -1
- package/dist/services/sms/Msg91SMSService.d.ts +1 -1
- package/dist/services/sms/Msg91SMSService.d.ts.map +1 -1
- package/dist/services/sms/Msg91SMSService.js.map +1 -1
- package/dist/services/sms/TwilioSMSService.d.ts +18 -0
- package/dist/services/sms/TwilioSMSService.d.ts.map +1 -0
- package/dist/services/sms/TwilioSMSService.js +115 -0
- package/dist/services/sms/TwilioSMSService.js.map +1 -0
- package/dist/solid-core.module.d.ts.map +1 -1
- package/dist/solid-core.module.js +10 -0
- package/dist/solid-core.module.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -3
- package/src/config/common.config.ts +5 -0
- package/src/index.ts +2 -0
- package/src/interfaces.ts +5 -5
- package/src/jobs/database/api-email-subscriber-database.service.ts +3 -1
- package/src/jobs/database/computed-field-evaluation-subscriber.service.ts +4 -2
- package/src/jobs/database/generate-code-subscriber-database.service.ts +3 -1
- package/src/jobs/database/otp-subscriber-database.service.ts +3 -1
- package/src/jobs/database/sms-subscriber-database.service.ts +4 -2
- package/src/jobs/database/smtp-email-subscriber-database.service.ts +3 -1
- package/src/jobs/database/test-queue-subscriber-database.service.ts +3 -1
- package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +3 -1
- package/src/jobs/database/twilio-sms-publisher-database.service.ts +23 -0
- package/src/jobs/database/twilio-sms-queue-database-options.ts +9 -0
- package/src/jobs/database/twilio-sms-subscriber-database.service.ts +32 -0
- package/src/jobs/database/whatsapp-subscriber-database.service.ts +3 -1
- package/src/jobs/sms-subscriber.service.ts +1 -1
- package/src/seeders/module-metadata-seeder.service.ts +18 -15
- package/src/services/mail/smtp-email.service.ts +18 -17
- package/src/services/poller.service.ts +163 -0
- package/src/services/queues/database-subscriber.service.ts +39 -12
- package/src/services/sms/Msg91BaseSMSService.ts +2 -2
- package/src/services/sms/Msg91OTPService.ts +1 -1
- package/src/services/sms/Msg91SMSService.ts +1 -1
- package/src/services/sms/TwilioSMSService.ts +118 -0
- package/src/solid-core.module.ts +13 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// src/common/poller/poller.service.ts
|
|
2
|
+
import {
|
|
3
|
+
Injectable,
|
|
4
|
+
Logger,
|
|
5
|
+
OnModuleDestroy,
|
|
6
|
+
BeforeApplicationShutdown,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
|
|
9
|
+
export interface PollOptions {
|
|
10
|
+
/** Wait after a successful iteration */
|
|
11
|
+
baseDelayMs?: number; // default 1000
|
|
12
|
+
/** Maximum delay after repeated failures */
|
|
13
|
+
maxDelayMs?: number; // default 30000
|
|
14
|
+
/** Per-iteration timeout guard */
|
|
15
|
+
timeoutPerIterationMs?: number; // default 60000
|
|
16
|
+
/** Add jitter to spread load */
|
|
17
|
+
jitter?: boolean; // default true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type ProcessNextFn = (queueName: string) => Promise<unknown>;
|
|
21
|
+
|
|
22
|
+
interface PollerState {
|
|
23
|
+
queueName: string;
|
|
24
|
+
processNext: ProcessNextFn;
|
|
25
|
+
opts: Required<PollOptions>;
|
|
26
|
+
inFlight: boolean;
|
|
27
|
+
stopped: boolean;
|
|
28
|
+
backoff: number;
|
|
29
|
+
nextTimer?: NodeJS.Timeout;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Injectable()
|
|
33
|
+
export class PollerService implements OnModuleDestroy, BeforeApplicationShutdown {
|
|
34
|
+
private readonly logger = new Logger(PollerService.name);
|
|
35
|
+
private readonly pollers = new Map<string, PollerState>();
|
|
36
|
+
|
|
37
|
+
start(queueName: string, processNext: ProcessNextFn, options: PollOptions = {}): void {
|
|
38
|
+
if (this.pollers.has(queueName)) {
|
|
39
|
+
this.logger.warn(`Poller "${queueName}" already started; ignoring.`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const opts: Required<PollOptions> = {
|
|
44
|
+
baseDelayMs: options.baseDelayMs ?? 1000,
|
|
45
|
+
maxDelayMs: options.maxDelayMs ?? 30_000,
|
|
46
|
+
timeoutPerIterationMs: options.timeoutPerIterationMs ?? 60_000,
|
|
47
|
+
jitter: options.jitter ?? true,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const state: PollerState = {
|
|
51
|
+
queueName,
|
|
52
|
+
processNext,
|
|
53
|
+
opts,
|
|
54
|
+
inFlight: false,
|
|
55
|
+
stopped: false,
|
|
56
|
+
backoff: opts.baseDelayMs,
|
|
57
|
+
nextTimer: undefined,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
this.pollers.set(queueName, state);
|
|
61
|
+
// kick off on next tick
|
|
62
|
+
setImmediate(() => this.poll(state).catch(() => { }));
|
|
63
|
+
this.logger.log(`Started poller "${queueName}"`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
stop(queueName: string): void {
|
|
67
|
+
const state = this.pollers.get(queueName);
|
|
68
|
+
if (!state) return;
|
|
69
|
+
|
|
70
|
+
state.stopped = true;
|
|
71
|
+
if (state.nextTimer) {
|
|
72
|
+
clearTimeout(state.nextTimer);
|
|
73
|
+
state.nextTimer = undefined;
|
|
74
|
+
}
|
|
75
|
+
this.pollers.delete(queueName);
|
|
76
|
+
this.logger.log(`Stopped poller "${queueName}"`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
stopAll(): void {
|
|
80
|
+
for (const name of Array.from(this.pollers.keys())) {
|
|
81
|
+
this.stop(name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async onModuleDestroy(): Promise<void> {
|
|
86
|
+
this.stopAll();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async beforeApplicationShutdown(): Promise<void> {
|
|
90
|
+
this.stopAll();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---- internals ----
|
|
94
|
+
|
|
95
|
+
private async poll(state: PollerState): Promise<void> {
|
|
96
|
+
if (state.stopped || state.inFlight) return;
|
|
97
|
+
state.inFlight = true;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
await this.withTimeout(
|
|
101
|
+
state.processNext(state.queueName),
|
|
102
|
+
state.opts.timeoutPerIterationMs,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
// success: reset backoff and schedule next run after base delay
|
|
106
|
+
state.backoff = state.opts.baseDelayMs;
|
|
107
|
+
// this.logger.debug(`[${state.queueName}] iteration completed`);
|
|
108
|
+
this.schedule(state, state.opts.baseDelayMs);
|
|
109
|
+
} catch (err: unknown) {
|
|
110
|
+
const msg = this.errorToString(err);
|
|
111
|
+
this.logger.error(`[${state.queueName}] iteration failed: ${msg}`);
|
|
112
|
+
|
|
113
|
+
// failure: schedule with backoff + optional jitter, then increase backoff
|
|
114
|
+
const wait = this.computeWait(state.backoff, state.opts);
|
|
115
|
+
state.backoff = Math.min(state.backoff * 2, state.opts.maxDelayMs);
|
|
116
|
+
this.schedule(state, wait);
|
|
117
|
+
} finally {
|
|
118
|
+
state.inFlight = false;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private schedule(state: PollerState, delayMs: number) {
|
|
123
|
+
if (state.stopped) return;
|
|
124
|
+
if (state.nextTimer) clearTimeout(state.nextTimer);
|
|
125
|
+
|
|
126
|
+
state.nextTimer = setTimeout(() => {
|
|
127
|
+
// clear reference before calling poll to avoid re-entrancy confusion
|
|
128
|
+
state.nextTimer = undefined;
|
|
129
|
+
this.poll(state).catch(() => { });
|
|
130
|
+
}, delayMs);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private computeWait(currentBackoff: number, opts: Required<PollOptions>): number {
|
|
134
|
+
if (!opts.jitter) return currentBackoff;
|
|
135
|
+
// Full jitter: random in [250ms, currentBackoff * 2], clamped to maxDelayMs
|
|
136
|
+
const doubled = Math.min(currentBackoff * 2, opts.maxDelayMs);
|
|
137
|
+
const jittered = Math.floor(Math.random() * doubled);
|
|
138
|
+
return Math.max(250, jittered);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private async withTimeout<T>(p: Promise<T>, ms: number): Promise<T> {
|
|
142
|
+
let timer: NodeJS.Timeout | undefined;
|
|
143
|
+
try {
|
|
144
|
+
return await Promise.race<T>([
|
|
145
|
+
p,
|
|
146
|
+
new Promise<never>((_, rej) => {
|
|
147
|
+
timer = setTimeout(() => rej(new Error(`Iteration timed out after ${ms} ms`)), ms);
|
|
148
|
+
}),
|
|
149
|
+
]);
|
|
150
|
+
} finally {
|
|
151
|
+
if (timer) clearTimeout(timer);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private errorToString(err: unknown): string {
|
|
156
|
+
if (err instanceof Error) return err.stack ?? err.message;
|
|
157
|
+
try {
|
|
158
|
+
return JSON.stringify(err);
|
|
159
|
+
} catch {
|
|
160
|
+
return String(err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
@@ -3,6 +3,7 @@ import { QueuesModuleOptions } from "../../interfaces";
|
|
|
3
3
|
import { QueueMessage, QueueSubscriber } from '../../interfaces/mq';
|
|
4
4
|
import { MqMessageQueueService } from '../mq-message-queue.service';
|
|
5
5
|
import { MqMessageService } from '../mq-message.service';
|
|
6
|
+
import { PollerService } from '../poller.service';
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscriber<T> {
|
|
@@ -13,6 +14,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
|
|
|
13
14
|
constructor(
|
|
14
15
|
protected readonly mqMessageService: MqMessageService,
|
|
15
16
|
protected readonly mqMessageQueueService: MqMessageQueueService,
|
|
17
|
+
protected readonly poller: PollerService,
|
|
16
18
|
) {
|
|
17
19
|
this.serviceRole = process.env.QUEUES_SERVICE_ROLE;
|
|
18
20
|
if (!this.serviceRole) {
|
|
@@ -70,6 +72,31 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
|
|
|
70
72
|
// this.logger.debug(`#### DatabaseSubscriber finished processing message from queue: ${queueName}`);
|
|
71
73
|
}
|
|
72
74
|
|
|
75
|
+
// async onModuleInit(): Promise<void> {
|
|
76
|
+
// // we will start subscriber only if the current service role is subscriber.
|
|
77
|
+
// if (['both', 'subscriber'].includes(this.serviceRole)) {
|
|
78
|
+
|
|
79
|
+
// const options = this.options();
|
|
80
|
+
|
|
81
|
+
// const queueName = options.queueName;
|
|
82
|
+
// // setInterval(() => this.processNext(queueName), 1000);
|
|
83
|
+
// const poll = async () => {
|
|
84
|
+
// try {
|
|
85
|
+
// await this.processNext(queueName);
|
|
86
|
+
// } catch (err) {
|
|
87
|
+
// this.logger.error(`Polling error: ${err.message}`);
|
|
88
|
+
// } finally {
|
|
89
|
+
// setTimeout(poll, 1000); // Wait 1s *after* processing finishes
|
|
90
|
+
// }
|
|
91
|
+
// };
|
|
92
|
+
|
|
93
|
+
// // start the loop
|
|
94
|
+
// poll();
|
|
95
|
+
|
|
96
|
+
// this.logger.log(`DatabaseSubscriber ready to consume messages: ${JSON.stringify(this.options())}`);
|
|
97
|
+
// }
|
|
98
|
+
// }
|
|
99
|
+
|
|
73
100
|
async onModuleInit(): Promise<void> {
|
|
74
101
|
// we will start subscriber only if the current service role is subscriber.
|
|
75
102
|
if (['both', 'subscriber'].includes(this.serviceRole)) {
|
|
@@ -77,24 +104,24 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
|
|
|
77
104
|
const options = this.options();
|
|
78
105
|
|
|
79
106
|
const queueName = options.queueName;
|
|
80
|
-
// setInterval(() => this.processNext(queueName), 1000);
|
|
81
|
-
const poll = async () => {
|
|
82
|
-
try {
|
|
83
|
-
await this.processNext(queueName);
|
|
84
|
-
} catch (err) {
|
|
85
|
-
this.logger.error(`Polling error: ${err.message}`);
|
|
86
|
-
} finally {
|
|
87
|
-
setTimeout(poll, 1000); // Wait 1s *after* processing finishes
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
107
|
|
|
91
|
-
|
|
92
|
-
|
|
108
|
+
this.poller.start(queueName, (q) => this.processNext(q), {
|
|
109
|
+
baseDelayMs: 1000,
|
|
110
|
+
maxDelayMs: 30_000,
|
|
111
|
+
timeoutPerIterationMs: 60_000,
|
|
112
|
+
jitter: true,
|
|
113
|
+
});
|
|
93
114
|
|
|
94
115
|
this.logger.log(`DatabaseSubscriber ready to consume messages: ${JSON.stringify(this.options())}`);
|
|
95
116
|
}
|
|
96
117
|
}
|
|
97
118
|
|
|
119
|
+
onModuleDestroy() {
|
|
120
|
+
const options = this.options();
|
|
121
|
+
const queueName = options.queueName;
|
|
122
|
+
this.poller.stop(queueName);
|
|
123
|
+
}
|
|
124
|
+
|
|
98
125
|
/**
|
|
99
126
|
* Abstract method for message processing logic.
|
|
100
127
|
*/
|
|
@@ -20,7 +20,7 @@ export abstract class Msg91BaseSMSService implements ISMS {
|
|
|
20
20
|
throw new Error(`Msg91 does not support sending plain text messages, you need to register a template and use the templateId to send the SMS.`);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
async sendSMSUsingTemplate(to: string, templateName: string, templateParams: any, shouldQueueSms = false): Promise<
|
|
23
|
+
async sendSMSUsingTemplate(to: string, templateName: string, templateParams: any, shouldQueueSms = false): Promise<any> {
|
|
24
24
|
// Load template and evaluate it.
|
|
25
25
|
const emailTemplate = await this.smsTemplateService.findOneByName(templateName);
|
|
26
26
|
if (!emailTemplate) {
|
|
@@ -76,5 +76,5 @@ export abstract class Msg91BaseSMSService implements ISMS {
|
|
|
76
76
|
this.logger.debug(`Queueing SMS to ${to} with message ${JSON.stringify(message)}`);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
abstract sendSMSSynchronously(message: QueueMessage<any>): Promise<
|
|
79
|
+
abstract sendSMSSynchronously(message: QueueMessage<any>): Promise<any>
|
|
80
80
|
}
|
|
@@ -28,7 +28,7 @@ export class Msg91OTPService extends Msg91BaseSMSService implements ISMS {
|
|
|
28
28
|
super(commonConfiguration, 'OTPQueuePublisher', publisherFactory, smsTemplateService);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
async sendSMSSynchronously(message: QueueMessage<any>): Promise<
|
|
31
|
+
async sendSMSSynchronously(message: QueueMessage<any>): Promise<any> {
|
|
32
32
|
const { to, templateId, otp } = message.payload;
|
|
33
33
|
const params = { otp, template_id: templateId, mobile: to, authkey: this.commonConfiguration.msg91Sms.apiKey }
|
|
34
34
|
const otpUrl = `${this.commonConfiguration.msg91Sms.url}/otp?${this.paramsToQueryString(params)}`;
|
|
@@ -21,7 +21,7 @@ export class Msg91SMSService extends Msg91BaseSMSService implements ISMS {
|
|
|
21
21
|
super(commonConfiguration, 'SmsQueuePublisher', publisherFactory, smsTemplateService)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
async sendSMSSynchronously(message: QueueMessage<any>): Promise<
|
|
24
|
+
async sendSMSSynchronously(message: QueueMessage<any>): Promise<any> {
|
|
25
25
|
const { to, templateId, ...templateParams } = message.payload;
|
|
26
26
|
const body = { template_id: templateId, short_url: "0", recipients: [{ mobiles: to, ...templateParams }] };
|
|
27
27
|
const headers = { "authkey": this.commonConfiguration.msg91Sms.apiKey };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import Handlebars from "handlebars";
|
|
2
|
+
import { Inject, Injectable, Logger } from "@nestjs/common";
|
|
3
|
+
import { ConfigType } from "@nestjs/config";
|
|
4
|
+
import commonConfig from "src/config/common.config";
|
|
5
|
+
import { SmsTemplateService } from "../sms-template.service";
|
|
6
|
+
import { ISMS } from "../../interfaces";
|
|
7
|
+
import { PublisherFactory } from "../queues/publisher-factory.service";
|
|
8
|
+
import twilio from 'twilio';
|
|
9
|
+
import { QueueMessage } from "src/interfaces/mq";
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@Injectable()
|
|
13
|
+
export class TwilioSMSService implements ISMS {
|
|
14
|
+
private readonly logger = new Logger(TwilioSMSService.name);
|
|
15
|
+
|
|
16
|
+
constructor(
|
|
17
|
+
@Inject(commonConfig.KEY)
|
|
18
|
+
private commonConfiguration: ConfigType<typeof commonConfig>,
|
|
19
|
+
private publisherFactory: PublisherFactory<any>,
|
|
20
|
+
private smsTemplateService: SmsTemplateService,
|
|
21
|
+
) {
|
|
22
|
+
// super(commonConfiguration, 'OTPQueuePublisher', publisherFactory, smsTemplateService);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async sendSMS(to: string, body: string, shouldQueueSms: boolean): Promise<any> {
|
|
26
|
+
const accountSid = this.commonConfiguration.twilio.accountSid;
|
|
27
|
+
const authToken = this.commonConfiguration.twilio.authToken;
|
|
28
|
+
const twilioNumber = this.commonConfiguration.twilio.number;
|
|
29
|
+
if (!accountSid || !authToken || !twilioNumber) {
|
|
30
|
+
throw new Error("Missing COMMON_TWILIO_ACCOUNT_SID or COMMON_TWILIO_AUTH_TOKEN or COMMON_TWILIO_NUMBER in env.");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const message = {
|
|
34
|
+
payload: {
|
|
35
|
+
body: body,
|
|
36
|
+
to: to,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Send using queue if the developer has explicitly invoked with true.
|
|
41
|
+
if (shouldQueueSms === true) {
|
|
42
|
+
await this.sendSMSAsynchronously(message);
|
|
43
|
+
}
|
|
44
|
+
// If developer has not, however system config mandates that we send using queue, still we send.
|
|
45
|
+
else if (shouldQueueSms === false && this.commonConfiguration.shouldQueueSms === true) {
|
|
46
|
+
await this.sendSMSAsynchronously(message);
|
|
47
|
+
}
|
|
48
|
+
// Else we send synch
|
|
49
|
+
else {
|
|
50
|
+
await this.sendSMSSynchronously(message);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return message;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async sendSMSUsingTemplate(to: string, templateName: string, templateParams: any, shouldQueueSms: boolean): Promise<any> {
|
|
57
|
+
// Load template and evaluate it.
|
|
58
|
+
const smsTemplate = await this.smsTemplateService.findOneByName(templateName);
|
|
59
|
+
if (!smsTemplate) {
|
|
60
|
+
throw new Error(`Invalid template name ${templateName}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Evaluate the body template.
|
|
64
|
+
let body = '';
|
|
65
|
+
try {
|
|
66
|
+
const bodyTemplate = Handlebars.compile(smsTemplate.body);
|
|
67
|
+
body = bodyTemplate(templateParams);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
throw new Error('Unable to compile sms template body');
|
|
70
|
+
}
|
|
71
|
+
// Finally send the email.
|
|
72
|
+
return await this.sendSMS(to, body, shouldQueueSms);
|
|
73
|
+
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async sendSMSAsynchronously(message) {
|
|
77
|
+
const { to } = message.payload;
|
|
78
|
+
this.publisherFactory.publish(message, 'TwilioSmsQueuePublisher');
|
|
79
|
+
this.logger.debug(`Queueing SMS to ${to} with message ${JSON.stringify(message)}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async sendSMSSynchronously(message: QueueMessage<any>): Promise<any> {
|
|
83
|
+
const accountSid = this.commonConfiguration.twilio.accountSid;
|
|
84
|
+
const authToken = this.commonConfiguration.twilio.authToken;
|
|
85
|
+
const twilioNumber = this.commonConfiguration.twilio.number;
|
|
86
|
+
if (!accountSid || !authToken || !twilioNumber) {
|
|
87
|
+
throw new Error("Missing COMMON_TWILIO_ACCOUNT_SID or COMMON_TWILIO_AUTH_TOKEN or COMMON_TWILIO_NUMBER in env.");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const { to, body } = message.payload;
|
|
91
|
+
|
|
92
|
+
const client = twilio(accountSid, authToken);
|
|
93
|
+
try {
|
|
94
|
+
const toSplit = to.split(',');
|
|
95
|
+
|
|
96
|
+
const r = [];
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < toSplit.length; i++) {
|
|
99
|
+
const actualTo = toSplit[i];
|
|
100
|
+
const twilioResponseMsg = await client.messages.create({
|
|
101
|
+
body: body,
|
|
102
|
+
from: twilioNumber,
|
|
103
|
+
to: actualTo,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
this.logger.debug(`Sending SMS to ${actualTo} using Twilio`);
|
|
107
|
+
this.logger.debug(`Twilio response: `);
|
|
108
|
+
this.logger.debug(JSON.stringify(twilioResponseMsg))
|
|
109
|
+
|
|
110
|
+
r.push(twilioResponseMsg);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return r;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
throw new Error(error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/solid-core.module.ts
CHANGED
|
@@ -186,6 +186,11 @@ import { OTPQueuePublisherDatabase } from './jobs/database/otp-publisher-databas
|
|
|
186
186
|
import { OTPQueueSubscriberDatabase } from './jobs/database/otp-subscriber-database.service';
|
|
187
187
|
import { SmsQueuePublisherDatabase } from './jobs/database/sms-publisher-database.service';
|
|
188
188
|
import { SmsQueueSubscriberDatabase } from './jobs/database/sms-subscriber-database.service';
|
|
189
|
+
|
|
190
|
+
import { TwilioSmsQueuePublisherDatabase } from './jobs/database/twilio-sms-publisher-database.service';
|
|
191
|
+
import { TwilioSmsQueueSubscriberDatabase } from './jobs/database/twilio-sms-subscriber-database.service';
|
|
192
|
+
|
|
193
|
+
|
|
189
194
|
import { TriggerMcpClientPublisherDatabase } from './jobs/database/trigger-mcp-client-publisher-database.service';
|
|
190
195
|
import { TriggerMcpClientSubscriberDatabase } from './jobs/database/trigger-mcp-client-subscriber-database.service';
|
|
191
196
|
import { WhatsappQueuePublisherDatabase } from './jobs/database/whatsapp-publisher-database.service';
|
|
@@ -260,6 +265,8 @@ import { SolidCreateModelLayoutMcpToolResponseHandler } from './services/mcp-too
|
|
|
260
265
|
import { NoopsEntityComputedFieldProviderService } from './services/computed-fields/entity/noops-entity-computed-field-provider.service';
|
|
261
266
|
import { AlphaNumExternalIdComputationProvider } from './services/computed-fields/entity/alpha-num-external-id-computed-field-provider';
|
|
262
267
|
import { MailFactory } from './factories/mail.factory';
|
|
268
|
+
import { TwilioSMSService } from './services/sms/TwilioSMSService';
|
|
269
|
+
import { PollerService } from './services/poller.service';
|
|
263
270
|
|
|
264
271
|
|
|
265
272
|
@Global()
|
|
@@ -427,9 +434,11 @@ import { MailFactory } from './factories/mail.factory';
|
|
|
427
434
|
Msg91SMSService,
|
|
428
435
|
Msg91OTPService,
|
|
429
436
|
Msg91WhatsappService,
|
|
437
|
+
TwilioSMSService,
|
|
430
438
|
SmsTemplateService,
|
|
431
439
|
EmailTemplateService,
|
|
432
440
|
PublisherFactory,
|
|
441
|
+
PollerService,
|
|
433
442
|
|
|
434
443
|
McpToolResponseHandlerFactory,
|
|
435
444
|
SolidCreateModuleMcpToolResponseHandler,
|
|
@@ -449,6 +458,8 @@ import { MailFactory } from './factories/mail.factory';
|
|
|
449
458
|
SmsQueueSubscriber,
|
|
450
459
|
SmsQueuePublisherDatabase,
|
|
451
460
|
SmsQueueSubscriberDatabase,
|
|
461
|
+
TwilioSmsQueuePublisherDatabase,
|
|
462
|
+
TwilioSmsQueueSubscriberDatabase,
|
|
452
463
|
OTPQueuePublisher,
|
|
453
464
|
OTPQueueSubscriber,
|
|
454
465
|
OTPQueuePublisherDatabase,
|
|
@@ -567,6 +578,7 @@ import { MailFactory } from './factories/mail.factory';
|
|
|
567
578
|
ElasticEmailService,
|
|
568
579
|
Msg91SMSService,
|
|
569
580
|
Msg91OTPService,
|
|
581
|
+
TwilioSMSService,
|
|
570
582
|
Msg91WhatsappService,
|
|
571
583
|
TinyUrlService,
|
|
572
584
|
PdfService,
|
|
@@ -592,6 +604,7 @@ import { MailFactory } from './factories/mail.factory';
|
|
|
592
604
|
ConfigModule,
|
|
593
605
|
PublisherFactory,
|
|
594
606
|
MailFactory,
|
|
607
|
+
PollerService
|
|
595
608
|
],
|
|
596
609
|
})
|
|
597
610
|
export class SolidCoreModule { }
|