@momentumcms/auth 0.4.1 → 0.5.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/CHANGELOG.md +8 -0
- package/index.cjs +1309 -116
- package/index.js +1313 -114
- package/package.json +9 -1
- package/src/lib/auth.d.ts +7 -4
- package/src/lib/email-components/password-reset-email.component.d.ts +11 -0
- package/src/lib/email-components/verification-email.component.d.ts +11 -0
- package/src/lib/email-templates.d.ts +28 -8
- package/src/lib/plugins/admin.d.ts +3 -6
- package/src/lib/plugins/organization.d.ts +3 -6
package/index.js
CHANGED
|
@@ -1,3 +1,1240 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __esm = (fn, res) => function __init() {
|
|
5
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
+
};
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __decorateClass = (decorators, target, key, kind) => {
|
|
12
|
+
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
|
|
13
|
+
for (var i = decorators.length - 1, decorator; i >= 0; i--)
|
|
14
|
+
if (decorator = decorators[i])
|
|
15
|
+
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
|
|
16
|
+
if (kind && result)
|
|
17
|
+
__defProp(target, key, result);
|
|
18
|
+
return result;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// libs/email/src/lib/utils/css-inliner.ts
|
|
22
|
+
import juice from "juice";
|
|
23
|
+
function inlineCss(html) {
|
|
24
|
+
return juice(html, {
|
|
25
|
+
removeStyleTags: true,
|
|
26
|
+
preserveMediaQueries: true,
|
|
27
|
+
preserveFontFaces: true,
|
|
28
|
+
insertPreservedExtraCss: true
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
var init_css_inliner = __esm({
|
|
32
|
+
"libs/email/src/lib/utils/css-inliner.ts"() {
|
|
33
|
+
"use strict";
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// libs/email/src/lib/utils/strip-artifacts.ts
|
|
38
|
+
function stripAngularArtifacts(html) {
|
|
39
|
+
return html.replace(/<!--[\s\S]*?-->/g, "").replace(/\s*ng-reflect-[\w-]+="[^"]*"/g, "").replace(/\s*_ng(?:host|content)-[\w-]+(?:="")?/g, "").replace(/\s*ng-version="[^"]*"/g, "").replace(/\s*ng-server-context="[^"]*"/g, "").replace(/\s*ngh="[^"]*"/g, "").replace(/\n\s*\n/g, "\n");
|
|
40
|
+
}
|
|
41
|
+
var init_strip_artifacts = __esm({
|
|
42
|
+
"libs/email/src/lib/utils/strip-artifacts.ts"() {
|
|
43
|
+
"use strict";
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// libs/email/src/lib/render/render-types.ts
|
|
48
|
+
import { InjectionToken, inject } from "@angular/core";
|
|
49
|
+
function injectEmailData() {
|
|
50
|
+
return inject(EMAIL_DATA);
|
|
51
|
+
}
|
|
52
|
+
var EMAIL_DATA;
|
|
53
|
+
var init_render_types = __esm({
|
|
54
|
+
"libs/email/src/lib/render/render-types.ts"() {
|
|
55
|
+
"use strict";
|
|
56
|
+
EMAIL_DATA = new InjectionToken("EMAIL_DATA");
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// libs/email/src/lib/render/render-email.ts
|
|
61
|
+
import "@angular/compiler";
|
|
62
|
+
import { reflectComponentType } from "@angular/core";
|
|
63
|
+
import { renderApplication } from "@angular/platform-server";
|
|
64
|
+
import { bootstrapApplication } from "@angular/platform-browser";
|
|
65
|
+
function buildDocument(selector) {
|
|
66
|
+
return `<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"></head><body><${selector}></${selector}></body></html>`;
|
|
67
|
+
}
|
|
68
|
+
async function renderEmail(component, data, options) {
|
|
69
|
+
const shouldInlineCss = options?.inlineCss ?? true;
|
|
70
|
+
const shouldStripArtifacts = options?.stripArtifacts ?? true;
|
|
71
|
+
const extraProviders = options?.providers ?? [];
|
|
72
|
+
const mirror = reflectComponentType(component);
|
|
73
|
+
if (!mirror) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`Cannot reflect component type: ${component.name}. Ensure it has a @Component decorator.`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const providers = [...extraProviders, { provide: EMAIL_DATA, useValue: data ?? {} }];
|
|
79
|
+
let html = await renderApplication(
|
|
80
|
+
(context) => bootstrapApplication(component, { providers }, context),
|
|
81
|
+
{
|
|
82
|
+
document: buildDocument(mirror.selector),
|
|
83
|
+
url: "/"
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
if (shouldStripArtifacts) {
|
|
87
|
+
html = stripAngularArtifacts(html);
|
|
88
|
+
}
|
|
89
|
+
if (shouldInlineCss) {
|
|
90
|
+
html = inlineCss(html);
|
|
91
|
+
}
|
|
92
|
+
return html;
|
|
93
|
+
}
|
|
94
|
+
var init_render_email = __esm({
|
|
95
|
+
"libs/email/src/lib/render/render-email.ts"() {
|
|
96
|
+
"use strict";
|
|
97
|
+
init_css_inliner();
|
|
98
|
+
init_strip_artifacts();
|
|
99
|
+
init_render_types();
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// libs/email/src/lib/utils/escape-html.ts
|
|
104
|
+
function escapeHtml(unsafe) {
|
|
105
|
+
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
106
|
+
}
|
|
107
|
+
var init_escape_html = __esm({
|
|
108
|
+
"libs/email/src/lib/utils/escape-html.ts"() {
|
|
109
|
+
"use strict";
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// libs/email/src/lib/utils/replace-variables.ts
|
|
114
|
+
function replaceVariables(text2, variables) {
|
|
115
|
+
return text2.replace(/\{\{(\w+)\}\}/g, (_, key) => variables[key] ?? "");
|
|
116
|
+
}
|
|
117
|
+
function replaceBlockVariables(blocks2, variables) {
|
|
118
|
+
return blocks2.map((block) => ({
|
|
119
|
+
...block,
|
|
120
|
+
data: replaceDataVariables(block.data, variables)
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
function replaceDataVariables(data, variables) {
|
|
124
|
+
const result = {};
|
|
125
|
+
for (const [key, value] of Object.entries(data)) {
|
|
126
|
+
if (typeof value === "string") {
|
|
127
|
+
result[key] = replaceVariables(value, variables);
|
|
128
|
+
} else if (Array.isArray(value)) {
|
|
129
|
+
result[key] = value.map((item) => {
|
|
130
|
+
if (typeof item === "object" && item !== null && "blocks" in item) {
|
|
131
|
+
const col = item;
|
|
132
|
+
const nestedBlocks = Array.isArray(col["blocks"]) ? col["blocks"] : [];
|
|
133
|
+
return { ...col, blocks: replaceBlockVariables(nestedBlocks, variables) };
|
|
134
|
+
}
|
|
135
|
+
return item;
|
|
136
|
+
});
|
|
137
|
+
} else {
|
|
138
|
+
result[key] = value;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
var init_replace_variables = __esm({
|
|
144
|
+
"libs/email/src/lib/utils/replace-variables.ts"() {
|
|
145
|
+
"use strict";
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// libs/email/src/lib/utils/sanitize.ts
|
|
150
|
+
function sanitizeAlignment(value) {
|
|
151
|
+
return VALID_ALIGNMENTS.has(value) ? value : "left";
|
|
152
|
+
}
|
|
153
|
+
function sanitizeCssValue(value) {
|
|
154
|
+
return value.replace(/[;{}()"'<>\\]/g, "");
|
|
155
|
+
}
|
|
156
|
+
function sanitizeFontFamily(value) {
|
|
157
|
+
return value.replace(/[;{}()"<>\\]/g, "");
|
|
158
|
+
}
|
|
159
|
+
function sanitizeCssNumber(value, fallback) {
|
|
160
|
+
if (value === null || value === void 0)
|
|
161
|
+
return String(fallback);
|
|
162
|
+
const num = Number(value);
|
|
163
|
+
return Number.isFinite(num) && num >= 0 ? String(num) : String(fallback);
|
|
164
|
+
}
|
|
165
|
+
function sanitizeUrl(url) {
|
|
166
|
+
const trimmed = url.trim();
|
|
167
|
+
if (!trimmed || trimmed === "#")
|
|
168
|
+
return trimmed || "#";
|
|
169
|
+
try {
|
|
170
|
+
const parsed = new URL(trimmed);
|
|
171
|
+
return SAFE_URL_PROTOCOLS.has(parsed.protocol) ? trimmed : "#";
|
|
172
|
+
} catch {
|
|
173
|
+
if (trimmed.startsWith("/") || trimmed.startsWith("#"))
|
|
174
|
+
return trimmed;
|
|
175
|
+
return "#";
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
var VALID_ALIGNMENTS, SAFE_URL_PROTOCOLS;
|
|
179
|
+
var init_sanitize = __esm({
|
|
180
|
+
"libs/email/src/lib/utils/sanitize.ts"() {
|
|
181
|
+
"use strict";
|
|
182
|
+
VALID_ALIGNMENTS = /* @__PURE__ */ new Set(["left", "center", "right"]);
|
|
183
|
+
SAFE_URL_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:", "mailto:"]);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// libs/email/src/types.ts
|
|
188
|
+
var DEFAULT_EMAIL_THEME;
|
|
189
|
+
var init_types = __esm({
|
|
190
|
+
"libs/email/src/types.ts"() {
|
|
191
|
+
"use strict";
|
|
192
|
+
DEFAULT_EMAIL_THEME = {
|
|
193
|
+
primaryColor: "#18181b",
|
|
194
|
+
backgroundColor: "#f4f4f5",
|
|
195
|
+
textColor: "#3f3f46",
|
|
196
|
+
mutedColor: "#71717a",
|
|
197
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif",
|
|
198
|
+
borderRadius: "8px"
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// libs/email/src/lib/render/render-blocks.ts
|
|
204
|
+
function renderEmailFromBlocks(template, options) {
|
|
205
|
+
const theme = { ...DEFAULT_EMAIL_THEME, ...template.theme };
|
|
206
|
+
const shouldInline = options?.inlineCss ?? true;
|
|
207
|
+
const blocks2 = options?.variables ? replaceBlockVariables(template.blocks, options.variables) : template.blocks;
|
|
208
|
+
const validBlocks = blocks2.filter((block) => {
|
|
209
|
+
if (!isValidBlock(block)) {
|
|
210
|
+
console.warn("[momentum:email] Skipping invalid email block:", block);
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
});
|
|
215
|
+
const blocksHtml = validBlocks.map((block) => renderBlock(block, theme, 0)).join("\n");
|
|
216
|
+
let html = wrapEmailDocument(blocksHtml, theme);
|
|
217
|
+
if (shouldInline) {
|
|
218
|
+
html = inlineCss(html);
|
|
219
|
+
}
|
|
220
|
+
return html;
|
|
221
|
+
}
|
|
222
|
+
function wrapEmailDocument(content, theme) {
|
|
223
|
+
const fontFamily = sanitizeFontFamily(theme.fontFamily);
|
|
224
|
+
const bgColor = sanitizeCssValue(theme.backgroundColor);
|
|
225
|
+
const borderRadius = sanitizeCssValue(theme.borderRadius);
|
|
226
|
+
return `<!DOCTYPE html>
|
|
227
|
+
<html lang="en">
|
|
228
|
+
<head>
|
|
229
|
+
<meta charset="UTF-8">
|
|
230
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
231
|
+
</head>
|
|
232
|
+
<body style="margin: 0; padding: 0; font-family: ${fontFamily}; background-color: ${bgColor}; line-height: 1.6;">
|
|
233
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="background-color: ${bgColor};">
|
|
234
|
+
<tr>
|
|
235
|
+
<td style="padding: 40px 20px;">
|
|
236
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 480px; margin: 0 auto; background-color: #ffffff; border-radius: ${borderRadius}; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
|
|
237
|
+
<tr>
|
|
238
|
+
<td style="padding: 40px;">
|
|
239
|
+
${content}
|
|
240
|
+
</td>
|
|
241
|
+
</tr>
|
|
242
|
+
</table>
|
|
243
|
+
</td>
|
|
244
|
+
</tr>
|
|
245
|
+
</table>
|
|
246
|
+
</body>
|
|
247
|
+
</html>`;
|
|
248
|
+
}
|
|
249
|
+
function isValidBlock(block) {
|
|
250
|
+
const rec = block;
|
|
251
|
+
return typeof block === "object" && block !== null && typeof rec["id"] === "string" && rec["id"].length > 0 && typeof rec["type"] === "string" && typeof rec["data"] === "object" && rec["data"] !== null;
|
|
252
|
+
}
|
|
253
|
+
function renderBlock(block, theme, depth) {
|
|
254
|
+
switch (block.type) {
|
|
255
|
+
case "header":
|
|
256
|
+
return renderHeaderBlock(block.data, theme);
|
|
257
|
+
case "text":
|
|
258
|
+
return renderTextBlock(block.data, theme);
|
|
259
|
+
case "button":
|
|
260
|
+
return renderButtonBlock(block.data, theme);
|
|
261
|
+
case "image":
|
|
262
|
+
return renderImageBlock(block.data);
|
|
263
|
+
case "divider":
|
|
264
|
+
return renderDividerBlock(block.data);
|
|
265
|
+
case "spacer":
|
|
266
|
+
return renderSpacerBlock(block.data);
|
|
267
|
+
case "columns":
|
|
268
|
+
if (depth >= MAX_BLOCK_DEPTH) {
|
|
269
|
+
console.warn("[momentum:email] Max nesting depth reached, skipping columns block");
|
|
270
|
+
return "";
|
|
271
|
+
}
|
|
272
|
+
return renderColumnsBlock(block.data, theme, depth);
|
|
273
|
+
case "footer":
|
|
274
|
+
return renderFooterBlock(block.data, theme);
|
|
275
|
+
default:
|
|
276
|
+
return `<!-- unknown block type: ${escapeHtml(block.type)} -->`;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
function renderHeaderBlock(data, theme) {
|
|
280
|
+
const title = escapeHtml(String(data["title"] ?? ""));
|
|
281
|
+
const subtitle = data["subtitle"] ? escapeHtml(String(data["subtitle"])) : "";
|
|
282
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
283
|
+
return `<h1 style="margin: 0 0 8px; font-size: 24px; font-weight: 600; color: ${sanitizeCssValue(theme.textColor)}; text-align: ${alignment};">${title}</h1>${subtitle ? `<p style="margin: 0 0 16px; font-size: 16px; color: ${sanitizeCssValue(theme.mutedColor)}; text-align: ${alignment};">${subtitle}</p>` : ""}`;
|
|
284
|
+
}
|
|
285
|
+
function renderTextBlock(data, theme) {
|
|
286
|
+
const content = escapeHtml(String(data["content"] ?? ""));
|
|
287
|
+
const fontSize = sanitizeCssNumber(data["fontSize"], 16);
|
|
288
|
+
const color = sanitizeCssValue(String(data["color"] ?? theme.textColor));
|
|
289
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
290
|
+
return `<p style="margin: 0 0 16px; font-size: ${fontSize}px; color: ${color}; text-align: ${alignment}; line-height: 1.6;">${content}</p>`;
|
|
291
|
+
}
|
|
292
|
+
function renderButtonBlock(data, theme) {
|
|
293
|
+
const label = escapeHtml(String(data["label"] ?? "Click here"));
|
|
294
|
+
const href = escapeHtml(sanitizeUrl(String(data["href"] ?? "#")));
|
|
295
|
+
const bgColor = sanitizeCssValue(String(data["backgroundColor"] ?? theme.primaryColor));
|
|
296
|
+
const color = sanitizeCssValue(String(data["color"] ?? "#ffffff"));
|
|
297
|
+
const alignment = sanitizeAlignment(String(data["alignment"] ?? "left"));
|
|
298
|
+
return `<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
299
|
+
<tr>
|
|
300
|
+
<td style="padding: 0 0 16px;" align="${alignment}">
|
|
301
|
+
<a href="${href}" style="display: inline-block; padding: 12px 24px; background-color: ${bgColor}; color: ${color}; text-decoration: none; border-radius: 6px; font-weight: 500; font-size: 16px;">${label}</a>
|
|
302
|
+
</td>
|
|
303
|
+
</tr>
|
|
304
|
+
</table>`;
|
|
305
|
+
}
|
|
306
|
+
function renderImageBlock(data) {
|
|
307
|
+
const rawSrc = String(data["src"] ?? "").trim();
|
|
308
|
+
if (!rawSrc)
|
|
309
|
+
return "<!-- image block: no src configured -->";
|
|
310
|
+
const src = escapeHtml(sanitizeUrl(rawSrc));
|
|
311
|
+
const alt = escapeHtml(String(data["alt"] ?? ""));
|
|
312
|
+
const width = sanitizeCssValue(String(data["width"] ?? "100%"));
|
|
313
|
+
const img = `<img src="${src}" alt="${alt}" width="${width}" style="display: block; max-width: 100%; height: auto; border: 0;">`;
|
|
314
|
+
if (data["href"]) {
|
|
315
|
+
const href = escapeHtml(sanitizeUrl(String(data["href"])));
|
|
316
|
+
return `<a href="${href}" style="display: block;">${img}</a>`;
|
|
317
|
+
}
|
|
318
|
+
return img;
|
|
319
|
+
}
|
|
320
|
+
function renderDividerBlock(data) {
|
|
321
|
+
const color = sanitizeCssValue(String(data["color"] ?? "#e4e4e7"));
|
|
322
|
+
const margin = sanitizeCssValue(String(data["margin"] ?? "24px 0"));
|
|
323
|
+
return `<hr style="border: none; border-top: 1px solid ${color}; margin: ${margin};">`;
|
|
324
|
+
}
|
|
325
|
+
function renderSpacerBlock(data) {
|
|
326
|
+
const height = sanitizeCssNumber(data["height"], 24);
|
|
327
|
+
return `<div style="height: ${height}px; line-height: ${height}px; font-size: 1px;"> </div>`;
|
|
328
|
+
}
|
|
329
|
+
function renderColumnsBlock(data, theme, depth) {
|
|
330
|
+
const rawColumns = data["columns"];
|
|
331
|
+
const columns = Array.isArray(rawColumns) ? rawColumns : [];
|
|
332
|
+
const width = Math.floor(100 / (columns.length || 1));
|
|
333
|
+
const tds = columns.map((col) => {
|
|
334
|
+
const colObj = (
|
|
335
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- narrowing unknown column objects
|
|
336
|
+
typeof col === "object" && col !== null ? col : {}
|
|
337
|
+
);
|
|
338
|
+
const rawBlocks = colObj["blocks"];
|
|
339
|
+
const colContent = (Array.isArray(rawBlocks) ? rawBlocks : []).filter(isValidBlock).map((b) => renderBlock(b, theme, depth + 1)).join("\n");
|
|
340
|
+
return `<td style="width: ${width}%; vertical-align: top; padding: 0 8px;">${colContent}</td>`;
|
|
341
|
+
}).join("\n");
|
|
342
|
+
return `<table role="presentation" width="100%" cellspacing="0" cellpadding="0"><tr>${tds}</tr></table>`;
|
|
343
|
+
}
|
|
344
|
+
function renderFooterBlock(data, theme) {
|
|
345
|
+
const text2 = escapeHtml(String(data["text"] ?? ""));
|
|
346
|
+
const color = sanitizeCssValue(String(data["color"] ?? theme.mutedColor));
|
|
347
|
+
return `<p style="margin: 16px 0 0; font-size: 12px; color: ${color}; text-align: center;">${text2}</p>`;
|
|
348
|
+
}
|
|
349
|
+
var MAX_BLOCK_DEPTH;
|
|
350
|
+
var init_render_blocks = __esm({
|
|
351
|
+
"libs/email/src/lib/render/render-blocks.ts"() {
|
|
352
|
+
"use strict";
|
|
353
|
+
init_escape_html();
|
|
354
|
+
init_css_inliner();
|
|
355
|
+
init_replace_variables();
|
|
356
|
+
init_sanitize();
|
|
357
|
+
init_types();
|
|
358
|
+
MAX_BLOCK_DEPTH = 5;
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// libs/email/src/lib/templates/default-templates.ts
|
|
363
|
+
var DEFAULT_PASSWORD_RESET_BLOCKS, DEFAULT_VERIFICATION_BLOCKS;
|
|
364
|
+
var init_default_templates = __esm({
|
|
365
|
+
"libs/email/src/lib/templates/default-templates.ts"() {
|
|
366
|
+
"use strict";
|
|
367
|
+
DEFAULT_PASSWORD_RESET_BLOCKS = [
|
|
368
|
+
{
|
|
369
|
+
id: "pr-header",
|
|
370
|
+
type: "header",
|
|
371
|
+
data: {
|
|
372
|
+
title: "Reset Your Password",
|
|
373
|
+
subtitle: "",
|
|
374
|
+
alignment: "left"
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
id: "pr-greeting",
|
|
379
|
+
type: "text",
|
|
380
|
+
data: {
|
|
381
|
+
content: "{{greeting}}",
|
|
382
|
+
fontSize: 16,
|
|
383
|
+
color: "#3f3f46",
|
|
384
|
+
alignment: "left"
|
|
385
|
+
}
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
id: "pr-body",
|
|
389
|
+
type: "text",
|
|
390
|
+
data: {
|
|
391
|
+
content: "We received a request to reset your password. Click the button below to choose a new password:",
|
|
392
|
+
fontSize: 16,
|
|
393
|
+
color: "#3f3f46",
|
|
394
|
+
alignment: "left"
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
id: "pr-button",
|
|
399
|
+
type: "button",
|
|
400
|
+
data: {
|
|
401
|
+
label: "Reset Password",
|
|
402
|
+
href: "{{url}}",
|
|
403
|
+
backgroundColor: "#18181b",
|
|
404
|
+
color: "#ffffff",
|
|
405
|
+
alignment: "center"
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
id: "pr-expiry",
|
|
410
|
+
type: "text",
|
|
411
|
+
data: {
|
|
412
|
+
content: "This link will expire in {{expiresIn}}.",
|
|
413
|
+
fontSize: 14,
|
|
414
|
+
color: "#71717a",
|
|
415
|
+
alignment: "left"
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
id: "pr-ignore",
|
|
420
|
+
type: "text",
|
|
421
|
+
data: {
|
|
422
|
+
content: "If you didn't request a password reset, you can safely ignore this email. Your password will remain unchanged.",
|
|
423
|
+
fontSize: 14,
|
|
424
|
+
color: "#71717a",
|
|
425
|
+
alignment: "left"
|
|
426
|
+
}
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
id: "pr-divider",
|
|
430
|
+
type: "divider",
|
|
431
|
+
data: {
|
|
432
|
+
color: "#e4e4e7",
|
|
433
|
+
margin: "24px 0"
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
id: "pr-footer",
|
|
438
|
+
type: "footer",
|
|
439
|
+
data: {
|
|
440
|
+
text: "The {{appName}} Team",
|
|
441
|
+
color: "#71717a"
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
];
|
|
445
|
+
DEFAULT_VERIFICATION_BLOCKS = [
|
|
446
|
+
{
|
|
447
|
+
id: "ev-header",
|
|
448
|
+
type: "header",
|
|
449
|
+
data: {
|
|
450
|
+
title: "Verify Your Email",
|
|
451
|
+
subtitle: "",
|
|
452
|
+
alignment: "left"
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
id: "ev-greeting",
|
|
457
|
+
type: "text",
|
|
458
|
+
data: {
|
|
459
|
+
content: "{{greeting}}",
|
|
460
|
+
fontSize: 16,
|
|
461
|
+
color: "#3f3f46",
|
|
462
|
+
alignment: "left"
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: "ev-body",
|
|
467
|
+
type: "text",
|
|
468
|
+
data: {
|
|
469
|
+
content: "Welcome to {{appName}}! Please verify your email address by clicking the button below:",
|
|
470
|
+
fontSize: 16,
|
|
471
|
+
color: "#3f3f46",
|
|
472
|
+
alignment: "left"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
id: "ev-button",
|
|
477
|
+
type: "button",
|
|
478
|
+
data: {
|
|
479
|
+
label: "Verify Email",
|
|
480
|
+
href: "{{url}}",
|
|
481
|
+
backgroundColor: "#18181b",
|
|
482
|
+
color: "#ffffff",
|
|
483
|
+
alignment: "center"
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
id: "ev-expiry",
|
|
488
|
+
type: "text",
|
|
489
|
+
data: {
|
|
490
|
+
content: "This link will expire in {{expiresIn}}.",
|
|
491
|
+
fontSize: 14,
|
|
492
|
+
color: "#71717a",
|
|
493
|
+
alignment: "left"
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
id: "ev-ignore",
|
|
498
|
+
type: "text",
|
|
499
|
+
data: {
|
|
500
|
+
content: "If you didn't create an account, you can safely ignore this email.",
|
|
501
|
+
fontSize: 14,
|
|
502
|
+
color: "#71717a",
|
|
503
|
+
alignment: "left"
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
id: "ev-divider",
|
|
508
|
+
type: "divider",
|
|
509
|
+
data: {
|
|
510
|
+
color: "#e4e4e7",
|
|
511
|
+
margin: "24px 0"
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
id: "ev-footer",
|
|
516
|
+
type: "footer",
|
|
517
|
+
data: {
|
|
518
|
+
text: "The {{appName}} Team",
|
|
519
|
+
color: "#71717a"
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
];
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
// libs/email/src/lib/components/eml-body.component.ts
|
|
527
|
+
import { Component, ChangeDetectionStrategy, input, computed } from "@angular/core";
|
|
528
|
+
var EmlBody;
|
|
529
|
+
var init_eml_body_component = __esm({
|
|
530
|
+
"libs/email/src/lib/components/eml-body.component.ts"() {
|
|
531
|
+
"use strict";
|
|
532
|
+
EmlBody = class {
|
|
533
|
+
constructor() {
|
|
534
|
+
this.backgroundColor = input("#f4f4f5");
|
|
535
|
+
this.fontFamily = input(
|
|
536
|
+
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"
|
|
537
|
+
);
|
|
538
|
+
this.padding = input("40px 20px");
|
|
539
|
+
this.tableStyle = computed(
|
|
540
|
+
() => `background-color: ${this.backgroundColor()}; font-family: ${this.fontFamily()}; line-height: 1.6; margin: 0; padding: 0;`
|
|
541
|
+
);
|
|
542
|
+
this.cellStyle = computed(() => `padding: ${this.padding()};`);
|
|
543
|
+
}
|
|
544
|
+
};
|
|
545
|
+
EmlBody = __decorateClass([
|
|
546
|
+
Component({
|
|
547
|
+
selector: "eml-body",
|
|
548
|
+
template: `
|
|
549
|
+
<table
|
|
550
|
+
role="presentation"
|
|
551
|
+
width="100%"
|
|
552
|
+
cellspacing="0"
|
|
553
|
+
cellpadding="0"
|
|
554
|
+
[attr.style]="tableStyle()"
|
|
555
|
+
>
|
|
556
|
+
<tr>
|
|
557
|
+
<td [attr.style]="cellStyle()">
|
|
558
|
+
<ng-content />
|
|
559
|
+
</td>
|
|
560
|
+
</tr>
|
|
561
|
+
</table>
|
|
562
|
+
`,
|
|
563
|
+
changeDetection: ChangeDetectionStrategy.OnPush
|
|
564
|
+
})
|
|
565
|
+
], EmlBody);
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
// libs/email/src/lib/components/eml-container.component.ts
|
|
570
|
+
import { Component as Component2, ChangeDetectionStrategy as ChangeDetectionStrategy2, input as input2, computed as computed2 } from "@angular/core";
|
|
571
|
+
var EmlContainer;
|
|
572
|
+
var init_eml_container_component = __esm({
|
|
573
|
+
"libs/email/src/lib/components/eml-container.component.ts"() {
|
|
574
|
+
"use strict";
|
|
575
|
+
EmlContainer = class {
|
|
576
|
+
constructor() {
|
|
577
|
+
this.maxWidth = input2("480px");
|
|
578
|
+
this.backgroundColor = input2("#ffffff");
|
|
579
|
+
this.borderRadius = input2("8px");
|
|
580
|
+
this.padding = input2("40px");
|
|
581
|
+
this.shadow = input2("0 1px 3px rgba(0,0,0,0.1)");
|
|
582
|
+
this.tableStyle = computed2(
|
|
583
|
+
() => `max-width: ${this.maxWidth()}; margin: 0 auto; background-color: ${this.backgroundColor()}; border-radius: ${this.borderRadius()}; box-shadow: ${this.shadow()};`
|
|
584
|
+
);
|
|
585
|
+
this.cellStyle = computed2(() => `padding: ${this.padding()};`);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
EmlContainer = __decorateClass([
|
|
589
|
+
Component2({
|
|
590
|
+
selector: "eml-container",
|
|
591
|
+
template: `
|
|
592
|
+
<table
|
|
593
|
+
role="presentation"
|
|
594
|
+
width="100%"
|
|
595
|
+
cellspacing="0"
|
|
596
|
+
cellpadding="0"
|
|
597
|
+
[attr.style]="tableStyle()"
|
|
598
|
+
>
|
|
599
|
+
<tr>
|
|
600
|
+
<td [attr.style]="cellStyle()">
|
|
601
|
+
<ng-content />
|
|
602
|
+
</td>
|
|
603
|
+
</tr>
|
|
604
|
+
</table>
|
|
605
|
+
`,
|
|
606
|
+
changeDetection: ChangeDetectionStrategy2.OnPush
|
|
607
|
+
})
|
|
608
|
+
], EmlContainer);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
// libs/email/src/lib/components/eml-section.component.ts
|
|
613
|
+
import { Component as Component3, ChangeDetectionStrategy as ChangeDetectionStrategy3, input as input3, computed as computed3 } from "@angular/core";
|
|
614
|
+
var EmlSection;
|
|
615
|
+
var init_eml_section_component = __esm({
|
|
616
|
+
"libs/email/src/lib/components/eml-section.component.ts"() {
|
|
617
|
+
"use strict";
|
|
618
|
+
EmlSection = class {
|
|
619
|
+
constructor() {
|
|
620
|
+
this.padding = input3("0");
|
|
621
|
+
this.cellStyle = computed3(() => `padding: ${this.padding()};`);
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
EmlSection = __decorateClass([
|
|
625
|
+
Component3({
|
|
626
|
+
selector: "eml-section",
|
|
627
|
+
template: `
|
|
628
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
629
|
+
<tr>
|
|
630
|
+
<td [attr.style]="cellStyle()">
|
|
631
|
+
<ng-content />
|
|
632
|
+
</td>
|
|
633
|
+
</tr>
|
|
634
|
+
</table>
|
|
635
|
+
`,
|
|
636
|
+
changeDetection: ChangeDetectionStrategy3.OnPush
|
|
637
|
+
})
|
|
638
|
+
], EmlSection);
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// libs/email/src/lib/components/eml-row.component.ts
|
|
643
|
+
import { Component as Component4, ChangeDetectionStrategy as ChangeDetectionStrategy4 } from "@angular/core";
|
|
644
|
+
var EmlRow;
|
|
645
|
+
var init_eml_row_component = __esm({
|
|
646
|
+
"libs/email/src/lib/components/eml-row.component.ts"() {
|
|
647
|
+
"use strict";
|
|
648
|
+
EmlRow = class {
|
|
649
|
+
};
|
|
650
|
+
EmlRow = __decorateClass([
|
|
651
|
+
Component4({
|
|
652
|
+
selector: "eml-row",
|
|
653
|
+
template: `
|
|
654
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
655
|
+
<tr>
|
|
656
|
+
<ng-content />
|
|
657
|
+
</tr>
|
|
658
|
+
</table>
|
|
659
|
+
`,
|
|
660
|
+
changeDetection: ChangeDetectionStrategy4.OnPush
|
|
661
|
+
})
|
|
662
|
+
], EmlRow);
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
// libs/email/src/lib/components/eml-column.component.ts
|
|
667
|
+
import { Component as Component5, ChangeDetectionStrategy as ChangeDetectionStrategy5, input as input4, computed as computed4 } from "@angular/core";
|
|
668
|
+
var EmlColumn;
|
|
669
|
+
var init_eml_column_component = __esm({
|
|
670
|
+
"libs/email/src/lib/components/eml-column.component.ts"() {
|
|
671
|
+
"use strict";
|
|
672
|
+
EmlColumn = class {
|
|
673
|
+
constructor() {
|
|
674
|
+
this.width = input4(void 0);
|
|
675
|
+
this.padding = input4("0");
|
|
676
|
+
this.verticalAlign = input4("top");
|
|
677
|
+
this.cellStyle = computed4(
|
|
678
|
+
() => `padding: ${this.padding()}; vertical-align: ${this.verticalAlign()};`
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
EmlColumn = __decorateClass([
|
|
683
|
+
Component5({
|
|
684
|
+
selector: "eml-column",
|
|
685
|
+
template: `
|
|
686
|
+
<td [attr.style]="cellStyle()" [attr.width]="width()" valign="top">
|
|
687
|
+
<ng-content />
|
|
688
|
+
</td>
|
|
689
|
+
`,
|
|
690
|
+
changeDetection: ChangeDetectionStrategy5.OnPush
|
|
691
|
+
})
|
|
692
|
+
], EmlColumn);
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
// libs/email/src/lib/components/eml-text.component.ts
|
|
697
|
+
import { Component as Component6, ChangeDetectionStrategy as ChangeDetectionStrategy6, input as input5, computed as computed5 } from "@angular/core";
|
|
698
|
+
var EmlText;
|
|
699
|
+
var init_eml_text_component = __esm({
|
|
700
|
+
"libs/email/src/lib/components/eml-text.component.ts"() {
|
|
701
|
+
"use strict";
|
|
702
|
+
EmlText = class {
|
|
703
|
+
constructor() {
|
|
704
|
+
this.color = input5("#3f3f46");
|
|
705
|
+
this.fontSize = input5("16px");
|
|
706
|
+
this.lineHeight = input5("1.6");
|
|
707
|
+
this.margin = input5("0 0 16px");
|
|
708
|
+
this.textAlign = input5("left");
|
|
709
|
+
this.pStyle = computed5(
|
|
710
|
+
() => `margin: ${this.margin()}; color: ${this.color()}; font-size: ${this.fontSize()}; line-height: ${this.lineHeight()}; text-align: ${this.textAlign()};`
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
};
|
|
714
|
+
EmlText = __decorateClass([
|
|
715
|
+
Component6({
|
|
716
|
+
selector: "eml-text",
|
|
717
|
+
template: `
|
|
718
|
+
<p [attr.style]="pStyle()">
|
|
719
|
+
<ng-content />
|
|
720
|
+
</p>
|
|
721
|
+
`,
|
|
722
|
+
changeDetection: ChangeDetectionStrategy6.OnPush
|
|
723
|
+
})
|
|
724
|
+
], EmlText);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
// libs/email/src/lib/components/eml-heading.component.ts
|
|
729
|
+
import { Component as Component7, ChangeDetectionStrategy as ChangeDetectionStrategy7, input as input6, computed as computed6 } from "@angular/core";
|
|
730
|
+
var EmlHeading;
|
|
731
|
+
var init_eml_heading_component = __esm({
|
|
732
|
+
"libs/email/src/lib/components/eml-heading.component.ts"() {
|
|
733
|
+
"use strict";
|
|
734
|
+
EmlHeading = class {
|
|
735
|
+
constructor() {
|
|
736
|
+
this.level = input6(1);
|
|
737
|
+
this.color = input6("#18181b");
|
|
738
|
+
this.margin = input6("0 0 24px");
|
|
739
|
+
this.textAlign = input6("left");
|
|
740
|
+
this.fontSizeMap = {
|
|
741
|
+
1: "24px",
|
|
742
|
+
2: "20px",
|
|
743
|
+
3: "16px"
|
|
744
|
+
};
|
|
745
|
+
this.headingStyle = computed6(
|
|
746
|
+
() => `margin: ${this.margin()}; font-size: ${this.fontSizeMap[this.level()] ?? "24px"}; font-weight: 600; color: ${this.color()}; text-align: ${this.textAlign()};`
|
|
747
|
+
);
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
EmlHeading = __decorateClass([
|
|
751
|
+
Component7({
|
|
752
|
+
selector: "eml-heading",
|
|
753
|
+
template: `<div [attr.style]="headingStyle()" [attr.role]="'heading'" [attr.aria-level]="level()">
|
|
754
|
+
<ng-content />
|
|
755
|
+
</div>`,
|
|
756
|
+
changeDetection: ChangeDetectionStrategy7.OnPush
|
|
757
|
+
})
|
|
758
|
+
], EmlHeading);
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
// libs/email/src/lib/components/eml-button.component.ts
|
|
763
|
+
import { Component as Component8, ChangeDetectionStrategy as ChangeDetectionStrategy8, input as input7, computed as computed7 } from "@angular/core";
|
|
764
|
+
var EmlButton;
|
|
765
|
+
var init_eml_button_component = __esm({
|
|
766
|
+
"libs/email/src/lib/components/eml-button.component.ts"() {
|
|
767
|
+
"use strict";
|
|
768
|
+
EmlButton = class {
|
|
769
|
+
constructor() {
|
|
770
|
+
this.href = input7("");
|
|
771
|
+
this.backgroundColor = input7("#18181b");
|
|
772
|
+
this.color = input7("#ffffff");
|
|
773
|
+
this.borderRadius = input7("6px");
|
|
774
|
+
this.padding = input7("12px 24px");
|
|
775
|
+
this.textAlign = input7("left");
|
|
776
|
+
this.alignStyle = computed7(() => `padding: 0; text-align: ${this.textAlign()};`);
|
|
777
|
+
this.linkStyle = computed7(
|
|
778
|
+
() => `display: inline-block; padding: ${this.padding()}; background-color: ${this.backgroundColor()}; color: ${this.color()}; text-decoration: none; border-radius: ${this.borderRadius()}; font-weight: 500;`
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
};
|
|
782
|
+
EmlButton = __decorateClass([
|
|
783
|
+
Component8({
|
|
784
|
+
selector: "eml-button",
|
|
785
|
+
template: `
|
|
786
|
+
<table role="presentation" width="100%" cellspacing="0" cellpadding="0">
|
|
787
|
+
<tr>
|
|
788
|
+
<td [attr.style]="alignStyle()">
|
|
789
|
+
<a [attr.href]="href()" [attr.style]="linkStyle()" target="_blank">
|
|
790
|
+
<ng-content />
|
|
791
|
+
</a>
|
|
792
|
+
</td>
|
|
793
|
+
</tr>
|
|
794
|
+
</table>
|
|
795
|
+
`,
|
|
796
|
+
changeDetection: ChangeDetectionStrategy8.OnPush
|
|
797
|
+
})
|
|
798
|
+
], EmlButton);
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
// libs/email/src/lib/components/eml-link.component.ts
|
|
803
|
+
import { Component as Component9, ChangeDetectionStrategy as ChangeDetectionStrategy9, input as input8, computed as computed8 } from "@angular/core";
|
|
804
|
+
var EmlLink;
|
|
805
|
+
var init_eml_link_component = __esm({
|
|
806
|
+
"libs/email/src/lib/components/eml-link.component.ts"() {
|
|
807
|
+
"use strict";
|
|
808
|
+
EmlLink = class {
|
|
809
|
+
constructor() {
|
|
810
|
+
this.href = input8("");
|
|
811
|
+
this.color = input8("#18181b");
|
|
812
|
+
this.textDecoration = input8("underline");
|
|
813
|
+
this.linkStyle = computed8(
|
|
814
|
+
() => `color: ${this.color()}; text-decoration: ${this.textDecoration()};`
|
|
815
|
+
);
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
EmlLink = __decorateClass([
|
|
819
|
+
Component9({
|
|
820
|
+
selector: "eml-link",
|
|
821
|
+
template: `
|
|
822
|
+
<a [attr.href]="href()" [attr.style]="linkStyle()" target="_blank">
|
|
823
|
+
<ng-content />
|
|
824
|
+
</a>
|
|
825
|
+
`,
|
|
826
|
+
changeDetection: ChangeDetectionStrategy9.OnPush
|
|
827
|
+
})
|
|
828
|
+
], EmlLink);
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
|
|
832
|
+
// libs/email/src/lib/components/eml-image.component.ts
|
|
833
|
+
import { Component as Component10, ChangeDetectionStrategy as ChangeDetectionStrategy10, input as input9, computed as computed9 } from "@angular/core";
|
|
834
|
+
var EmlImage;
|
|
835
|
+
var init_eml_image_component = __esm({
|
|
836
|
+
"libs/email/src/lib/components/eml-image.component.ts"() {
|
|
837
|
+
"use strict";
|
|
838
|
+
EmlImage = class {
|
|
839
|
+
constructor() {
|
|
840
|
+
this.src = input9("");
|
|
841
|
+
this.alt = input9("");
|
|
842
|
+
this.width = input9(void 0);
|
|
843
|
+
this.height = input9(void 0);
|
|
844
|
+
this.borderRadius = input9("0");
|
|
845
|
+
this.imgStyle = computed9(
|
|
846
|
+
() => `display: block; max-width: 100%; border: 0; outline: none; border-radius: ${this.borderRadius()};`
|
|
847
|
+
);
|
|
848
|
+
}
|
|
849
|
+
};
|
|
850
|
+
EmlImage = __decorateClass([
|
|
851
|
+
Component10({
|
|
852
|
+
selector: "eml-image",
|
|
853
|
+
template: `
|
|
854
|
+
<img
|
|
855
|
+
[attr.src]="src()"
|
|
856
|
+
[attr.alt]="alt()"
|
|
857
|
+
[attr.width]="width()"
|
|
858
|
+
[attr.height]="height()"
|
|
859
|
+
[attr.style]="imgStyle()"
|
|
860
|
+
/>
|
|
861
|
+
`,
|
|
862
|
+
changeDetection: ChangeDetectionStrategy10.OnPush
|
|
863
|
+
})
|
|
864
|
+
], EmlImage);
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
// libs/email/src/lib/components/eml-divider.component.ts
|
|
869
|
+
import { Component as Component11, ChangeDetectionStrategy as ChangeDetectionStrategy11, input as input10, computed as computed10 } from "@angular/core";
|
|
870
|
+
var EmlDivider;
|
|
871
|
+
var init_eml_divider_component = __esm({
|
|
872
|
+
"libs/email/src/lib/components/eml-divider.component.ts"() {
|
|
873
|
+
"use strict";
|
|
874
|
+
EmlDivider = class {
|
|
875
|
+
constructor() {
|
|
876
|
+
this.color = input10("#e4e4e7");
|
|
877
|
+
this.margin = input10("24px 0");
|
|
878
|
+
this.hrStyle = computed10(
|
|
879
|
+
() => `border: none; border-top: 1px solid ${this.color()}; margin: ${this.margin()};`
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
EmlDivider = __decorateClass([
|
|
884
|
+
Component11({
|
|
885
|
+
selector: "eml-divider",
|
|
886
|
+
template: `<hr [attr.style]="hrStyle()" />`,
|
|
887
|
+
changeDetection: ChangeDetectionStrategy11.OnPush
|
|
888
|
+
})
|
|
889
|
+
], EmlDivider);
|
|
890
|
+
}
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
// libs/email/src/lib/components/eml-preview.component.ts
|
|
894
|
+
import { Component as Component12, ChangeDetectionStrategy as ChangeDetectionStrategy12 } from "@angular/core";
|
|
895
|
+
var EmlPreview;
|
|
896
|
+
var init_eml_preview_component = __esm({
|
|
897
|
+
"libs/email/src/lib/components/eml-preview.component.ts"() {
|
|
898
|
+
"use strict";
|
|
899
|
+
EmlPreview = class {
|
|
900
|
+
};
|
|
901
|
+
EmlPreview = __decorateClass([
|
|
902
|
+
Component12({
|
|
903
|
+
selector: "eml-preview",
|
|
904
|
+
template: `
|
|
905
|
+
<div style="display: none; max-height: 0; overflow: hidden; mso-hide: all;">
|
|
906
|
+
<ng-content />
|
|
907
|
+
</div>
|
|
908
|
+
`,
|
|
909
|
+
changeDetection: ChangeDetectionStrategy12.OnPush
|
|
910
|
+
})
|
|
911
|
+
], EmlPreview);
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
|
|
915
|
+
// libs/email/src/lib/components/eml-spacer.component.ts
|
|
916
|
+
import { Component as Component13, ChangeDetectionStrategy as ChangeDetectionStrategy13, input as input11, computed as computed11 } from "@angular/core";
|
|
917
|
+
var EmlSpacer;
|
|
918
|
+
var init_eml_spacer_component = __esm({
|
|
919
|
+
"libs/email/src/lib/components/eml-spacer.component.ts"() {
|
|
920
|
+
"use strict";
|
|
921
|
+
EmlSpacer = class {
|
|
922
|
+
constructor() {
|
|
923
|
+
this.height = input11("24px");
|
|
924
|
+
this.spacerStyle = computed11(
|
|
925
|
+
() => `height: ${this.height()}; line-height: ${this.height()}; font-size: 1px;`
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
};
|
|
929
|
+
EmlSpacer = __decorateClass([
|
|
930
|
+
Component13({
|
|
931
|
+
selector: "eml-spacer",
|
|
932
|
+
template: `<div [attr.style]="spacerStyle()"></div>`,
|
|
933
|
+
changeDetection: ChangeDetectionStrategy13.OnPush
|
|
934
|
+
})
|
|
935
|
+
], EmlSpacer);
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
// libs/email/src/lib/components/eml-footer.component.ts
|
|
940
|
+
import { Component as Component14, ChangeDetectionStrategy as ChangeDetectionStrategy14, input as input12, computed as computed12 } from "@angular/core";
|
|
941
|
+
var EmlFooter;
|
|
942
|
+
var init_eml_footer_component = __esm({
|
|
943
|
+
"libs/email/src/lib/components/eml-footer.component.ts"() {
|
|
944
|
+
"use strict";
|
|
945
|
+
EmlFooter = class {
|
|
946
|
+
constructor() {
|
|
947
|
+
this.maxWidth = input12("480px");
|
|
948
|
+
this.color = input12("#71717a");
|
|
949
|
+
this.fontSize = input12("12px");
|
|
950
|
+
this.textAlign = input12("center");
|
|
951
|
+
this.padding = input12("20px 0 0");
|
|
952
|
+
this.tableStyle = computed12(() => `max-width: ${this.maxWidth()}; margin: 0 auto;`);
|
|
953
|
+
this.cellStyle = computed12(
|
|
954
|
+
() => `text-align: ${this.textAlign()}; color: ${this.color()}; font-size: ${this.fontSize()}; padding: ${this.padding()};`
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
EmlFooter = __decorateClass([
|
|
959
|
+
Component14({
|
|
960
|
+
selector: "eml-footer",
|
|
961
|
+
template: `
|
|
962
|
+
<table
|
|
963
|
+
role="presentation"
|
|
964
|
+
width="100%"
|
|
965
|
+
cellspacing="0"
|
|
966
|
+
cellpadding="0"
|
|
967
|
+
[attr.style]="tableStyle()"
|
|
968
|
+
>
|
|
969
|
+
<tr>
|
|
970
|
+
<td [attr.style]="cellStyle()">
|
|
971
|
+
<ng-content />
|
|
972
|
+
</td>
|
|
973
|
+
</tr>
|
|
974
|
+
</table>
|
|
975
|
+
`,
|
|
976
|
+
changeDetection: ChangeDetectionStrategy14.OnPush
|
|
977
|
+
})
|
|
978
|
+
], EmlFooter);
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
|
|
982
|
+
// libs/email/src/lib/utils/blocks-to-plain-text.ts
|
|
983
|
+
function blocksToPlainText(blocks2, depth = 0) {
|
|
984
|
+
const lines = [];
|
|
985
|
+
for (const block of blocks2) {
|
|
986
|
+
const text2 = blockToText(block, depth);
|
|
987
|
+
if (text2) {
|
|
988
|
+
lines.push(text2);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
return lines.join("\n\n");
|
|
992
|
+
}
|
|
993
|
+
function blockToText(block, depth) {
|
|
994
|
+
switch (block.type) {
|
|
995
|
+
case "header":
|
|
996
|
+
return headerToText(block.data);
|
|
997
|
+
case "text":
|
|
998
|
+
return String(block.data["content"] ?? "");
|
|
999
|
+
case "button":
|
|
1000
|
+
return buttonToText(block.data);
|
|
1001
|
+
case "footer":
|
|
1002
|
+
return String(block.data["text"] ?? "");
|
|
1003
|
+
case "columns":
|
|
1004
|
+
if (depth >= MAX_BLOCK_DEPTH2) {
|
|
1005
|
+
console.warn("[momentum:email] Max nesting depth reached, skipping columns block");
|
|
1006
|
+
return "";
|
|
1007
|
+
}
|
|
1008
|
+
return columnsToText(block.data, depth);
|
|
1009
|
+
case "divider":
|
|
1010
|
+
case "spacer":
|
|
1011
|
+
case "image":
|
|
1012
|
+
return "";
|
|
1013
|
+
default:
|
|
1014
|
+
return "";
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
function headerToText(data) {
|
|
1018
|
+
const title = String(data["title"] ?? "");
|
|
1019
|
+
const subtitle = data["subtitle"] ? String(data["subtitle"]) : "";
|
|
1020
|
+
if (!title && !subtitle)
|
|
1021
|
+
return "";
|
|
1022
|
+
if (!subtitle)
|
|
1023
|
+
return title;
|
|
1024
|
+
return `${title}
|
|
1025
|
+
${subtitle}`;
|
|
1026
|
+
}
|
|
1027
|
+
function buttonToText(data) {
|
|
1028
|
+
const label = String(data["label"] ?? "");
|
|
1029
|
+
const href = data["href"] ? String(data["href"]) : "";
|
|
1030
|
+
if (!label)
|
|
1031
|
+
return "";
|
|
1032
|
+
if (!href)
|
|
1033
|
+
return label;
|
|
1034
|
+
return `${label}: ${href}`;
|
|
1035
|
+
}
|
|
1036
|
+
function columnsToText(data, depth) {
|
|
1037
|
+
const columns = data["columns"];
|
|
1038
|
+
if (!Array.isArray(columns))
|
|
1039
|
+
return "";
|
|
1040
|
+
const parts = [];
|
|
1041
|
+
for (const col of columns) {
|
|
1042
|
+
if (col && typeof col === "object" && Array.isArray(col.blocks)) {
|
|
1043
|
+
const colText = blocksToPlainText(col.blocks, depth + 1);
|
|
1044
|
+
if (colText) {
|
|
1045
|
+
parts.push(colText);
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return parts.join("\n\n");
|
|
1050
|
+
}
|
|
1051
|
+
var MAX_BLOCK_DEPTH2;
|
|
1052
|
+
var init_blocks_to_plain_text = __esm({
|
|
1053
|
+
"libs/email/src/lib/utils/blocks-to-plain-text.ts"() {
|
|
1054
|
+
"use strict";
|
|
1055
|
+
MAX_BLOCK_DEPTH2 = 5;
|
|
1056
|
+
}
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
// libs/email/src/index.ts
|
|
1060
|
+
var src_exports = {};
|
|
1061
|
+
__export(src_exports, {
|
|
1062
|
+
DEFAULT_EMAIL_THEME: () => DEFAULT_EMAIL_THEME,
|
|
1063
|
+
DEFAULT_PASSWORD_RESET_BLOCKS: () => DEFAULT_PASSWORD_RESET_BLOCKS,
|
|
1064
|
+
DEFAULT_VERIFICATION_BLOCKS: () => DEFAULT_VERIFICATION_BLOCKS,
|
|
1065
|
+
EMAIL_DATA: () => EMAIL_DATA,
|
|
1066
|
+
EmlBody: () => EmlBody,
|
|
1067
|
+
EmlButton: () => EmlButton,
|
|
1068
|
+
EmlColumn: () => EmlColumn,
|
|
1069
|
+
EmlContainer: () => EmlContainer,
|
|
1070
|
+
EmlDivider: () => EmlDivider,
|
|
1071
|
+
EmlFooter: () => EmlFooter,
|
|
1072
|
+
EmlHeading: () => EmlHeading,
|
|
1073
|
+
EmlImage: () => EmlImage,
|
|
1074
|
+
EmlLink: () => EmlLink,
|
|
1075
|
+
EmlPreview: () => EmlPreview,
|
|
1076
|
+
EmlRow: () => EmlRow,
|
|
1077
|
+
EmlSection: () => EmlSection,
|
|
1078
|
+
EmlSpacer: () => EmlSpacer,
|
|
1079
|
+
EmlText: () => EmlText,
|
|
1080
|
+
blocksToPlainText: () => blocksToPlainText,
|
|
1081
|
+
escapeHtml: () => escapeHtml,
|
|
1082
|
+
injectEmailData: () => injectEmailData,
|
|
1083
|
+
inlineCss: () => inlineCss,
|
|
1084
|
+
isValidBlock: () => isValidBlock,
|
|
1085
|
+
renderEmail: () => renderEmail,
|
|
1086
|
+
renderEmailFromBlocks: () => renderEmailFromBlocks,
|
|
1087
|
+
replaceBlockVariables: () => replaceBlockVariables,
|
|
1088
|
+
replaceVariables: () => replaceVariables,
|
|
1089
|
+
sanitizeAlignment: () => sanitizeAlignment,
|
|
1090
|
+
sanitizeCssNumber: () => sanitizeCssNumber,
|
|
1091
|
+
sanitizeCssValue: () => sanitizeCssValue,
|
|
1092
|
+
sanitizeFontFamily: () => sanitizeFontFamily,
|
|
1093
|
+
sanitizeUrl: () => sanitizeUrl
|
|
1094
|
+
});
|
|
1095
|
+
var init_src = __esm({
|
|
1096
|
+
"libs/email/src/index.ts"() {
|
|
1097
|
+
"use strict";
|
|
1098
|
+
init_render_email();
|
|
1099
|
+
init_render_blocks();
|
|
1100
|
+
init_render_types();
|
|
1101
|
+
init_replace_variables();
|
|
1102
|
+
init_default_templates();
|
|
1103
|
+
init_eml_body_component();
|
|
1104
|
+
init_eml_container_component();
|
|
1105
|
+
init_eml_section_component();
|
|
1106
|
+
init_eml_row_component();
|
|
1107
|
+
init_eml_column_component();
|
|
1108
|
+
init_eml_text_component();
|
|
1109
|
+
init_eml_heading_component();
|
|
1110
|
+
init_eml_button_component();
|
|
1111
|
+
init_eml_link_component();
|
|
1112
|
+
init_eml_image_component();
|
|
1113
|
+
init_eml_divider_component();
|
|
1114
|
+
init_eml_preview_component();
|
|
1115
|
+
init_eml_spacer_component();
|
|
1116
|
+
init_eml_footer_component();
|
|
1117
|
+
init_escape_html();
|
|
1118
|
+
init_css_inliner();
|
|
1119
|
+
init_sanitize();
|
|
1120
|
+
init_blocks_to_plain_text();
|
|
1121
|
+
init_render_blocks();
|
|
1122
|
+
init_types();
|
|
1123
|
+
}
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
// libs/auth/src/lib/email-components/password-reset-email.component.ts
|
|
1127
|
+
var password_reset_email_component_exports = {};
|
|
1128
|
+
__export(password_reset_email_component_exports, {
|
|
1129
|
+
PasswordResetEmailComponent: () => PasswordResetEmailComponent
|
|
1130
|
+
});
|
|
1131
|
+
import { Component as Component15, ChangeDetectionStrategy as ChangeDetectionStrategy15 } from "@angular/core";
|
|
1132
|
+
var PasswordResetEmailComponent;
|
|
1133
|
+
var init_password_reset_email_component = __esm({
|
|
1134
|
+
"libs/auth/src/lib/email-components/password-reset-email.component.ts"() {
|
|
1135
|
+
"use strict";
|
|
1136
|
+
init_src();
|
|
1137
|
+
init_src();
|
|
1138
|
+
PasswordResetEmailComponent = class {
|
|
1139
|
+
constructor() {
|
|
1140
|
+
this.data = injectEmailData();
|
|
1141
|
+
this.year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
1142
|
+
}
|
|
1143
|
+
get greeting() {
|
|
1144
|
+
return this.data.name ? `Hi ${this.data.name},` : "Hi,";
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
PasswordResetEmailComponent = __decorateClass([
|
|
1148
|
+
Component15({
|
|
1149
|
+
selector: "auth-password-reset-email",
|
|
1150
|
+
imports: [EmlBody, EmlContainer, EmlHeading, EmlText, EmlButton, EmlDivider, EmlFooter],
|
|
1151
|
+
template: `
|
|
1152
|
+
<eml-body>
|
|
1153
|
+
<eml-container>
|
|
1154
|
+
<eml-heading>Reset your password</eml-heading>
|
|
1155
|
+
<eml-text>{{ greeting }}</eml-text>
|
|
1156
|
+
<eml-text>
|
|
1157
|
+
We received a request to reset your password. Click the button below to choose a new
|
|
1158
|
+
password:
|
|
1159
|
+
</eml-text>
|
|
1160
|
+
<eml-button [href]="data.url">Reset Password</eml-button>
|
|
1161
|
+
<eml-text color="#71717a" fontSize="14px">
|
|
1162
|
+
This link will expire in {{ data.expiresIn }}.
|
|
1163
|
+
</eml-text>
|
|
1164
|
+
<eml-text color="#71717a" fontSize="14px">
|
|
1165
|
+
If you didn't request a password reset, you can safely ignore this email.
|
|
1166
|
+
</eml-text>
|
|
1167
|
+
<eml-divider />
|
|
1168
|
+
<eml-text color="#71717a" fontSize="12px">
|
|
1169
|
+
If the button doesn't work, copy and paste this URL into your browser:
|
|
1170
|
+
</eml-text>
|
|
1171
|
+
<eml-text color="#71717a" fontSize="12px">{{ data.url }}</eml-text>
|
|
1172
|
+
</eml-container>
|
|
1173
|
+
<eml-footer> © {{ year }} {{ data.appName }}. All rights reserved. </eml-footer>
|
|
1174
|
+
</eml-body>
|
|
1175
|
+
`,
|
|
1176
|
+
changeDetection: ChangeDetectionStrategy15.OnPush
|
|
1177
|
+
})
|
|
1178
|
+
], PasswordResetEmailComponent);
|
|
1179
|
+
}
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
// libs/auth/src/lib/email-components/verification-email.component.ts
|
|
1183
|
+
var verification_email_component_exports = {};
|
|
1184
|
+
__export(verification_email_component_exports, {
|
|
1185
|
+
VerificationEmailComponent: () => VerificationEmailComponent
|
|
1186
|
+
});
|
|
1187
|
+
import { Component as Component16, ChangeDetectionStrategy as ChangeDetectionStrategy16 } from "@angular/core";
|
|
1188
|
+
var VerificationEmailComponent;
|
|
1189
|
+
var init_verification_email_component = __esm({
|
|
1190
|
+
"libs/auth/src/lib/email-components/verification-email.component.ts"() {
|
|
1191
|
+
"use strict";
|
|
1192
|
+
init_src();
|
|
1193
|
+
init_src();
|
|
1194
|
+
VerificationEmailComponent = class {
|
|
1195
|
+
constructor() {
|
|
1196
|
+
this.data = injectEmailData();
|
|
1197
|
+
this.year = (/* @__PURE__ */ new Date()).getFullYear();
|
|
1198
|
+
}
|
|
1199
|
+
get greeting() {
|
|
1200
|
+
return this.data.name ? `Hi ${this.data.name},` : "Hi,";
|
|
1201
|
+
}
|
|
1202
|
+
};
|
|
1203
|
+
VerificationEmailComponent = __decorateClass([
|
|
1204
|
+
Component16({
|
|
1205
|
+
selector: "auth-verification-email",
|
|
1206
|
+
imports: [EmlBody, EmlContainer, EmlHeading, EmlText, EmlButton, EmlDivider, EmlFooter],
|
|
1207
|
+
template: `
|
|
1208
|
+
<eml-body>
|
|
1209
|
+
<eml-container>
|
|
1210
|
+
<eml-heading>Verify your email</eml-heading>
|
|
1211
|
+
<eml-text>{{ greeting }}</eml-text>
|
|
1212
|
+
<eml-text>
|
|
1213
|
+
Welcome to {{ data.appName }}! Please verify your email address by clicking the button
|
|
1214
|
+
below:
|
|
1215
|
+
</eml-text>
|
|
1216
|
+
<eml-button [href]="data.url">Verify Email</eml-button>
|
|
1217
|
+
<eml-text color="#71717a" fontSize="14px">
|
|
1218
|
+
This link will expire in {{ data.expiresIn }}.
|
|
1219
|
+
</eml-text>
|
|
1220
|
+
<eml-text color="#71717a" fontSize="14px">
|
|
1221
|
+
If you didn't create an account, you can safely ignore this email.
|
|
1222
|
+
</eml-text>
|
|
1223
|
+
<eml-divider />
|
|
1224
|
+
<eml-text color="#71717a" fontSize="12px">
|
|
1225
|
+
If the button doesn't work, copy and paste this URL into your browser:
|
|
1226
|
+
</eml-text>
|
|
1227
|
+
<eml-text color="#71717a" fontSize="12px">{{ data.url }}</eml-text>
|
|
1228
|
+
</eml-container>
|
|
1229
|
+
<eml-footer> © {{ year }} {{ data.appName }}. All rights reserved. </eml-footer>
|
|
1230
|
+
</eml-body>
|
|
1231
|
+
`,
|
|
1232
|
+
changeDetection: ChangeDetectionStrategy16.OnPush
|
|
1233
|
+
})
|
|
1234
|
+
], VerificationEmailComponent);
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
|
|
1
1238
|
// libs/auth/src/lib/auth-core.ts
|
|
2
1239
|
var AUTH_ROLES = [
|
|
3
1240
|
{ label: "Admin", value: "admin" },
|
|
@@ -8,7 +1245,6 @@ var AUTH_ROLES = [
|
|
|
8
1245
|
|
|
9
1246
|
// libs/auth/src/lib/auth.ts
|
|
10
1247
|
import { betterAuth } from "better-auth";
|
|
11
|
-
import { twoFactor } from "better-auth/plugins";
|
|
12
1248
|
|
|
13
1249
|
// libs/auth/src/lib/email.ts
|
|
14
1250
|
import * as nodemailer from "nodemailer";
|
|
@@ -66,48 +1302,27 @@ function createEmailService(config) {
|
|
|
66
1302
|
}
|
|
67
1303
|
|
|
68
1304
|
// libs/auth/src/lib/email-templates.ts
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<td style="padding: 40px 20px;">
|
|
85
|
-
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 480px; margin: 0 auto; background-color: #ffffff; border-radius: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.1);">
|
|
86
|
-
<tr>
|
|
87
|
-
<td style="padding: 40px;">
|
|
88
|
-
${content}
|
|
89
|
-
</td>
|
|
90
|
-
</tr>
|
|
91
|
-
</table>
|
|
92
|
-
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" style="max-width: 480px; margin: 20px auto 0;">
|
|
93
|
-
<tr>
|
|
94
|
-
<td style="text-align: center; color: #71717a; font-size: 12px;">
|
|
95
|
-
<p style="margin: 0;">© ${(/* @__PURE__ */ new Date()).getFullYear()} ${safeAppName}. All rights reserved.</p>
|
|
96
|
-
</td>
|
|
97
|
-
</tr>
|
|
98
|
-
</table>
|
|
99
|
-
</td>
|
|
100
|
-
</tr>
|
|
101
|
-
</table>
|
|
102
|
-
</body>
|
|
103
|
-
</html>
|
|
104
|
-
`.trim();
|
|
1305
|
+
async function renderFromDbTemplate(template, variables, defaultSubject, defaultText) {
|
|
1306
|
+
if (!template.emailBlocks || !Array.isArray(template.emailBlocks) || template.emailBlocks.length === 0) {
|
|
1307
|
+
return null;
|
|
1308
|
+
}
|
|
1309
|
+
const { renderEmailFromBlocks: renderEmailFromBlocks2, replaceVariables: replaceVariables2, blocksToPlainText: blocksToPlainText2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
1310
|
+
const subject = template.subject ? replaceVariables2(template.subject, variables) : defaultSubject;
|
|
1311
|
+
const blocks2 = template.emailBlocks;
|
|
1312
|
+
const html = renderEmailFromBlocks2(
|
|
1313
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- DB blocks stored as unknown[], narrowed by array check above
|
|
1314
|
+
{ blocks: blocks2 },
|
|
1315
|
+
{ variables }
|
|
1316
|
+
);
|
|
1317
|
+
const generatedText = blocksToPlainText2(blocks2);
|
|
1318
|
+
const text2 = generatedText ? replaceVariables2(generatedText, variables) : defaultText;
|
|
1319
|
+
return { subject, text: text2, html };
|
|
105
1320
|
}
|
|
106
|
-
function getPasswordResetEmail(options) {
|
|
1321
|
+
async function getPasswordResetEmail(options) {
|
|
107
1322
|
const { name, url, appName = "Momentum CMS", expiresIn = "1 hour" } = options;
|
|
108
1323
|
const greeting = name ? `Hi ${name},` : "Hi,";
|
|
109
|
-
const
|
|
110
|
-
const
|
|
1324
|
+
const defaultSubject = `Reset your password - ${appName}`;
|
|
1325
|
+
const defaultText = `
|
|
111
1326
|
${greeting}
|
|
112
1327
|
|
|
113
1328
|
We received a request to reset your password. Click the link below to choose a new password:
|
|
@@ -121,37 +1336,33 @@ If you didn't request a password reset, you can safely ignore this email. Your p
|
|
|
121
1336
|
Thanks,
|
|
122
1337
|
The ${appName} Team
|
|
123
1338
|
`.trim();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
`,
|
|
146
|
-
safeAppName
|
|
147
|
-
);
|
|
148
|
-
return { subject, text: text2, html };
|
|
1339
|
+
if (options.findEmailTemplate) {
|
|
1340
|
+
try {
|
|
1341
|
+
const template = await options.findEmailTemplate("password-reset");
|
|
1342
|
+
if (template) {
|
|
1343
|
+
const variables = { greeting, url, appName, expiresIn };
|
|
1344
|
+
const result = await renderFromDbTemplate(template, variables, defaultSubject, defaultText);
|
|
1345
|
+
if (result)
|
|
1346
|
+
return result;
|
|
1347
|
+
}
|
|
1348
|
+
} catch (error) {
|
|
1349
|
+
console.warn(
|
|
1350
|
+
"[momentum:email] Failed to render DB template for password-reset, falling back to SSR:",
|
|
1351
|
+
error
|
|
1352
|
+
);
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
const { renderEmail: renderEmail2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
1356
|
+
const { PasswordResetEmailComponent: PasswordResetEmailComponent2 } = await Promise.resolve().then(() => (init_password_reset_email_component(), password_reset_email_component_exports));
|
|
1357
|
+
const data = { name, url, appName, expiresIn };
|
|
1358
|
+
const html = await renderEmail2(PasswordResetEmailComponent2, data);
|
|
1359
|
+
return { subject: defaultSubject, text: defaultText, html };
|
|
149
1360
|
}
|
|
150
|
-
function getVerificationEmail(options) {
|
|
1361
|
+
async function getVerificationEmail(options) {
|
|
151
1362
|
const { name, url, appName = "Momentum CMS", expiresIn = "24 hours" } = options;
|
|
152
1363
|
const greeting = name ? `Hi ${name},` : "Hi,";
|
|
153
|
-
const
|
|
154
|
-
const
|
|
1364
|
+
const defaultSubject = `Verify your email - ${appName}`;
|
|
1365
|
+
const defaultText = `
|
|
155
1366
|
${greeting}
|
|
156
1367
|
|
|
157
1368
|
Welcome to ${appName}! Please verify your email address by clicking the link below:
|
|
@@ -165,31 +1376,27 @@ If you didn't create an account, you can safely ignore this email.
|
|
|
165
1376
|
Thanks,
|
|
166
1377
|
The ${appName} Team
|
|
167
1378
|
`.trim();
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
`,
|
|
190
|
-
safeAppName
|
|
191
|
-
);
|
|
192
|
-
return { subject, text: text2, html };
|
|
1379
|
+
if (options.findEmailTemplate) {
|
|
1380
|
+
try {
|
|
1381
|
+
const template = await options.findEmailTemplate("verification");
|
|
1382
|
+
if (template) {
|
|
1383
|
+
const variables = { greeting, url, appName, expiresIn };
|
|
1384
|
+
const result = await renderFromDbTemplate(template, variables, defaultSubject, defaultText);
|
|
1385
|
+
if (result)
|
|
1386
|
+
return result;
|
|
1387
|
+
}
|
|
1388
|
+
} catch (error) {
|
|
1389
|
+
console.warn(
|
|
1390
|
+
"[momentum:email] Failed to render DB template for verification, falling back to SSR:",
|
|
1391
|
+
error
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
const { renderEmail: renderEmail2 } = await Promise.resolve().then(() => (init_src(), src_exports));
|
|
1396
|
+
const { VerificationEmailComponent: VerificationEmailComponent2 } = await Promise.resolve().then(() => (init_verification_email_component(), verification_email_component_exports));
|
|
1397
|
+
const data = { name, url, appName, expiresIn };
|
|
1398
|
+
const html = await renderEmail2(VerificationEmailComponent2, data);
|
|
1399
|
+
return { subject: defaultSubject, text: defaultText, html };
|
|
193
1400
|
}
|
|
194
1401
|
|
|
195
1402
|
// libs/logger/src/lib/log-level.ts
|
|
@@ -489,14 +1696,7 @@ function convertFieldsToAdditionalFields(fields) {
|
|
|
489
1696
|
}
|
|
490
1697
|
function createMomentumAuth(config) {
|
|
491
1698
|
const dbConfig = isLegacyConfig(config) ? { type: "sqlite", database: config.database } : config.db;
|
|
492
|
-
const {
|
|
493
|
-
baseURL,
|
|
494
|
-
secret,
|
|
495
|
-
trustedOrigins,
|
|
496
|
-
email: emailConfig,
|
|
497
|
-
socialProviders,
|
|
498
|
-
twoFactorAuth
|
|
499
|
-
} = config;
|
|
1699
|
+
const { baseURL, secret, trustedOrigins, email: emailConfig, socialProviders } = config;
|
|
500
1700
|
const extraPlugins = !isLegacyConfig(config) ? config.plugins ?? [] : [];
|
|
501
1701
|
const extraUserFields = !isLegacyConfig(config) ? config.userFields ?? [] : [];
|
|
502
1702
|
const databaseOption = dbConfig.type === "sqlite" ? dbConfig.database : dbConfig.pool;
|
|
@@ -512,11 +1712,12 @@ function createMomentumAuth(config) {
|
|
|
512
1712
|
};
|
|
513
1713
|
if (emailService) {
|
|
514
1714
|
emailAndPasswordConfig.sendResetPassword = async ({ user, url }) => {
|
|
515
|
-
const { subject, text: text2, html } = getPasswordResetEmail({
|
|
1715
|
+
const { subject, text: text2, html } = await getPasswordResetEmail({
|
|
516
1716
|
name: user.name,
|
|
517
1717
|
url,
|
|
518
1718
|
appName,
|
|
519
|
-
expiresIn: "1 hour"
|
|
1719
|
+
expiresIn: "1 hour",
|
|
1720
|
+
findEmailTemplate: emailConfig?.findEmailTemplate
|
|
520
1721
|
});
|
|
521
1722
|
emailService.sendEmail({
|
|
522
1723
|
to: user.email,
|
|
@@ -540,11 +1741,12 @@ function createMomentumAuth(config) {
|
|
|
540
1741
|
user,
|
|
541
1742
|
url
|
|
542
1743
|
}) => {
|
|
543
|
-
const { subject, text: text2, html } = getVerificationEmail({
|
|
1744
|
+
const { subject, text: text2, html } = await getVerificationEmail({
|
|
544
1745
|
name: user.name,
|
|
545
1746
|
url,
|
|
546
1747
|
appName,
|
|
547
|
-
expiresIn: "24 hours"
|
|
1748
|
+
expiresIn: "24 hours",
|
|
1749
|
+
findEmailTemplate: emailConfig?.findEmailTemplate
|
|
548
1750
|
});
|
|
549
1751
|
emailService.sendEmail({
|
|
550
1752
|
to: user.email,
|
|
@@ -563,9 +1765,6 @@ function createMomentumAuth(config) {
|
|
|
563
1765
|
}
|
|
564
1766
|
const socialProvidersConfig = buildSocialProviders(socialProviders, baseURL);
|
|
565
1767
|
const plugins = [];
|
|
566
|
-
if (twoFactorAuth) {
|
|
567
|
-
plugins.push(twoFactor());
|
|
568
|
-
}
|
|
569
1768
|
for (const p of extraPlugins) {
|
|
570
1769
|
if (p !== void 0) {
|
|
571
1770
|
plugins.push(p);
|
|
@@ -1017,7 +2216,7 @@ function momentumAuth(config) {
|
|
|
1017
2216
|
}
|
|
1018
2217
|
|
|
1019
2218
|
// libs/auth/src/lib/plugins/two-factor.ts
|
|
1020
|
-
import { twoFactor
|
|
2219
|
+
import { twoFactor } from "better-auth/plugins";
|
|
1021
2220
|
var AuthTwoFactorCollection = defineCollection({
|
|
1022
2221
|
slug: "auth-two-factor",
|
|
1023
2222
|
dbName: "twoFactor",
|
|
@@ -1044,24 +2243,25 @@ var AuthTwoFactorCollection = defineCollection({
|
|
|
1044
2243
|
function authTwoFactor() {
|
|
1045
2244
|
return {
|
|
1046
2245
|
name: "two-factor",
|
|
1047
|
-
betterAuthPlugin:
|
|
2246
|
+
betterAuthPlugin: twoFactor(),
|
|
1048
2247
|
collections: [AuthTwoFactorCollection],
|
|
1049
2248
|
userFields: [checkbox("twoFactorEnabled")]
|
|
1050
2249
|
};
|
|
1051
2250
|
}
|
|
1052
2251
|
|
|
1053
2252
|
// libs/auth/src/lib/plugins/admin.ts
|
|
2253
|
+
import { admin } from "better-auth/plugins";
|
|
1054
2254
|
function authAdmin() {
|
|
1055
2255
|
return {
|
|
1056
2256
|
name: "admin",
|
|
1057
|
-
|
|
1058
|
-
betterAuthPlugin: void 0,
|
|
2257
|
+
betterAuthPlugin: admin(),
|
|
1059
2258
|
userFields: [checkbox("banned"), text("banReason"), date("banExpires")],
|
|
1060
2259
|
sessionFields: [text("impersonatedBy")]
|
|
1061
2260
|
};
|
|
1062
2261
|
}
|
|
1063
2262
|
|
|
1064
2263
|
// libs/auth/src/lib/plugins/organization.ts
|
|
2264
|
+
import { organization } from "better-auth/plugins";
|
|
1065
2265
|
var AuthOrganizationCollection = defineCollection({
|
|
1066
2266
|
slug: "auth-organization",
|
|
1067
2267
|
dbName: "organization",
|
|
@@ -1163,8 +2363,7 @@ var AuthInvitationCollection = defineCollection({
|
|
|
1163
2363
|
function authOrganization() {
|
|
1164
2364
|
return {
|
|
1165
2365
|
name: "organization",
|
|
1166
|
-
|
|
1167
|
-
betterAuthPlugin: void 0,
|
|
2366
|
+
betterAuthPlugin: organization(),
|
|
1168
2367
|
collections: [AuthOrganizationCollection, AuthMemberCollection, AuthInvitationCollection]
|
|
1169
2368
|
};
|
|
1170
2369
|
}
|