@vritti/api-sdk 0.3.15 → 0.4.0
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/auth.cjs +178 -152
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +25 -43
- package/dist/auth.d.ts +25 -43
- package/dist/auth.js +101 -72
- package/dist/auth.js.map +1 -1
- package/dist/data-table.cjs +4 -5
- package/dist/data-table.cjs.map +1 -1
- package/dist/data-table.js +4 -5
- package/dist/data-table.js.map +1 -1
- package/dist/email.cjs +397 -422
- package/dist/email.cjs.map +1 -1
- package/dist/email.d.cts +15 -0
- package/dist/email.d.ts +15 -0
- package/dist/email.js +387 -422
- package/dist/email.js.map +1 -1
- package/dist/index.d.cts +15 -9
- package/dist/index.d.ts +15 -9
- package/dist/nats.cjs +30 -36
- package/dist/nats.cjs.map +1 -1
- package/dist/nats.d.cts +20 -9
- package/dist/nats.d.ts +20 -9
- package/dist/nats.js +30 -36
- package/dist/nats.js.map +1 -1
- package/dist/root.cjs +15 -4
- package/dist/root.cjs.map +1 -1
- package/dist/root.js +15 -4
- package/dist/root.js.map +1 -1
- package/package.json +1 -1
package/dist/email.cjs
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
7
9
|
var __export = (target, all) => {
|
|
@@ -16,6 +18,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
18
|
}
|
|
17
19
|
return to;
|
|
18
20
|
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
19
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
30
|
|
|
21
31
|
// src/email/index.ts
|
|
@@ -34,6 +44,11 @@ var import_config2 = require("@nestjs/config");
|
|
|
34
44
|
var import_brevo = require("@getbrevo/brevo");
|
|
35
45
|
var import_common = require("@nestjs/common");
|
|
36
46
|
var import_config = require("@nestjs/config");
|
|
47
|
+
|
|
48
|
+
// src/pluralize.ts
|
|
49
|
+
var import_pluralize_esm = __toESM(require("pluralize-esm"), 1);
|
|
50
|
+
|
|
51
|
+
// src/email/email.service.ts
|
|
37
52
|
function _ts_decorate(decorators, target, key, desc) {
|
|
38
53
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
39
54
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -45,6 +60,34 @@ function _ts_metadata(k, v) {
|
|
|
45
60
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
46
61
|
}
|
|
47
62
|
__name(_ts_metadata, "_ts_metadata");
|
|
63
|
+
var EMAIL_SANS = "'IBM Plex Sans',Helvetica,Arial,sans-serif";
|
|
64
|
+
var EMAIL_MONO = "'JetBrains Mono','Courier New',Courier,monospace";
|
|
65
|
+
var VRITTI_CLOUD_LOGO_LIGHT = `<svg width="170" height="28" viewBox="0 0 407 67" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M78.9948 52L66.7748 37.44H72.2868L84.5588 51.948V52H78.9948ZM47.3788 52V14.612H78.5788C79.7228 14.612 80.7628 14.9067 81.6988 15.496C82.6695 16.0507 83.4321 16.796 83.9868 17.732C84.5415 18.668 84.8188 19.708 84.8188 20.852V31.824C84.8188 32.968 84.5415 34.008 83.9868 34.944C83.4321 35.88 82.6695 36.6427 81.6988 37.232C80.7628 37.7867 79.7228 38.064 78.5788 38.064H51.5908V52H47.3788ZM53.6188 33.8H78.5788C79.1335 33.8 79.6015 33.6093 79.9828 33.228C80.3988 32.8467 80.6068 32.3787 80.6068 31.824V20.852C80.6068 20.2973 80.3988 19.8293 79.9828 19.448C79.6015 19.032 79.1335 18.824 78.5788 18.824H53.6188C53.0641 18.824 52.5788 19.032 52.1628 19.448C51.7815 19.8293 51.5908 20.2973 51.5908 20.852V31.824C51.5908 32.3787 51.7815 32.8467 52.1628 33.228C52.5788 33.6093 53.0641 33.8 53.6188 33.8ZM90.809 52V14.56H95.073V52H90.809ZM116.178 52V18.772H99.538V14.56H136.978V18.772H120.39V52H116.178ZM155.635 52V18.772H138.995V14.56H176.435V18.772H159.847V52H155.635ZM180.844 52V14.56H185.108V52H180.844Z" fill="#0066CC"/><path d="M218.027 50.336C215.479 49.244 213.295 47.736 211.371 45.864C209.499 43.94 207.991 41.756 206.899 39.208C205.807 36.66 205.235 34.008 205.235 31.2C205.235 28.34 205.807 25.636 206.899 23.088C207.991 20.592 209.499 18.408 211.371 16.536C213.295 14.612 215.479 13.156 218.027 12.064C220.523 10.972 223.227 10.4 226.087 10.4C228.947 10.4 231.651 10.92 234.147 12.012C236.643 13.104 238.879 14.612 240.803 16.536L235.863 21.372C233.107 18.668 229.831 17.368 226.087 17.368C224.163 17.368 222.343 17.732 220.679 18.408C218.963 19.136 217.507 20.124 216.259 21.424C215.011 22.672 214.023 24.128 213.295 25.792C212.567 27.508 212.203 29.276 212.203 31.2C212.203 33.124 212.567 34.892 213.295 36.608C214.023 38.272 215.011 39.728 216.311 40.976C217.559 42.276 219.015 43.264 220.679 43.992C222.395 44.668 224.163 45.032 226.087 45.032L234.147 50.388C231.651 51.48 228.947 52 226.087 52C223.227 52 220.523 51.428 218.027 50.336ZM252.177 45.032H272.977V52H245.261L252.177 45.032ZM245.261 10.4H252.177V11.544V35.62L245.261 42.588V11.544V10.4ZM295.636 10.4C301.408 10.4 306.244 12.48 310.3 16.536C314.408 20.592 316.488 25.48 316.488 31.2C316.488 36.972 314.408 41.86 310.3 45.864C306.244 49.92 301.408 52 295.636 52C289.864 52 285.028 49.92 280.972 45.864C276.916 41.808 274.836 36.972 274.836 31.2C274.836 25.48 276.916 20.592 280.972 16.536C284.976 12.48 289.864 10.4 295.636 10.4ZM295.636 17.368C291.84 17.368 288.564 18.668 285.808 21.424C283.104 24.128 281.804 27.404 281.804 31.2C281.804 34.996 283.104 38.324 285.808 41.028C288.512 43.732 291.84 45.032 295.636 45.032C299.432 45.032 302.708 43.732 305.412 41.028C308.168 38.272 309.468 34.996 309.468 31.2C309.468 27.404 308.116 24.128 305.412 21.424C302.708 18.72 299.432 17.368 295.636 17.368ZM352.723 34.684H359.639C359.639 39.468 357.975 43.576 354.543 46.956C351.163 50.336 347.003 52 342.271 52C337.539 52 333.431 50.336 330.051 46.956C326.671 43.576 325.007 39.416 325.007 34.684V10.4H331.923V34.684C331.923 37.544 332.911 39.988 334.887 41.964C336.915 44.044 339.411 45.032 342.271 45.032C345.131 45.032 347.575 43.992 349.603 41.964C351.631 39.936 352.723 37.492 352.723 34.684ZM396.621 23.192C397.713 25.688 398.233 28.34 398.233 31.2C398.233 34.06 397.713 36.712 396.621 39.208C395.477 41.756 394.021 43.94 392.149 45.864C390.277 47.736 388.041 49.244 385.493 50.336C382.997 51.428 380.293 52 377.433 52H363.601L370.517 45.032H377.433C379.357 45.032 381.177 44.668 382.841 43.992C384.505 43.264 386.013 42.276 387.261 40.976C388.509 39.676 389.497 38.22 390.225 36.556C390.953 34.84 391.317 33.072 391.317 31.2C391.317 29.328 390.953 27.56 390.225 25.844C389.497 24.18 388.509 22.724 387.209 21.424C385.961 20.176 384.453 19.136 382.789 18.408C381.125 17.732 379.305 17.368 377.433 17.368H370.517L363.601 10.4H377.433C380.293 10.4 382.945 10.972 385.493 12.064C387.989 13.156 390.225 14.664 392.097 16.536C394.021 18.46 395.477 20.644 396.621 23.192Z" fill="url(#paint0_linear_56_379)"/><path d="M7.99999 14H1.30874C0.614275 14.0193 0 14 0 14L18.8085 51.5C19.7652 52.3585 19.9594 52.3219 19.8087 51.5C19.3792 51.0014 19.4775 50.4593 20.3087 49C21.1421 46.2568 21.2304 43.8491 19.3087 41.5C18.2179 39.4696 18.9309 37.1826 20.8087 34L16.8087 26.5L14.8087 22.5L12.3087 18L11 15C10.5113 14.44 10.1886 14.2085 9.5 14H8.49999H7.99999Z" fill="url(#paint1_linear_56_379)"/><path d="M35.0507 14C37.2595 14.0004 39.0507 15.7911 39.0507 18C39.0507 20.2089 37.2595 21.9996 35.0507 22C34.4331 21.9999 33.8472 21.8594 33.3251 21.6094C33.1714 21.6936 33.0228 21.829 32.8553 22.0293L24.3583 35.0234C24.2882 35.1904 24.2274 35.3388 24.1805 35.4658C24.1331 35.5943 24.1019 35.6986 24.0927 35.7783C24.0834 35.8587 24.099 35.9018 24.1229 35.9238C24.1488 35.9469 24.2031 35.9626 24.3085 35.9502C26.1439 35.5539 27.4169 35.1177 28.4637 34.5029C29.5109 33.8878 30.3371 33.0905 31.2772 31.9678L33.3827 28.5957C33.1697 28.1068 33.0507 27.5673 33.0507 27C33.0507 24.791 34.8417 23.0002 37.0507 23C39.2597 23.0002 41.0507 24.791 41.0507 27C41.0507 29.0504 39.5075 30.7394 37.5194 30.9717C37.459 30.9962 37.397 31.0224 37.3339 31.0469L37.3173 31.0527L37.3007 31.0479C37.2448 31.0305 37.1894 31.0136 37.1356 30.9971C37.1076 30.9977 37.0789 31 37.0507 31C36.4652 30.9999 35.9092 30.8725 35.4081 30.6465C35.0278 30.6575 34.6938 30.7719 34.3505 31.0352L21.3573 51.5264L21.3534 51.5342L21.3466 51.5391C21.2336 51.6277 21.1425 51.6842 21.0507 51.6836C20.9809 51.6826 20.9167 51.6494 20.8505 51.5967L20.7821 51.5371C20.6425 51.4109 20.5854 51.2341 20.5887 51.0195C20.5921 50.8061 20.6543 50.5492 20.7577 50.2539C20.9644 49.664 21.3412 48.8997 21.7675 47.9834C22.0915 46.9083 22.1753 46.1149 22.0751 45.3691C21.9746 44.6219 21.6897 43.9177 21.2704 43.0215V43.0205C20.4116 41.1274 20.0867 40.2768 20.0155 39.6953C19.9796 39.4019 20.0079 39.1768 20.0624 38.9248C20.117 38.6719 20.1966 38.3975 20.2665 37.9912L20.2675 37.9824L20.2723 37.9736L31.2723 19.9736H31.2733C31.3431 19.8632 31.3907 19.7685 31.4198 19.6807C31.1829 19.1697 31.0507 18.6002 31.0507 18C31.0507 15.7911 32.8418 14.0004 35.0507 14ZM36.9999 25C35.8954 25.0001 34.9999 25.8955 34.9999 27C34.9999 28.1045 35.8954 28.9999 36.9999 29C38.1043 28.9999 38.9999 28.1045 38.9999 27C38.9999 25.8955 38.1043 25.0001 36.9999 25ZM34.9999 16C33.8953 16 32.9999 16.8954 32.9999 18C32.9999 19.1046 33.8953 20 34.9999 20C36.1044 19.9999 36.9999 19.1045 36.9999 18C36.9999 16.8955 36.1044 16.0001 34.9999 16Z" fill="url(#paint2_linear_56_379)"/><defs><linearGradient id="paint0_linear_56_379" x1="306" y1="5" x2="306" y2="67" gradientUnits="userSpaceOnUse"><stop stop-color="#9E9E9E"/><stop offset="1" stop-color="#464646"/></linearGradient><linearGradient id="paint1_linear_56_379" x1="19.5" y1="52" x2="0.999996" y2="16" gradientUnits="userSpaceOnUse"><stop stop-color="#0959B9"/><stop offset="1" stop-color="#1B74D1"/></linearGradient><linearGradient id="paint2_linear_56_379" x1="30.5245" y1="14" x2="30.5245" y2="51.6836" gradientUnits="userSpaceOnUse"><stop stop-color="#9E9E9E"/><stop offset="1" stop-color="#464646"/></linearGradient></defs></svg>`;
|
|
66
|
+
var VRITTI_CLOUD_LOGO_DARK = `<svg width="170" height="28" viewBox="0 0 407 67" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M78.9948 52L66.7748 37.44H72.2868L84.5588 51.948V52H78.9948ZM47.3788 52V14.612H78.5788C79.7228 14.612 80.7628 14.9067 81.6988 15.496C82.6695 16.0507 83.4321 16.796 83.9868 17.732C84.5415 18.668 84.8188 19.708 84.8188 20.852V31.824C84.8188 32.968 84.5415 34.008 83.9868 34.944C83.4321 35.88 82.6695 36.6427 81.6988 37.232C80.7628 37.7867 79.7228 38.064 78.5788 38.064H51.5908V52H47.3788ZM53.6188 33.8H78.5788C79.1335 33.8 79.6015 33.6093 79.9828 33.228C80.3988 32.8467 80.6068 32.3787 80.6068 31.824V20.852C80.6068 20.2973 80.3988 19.8293 79.9828 19.448C79.6015 19.032 79.1335 18.824 78.5788 18.824H53.6188C53.0641 18.824 52.5788 19.032 52.1628 19.448C51.7815 19.8293 51.5908 20.2973 51.5908 20.852V31.824C51.5908 32.3787 51.7815 32.8467 52.1628 33.228C52.5788 33.6093 53.0641 33.8 53.6188 33.8ZM90.809 52V14.56H95.073V52H90.809ZM116.178 52V18.772H99.538V14.56H136.978V18.772H120.39V52H116.178ZM155.635 52V18.772H138.995V14.56H176.435V18.772H159.847V52H155.635ZM180.844 52V14.56H185.108V52H180.844Z" fill="#0066CC"/><path d="M218.027 50.336C215.479 49.244 213.295 47.736 211.371 45.864C209.499 43.94 207.991 41.756 206.899 39.208C205.807 36.66 205.235 34.008 205.235 31.2C205.235 28.34 205.807 25.636 206.899 23.088C207.991 20.592 209.499 18.408 211.371 16.536C213.295 14.612 215.479 13.156 218.027 12.064C220.523 10.972 223.227 10.4 226.087 10.4C228.947 10.4 231.651 10.92 234.147 12.012C236.643 13.104 238.879 14.612 240.803 16.536L235.863 21.372C233.107 18.668 229.831 17.368 226.087 17.368C224.163 17.368 222.343 17.732 220.679 18.408C218.963 19.136 217.507 20.124 216.259 21.424C215.011 22.672 214.023 24.128 213.295 25.792C212.567 27.508 212.203 29.276 212.203 31.2C212.203 33.124 212.567 34.892 213.295 36.608C214.023 38.272 215.011 39.728 216.311 40.976C217.559 42.276 219.015 43.264 220.679 43.992C222.395 44.668 224.163 45.032 226.087 45.032L234.147 50.388C231.651 51.48 228.947 52 226.087 52C223.227 52 220.523 51.428 218.027 50.336ZM252.177 45.032H272.977V52H245.261L252.177 45.032ZM245.261 10.4H252.177V11.544V35.62L245.261 42.588V11.544V10.4ZM295.636 10.4C301.408 10.4 306.244 12.48 310.3 16.536C314.408 20.592 316.488 25.48 316.488 31.2C316.488 36.972 314.408 41.86 310.3 45.864C306.244 49.92 301.408 52 295.636 52C289.864 52 285.028 49.92 280.972 45.864C276.916 41.808 274.836 36.972 274.836 31.2C274.836 25.48 276.916 20.592 280.972 16.536C284.976 12.48 289.864 10.4 295.636 10.4ZM295.636 17.368C291.84 17.368 288.564 18.668 285.808 21.424C283.104 24.128 281.804 27.404 281.804 31.2C281.804 34.996 283.104 38.324 285.808 41.028C288.512 43.732 291.84 45.032 295.636 45.032C299.432 45.032 302.708 43.732 305.412 41.028C308.168 38.272 309.468 34.996 309.468 31.2C309.468 27.404 308.116 24.128 305.412 21.424C302.708 18.72 299.432 17.368 295.636 17.368ZM352.723 34.684H359.639C359.639 39.468 357.975 43.576 354.543 46.956C351.163 50.336 347.003 52 342.271 52C337.539 52 333.431 50.336 330.051 46.956C326.671 43.576 325.007 39.416 325.007 34.684V10.4H331.923V34.684C331.923 37.544 332.911 39.988 334.887 41.964C336.915 44.044 339.411 45.032 342.271 45.032C345.131 45.032 347.575 43.992 349.603 41.964C351.631 39.936 352.723 37.492 352.723 34.684ZM396.621 23.192C397.713 25.688 398.233 28.34 398.233 31.2C398.233 34.06 397.713 36.712 396.621 39.208C395.477 41.756 394.021 43.94 392.149 45.864C390.277 47.736 388.041 49.244 385.493 50.336C382.997 51.428 380.293 52 377.433 52H363.601L370.517 45.032H377.433C379.357 45.032 381.177 44.668 382.841 43.992C384.505 43.264 386.013 42.276 387.261 40.976C388.509 39.676 389.497 38.22 390.225 36.556C390.953 34.84 391.317 33.072 391.317 31.2C391.317 29.328 390.953 27.56 390.225 25.844C389.497 24.18 388.509 22.724 387.209 21.424C385.961 20.176 384.453 19.136 382.789 18.408C381.125 17.732 379.305 17.368 377.433 17.368H370.517L363.601 10.4H377.433C380.293 10.4 382.945 10.972 385.493 12.064C387.989 13.156 390.225 14.664 392.097 16.536C394.021 18.46 395.477 20.644 396.621 23.192Z" fill="url(#paint0_linear_92_90)"/><path d="M7.99999 14H1.30874C0.614275 14.0193 0 14 0 14L18.8085 51.5C19.7652 52.3585 19.9594 52.3219 19.8087 51.5C19.3792 51.0014 19.4775 50.4593 20.3087 49C21.1421 46.2568 21.2304 43.8491 19.3087 41.5C18.2179 39.4696 18.9309 37.1826 20.8087 34L16.8087 26.5L14.8087 22.5L12.3087 18L11 15C10.5113 14.44 10.1886 14.2085 9.5 14H8.49999H7.99999Z" fill="url(#paint1_linear_92_90)"/><path fill-rule="evenodd" clip-rule="evenodd" d="M35.0497 14C37.2588 14 39.0497 15.7909 39.0497 18C39.0497 20.2091 37.2588 22 35.0497 22C34.4321 21.9998 33.8463 21.8595 33.3241 21.6094C33.1708 21.6937 33.0224 21.8295 32.8554 22.0293L24.3583 35.0234C24.2882 35.1903 24.2274 35.3389 24.1805 35.4658C24.1332 35.5943 24.1019 35.6986 24.0927 35.7783C24.0834 35.8586 24.099 35.9018 24.1229 35.9238C24.1489 35.9468 24.2034 35.9625 24.3085 35.9502C26.1438 35.5539 27.4169 35.1177 28.4638 34.5029C29.5108 33.8878 30.3372 33.0905 31.2772 31.9678L33.3817 28.5967C33.1686 28.1076 33.0497 27.5675 33.0497 27C33.0497 24.7912 34.841 23.0006 37.0497 23C39.2588 23 41.0497 24.7909 41.0497 27C41.0497 29.0499 39.5078 30.7378 37.5204 30.9707C37.4596 30.9954 37.3974 31.0223 37.3339 31.0469L37.3173 31.0527L37.3007 31.0479C37.2448 31.0305 37.1894 31.0136 37.1356 30.9971C37.1074 30.9977 37.0781 31 37.0497 31C36.4643 30.9999 35.9082 30.8726 35.4071 30.6465C35.0273 30.6577 34.6935 30.7722 34.3505 31.0352L21.3573 51.5264L21.3534 51.5342L21.3466 51.5391C21.2337 51.6276 21.1424 51.6841 21.0507 51.6836C20.981 51.6826 20.9167 51.6493 20.8505 51.5967L20.7821 51.5371C20.6425 51.4109 20.5854 51.2341 20.5888 51.0195C20.5921 50.8061 20.6543 50.5492 20.7577 50.2539C20.9644 49.664 21.3413 48.8996 21.7675 47.9834C22.0915 46.9083 22.1753 46.1149 22.0751 45.3691C21.9746 44.6219 21.6897 43.9177 21.2704 43.0215V43.0205C20.4116 41.1274 20.0867 40.2768 20.0155 39.6953C19.9796 39.4019 20.008 39.1768 20.0624 38.9248C20.117 38.672 20.1966 38.3974 20.2665 37.9912L20.2675 37.9824L20.2723 37.9736L31.2723 19.9736H31.2733C31.3431 19.8632 31.3907 19.7685 31.4198 19.6807C31.1829 19.1697 31.0497 18.6003 31.0497 18C31.0497 15.7912 32.841 14.0006 35.0497 14ZM36.9999 25C35.8955 25.0002 34.9999 25.8956 34.9999 27C34.9999 28.1044 35.8955 28.9998 36.9999 29C38.1042 28.9998 38.9999 28.1044 38.9999 27C38.9999 25.8956 38.1042 25.0002 36.9999 25ZM34.9999 16C33.8953 16 32.9999 16.8954 32.9999 18C32.9999 19.1046 33.8953 20 34.9999 20C36.1044 19.9999 36.9999 19.1045 36.9999 18C36.9999 16.8955 36.1044 16.0001 34.9999 16Z" fill="url(#paint2_linear_92_90)"/><defs><linearGradient id="paint0_linear_92_90" x1="306" y1="5" x2="306" y2="67" gradientUnits="userSpaceOnUse"><stop stop-color="#D9D9D9"/><stop offset="1" stop-color="#737373"/></linearGradient><linearGradient id="paint1_linear_92_90" x1="19.5" y1="52" x2="0.999996" y2="16" gradientUnits="userSpaceOnUse"><stop stop-color="#0959B9"/><stop offset="1" stop-color="#1B74D1"/></linearGradient><linearGradient id="paint2_linear_92_90" x1="30.5246" y1="14" x2="30.5246" y2="51.6836" gradientUnits="userSpaceOnUse"><stop stop-color="#D9D9D9"/><stop offset="1" stop-color="#737373"/></linearGradient></defs></svg>`;
|
|
67
|
+
var EMAIL_NOTICE_TONES = {
|
|
68
|
+
warn: {
|
|
69
|
+
background: "#fdf6e7",
|
|
70
|
+
color: "#8a6410",
|
|
71
|
+
boxClass: "e-warnbox",
|
|
72
|
+
textClass: "e-warn"
|
|
73
|
+
},
|
|
74
|
+
danger: {
|
|
75
|
+
background: "#fdeceb",
|
|
76
|
+
color: "#a32b21",
|
|
77
|
+
boxClass: "e-dangerbox",
|
|
78
|
+
textClass: "e-danger"
|
|
79
|
+
},
|
|
80
|
+
success: {
|
|
81
|
+
background: "#eaf6ee",
|
|
82
|
+
color: "#1c6b3f",
|
|
83
|
+
boxClass: "e-successbox",
|
|
84
|
+
textClass: "e-success"
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var EMAIL_BUTTON_TONES = {
|
|
88
|
+
primary: "#1b74d1",
|
|
89
|
+
danger: "#c0392b"
|
|
90
|
+
};
|
|
48
91
|
var EmailService = class _EmailService {
|
|
49
92
|
static {
|
|
50
93
|
__name(this, "EmailService");
|
|
@@ -54,6 +97,9 @@ var EmailService = class _EmailService {
|
|
|
54
97
|
brevoClient;
|
|
55
98
|
senderEmail;
|
|
56
99
|
senderName;
|
|
100
|
+
logoLightUrl;
|
|
101
|
+
logoDarkUrl;
|
|
102
|
+
frontendBaseUrl;
|
|
57
103
|
constructor(configService) {
|
|
58
104
|
this.configService = configService;
|
|
59
105
|
const apiKey = this.configService.get("BREVO_API_KEY");
|
|
@@ -73,91 +119,39 @@ var EmailService = class _EmailService {
|
|
|
73
119
|
}
|
|
74
120
|
this.senderEmail = senderEmail;
|
|
75
121
|
this.senderName = senderName;
|
|
122
|
+
this.logoLightUrl = this.configService.get("EMAIL_LOGO_LIGHT_URL");
|
|
123
|
+
this.logoDarkUrl = this.configService.get("EMAIL_LOGO_DARK_URL") || this.logoLightUrl;
|
|
124
|
+
if (!this.logoLightUrl) {
|
|
125
|
+
this.logger.warn("EMAIL_LOGO_LIGHT_URL is not configured. Emails fall back to an inline SVG wordmark, which Gmail, Outlook and Yahoo strip.");
|
|
126
|
+
}
|
|
127
|
+
this.frontendBaseUrl = this.configService.get("FRONTEND_BASE_URL")?.replace(/\/+$/, "");
|
|
128
|
+
if (!this.frontendBaseUrl) {
|
|
129
|
+
this.logger.warn("FRONTEND_BASE_URL is not configured. Emails containing web app links will fail to send.");
|
|
130
|
+
}
|
|
76
131
|
this.logger.log("Brevo email service initialized successfully");
|
|
77
132
|
}
|
|
78
133
|
// Sends an email verification OTP to the given recipient
|
|
79
134
|
async sendVerificationEmail(email, otp, expiresAt, displayName) {
|
|
80
135
|
const name = displayName || "there";
|
|
81
|
-
const
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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();
|
|
136
|
+
const expiry = this.formatExpiry(expiresAt);
|
|
137
|
+
const htmlContent = this.renderEmailShell({
|
|
138
|
+
preheader: `Your verification code is ${otp}. It expires in ${expiry}.`,
|
|
139
|
+
heading: "Verify Your Email",
|
|
140
|
+
body: [
|
|
141
|
+
this.emailText(`Hello ${name} — enter the code below to finish setting up your Vritti AI Cloud account.`),
|
|
142
|
+
this.emailCode("Verification Code", otp),
|
|
143
|
+
this.emailNotice(`This code expires ${expiry} after it was sent.`, "warn"),
|
|
144
|
+
this.emailDivider(),
|
|
145
|
+
this.emailMuted("Didn't request this? No account was created. You can ignore this email and the code will expire on its own.")
|
|
146
|
+
].join("")
|
|
147
|
+
});
|
|
148
|
+
const textContent = this.renderTextShell([
|
|
149
|
+
`Hello ${name},`,
|
|
150
|
+
"Enter the code below to finish setting up your Vritti AI Cloud account.",
|
|
151
|
+
`Verification Code: ${otp}`,
|
|
152
|
+
`This code expires ${expiry} after it was sent.`,
|
|
153
|
+
"Didn't request this? No account was created. You can ignore this email and the code will expire on its own."
|
|
154
|
+
]);
|
|
161
155
|
await this.sendEmail({
|
|
162
156
|
to: [
|
|
163
157
|
{
|
|
@@ -165,7 +159,7 @@ This is an automated message, please do not reply.
|
|
|
165
159
|
name
|
|
166
160
|
}
|
|
167
161
|
],
|
|
168
|
-
subject,
|
|
162
|
+
subject: "Verify Your Email - Vritti AI Cloud",
|
|
169
163
|
htmlContent,
|
|
170
164
|
textContent
|
|
171
165
|
});
|
|
@@ -174,93 +168,27 @@ This is an automated message, please do not reply.
|
|
|
174
168
|
// Sends a password reset OTP to the given recipient
|
|
175
169
|
async sendPasswordResetEmail(email, otp, expiresAt, displayName) {
|
|
176
170
|
const name = displayName || "there";
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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();
|
|
171
|
+
const expiry = this.formatExpiry(expiresAt);
|
|
172
|
+
const htmlContent = this.renderEmailShell({
|
|
173
|
+
preheader: `Your password reset code is ${otp}. It expires in ${expiry}.`,
|
|
174
|
+
heading: "Reset Your Password",
|
|
175
|
+
body: [
|
|
176
|
+
this.emailText(`Hello ${name} — we received a request to reset your password. Enter the code below to choose a new one.`),
|
|
177
|
+
this.emailCode("Reset Code", otp),
|
|
178
|
+
this.emailNotice(`This code expires ${expiry} after it was sent.`, "warn"),
|
|
179
|
+
this.emailNotice("<strong>Never share this code.</strong> Vritti will never ask you for it by email, phone or chat.", "danger"),
|
|
180
|
+
this.emailDivider(),
|
|
181
|
+
this.emailMuted("Didn't request a reset? You can ignore this email — your password stays unchanged and the code will expire on its own.")
|
|
182
|
+
].join("")
|
|
183
|
+
});
|
|
184
|
+
const textContent = this.renderTextShell([
|
|
185
|
+
`Hello ${name},`,
|
|
186
|
+
"We received a request to reset your password. Enter the code below to choose a new one.",
|
|
187
|
+
`Reset Code: ${otp}`,
|
|
188
|
+
`This code expires ${expiry} after it was sent.`,
|
|
189
|
+
"Never share this code. Vritti will never ask you for it by email, phone or chat.",
|
|
190
|
+
"Didn't request a reset? You can ignore this email - your password stays unchanged and the code will expire on its own."
|
|
191
|
+
]);
|
|
264
192
|
await this.sendEmail({
|
|
265
193
|
to: [
|
|
266
194
|
{
|
|
@@ -268,7 +196,7 @@ This is an automated message, please do not reply.
|
|
|
268
196
|
name
|
|
269
197
|
}
|
|
270
198
|
],
|
|
271
|
-
subject,
|
|
199
|
+
subject: "Reset Your Password - Vritti AI Cloud",
|
|
272
200
|
htmlContent,
|
|
273
201
|
textContent
|
|
274
202
|
});
|
|
@@ -277,110 +205,39 @@ This is an automated message, please do not reply.
|
|
|
277
205
|
// Sends an email change notification to the old address with a revert link
|
|
278
206
|
async sendEmailChangeNotification(oldEmail, newEmail, revertToken, revertExpiresAt, displayName) {
|
|
279
207
|
const name = displayName || "there";
|
|
280
|
-
const
|
|
281
|
-
const
|
|
282
|
-
const revertLink =
|
|
283
|
-
const htmlContent =
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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();
|
|
208
|
+
const hoursUntilExpiry = Math.floor((revertExpiresAt.getTime() - Date.now()) / 36e5);
|
|
209
|
+
const window = `${hoursUntilExpiry} ${(0, import_pluralize_esm.default)("hour", hoursUntilExpiry)}`;
|
|
210
|
+
const revertLink = `${this.requireFrontendBaseUrl()}/settings/profile/revert-email?token=${revertToken}`;
|
|
211
|
+
const htmlContent = this.renderEmailShell({
|
|
212
|
+
preheader: `Your Vritti AI Cloud email address is now ${newEmail}.`,
|
|
213
|
+
heading: "Your Email Address Changed",
|
|
214
|
+
body: [
|
|
215
|
+
this.emailText(`Hello ${name} — the email address on your Vritti AI Cloud account has been changed.`),
|
|
216
|
+
this.emailFields([
|
|
217
|
+
{
|
|
218
|
+
label: "Previous Email",
|
|
219
|
+
value: oldEmail
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
label: "New Email",
|
|
223
|
+
value: newEmail
|
|
224
|
+
}
|
|
225
|
+
]),
|
|
226
|
+
this.emailNotice(`<strong>Didn't make this change?</strong> You can revert it within the next ${window}.`, "danger"),
|
|
227
|
+
this.emailButton(revertLink, "Revert Email Change", "danger"),
|
|
228
|
+
this.emailDivider(),
|
|
229
|
+
this.emailMuted("If you made this change yourself, no action is needed — you can ignore this email.")
|
|
230
|
+
].join("")
|
|
231
|
+
});
|
|
232
|
+
const textContent = this.renderTextShell([
|
|
233
|
+
`Hello ${name},`,
|
|
234
|
+
"The email address on your Vritti AI Cloud account has been changed.",
|
|
235
|
+
`Previous Email: ${oldEmail}`,
|
|
236
|
+
`New Email: ${newEmail}`,
|
|
237
|
+
`Didn't make this change? You can revert it within the next ${window}:`,
|
|
238
|
+
revertLink,
|
|
239
|
+
"If you made this change yourself, no action is needed - you can ignore this email."
|
|
240
|
+
]);
|
|
384
241
|
await this.sendEmail({
|
|
385
242
|
to: [
|
|
386
243
|
{
|
|
@@ -388,7 +245,7 @@ This is an automated message, please do not reply.
|
|
|
388
245
|
name
|
|
389
246
|
}
|
|
390
247
|
],
|
|
391
|
-
subject,
|
|
248
|
+
subject: "Your Email Address Has Been Changed - Vritti AI Cloud",
|
|
392
249
|
htmlContent,
|
|
393
250
|
textContent
|
|
394
251
|
});
|
|
@@ -397,79 +254,22 @@ This is an automated message, please do not reply.
|
|
|
397
254
|
// Sends a confirmation to the restored email address after a revert
|
|
398
255
|
async sendEmailRevertConfirmation(email, displayName) {
|
|
399
256
|
const name = displayName || "there";
|
|
400
|
-
const
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
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();
|
|
257
|
+
const htmlContent = this.renderEmailShell({
|
|
258
|
+
preheader: `Your Vritti AI Cloud email address is back to ${email}.`,
|
|
259
|
+
heading: "Email Change Reverted",
|
|
260
|
+
body: [
|
|
261
|
+
this.emailText(`Hello ${name} — the recent change to your email address has been reverted. Your account email is now:`),
|
|
262
|
+
this.emailNotice(email, "success", true),
|
|
263
|
+
this.emailDivider(),
|
|
264
|
+
this.emailMuted("Didn't request this revert? Contact our support team immediately.")
|
|
265
|
+
].join("")
|
|
266
|
+
});
|
|
267
|
+
const textContent = this.renderTextShell([
|
|
268
|
+
`Hello ${name},`,
|
|
269
|
+
"The recent change to your email address has been reverted. Your account email is now:",
|
|
270
|
+
email,
|
|
271
|
+
"Didn't request this revert? Contact our support team immediately."
|
|
272
|
+
]);
|
|
473
273
|
await this.sendEmail({
|
|
474
274
|
to: [
|
|
475
275
|
{
|
|
@@ -477,7 +277,7 @@ This is an automated message, please do not reply.
|
|
|
477
277
|
name
|
|
478
278
|
}
|
|
479
279
|
],
|
|
480
|
-
subject,
|
|
280
|
+
subject: "Email Address Change Reverted - Vritti AI Cloud",
|
|
481
281
|
htmlContent,
|
|
482
282
|
textContent
|
|
483
283
|
});
|
|
@@ -486,79 +286,23 @@ This is an automated message, please do not reply.
|
|
|
486
286
|
// Sends an invite email to a new portal user with their set-password link
|
|
487
287
|
async sendInviteEmail(params) {
|
|
488
288
|
const { to, name, inviteUrl } = params;
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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();
|
|
289
|
+
const htmlContent = this.renderEmailShell({
|
|
290
|
+
preheader: "Set your password to activate your Vritti AI Cloud account.",
|
|
291
|
+
heading: "You're Invited",
|
|
292
|
+
body: [
|
|
293
|
+
this.emailText(`Hello ${name} — you've been invited to join Vritti AI Cloud. Set your password to activate your account.`),
|
|
294
|
+
this.emailButton(inviteUrl, "Set Your Password"),
|
|
295
|
+
this.emailNotice("This invite link is single-use and expires once your password is set.", "warn"),
|
|
296
|
+
this.emailDivider(),
|
|
297
|
+
this.emailMuted(`If the button doesn't work, copy this link into your browser:<br><span style="word-break:break-all;">${inviteUrl}</span>`)
|
|
298
|
+
].join("")
|
|
299
|
+
});
|
|
300
|
+
const textContent = this.renderTextShell([
|
|
301
|
+
`Hello ${name},`,
|
|
302
|
+
"You've been invited to join Vritti AI Cloud. Visit the link below to set your password and activate your account:",
|
|
303
|
+
inviteUrl,
|
|
304
|
+
"This invite link is single-use and expires once your password is set."
|
|
305
|
+
]);
|
|
562
306
|
await this.sendEmail({
|
|
563
307
|
to: [
|
|
564
308
|
{
|
|
@@ -566,7 +310,7 @@ This is an automated message, please do not reply.
|
|
|
566
310
|
name
|
|
567
311
|
}
|
|
568
312
|
],
|
|
569
|
-
subject,
|
|
313
|
+
subject: "You have been invited to Vritti AI",
|
|
570
314
|
htmlContent,
|
|
571
315
|
textContent
|
|
572
316
|
});
|
|
@@ -610,6 +354,237 @@ This is an automated message, please do not reply.
|
|
|
610
354
|
}
|
|
611
355
|
}
|
|
612
356
|
// Sends a transactional email via Brevo — retries handled internally by BrevoClient
|
|
357
|
+
// Renders the shared transactional email shell: brand header, card, heading, body rows, footer
|
|
358
|
+
renderEmailShell(params) {
|
|
359
|
+
const { preheader, heading, body } = params;
|
|
360
|
+
return `
|
|
361
|
+
<!DOCTYPE html>
|
|
362
|
+
<html lang="en">
|
|
363
|
+
<head>
|
|
364
|
+
<meta charset="utf-8">
|
|
365
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
366
|
+
<meta name="x-apple-disable-message-reformatting">
|
|
367
|
+
<meta name="color-scheme" content="light dark">
|
|
368
|
+
<meta name="supported-color-schemes" content="light dark">
|
|
369
|
+
<title>${heading}</title>
|
|
370
|
+
<!--[if mso]>
|
|
371
|
+
<style>body,table,td,a{font-family:Arial,Helvetica,sans-serif !important;}</style>
|
|
372
|
+
<![endif]-->
|
|
373
|
+
<style>
|
|
374
|
+
@media only screen and (max-width:620px){
|
|
375
|
+
.m-pad{padding-left:24px !important;padding-right:24px !important;}
|
|
376
|
+
.m-code{font-size:32px !important;letter-spacing:8px !important;}
|
|
377
|
+
.m-h1{font-size:26px !important;line-height:32px !important;}
|
|
378
|
+
}
|
|
379
|
+
@media (prefers-color-scheme:dark){
|
|
380
|
+
.e-bg{background-color:#0f1214 !important;}
|
|
381
|
+
.e-card{background-color:#1c2126 !important;border-color:#2b3238 !important;}
|
|
382
|
+
.e-h1{color:#f0f3f5 !important;}
|
|
383
|
+
.e-body{color:#a3adb8 !important;}
|
|
384
|
+
.e-panel{background-color:#14181c !important;border-color:#2b3238 !important;}
|
|
385
|
+
.e-label{color:#8b96a2 !important;}
|
|
386
|
+
.e-code{color:#63a8ee !important;}
|
|
387
|
+
.e-value{color:#f0f3f5 !important;}
|
|
388
|
+
.e-warnbox{background-color:#2a2317 !important;}
|
|
389
|
+
.e-warn{color:#e0b45e !important;}
|
|
390
|
+
.e-dangerbox{background-color:#2b1a18 !important;}
|
|
391
|
+
.e-danger{color:#ef8a7f !important;}
|
|
392
|
+
.e-successbox{background-color:#16261c !important;}
|
|
393
|
+
.e-success{color:#5fc98a !important;}
|
|
394
|
+
.e-rule{background-color:#2b3238 !important;}
|
|
395
|
+
.e-muted{color:#8b96a2 !important;}
|
|
396
|
+
.e-footer{color:#79838e !important;}
|
|
397
|
+
.e-logo-light{display:none !important;}
|
|
398
|
+
.e-logo-dark{display:block !important;}
|
|
399
|
+
}
|
|
400
|
+
</style>
|
|
401
|
+
</head>
|
|
402
|
+
<body class="e-bg" style="margin:0;padding:0;background-color:#f7f9fb;">
|
|
403
|
+
<span style="display:none!important;visibility:hidden;opacity:0;color:transparent;height:0;width:0;overflow:hidden;mso-hide:all;">${preheader}</span>
|
|
404
|
+
|
|
405
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" class="e-bg" style="background-color:#f7f9fb;">
|
|
406
|
+
<tr>
|
|
407
|
+
<td align="center" style="padding:40px 12px 56px 12px;">
|
|
408
|
+
|
|
409
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="600" style="width:600px;max-width:600px;">
|
|
410
|
+
|
|
411
|
+
<tr>
|
|
412
|
+
<td style="padding:0 0 20px 2px;">
|
|
413
|
+
${this.renderEmailBrand()}
|
|
414
|
+
</td>
|
|
415
|
+
</tr>
|
|
416
|
+
|
|
417
|
+
<tr>
|
|
418
|
+
<td class="e-card" style="background-color:#ffffff;border:1px solid #e6ebf2;border-radius:12px;">
|
|
419
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
|
|
420
|
+
|
|
421
|
+
<tr>
|
|
422
|
+
<td class="m-pad m-h1 e-h1" style="padding:40px 40px 0 40px;font-family:${EMAIL_SANS};font-size:28px;line-height:34px;mso-line-height-rule:exactly;color:#1b2434;font-weight:600;letter-spacing:-0.4px;">
|
|
423
|
+
${heading}
|
|
424
|
+
</td>
|
|
425
|
+
</tr>
|
|
426
|
+
${body}
|
|
427
|
+
<tr><td style="height:36px;line-height:36px;font-size:0;"> </td></tr>
|
|
428
|
+
|
|
429
|
+
</table>
|
|
430
|
+
</td>
|
|
431
|
+
</tr>
|
|
432
|
+
|
|
433
|
+
<tr>
|
|
434
|
+
<td class="m-pad e-footer" style="padding:22px 2px 0 2px;font-family:${EMAIL_SANS};font-size:12px;line-height:20px;mso-line-height-rule:exactly;color:#8a95a5;letter-spacing:0.01em;">
|
|
435
|
+
Vritti AI Cloud<br>
|
|
436
|
+
Automated message — replies aren't monitored.
|
|
437
|
+
</td>
|
|
438
|
+
</tr>
|
|
439
|
+
|
|
440
|
+
</table>
|
|
441
|
+
|
|
442
|
+
</td>
|
|
443
|
+
</tr>
|
|
444
|
+
</table>
|
|
445
|
+
</body>
|
|
446
|
+
</html>
|
|
447
|
+
`.trim();
|
|
448
|
+
}
|
|
449
|
+
// Builds the plain-text alternative from the same blocks the HTML shell renders
|
|
450
|
+
renderTextShell(blocks) {
|
|
451
|
+
const footer = [
|
|
452
|
+
"---",
|
|
453
|
+
"Vritti AI Cloud",
|
|
454
|
+
"Automated message - replies aren't monitored."
|
|
455
|
+
].join("\n");
|
|
456
|
+
return [
|
|
457
|
+
...blocks,
|
|
458
|
+
footer
|
|
459
|
+
].join("\n\n");
|
|
460
|
+
}
|
|
461
|
+
// Body paragraph
|
|
462
|
+
emailText(html) {
|
|
463
|
+
return `
|
|
464
|
+
<tr>
|
|
465
|
+
<td class="m-pad e-body" style="padding:14px 40px 0 40px;font-family:${EMAIL_SANS};font-size:15px;line-height:25px;mso-line-height-rule:exactly;color:#5d6b80;letter-spacing:0.01em;">
|
|
466
|
+
${html}
|
|
467
|
+
</td>
|
|
468
|
+
</tr>`;
|
|
469
|
+
}
|
|
470
|
+
// Monospace OTP tile with an uppercase label
|
|
471
|
+
emailCode(label, code) {
|
|
472
|
+
return `
|
|
473
|
+
<tr>
|
|
474
|
+
<td class="m-pad" style="padding:28px 40px 0 40px;">
|
|
475
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" class="e-panel" style="background-color:#f7f9fb;border:1px solid #e6ebf2;border-radius:10px;">
|
|
476
|
+
<tr>
|
|
477
|
+
<td align="center" class="e-label" style="padding:18px 16px 6px 16px;font-family:${EMAIL_SANS};font-size:11px;line-height:16px;mso-line-height-rule:exactly;letter-spacing:1.6px;text-transform:uppercase;color:#7b8798;font-weight:500;">
|
|
478
|
+
${label}
|
|
479
|
+
</td>
|
|
480
|
+
</tr>
|
|
481
|
+
<tr>
|
|
482
|
+
<td align="center" class="m-code e-code" style="padding:0 16px 20px 16px;font-family:${EMAIL_MONO};font-size:38px;line-height:46px;mso-line-height-rule:exactly;letter-spacing:11px;color:#1b74d1;font-weight:600;text-indent:11px;">
|
|
483
|
+
${code}
|
|
484
|
+
</td>
|
|
485
|
+
</tr>
|
|
486
|
+
</table>
|
|
487
|
+
</td>
|
|
488
|
+
</tr>`;
|
|
489
|
+
}
|
|
490
|
+
// Tinted callout. `center` renders the content as a centred mono value instead of a sentence
|
|
491
|
+
emailNotice(html, tone, center = false) {
|
|
492
|
+
const palette = EMAIL_NOTICE_TONES[tone];
|
|
493
|
+
const typography = center ? `font-family:${EMAIL_MONO};font-size:17px;line-height:26px;font-weight:600;` : `font-family:${EMAIL_SANS};font-size:13px;line-height:19px;letter-spacing:0.01em;`;
|
|
494
|
+
return `
|
|
495
|
+
<tr>
|
|
496
|
+
<td class="m-pad" style="padding:16px 40px 0 40px;">
|
|
497
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" class="${palette.boxClass}" style="background-color:${palette.background};border-radius:8px;">
|
|
498
|
+
<tr>
|
|
499
|
+
<td align="${center ? "center" : "left"}" class="${palette.textClass}" style="padding:${center ? "16px 14px" : "11px 14px"};${typography}mso-line-height-rule:exactly;color:${palette.color};">
|
|
500
|
+
${html}
|
|
501
|
+
</td>
|
|
502
|
+
</tr>
|
|
503
|
+
</table>
|
|
504
|
+
</td>
|
|
505
|
+
</tr>`;
|
|
506
|
+
}
|
|
507
|
+
// Label/value list rendered with monospace values
|
|
508
|
+
emailFields(fields) {
|
|
509
|
+
const rows = fields.map((field, index) => `
|
|
510
|
+
<tr>
|
|
511
|
+
<td class="e-label" style="padding:${index === 0 ? "18px" : "14px"} 18px 0 18px;font-family:${EMAIL_SANS};font-size:11px;line-height:16px;mso-line-height-rule:exactly;letter-spacing:1.6px;text-transform:uppercase;color:#7b8798;font-weight:500;">
|
|
512
|
+
${field.label}
|
|
513
|
+
</td>
|
|
514
|
+
</tr>
|
|
515
|
+
<tr>
|
|
516
|
+
<td class="e-value" style="padding:4px 18px ${index === fields.length - 1 ? "18px" : "0"} 18px;font-family:${EMAIL_MONO};font-size:15px;line-height:23px;mso-line-height-rule:exactly;color:#1b2434;word-break:break-all;">
|
|
517
|
+
${field.value}
|
|
518
|
+
</td>
|
|
519
|
+
</tr>`).join("");
|
|
520
|
+
return `
|
|
521
|
+
<tr>
|
|
522
|
+
<td class="m-pad" style="padding:28px 40px 0 40px;">
|
|
523
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%" class="e-panel" style="background-color:#f7f9fb;border:1px solid #e6ebf2;border-radius:10px;">${rows}
|
|
524
|
+
</table>
|
|
525
|
+
</td>
|
|
526
|
+
</tr>`;
|
|
527
|
+
}
|
|
528
|
+
// Solid CTA button. Colours are theme-independent so the fill reads the same in light and dark
|
|
529
|
+
emailButton(href, label, tone = "primary") {
|
|
530
|
+
const background = EMAIL_BUTTON_TONES[tone];
|
|
531
|
+
return `
|
|
532
|
+
<tr>
|
|
533
|
+
<td class="m-pad" style="padding:24px 40px 0 40px;">
|
|
534
|
+
<!--[if mso]>
|
|
535
|
+
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="${href}" style="height:44px;v-text-anchor:middle;width:240px;" arcsize="20%" stroke="f" fillcolor="${background}">
|
|
536
|
+
<w:anchorlock/>
|
|
537
|
+
<center style="color:#ffffff;font-family:Arial,sans-serif;font-size:15px;font-weight:600;">${label}</center>
|
|
538
|
+
</v:roundrect>
|
|
539
|
+
<![endif]-->
|
|
540
|
+
<!--[if !mso]><!-->
|
|
541
|
+
<a href="${href}" style="display:inline-block;padding:13px 28px;background-color:${background};color:#ffffff;text-decoration:none;border-radius:8px;font-family:${EMAIL_SANS};font-size:15px;line-height:18px;font-weight:600;letter-spacing:0.01em;">${label}</a>
|
|
542
|
+
<!--<![endif]-->
|
|
543
|
+
</td>
|
|
544
|
+
</tr>`;
|
|
545
|
+
}
|
|
546
|
+
// Hairline rule separating the body from the closing note
|
|
547
|
+
emailDivider() {
|
|
548
|
+
return `
|
|
549
|
+
<tr>
|
|
550
|
+
<td class="m-pad" style="padding:28px 40px 0 40px;">
|
|
551
|
+
<table role="presentation" cellpadding="0" cellspacing="0" border="0" width="100%">
|
|
552
|
+
<tr><td height="1" class="e-rule" style="height:1px;line-height:1px;font-size:0;background-color:#eef2f7;"> </td></tr>
|
|
553
|
+
</table>
|
|
554
|
+
</td>
|
|
555
|
+
</tr>`;
|
|
556
|
+
}
|
|
557
|
+
// Small closing note below the divider
|
|
558
|
+
emailMuted(html) {
|
|
559
|
+
return `
|
|
560
|
+
<tr>
|
|
561
|
+
<td class="m-pad e-muted" style="padding:18px 40px 0 40px;font-family:${EMAIL_SANS};font-size:13px;line-height:21px;mso-line-height-rule:exactly;color:#7b8798;letter-spacing:0.01em;">
|
|
562
|
+
${html}
|
|
563
|
+
</td>
|
|
564
|
+
</tr>`;
|
|
565
|
+
}
|
|
566
|
+
// Renders the email header brand mark: a hosted logo when configured, inline SVG otherwise
|
|
567
|
+
renderEmailBrand() {
|
|
568
|
+
const imgStyle = "border:0;outline:none;text-decoration:none;width:170px;height:auto;";
|
|
569
|
+
if (this.logoLightUrl) {
|
|
570
|
+
return `<img src="${this.logoLightUrl}" width="170" height="28" alt="Vritti AI Cloud" class="e-logo-light" style="display:block;${imgStyle}">
|
|
571
|
+
<img src="${this.logoDarkUrl}" width="170" height="28" alt="Vritti AI Cloud" class="e-logo-dark" style="display:none;${imgStyle}">`;
|
|
572
|
+
}
|
|
573
|
+
return `<div class="e-logo-light" style="display:block;font-size:0;line-height:0;">${VRITTI_CLOUD_LOGO_LIGHT}</div>
|
|
574
|
+
<div class="e-logo-dark" style="display:none;font-size:0;line-height:0;">${VRITTI_CLOUD_LOGO_DARK}</div>`;
|
|
575
|
+
}
|
|
576
|
+
// Frontend base URL for emails that link back into the web app
|
|
577
|
+
requireFrontendBaseUrl() {
|
|
578
|
+
if (!this.frontendBaseUrl) {
|
|
579
|
+
throw new Error("Email service configuration error: Missing FRONTEND_BASE_URL");
|
|
580
|
+
}
|
|
581
|
+
return this.frontendBaseUrl;
|
|
582
|
+
}
|
|
583
|
+
// Renders an OTP expiry window as a pluralised minute count
|
|
584
|
+
formatExpiry(expiresAt) {
|
|
585
|
+
const minutes = Math.ceil((expiresAt.getTime() - Date.now()) / 6e4);
|
|
586
|
+
return `${minutes} ${(0, import_pluralize_esm.default)("minute", minutes)}`;
|
|
587
|
+
}
|
|
613
588
|
async sendEmail(emailData) {
|
|
614
589
|
try {
|
|
615
590
|
const result = await this.brevoClient.transactionalEmails.sendTransacEmail({
|