@vritti/api-sdk 0.2.9 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cache.cjs +252 -0
- package/dist/cache.cjs.map +1 -0
- package/dist/cache.d.cts +25 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +214 -0
- package/dist/cache.js.map +1 -0
- package/dist/cache.service-_SuJIcNA.d.cts +20 -0
- package/dist/cache.service-_SuJIcNA.d.ts +20 -0
- package/dist/catalog-resolver.cjs +364 -0
- package/dist/catalog-resolver.cjs.map +1 -0
- package/dist/catalog-resolver.d.cts +109 -0
- package/dist/catalog-resolver.d.ts +109 -0
- package/dist/catalog-resolver.js +330 -0
- package/dist/catalog-resolver.js.map +1 -0
- package/dist/email.cjs +691 -0
- package/dist/email.cjs.map +1 -0
- package/dist/email.d.cts +35 -0
- package/dist/email.d.ts +35 -0
- package/dist/email.js +665 -0
- package/dist/email.js.map +1 -0
- package/dist/fastify-augmentation-CQPBNS5a.d.cts +105 -0
- package/dist/fastify-augmentation-CQPBNS5a.d.ts +105 -0
- package/dist/icons.cjs +13803 -0
- package/dist/icons.cjs.map +1 -0
- package/dist/icons.d.cts +8 -0
- package/dist/icons.d.ts +8 -0
- package/dist/icons.js +13778 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.cjs +750 -1368
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +103 -181
- package/dist/index.d.ts +103 -181
- package/dist/index.js +609 -1234
- package/dist/index.js.map +1 -1
- package/dist/license.cjs +104 -0
- package/dist/license.cjs.map +1 -0
- package/dist/license.d.cts +32 -0
- package/dist/license.d.ts +32 -0
- package/dist/license.js +75 -0
- package/dist/license.js.map +1 -0
- package/dist/money.cjs +93 -6
- package/dist/money.cjs.map +1 -1
- package/dist/money.d.cts +13 -1
- package/dist/money.d.ts +13 -1
- package/dist/money.js +115 -3
- package/dist/money.js.map +1 -1
- package/dist/nats.cjs +196 -179
- package/dist/nats.cjs.map +1 -1
- package/dist/nats.d.cts +13 -11
- package/dist/nats.d.ts +13 -11
- package/dist/nats.js +172 -155
- package/dist/nats.js.map +1 -1
- package/dist/types-DQRwj5rr.d.cts +113 -0
- package/dist/types-DQRwj5rr.d.ts +113 -0
- package/package.json +55 -1
package/dist/email.cjs
ADDED
|
@@ -0,0 +1,691 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/email/index.ts
|
|
22
|
+
var email_exports = {};
|
|
23
|
+
__export(email_exports, {
|
|
24
|
+
EmailModule: () => EmailModule,
|
|
25
|
+
EmailService: () => EmailService
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(email_exports);
|
|
28
|
+
|
|
29
|
+
// src/email/email.module.ts
|
|
30
|
+
var import_common2 = require("@nestjs/common");
|
|
31
|
+
var import_config2 = require("@nestjs/config");
|
|
32
|
+
|
|
33
|
+
// src/email/email.service.ts
|
|
34
|
+
var import_brevo = require("@getbrevo/brevo");
|
|
35
|
+
var import_common = require("@nestjs/common");
|
|
36
|
+
var import_config = require("@nestjs/config");
|
|
37
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
38
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
39
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
40
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
41
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
42
|
+
}
|
|
43
|
+
__name(_ts_decorate, "_ts_decorate");
|
|
44
|
+
function _ts_metadata(k, v) {
|
|
45
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
46
|
+
}
|
|
47
|
+
__name(_ts_metadata, "_ts_metadata");
|
|
48
|
+
var EmailService = class _EmailService {
|
|
49
|
+
static {
|
|
50
|
+
__name(this, "EmailService");
|
|
51
|
+
}
|
|
52
|
+
configService;
|
|
53
|
+
logger = new import_common.Logger(_EmailService.name);
|
|
54
|
+
brevoClient;
|
|
55
|
+
senderEmail;
|
|
56
|
+
senderName;
|
|
57
|
+
constructor(configService) {
|
|
58
|
+
this.configService = configService;
|
|
59
|
+
const apiKey = this.configService.get("BREVO_API_KEY");
|
|
60
|
+
if (!apiKey) {
|
|
61
|
+
this.logger.error("BREVO_API_KEY is not configured. Email sending will fail.");
|
|
62
|
+
throw new Error("Email service configuration error: Missing BREVO_API_KEY");
|
|
63
|
+
}
|
|
64
|
+
this.brevoClient = new import_brevo.BrevoClient({
|
|
65
|
+
apiKey,
|
|
66
|
+
maxRetries: 3
|
|
67
|
+
});
|
|
68
|
+
const senderEmail = this.configService.get("SENDER_EMAIL");
|
|
69
|
+
const senderName = this.configService.get("SENDER_NAME");
|
|
70
|
+
if (!senderEmail || !senderName) {
|
|
71
|
+
this.logger.error("Sender email or name is not configured.");
|
|
72
|
+
throw new Error("Email service configuration error: Missing SENDER_EMAIL or SENDER_NAME");
|
|
73
|
+
}
|
|
74
|
+
this.senderEmail = senderEmail;
|
|
75
|
+
this.senderName = senderName;
|
|
76
|
+
this.logger.log("Brevo email service initialized successfully");
|
|
77
|
+
}
|
|
78
|
+
// Sends an email verification OTP to the given recipient
|
|
79
|
+
async sendVerificationEmail(email, otp, expiresAt, displayName) {
|
|
80
|
+
const name = displayName || "there";
|
|
81
|
+
const expiryMinutes = Math.ceil((expiresAt.getTime() - Date.now()) / 6e4);
|
|
82
|
+
const subject = "Verify Your Email - Vritti AI Cloud";
|
|
83
|
+
const htmlContent = `
|
|
84
|
+
<!DOCTYPE html>
|
|
85
|
+
<html>
|
|
86
|
+
<head>
|
|
87
|
+
<meta charset="UTF-8">
|
|
88
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
89
|
+
</head>
|
|
90
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
91
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
92
|
+
<tr>
|
|
93
|
+
<td style="padding: 40px 20px;">
|
|
94
|
+
<table role="presentation" style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
95
|
+
<!-- Header -->
|
|
96
|
+
<tr>
|
|
97
|
+
<td style="padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;">
|
|
98
|
+
<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">Email Verification</h1>
|
|
99
|
+
</td>
|
|
100
|
+
</tr>
|
|
101
|
+
|
|
102
|
+
<!-- Content -->
|
|
103
|
+
<tr>
|
|
104
|
+
<td style="padding: 40px;">
|
|
105
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
106
|
+
Hello <strong>${name}</strong>,
|
|
107
|
+
</p>
|
|
108
|
+
<p style="margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
109
|
+
Thank you for signing up with Vritti AI Cloud. Please use the following verification code to complete your registration:
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
<!-- OTP Box -->
|
|
113
|
+
<div style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 30px; border-radius: 8px; text-align: center; margin: 30px 0;">
|
|
114
|
+
<div style="color: #ffffff; font-size: 36px; font-weight: bold; letter-spacing: 10px; font-family: 'Courier New', monospace;">
|
|
115
|
+
${otp}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<p style="margin: 30px 0 20px; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
120
|
+
<strong>Important:</strong> This code will expire in <strong>${expiryMinutes} minute${expiryMinutes === 1 ? "" : "s"}</strong>.
|
|
121
|
+
</p>
|
|
122
|
+
<p style="margin: 0; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
123
|
+
If you didn't request this verification, please ignore this email.
|
|
124
|
+
</p>
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
127
|
+
|
|
128
|
+
<!-- Footer -->
|
|
129
|
+
<tr>
|
|
130
|
+
<td style="padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;">
|
|
131
|
+
<p style="margin: 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
132
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
133
|
+
</p>
|
|
134
|
+
<p style="margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
135
|
+
This is an automated message, please do not reply.
|
|
136
|
+
</p>
|
|
137
|
+
</td>
|
|
138
|
+
</tr>
|
|
139
|
+
</table>
|
|
140
|
+
</td>
|
|
141
|
+
</tr>
|
|
142
|
+
</table>
|
|
143
|
+
</body>
|
|
144
|
+
</html>
|
|
145
|
+
`;
|
|
146
|
+
const textContent = `
|
|
147
|
+
Hello ${name},
|
|
148
|
+
|
|
149
|
+
Thank you for signing up with Vritti AI Cloud. Please use the following verification code to complete your registration:
|
|
150
|
+
|
|
151
|
+
Verification Code: ${otp}
|
|
152
|
+
|
|
153
|
+
This code will expire in ${expiryMinutes} minute${expiryMinutes === 1 ? "" : "s"}.
|
|
154
|
+
|
|
155
|
+
If you didn't request this verification, please ignore this email.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
159
|
+
This is an automated message, please do not reply.
|
|
160
|
+
`.trim();
|
|
161
|
+
await this.sendEmail({
|
|
162
|
+
to: [
|
|
163
|
+
{
|
|
164
|
+
email,
|
|
165
|
+
name
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
subject,
|
|
169
|
+
htmlContent,
|
|
170
|
+
textContent
|
|
171
|
+
});
|
|
172
|
+
this.logger.log(`Verification email sent to ${email}`);
|
|
173
|
+
}
|
|
174
|
+
// Sends a password reset OTP to the given recipient
|
|
175
|
+
async sendPasswordResetEmail(email, otp, expiresAt, displayName) {
|
|
176
|
+
const name = displayName || "there";
|
|
177
|
+
const expiryMinutes = Math.ceil((expiresAt.getTime() - Date.now()) / 6e4);
|
|
178
|
+
const subject = "Reset Your Password - Vritti AI Cloud";
|
|
179
|
+
const htmlContent = `
|
|
180
|
+
<!DOCTYPE html>
|
|
181
|
+
<html>
|
|
182
|
+
<head>
|
|
183
|
+
<meta charset="UTF-8">
|
|
184
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
185
|
+
</head>
|
|
186
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
187
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
188
|
+
<tr>
|
|
189
|
+
<td style="padding: 40px 20px;">
|
|
190
|
+
<table role="presentation" style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
191
|
+
<!-- Header -->
|
|
192
|
+
<tr>
|
|
193
|
+
<td style="padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;">
|
|
194
|
+
<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">Password Reset</h1>
|
|
195
|
+
</td>
|
|
196
|
+
</tr>
|
|
197
|
+
|
|
198
|
+
<!-- Content -->
|
|
199
|
+
<tr>
|
|
200
|
+
<td style="padding: 40px;">
|
|
201
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
202
|
+
Hello <strong>${name}</strong>,
|
|
203
|
+
</p>
|
|
204
|
+
<p style="margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
205
|
+
We received a request to reset your password. Use the following code to complete the process:
|
|
206
|
+
</p>
|
|
207
|
+
|
|
208
|
+
<!-- OTP Box -->
|
|
209
|
+
<div style="background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); padding: 30px; border-radius: 8px; text-align: center; margin: 30px 0;">
|
|
210
|
+
<div style="color: #ffffff; font-size: 36px; font-weight: bold; letter-spacing: 10px; font-family: 'Courier New', monospace;">
|
|
211
|
+
${otp}
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
<p style="margin: 30px 0 20px; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
216
|
+
<strong>Important:</strong> This code will expire in <strong>${expiryMinutes} minute${expiryMinutes === 1 ? "" : "s"}</strong>.
|
|
217
|
+
</p>
|
|
218
|
+
<p style="margin: 0 0 20px; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
219
|
+
If you didn't request a password reset, please ignore this email and your password will remain unchanged.
|
|
220
|
+
</p>
|
|
221
|
+
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin-top: 20px; border-radius: 4px;">
|
|
222
|
+
<p style="margin: 0; color: #856404; font-size: 13px; line-height: 1.5;">
|
|
223
|
+
<strong>Security Tip:</strong> Never share this code with anyone. Vritti will never ask for your verification code.
|
|
224
|
+
</p>
|
|
225
|
+
</div>
|
|
226
|
+
</td>
|
|
227
|
+
</tr>
|
|
228
|
+
|
|
229
|
+
<!-- Footer -->
|
|
230
|
+
<tr>
|
|
231
|
+
<td style="padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;">
|
|
232
|
+
<p style="margin: 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
233
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
234
|
+
</p>
|
|
235
|
+
<p style="margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
236
|
+
This is an automated message, please do not reply.
|
|
237
|
+
</p>
|
|
238
|
+
</td>
|
|
239
|
+
</tr>
|
|
240
|
+
</table>
|
|
241
|
+
</td>
|
|
242
|
+
</tr>
|
|
243
|
+
</table>
|
|
244
|
+
</body>
|
|
245
|
+
</html>
|
|
246
|
+
`;
|
|
247
|
+
const textContent = `
|
|
248
|
+
Hello ${name},
|
|
249
|
+
|
|
250
|
+
We received a request to reset your password. Use the following code to complete the process:
|
|
251
|
+
|
|
252
|
+
Reset Code: ${otp}
|
|
253
|
+
|
|
254
|
+
This code will expire in ${expiryMinutes} minute${expiryMinutes === 1 ? "" : "s"}.
|
|
255
|
+
|
|
256
|
+
If you didn't request a password reset, please ignore this email and your password will remain unchanged.
|
|
257
|
+
|
|
258
|
+
SECURITY TIP: Never share this code with anyone. Vritti will never ask for your verification code.
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
262
|
+
This is an automated message, please do not reply.
|
|
263
|
+
`.trim();
|
|
264
|
+
await this.sendEmail({
|
|
265
|
+
to: [
|
|
266
|
+
{
|
|
267
|
+
email,
|
|
268
|
+
name
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
subject,
|
|
272
|
+
htmlContent,
|
|
273
|
+
textContent
|
|
274
|
+
});
|
|
275
|
+
this.logger.log(`Password reset email sent to ${email}`);
|
|
276
|
+
}
|
|
277
|
+
// Sends an email change notification to the old address with a revert link
|
|
278
|
+
async sendEmailChangeNotification(oldEmail, newEmail, revertToken, revertExpiresAt, displayName) {
|
|
279
|
+
const name = displayName || "there";
|
|
280
|
+
const subject = "Your Email Address Has Been Changed - Vritti AI Cloud";
|
|
281
|
+
const hoursUntilExpiry = Math.floor((revertExpiresAt.getTime() - Date.now()) / (1e3 * 60 * 60));
|
|
282
|
+
const revertLink = `https://local.vrittiai.com:3012/settings/profile/revert-email?token=${revertToken}`;
|
|
283
|
+
const htmlContent = `
|
|
284
|
+
<!DOCTYPE html>
|
|
285
|
+
<html>
|
|
286
|
+
<head>
|
|
287
|
+
<meta charset="UTF-8">
|
|
288
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
289
|
+
</head>
|
|
290
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
291
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
292
|
+
<tr>
|
|
293
|
+
<td style="padding: 40px 20px;">
|
|
294
|
+
<table role="presentation" style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
295
|
+
<!-- Header -->
|
|
296
|
+
<tr>
|
|
297
|
+
<td style="padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;">
|
|
298
|
+
<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">Email Address Changed</h1>
|
|
299
|
+
</td>
|
|
300
|
+
</tr>
|
|
301
|
+
|
|
302
|
+
<!-- Content -->
|
|
303
|
+
<tr>
|
|
304
|
+
<td style="padding: 40px;">
|
|
305
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
306
|
+
Hello <strong>${name}</strong>,
|
|
307
|
+
</p>
|
|
308
|
+
<p style="margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
309
|
+
We're writing to inform you that your Vritti AI Cloud email address has been successfully changed.
|
|
310
|
+
</p>
|
|
311
|
+
|
|
312
|
+
<div style="background-color: #f8f9fa; padding: 20px; border-radius: 8px; margin: 30px 0;">
|
|
313
|
+
<p style="margin: 0 0 10px; color: #666666; font-size: 14px;">
|
|
314
|
+
<strong>Previous Email:</strong>
|
|
315
|
+
</p>
|
|
316
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; font-family: monospace;">
|
|
317
|
+
${oldEmail}
|
|
318
|
+
</p>
|
|
319
|
+
<p style="margin: 0 0 10px; color: #666666; font-size: 14px;">
|
|
320
|
+
<strong>New Email:</strong>
|
|
321
|
+
</p>
|
|
322
|
+
<p style="margin: 0; color: #333333; font-size: 16px; font-family: monospace;">
|
|
323
|
+
${newEmail}
|
|
324
|
+
</p>
|
|
325
|
+
</div>
|
|
326
|
+
|
|
327
|
+
<div style="background-color: #fff3cd; border-left: 4px solid #ffc107; padding: 20px; margin: 30px 0; border-radius: 4px;">
|
|
328
|
+
<p style="margin: 0 0 15px; color: #856404; font-size: 14px; line-height: 1.6;">
|
|
329
|
+
<strong>Didn't make this change?</strong>
|
|
330
|
+
</p>
|
|
331
|
+
<p style="margin: 0 0 20px; color: #856404; font-size: 14px; line-height: 1.6;">
|
|
332
|
+
If you did not authorize this change, you can revert it within the next <strong>${hoursUntilExpiry} hours</strong> by clicking the button below:
|
|
333
|
+
</p>
|
|
334
|
+
<div style="text-align: center;">
|
|
335
|
+
<a href="${revertLink}" style="display: inline-block; padding: 12px 30px; background-color: #dc3545; color: #ffffff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 14px;">
|
|
336
|
+
Revert Email Change
|
|
337
|
+
</a>
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
341
|
+
<p style="margin: 30px 0 0; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
342
|
+
If you made this change, you can safely ignore this email.
|
|
343
|
+
</p>
|
|
344
|
+
</td>
|
|
345
|
+
</tr>
|
|
346
|
+
|
|
347
|
+
<!-- Footer -->
|
|
348
|
+
<tr>
|
|
349
|
+
<td style="padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;">
|
|
350
|
+
<p style="margin: 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
351
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
352
|
+
</p>
|
|
353
|
+
<p style="margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
354
|
+
This is an automated message, please do not reply.
|
|
355
|
+
</p>
|
|
356
|
+
</td>
|
|
357
|
+
</tr>
|
|
358
|
+
</table>
|
|
359
|
+
</td>
|
|
360
|
+
</tr>
|
|
361
|
+
</table>
|
|
362
|
+
</body>
|
|
363
|
+
</html>
|
|
364
|
+
`;
|
|
365
|
+
const textContent = `
|
|
366
|
+
Hello ${name},
|
|
367
|
+
|
|
368
|
+
We're writing to inform you that your Vritti AI Cloud email address has been successfully changed.
|
|
369
|
+
|
|
370
|
+
Previous Email: ${oldEmail}
|
|
371
|
+
New Email: ${newEmail}
|
|
372
|
+
|
|
373
|
+
DIDN'T MAKE THIS CHANGE?
|
|
374
|
+
|
|
375
|
+
If you did not authorize this change, you can revert it within the next ${hoursUntilExpiry} hours by visiting:
|
|
376
|
+
${revertLink}
|
|
377
|
+
|
|
378
|
+
If you made this change, you can safely ignore this email.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
382
|
+
This is an automated message, please do not reply.
|
|
383
|
+
`.trim();
|
|
384
|
+
await this.sendEmail({
|
|
385
|
+
to: [
|
|
386
|
+
{
|
|
387
|
+
email: oldEmail,
|
|
388
|
+
name
|
|
389
|
+
}
|
|
390
|
+
],
|
|
391
|
+
subject,
|
|
392
|
+
htmlContent,
|
|
393
|
+
textContent
|
|
394
|
+
});
|
|
395
|
+
this.logger.log(`Email change notification sent to ${oldEmail}`);
|
|
396
|
+
}
|
|
397
|
+
// Sends a confirmation to the restored email address after a revert
|
|
398
|
+
async sendEmailRevertConfirmation(email, displayName) {
|
|
399
|
+
const name = displayName || "there";
|
|
400
|
+
const subject = "Email Address Change Reverted - Vritti AI Cloud";
|
|
401
|
+
const htmlContent = `
|
|
402
|
+
<!DOCTYPE html>
|
|
403
|
+
<html>
|
|
404
|
+
<head>
|
|
405
|
+
<meta charset="UTF-8">
|
|
406
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
407
|
+
</head>
|
|
408
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
409
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
410
|
+
<tr>
|
|
411
|
+
<td style="padding: 40px 20px;">
|
|
412
|
+
<table role="presentation" style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
413
|
+
<!-- Header -->
|
|
414
|
+
<tr>
|
|
415
|
+
<td style="padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;">
|
|
416
|
+
<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">Email Change Reverted</h1>
|
|
417
|
+
</td>
|
|
418
|
+
</tr>
|
|
419
|
+
|
|
420
|
+
<!-- Content -->
|
|
421
|
+
<tr>
|
|
422
|
+
<td style="padding: 40px;">
|
|
423
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
424
|
+
Hello <strong>${name}</strong>,
|
|
425
|
+
</p>
|
|
426
|
+
<p style="margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
427
|
+
Your recent email address change has been successfully reverted. Your email is now:
|
|
428
|
+
</p>
|
|
429
|
+
|
|
430
|
+
<div style="background-color: #d4edda; padding: 20px; border-radius: 8px; margin: 30px 0; text-align: center;">
|
|
431
|
+
<p style="margin: 0; color: #155724; font-size: 18px; font-weight: 600; font-family: monospace;">
|
|
432
|
+
${email}
|
|
433
|
+
</p>
|
|
434
|
+
</div>
|
|
435
|
+
|
|
436
|
+
<p style="margin: 30px 0 20px; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
437
|
+
If you did not request this revert, please contact our support team immediately.
|
|
438
|
+
</p>
|
|
439
|
+
</td>
|
|
440
|
+
</tr>
|
|
441
|
+
|
|
442
|
+
<!-- Footer -->
|
|
443
|
+
<tr>
|
|
444
|
+
<td style="padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;">
|
|
445
|
+
<p style="margin: 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
446
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
447
|
+
</p>
|
|
448
|
+
<p style="margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
449
|
+
This is an automated message, please do not reply.
|
|
450
|
+
</p>
|
|
451
|
+
</td>
|
|
452
|
+
</tr>
|
|
453
|
+
</table>
|
|
454
|
+
</td>
|
|
455
|
+
</tr>
|
|
456
|
+
</table>
|
|
457
|
+
</body>
|
|
458
|
+
</html>
|
|
459
|
+
`;
|
|
460
|
+
const textContent = `
|
|
461
|
+
Hello ${name},
|
|
462
|
+
|
|
463
|
+
Your recent email address change has been successfully reverted. Your email is now:
|
|
464
|
+
|
|
465
|
+
${email}
|
|
466
|
+
|
|
467
|
+
If you did not request this revert, please contact our support team immediately.
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
471
|
+
This is an automated message, please do not reply.
|
|
472
|
+
`.trim();
|
|
473
|
+
await this.sendEmail({
|
|
474
|
+
to: [
|
|
475
|
+
{
|
|
476
|
+
email,
|
|
477
|
+
name
|
|
478
|
+
}
|
|
479
|
+
],
|
|
480
|
+
subject,
|
|
481
|
+
htmlContent,
|
|
482
|
+
textContent
|
|
483
|
+
});
|
|
484
|
+
this.logger.log(`Email revert confirmation sent to ${email}`);
|
|
485
|
+
}
|
|
486
|
+
// Sends an invite email to a new portal user with their set-password link
|
|
487
|
+
async sendInviteEmail(params) {
|
|
488
|
+
const { to, name, inviteUrl } = params;
|
|
489
|
+
const subject = "You have been invited to Vritti AI";
|
|
490
|
+
const htmlContent = `
|
|
491
|
+
<!DOCTYPE html>
|
|
492
|
+
<html>
|
|
493
|
+
<head>
|
|
494
|
+
<meta charset="UTF-8">
|
|
495
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
496
|
+
</head>
|
|
497
|
+
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
|
|
498
|
+
<table role="presentation" style="width: 100%; border-collapse: collapse;">
|
|
499
|
+
<tr>
|
|
500
|
+
<td style="padding: 40px 20px;">
|
|
501
|
+
<table role="presentation" style="max-width: 600px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
|
|
502
|
+
<!-- Header -->
|
|
503
|
+
<tr>
|
|
504
|
+
<td style="padding: 40px 40px 20px; text-align: center; border-bottom: 1px solid #e0e0e0;">
|
|
505
|
+
<h1 style="margin: 0; color: #1a1a1a; font-size: 24px; font-weight: 600;">You're Invited</h1>
|
|
506
|
+
</td>
|
|
507
|
+
</tr>
|
|
508
|
+
|
|
509
|
+
<!-- Content -->
|
|
510
|
+
<tr>
|
|
511
|
+
<td style="padding: 40px;">
|
|
512
|
+
<p style="margin: 0 0 20px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
513
|
+
Hello <strong>${name}</strong>,
|
|
514
|
+
</p>
|
|
515
|
+
<p style="margin: 0 0 30px; color: #333333; font-size: 16px; line-height: 1.6;">
|
|
516
|
+
You have been invited to join Vritti AI. Click the button below to set your password and get started.
|
|
517
|
+
</p>
|
|
518
|
+
|
|
519
|
+
<div style="text-align: center; margin: 30px 0;">
|
|
520
|
+
<a href="${inviteUrl}" style="display: inline-block; padding: 14px 32px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #ffffff; text-decoration: none; border-radius: 6px; font-weight: 600; font-size: 16px;">
|
|
521
|
+
Set Your Password
|
|
522
|
+
</a>
|
|
523
|
+
</div>
|
|
524
|
+
|
|
525
|
+
<p style="margin: 30px 0 0; color: #666666; font-size: 14px; line-height: 1.6;">
|
|
526
|
+
If you did not expect this invitation, you can safely ignore this email.
|
|
527
|
+
</p>
|
|
528
|
+
</td>
|
|
529
|
+
</tr>
|
|
530
|
+
|
|
531
|
+
<!-- Footer -->
|
|
532
|
+
<tr>
|
|
533
|
+
<td style="padding: 30px 40px; border-top: 1px solid #e0e0e0; text-align: center;">
|
|
534
|
+
<p style="margin: 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
535
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
536
|
+
</p>
|
|
537
|
+
<p style="margin: 8px 0 0; color: #999999; font-size: 12px; line-height: 1.5;">
|
|
538
|
+
This is an automated message, please do not reply.
|
|
539
|
+
</p>
|
|
540
|
+
</td>
|
|
541
|
+
</tr>
|
|
542
|
+
</table>
|
|
543
|
+
</td>
|
|
544
|
+
</tr>
|
|
545
|
+
</table>
|
|
546
|
+
</body>
|
|
547
|
+
</html>
|
|
548
|
+
`;
|
|
549
|
+
const textContent = `
|
|
550
|
+
Hello ${name},
|
|
551
|
+
|
|
552
|
+
You have been invited to join Vritti AI. Visit the link below to set your password and get started:
|
|
553
|
+
|
|
554
|
+
${inviteUrl}
|
|
555
|
+
|
|
556
|
+
If you did not expect this invitation, you can safely ignore this email.
|
|
557
|
+
|
|
558
|
+
---
|
|
559
|
+
Vritti AI Cloud - Cloud Management Platform
|
|
560
|
+
This is an automated message, please do not reply.
|
|
561
|
+
`.trim();
|
|
562
|
+
await this.sendEmail({
|
|
563
|
+
to: [
|
|
564
|
+
{
|
|
565
|
+
email: to,
|
|
566
|
+
name
|
|
567
|
+
}
|
|
568
|
+
],
|
|
569
|
+
subject,
|
|
570
|
+
htmlContent,
|
|
571
|
+
textContent
|
|
572
|
+
});
|
|
573
|
+
this.logger.log(`Invite email sent to ${to}`);
|
|
574
|
+
}
|
|
575
|
+
// Sends a transactional email with custom subject, HTML, and text content
|
|
576
|
+
async sendTransactionalEmail(params) {
|
|
577
|
+
await this.sendEmail({
|
|
578
|
+
to: [
|
|
579
|
+
params.to
|
|
580
|
+
],
|
|
581
|
+
subject: params.subject,
|
|
582
|
+
htmlContent: params.htmlContent,
|
|
583
|
+
textContent: params.textContent
|
|
584
|
+
});
|
|
585
|
+
this.logger.log(`Transactional email sent to ${params.to.email}`);
|
|
586
|
+
}
|
|
587
|
+
// Verifies Brevo API connectivity — a 400 response means the API is reachable
|
|
588
|
+
async verifyConnection() {
|
|
589
|
+
try {
|
|
590
|
+
await this.brevoClient.transactionalEmails.sendTransacEmail({
|
|
591
|
+
sender: {
|
|
592
|
+
email: this.senderEmail,
|
|
593
|
+
name: this.senderName
|
|
594
|
+
},
|
|
595
|
+
to: [
|
|
596
|
+
{
|
|
597
|
+
email: this.senderEmail
|
|
598
|
+
}
|
|
599
|
+
],
|
|
600
|
+
subject: "Connection Test",
|
|
601
|
+
htmlContent: "<p>Test</p>"
|
|
602
|
+
});
|
|
603
|
+
return true;
|
|
604
|
+
} catch (err) {
|
|
605
|
+
if (err instanceof import_brevo.BrevoError && err.statusCode === 400) {
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
this.logger.error("Brevo connection verification failed:", err);
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
// Sends a transactional email via Brevo — retries handled internally by BrevoClient
|
|
613
|
+
async sendEmail(emailData) {
|
|
614
|
+
try {
|
|
615
|
+
const result = await this.brevoClient.transactionalEmails.sendTransacEmail({
|
|
616
|
+
sender: {
|
|
617
|
+
email: this.senderEmail,
|
|
618
|
+
name: this.senderName
|
|
619
|
+
},
|
|
620
|
+
to: emailData.to,
|
|
621
|
+
subject: emailData.subject,
|
|
622
|
+
htmlContent: emailData.htmlContent,
|
|
623
|
+
textContent: emailData.textContent
|
|
624
|
+
});
|
|
625
|
+
this.logger.debug(`Email sent successfully. Message ID: ${result.messageId}`);
|
|
626
|
+
} catch (err) {
|
|
627
|
+
if (err instanceof import_brevo.BrevoTimeoutError) {
|
|
628
|
+
this.logger.error("Brevo request timed out after retries.");
|
|
629
|
+
throw new Error("Email sending failed: timeout");
|
|
630
|
+
}
|
|
631
|
+
if (err instanceof import_brevo.BrevoError) {
|
|
632
|
+
if (err.statusCode === 429) {
|
|
633
|
+
this.logger.error("Brevo rate limit exceeded after retries.");
|
|
634
|
+
throw new Error("Email sending failed: rate limit exceeded");
|
|
635
|
+
}
|
|
636
|
+
if (err.statusCode === 401) {
|
|
637
|
+
this.logger.error("Brevo authentication failed. Check your API key.");
|
|
638
|
+
throw new Error("Email service authentication failed");
|
|
639
|
+
}
|
|
640
|
+
if (err.statusCode === 400) {
|
|
641
|
+
this.logger.error("Bad request to Brevo API:", err.message);
|
|
642
|
+
throw new Error(`Invalid email parameters: ${err.message}`);
|
|
643
|
+
}
|
|
644
|
+
this.logger.error(`Brevo API error ${err.statusCode}:`, err.message);
|
|
645
|
+
throw new Error(`Email sending failed: ${err.message}`);
|
|
646
|
+
}
|
|
647
|
+
throw err;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
EmailService = _ts_decorate([
|
|
652
|
+
(0, import_common.Injectable)(),
|
|
653
|
+
_ts_metadata("design:type", Function),
|
|
654
|
+
_ts_metadata("design:paramtypes", [
|
|
655
|
+
typeof import_config.ConfigService === "undefined" ? Object : import_config.ConfigService
|
|
656
|
+
])
|
|
657
|
+
], EmailService);
|
|
658
|
+
|
|
659
|
+
// src/email/email.module.ts
|
|
660
|
+
function _ts_decorate2(decorators, target, key, desc) {
|
|
661
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
662
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
663
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
664
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
665
|
+
}
|
|
666
|
+
__name(_ts_decorate2, "_ts_decorate");
|
|
667
|
+
var EmailModule = class {
|
|
668
|
+
static {
|
|
669
|
+
__name(this, "EmailModule");
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
EmailModule = _ts_decorate2([
|
|
673
|
+
(0, import_common2.Global)(),
|
|
674
|
+
(0, import_common2.Module)({
|
|
675
|
+
imports: [
|
|
676
|
+
import_config2.ConfigModule
|
|
677
|
+
],
|
|
678
|
+
providers: [
|
|
679
|
+
EmailService
|
|
680
|
+
],
|
|
681
|
+
exports: [
|
|
682
|
+
EmailService
|
|
683
|
+
]
|
|
684
|
+
})
|
|
685
|
+
], EmailModule);
|
|
686
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
687
|
+
0 && (module.exports = {
|
|
688
|
+
EmailModule,
|
|
689
|
+
EmailService
|
|
690
|
+
});
|
|
691
|
+
//# sourceMappingURL=email.cjs.map
|