hazo_auth 5.2.0 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/SETUP_CHECKLIST.md +46 -0
- package/cli-src/lib/auth/hazo_get_auth.server.ts +1 -1
- package/cli-src/lib/auth/index.ts +1 -1
- package/cli-src/lib/services/email_service.ts +136 -289
- package/cli-src/lib/services/email_template_manifest.ts +104 -0
- package/cli-src/lib/services/email_templates/email_verification.html +18 -0
- package/cli-src/lib/services/email_templates/email_verification.txt +9 -0
- package/cli-src/lib/services/email_templates/forgot_password.html +18 -0
- package/cli-src/lib/services/email_templates/forgot_password.txt +9 -0
- package/cli-src/lib/services/email_templates/password_changed.html +14 -0
- package/cli-src/lib/services/email_templates/password_changed.txt +9 -0
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/lib/auth/hazo_get_auth.server.d.ts +6 -0
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +1 -1
- package/dist/lib/auth/index.d.ts +1 -1
- package/dist/lib/auth/index.d.ts.map +1 -1
- package/dist/lib/auth/index.js +1 -1
- package/dist/lib/services/email_service.d.ts +17 -4
- package/dist/lib/services/email_service.d.ts.map +1 -1
- package/dist/lib/services/email_service.js +93 -221
- package/dist/lib/services/email_template_manifest.d.ts +11 -0
- package/dist/lib/services/email_template_manifest.d.ts.map +1 -0
- package/dist/lib/services/email_template_manifest.js +95 -0
- package/dist/lib/services/email_templates/email_verification.html +18 -0
- package/dist/lib/services/email_templates/email_verification.txt +9 -0
- package/dist/lib/services/email_templates/forgot_password.html +18 -0
- package/dist/lib/services/email_templates/forgot_password.txt +9 -0
- package/dist/lib/services/email_templates/password_changed.html +14 -0
- package/dist/lib/services/email_templates/password_changed.txt +9 -0
- package/dist/server-lib.d.ts +1 -0
- package/dist/server-lib.d.ts.map +1 -1
- package/dist/server-lib.js +1 -0
- package/package.json +4 -3
- package/cli-src/assets/images/new_firm_default.jpg +0 -0
- package/cli-src/lib/auth/org_cache.ts +0 -148
- package/cli-src/lib/index.ts +0 -48
- package/cli-src/lib/services/org_service.ts +0 -965
- package/cli-src/lib/services/scope_labels_service.ts +0 -348
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
// file_description: service for sending emails with template support
|
|
2
2
|
// section: imports
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
3
|
import { create_app_logger } from "../app_logger.js";
|
|
6
4
|
import { read_config_section } from "../config/config_loader.server.js";
|
|
7
|
-
// section: constants
|
|
8
|
-
const DEFAULT_EMAIL_FROM = "noreply@hazo_auth.local";
|
|
9
|
-
/**
|
|
10
|
-
* Gets the default email template directory (lazy-evaluated to avoid Edge Runtime issues)
|
|
11
|
-
*/
|
|
12
|
-
function get_default_email_template_dir() {
|
|
13
|
-
return path.resolve(process.cwd(), "email_templates");
|
|
14
|
-
}
|
|
15
5
|
// section: singleton
|
|
16
6
|
/**
|
|
17
7
|
* Singleton instance for hazo_notify emailer configuration
|
|
18
8
|
* This is initialized once in instrumentation.ts and reused across all email sends
|
|
19
9
|
*/
|
|
20
10
|
let hazo_notify_config = null;
|
|
11
|
+
/**
|
|
12
|
+
* Singleton hazo_notify-compatible hazo_connect instance used by the template
|
|
13
|
+
* manager. The consuming app builds this once (typically the same instance fed
|
|
14
|
+
* into `init_template_manager({ hazo_connect_factory })`) and registers it via
|
|
15
|
+
* {@link set_hazo_notify_connect}. Required for `send_template_email`.
|
|
16
|
+
*/
|
|
17
|
+
let hazo_notify_connect = null;
|
|
21
18
|
/**
|
|
22
19
|
* Sets the hazo_notify emailer configuration instance
|
|
23
20
|
* This is called from instrumentation.ts during initialization
|
|
@@ -26,6 +23,16 @@ let hazo_notify_config = null;
|
|
|
26
23
|
export function set_hazo_notify_instance(config) {
|
|
27
24
|
hazo_notify_config = config;
|
|
28
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Registers the hazo_notify-compatible hazo_connect instance used by
|
|
28
|
+
* `send_template_email`. Call this from `instrumentation.ts` with the same
|
|
29
|
+
* instance you pass to `init_template_manager({ hazo_connect_factory })`.
|
|
30
|
+
*
|
|
31
|
+
* @param instance - hazo_notify-shaped database adapter
|
|
32
|
+
*/
|
|
33
|
+
export function set_hazo_notify_connect(instance) {
|
|
34
|
+
hazo_notify_connect = instance;
|
|
35
|
+
}
|
|
29
36
|
/**
|
|
30
37
|
* Gets the hazo_notify emailer configuration instance
|
|
31
38
|
* If not set, loads it from config file as fallback
|
|
@@ -58,21 +65,35 @@ async function get_hazo_notify_instance() {
|
|
|
58
65
|
}
|
|
59
66
|
return hazo_notify_config;
|
|
60
67
|
}
|
|
61
|
-
// section:
|
|
68
|
+
// section: deprecated_config_helpers
|
|
69
|
+
/**
|
|
70
|
+
* One-time warning state for the deprecated email_template_main_directory key.
|
|
71
|
+
*/
|
|
72
|
+
let warned_about_template_dir = false;
|
|
62
73
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
74
|
+
* Reads the deprecated [hazo_auth__email] email_template_main_directory key
|
|
75
|
+
* solely to emit a one-time deprecation warning. The value is no longer used —
|
|
76
|
+
* template overrides now live in hazo_notify's database and are managed via
|
|
77
|
+
* the template admin UI. The key will be removed in hazo_auth@6.0.0.
|
|
65
78
|
*/
|
|
66
|
-
function
|
|
79
|
+
function warn_if_template_directory_configured() {
|
|
80
|
+
if (warned_about_template_dir)
|
|
81
|
+
return;
|
|
67
82
|
const email_section = read_config_section("hazo_auth__email");
|
|
68
83
|
const template_dir = email_section === null || email_section === void 0 ? void 0 : email_section.email_template_main_directory;
|
|
69
|
-
if (template_dir)
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
if (!template_dir)
|
|
85
|
+
return;
|
|
86
|
+
warned_about_template_dir = true;
|
|
87
|
+
create_app_logger().warn("hazo_auth_email_template_main_directory_deprecated", {
|
|
88
|
+
filename: "email_service.ts",
|
|
89
|
+
line_number: 0,
|
|
90
|
+
note: "Filesystem template overrides are deprecated in hazo_auth@5.3.0. " +
|
|
91
|
+
"Use the hazo_notify template admin UI (mounted via <TemplateManagerAdmin />) " +
|
|
92
|
+
"to override the email_verification / forgot_password / password_changed templates per scope. " +
|
|
93
|
+
"This config key will be removed in hazo_auth@6.0.0.",
|
|
94
|
+
});
|
|
75
95
|
}
|
|
96
|
+
// section: helpers
|
|
76
97
|
/**
|
|
77
98
|
* Gets email from address from config
|
|
78
99
|
* Priority: 1. hazo_auth__email.from_email, 2. hazo_notify_config.from_email
|
|
@@ -157,170 +178,6 @@ function get_reset_password_url(token) {
|
|
|
157
178
|
const url = base_url ? `${base_url}${path}?token=${encodeURIComponent(token)}` : `${path}?token=${encodeURIComponent(token)}`;
|
|
158
179
|
return url;
|
|
159
180
|
}
|
|
160
|
-
/**
|
|
161
|
-
* Gets default HTML template for a given template type
|
|
162
|
-
* @param template_type - Type of email template
|
|
163
|
-
* @param data - Template data for variable substitution
|
|
164
|
-
* @returns Default HTML template content
|
|
165
|
-
*/
|
|
166
|
-
function get_default_html_template(template_type, data) {
|
|
167
|
-
switch (template_type) {
|
|
168
|
-
case "email_verification":
|
|
169
|
-
return `
|
|
170
|
-
<!DOCTYPE html>
|
|
171
|
-
<html>
|
|
172
|
-
<head>
|
|
173
|
-
<meta charset="UTF-8">
|
|
174
|
-
<title>Verify Your Email</title>
|
|
175
|
-
</head>
|
|
176
|
-
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
177
|
-
<h1 style="color: #0f172a;">Verify Your Email Address</h1>
|
|
178
|
-
<p>Thank you for registering! Please click the link below to verify your email address:</p>
|
|
179
|
-
<p style="margin: 20px 0;">
|
|
180
|
-
<a href="${data.verification_url || "#"}" style="display: inline-block; padding: 12px 24px; background-color: #0f172a; color: #ffffff; text-decoration: none; border-radius: 4px;">Verify Email Address</a>
|
|
181
|
-
</p>
|
|
182
|
-
<p>Or copy and paste this link into your browser:</p>
|
|
183
|
-
<p style="word-break: break-all; color: #666;">${data.verification_url || data.token || ""}</p>
|
|
184
|
-
<p>This link will expire in 48 hours.</p>
|
|
185
|
-
<p style="margin-top: 30px; color: #666; font-size: 12px;">If you didn't create an account, you can safely ignore this email.</p>
|
|
186
|
-
</body>
|
|
187
|
-
</html>
|
|
188
|
-
`.trim();
|
|
189
|
-
case "forgot_password":
|
|
190
|
-
return `
|
|
191
|
-
<!DOCTYPE html>
|
|
192
|
-
<html>
|
|
193
|
-
<head>
|
|
194
|
-
<meta charset="UTF-8">
|
|
195
|
-
<title>Reset Your Password</title>
|
|
196
|
-
</head>
|
|
197
|
-
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
198
|
-
<h1 style="color: #0f172a;">Reset Your Password</h1>
|
|
199
|
-
<p>We received a request to reset your password. Click the link below to reset it:</p>
|
|
200
|
-
<p style="margin: 20px 0;">
|
|
201
|
-
<a href="${data.reset_url || "#"}" style="display: inline-block; padding: 12px 24px; background-color: #0f172a; color: #ffffff; text-decoration: none; border-radius: 4px;">Reset Password</a>
|
|
202
|
-
</p>
|
|
203
|
-
<p>Or copy and paste this link into your browser:</p>
|
|
204
|
-
<p style="word-break: break-all; color: #666;">${data.reset_url || data.token || ""}</p>
|
|
205
|
-
<p>This link will expire in 10 minutes.</p>
|
|
206
|
-
<p style="margin-top: 30px; color: #666; font-size: 12px;">If you didn't request a password reset, you can safely ignore this email.</p>
|
|
207
|
-
</body>
|
|
208
|
-
</html>
|
|
209
|
-
`.trim();
|
|
210
|
-
case "password_changed":
|
|
211
|
-
return `
|
|
212
|
-
<!DOCTYPE html>
|
|
213
|
-
<html>
|
|
214
|
-
<head>
|
|
215
|
-
<meta charset="UTF-8">
|
|
216
|
-
<title>Password Changed</title>
|
|
217
|
-
</head>
|
|
218
|
-
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
219
|
-
<h1 style="color: #0f172a;">Password Changed Successfully</h1>
|
|
220
|
-
<p>Hello${data.user_name ? ` ${data.user_name}` : ""},</p>
|
|
221
|
-
<p>This email confirms that your password has been changed successfully.</p>
|
|
222
|
-
<p>If you did not make this change, please contact support immediately to secure your account.</p>
|
|
223
|
-
<p style="margin-top: 30px; color: #666; font-size: 12px;">This is an automated notification. Please do not reply to this email.</p>
|
|
224
|
-
</body>
|
|
225
|
-
</html>
|
|
226
|
-
`.trim();
|
|
227
|
-
default:
|
|
228
|
-
return "<p>Email content</p>";
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/**
|
|
232
|
-
* Gets default text template for a given template type
|
|
233
|
-
* @param template_type - Type of email template
|
|
234
|
-
* @param data - Template data for variable substitution
|
|
235
|
-
* @returns Default text template content
|
|
236
|
-
*/
|
|
237
|
-
function get_default_text_template(template_type, data) {
|
|
238
|
-
switch (template_type) {
|
|
239
|
-
case "email_verification":
|
|
240
|
-
return `
|
|
241
|
-
Verify Your Email Address
|
|
242
|
-
|
|
243
|
-
Thank you for registering! Please click the link below to verify your email address:
|
|
244
|
-
|
|
245
|
-
${data.verification_url || data.token || ""}
|
|
246
|
-
|
|
247
|
-
This link will expire in 48 hours.
|
|
248
|
-
|
|
249
|
-
If you didn't create an account, you can safely ignore this email.
|
|
250
|
-
`.trim();
|
|
251
|
-
case "forgot_password":
|
|
252
|
-
return `
|
|
253
|
-
Reset Your Password
|
|
254
|
-
|
|
255
|
-
We received a request to reset your password. Click the link below to reset it:
|
|
256
|
-
|
|
257
|
-
${data.reset_url || data.token || ""}
|
|
258
|
-
|
|
259
|
-
This link will expire in 10 minutes.
|
|
260
|
-
|
|
261
|
-
If you didn't request a password reset, you can safely ignore this email.
|
|
262
|
-
`.trim();
|
|
263
|
-
case "password_changed":
|
|
264
|
-
return `
|
|
265
|
-
Password Changed Successfully
|
|
266
|
-
|
|
267
|
-
Hello${data.user_name ? ` ${data.user_name}` : ""},
|
|
268
|
-
|
|
269
|
-
This email confirms that your password has been changed successfully.
|
|
270
|
-
|
|
271
|
-
If you did not make this change, please contact support immediately to secure your account.
|
|
272
|
-
|
|
273
|
-
This is an automated notification. Please do not reply to this email.
|
|
274
|
-
`.trim();
|
|
275
|
-
default:
|
|
276
|
-
return "Email content";
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* Loads email template from file system
|
|
281
|
-
* @param template_type - Type of email template
|
|
282
|
-
* @param extension - File extension (html or txt)
|
|
283
|
-
* @returns Template content or undefined if not found
|
|
284
|
-
*/
|
|
285
|
-
function load_template_file(template_type, extension) {
|
|
286
|
-
const template_dir = get_email_template_directory();
|
|
287
|
-
const template_filename = `${template_type}.${extension}`;
|
|
288
|
-
const template_path = path.join(template_dir, template_filename);
|
|
289
|
-
if (!fs.existsSync(template_path)) {
|
|
290
|
-
return undefined;
|
|
291
|
-
}
|
|
292
|
-
try {
|
|
293
|
-
return fs.readFileSync(template_path, "utf-8");
|
|
294
|
-
}
|
|
295
|
-
catch (error) {
|
|
296
|
-
const logger = create_app_logger();
|
|
297
|
-
logger.error("email_service_template_load_failed", {
|
|
298
|
-
filename: "email_service.ts",
|
|
299
|
-
line_number: 0,
|
|
300
|
-
template_path,
|
|
301
|
-
error: error instanceof Error ? error.message : "Unknown error",
|
|
302
|
-
});
|
|
303
|
-
return undefined;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
/**
|
|
307
|
-
* Simple template variable substitution
|
|
308
|
-
* Replaces {{variable_name}} with values from data object
|
|
309
|
-
* @param template - Template string with variables
|
|
310
|
-
* @param data - Data object for variable substitution
|
|
311
|
-
* @returns Template with variables substituted
|
|
312
|
-
*/
|
|
313
|
-
function substitute_template_variables(template, data) {
|
|
314
|
-
let result = template;
|
|
315
|
-
// Replace {{variable}} with values from data
|
|
316
|
-
Object.entries(data).forEach(([key, value]) => {
|
|
317
|
-
if (value !== undefined) {
|
|
318
|
-
const regex = new RegExp(`{{\\s*${key}\\s*}}`, "g");
|
|
319
|
-
result = result.replace(regex, value);
|
|
320
|
-
}
|
|
321
|
-
});
|
|
322
|
-
return result;
|
|
323
|
-
}
|
|
324
181
|
/**
|
|
325
182
|
* Gets email subject for a given template type
|
|
326
183
|
* @param template_type - Type of email template
|
|
@@ -347,26 +204,6 @@ function get_email_subject(template_type) {
|
|
|
347
204
|
return "Email from hazo_auth";
|
|
348
205
|
}
|
|
349
206
|
}
|
|
350
|
-
/**
|
|
351
|
-
* Gets email templates (HTML and text) for a given template type
|
|
352
|
-
* Falls back to default templates if custom templates are not found
|
|
353
|
-
* @param template_type - Type of email template
|
|
354
|
-
* @param data - Template data for variable substitution
|
|
355
|
-
* @returns Object with html_body and text_body
|
|
356
|
-
*/
|
|
357
|
-
function get_email_templates(template_type, data) {
|
|
358
|
-
// Try to load custom templates
|
|
359
|
-
const html_template = load_template_file(template_type, "html");
|
|
360
|
-
const text_template = load_template_file(template_type, "txt");
|
|
361
|
-
// Use custom templates if found, otherwise use defaults
|
|
362
|
-
const html_body = html_template
|
|
363
|
-
? substitute_template_variables(html_template, data)
|
|
364
|
-
: get_default_html_template(template_type, data);
|
|
365
|
-
const text_body = text_template
|
|
366
|
-
? substitute_template_variables(text_template, data)
|
|
367
|
-
: get_default_text_template(template_type, data);
|
|
368
|
-
return { html_body, text_body };
|
|
369
|
-
}
|
|
370
207
|
/**
|
|
371
208
|
* Sends an email using hazo_notify
|
|
372
209
|
* @param options - Email options (to, from, subject, html_body, text_body)
|
|
@@ -434,16 +271,22 @@ export async function send_email(options) {
|
|
|
434
271
|
}
|
|
435
272
|
}
|
|
436
273
|
/**
|
|
437
|
-
* Sends an email using a template
|
|
438
|
-
*
|
|
274
|
+
* Sends an email using a template, delegating rendering to hazo_notify's
|
|
275
|
+
* scope-aware template manager. Templates and per-scope overrides live in
|
|
276
|
+
* hazo_notify's database; hazo_auth ships the global defaults via
|
|
277
|
+
* `hazo_auth_template_manifest`.
|
|
278
|
+
*
|
|
279
|
+
* @param template_type - System template name (email_verification | forgot_password | password_changed)
|
|
439
280
|
* @param to - Recipient email address
|
|
440
|
-
* @param data - Template data
|
|
441
|
-
* @returns Promise that resolves
|
|
281
|
+
* @param data - Template data; `token` is auto-expanded into verification_url/reset_url
|
|
282
|
+
* @returns Promise that resolves with success status
|
|
442
283
|
*/
|
|
443
284
|
export async function send_template_email(template_type, to, data) {
|
|
444
285
|
const logger = create_app_logger();
|
|
286
|
+
// Emit a one-time deprecation warning if the old filesystem override key is set.
|
|
287
|
+
warn_if_template_directory_configured();
|
|
445
288
|
try {
|
|
446
|
-
//
|
|
289
|
+
// URL construction stays here — hazo_notify just renders variables.
|
|
447
290
|
const enhanced_data = Object.assign({}, data);
|
|
448
291
|
if (data.token) {
|
|
449
292
|
if (template_type === "email_verification") {
|
|
@@ -453,23 +296,52 @@ export async function send_template_email(template_type, to, data) {
|
|
|
453
296
|
enhanced_data.reset_url = get_reset_password_url(data.token);
|
|
454
297
|
}
|
|
455
298
|
}
|
|
456
|
-
//
|
|
457
|
-
const
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
299
|
+
// Coerce `string | undefined` data into `Record<string, string>` for Handlebars.
|
|
300
|
+
const variables = {};
|
|
301
|
+
for (const [key, value] of Object.entries(enhanced_data)) {
|
|
302
|
+
if (typeof value === "string") {
|
|
303
|
+
variables[key] = value;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
if (!hazo_notify_connect) {
|
|
307
|
+
throw new Error("hazo_notify connect not initialized. Call set_hazo_notify_connect() " +
|
|
308
|
+
"from instrumentation.ts with the same HazoConnectInstance you pass " +
|
|
309
|
+
"to init_template_manager({ hazo_connect_factory }).");
|
|
310
|
+
}
|
|
461
311
|
const notify_config = await get_hazo_notify_instance();
|
|
462
|
-
// Get email from address and from name
|
|
463
|
-
// Priority: 1. hazo_auth_config.from_email/from_name, 2. hazo_notify_config.from_email/from_name
|
|
464
312
|
const from = await get_email_from(notify_config);
|
|
465
|
-
|
|
466
|
-
|
|
313
|
+
const from_name = await get_email_from_name(notify_config);
|
|
314
|
+
const subject = get_email_subject(template_type);
|
|
315
|
+
// Dynamic import keeps hazo_notify optional at build time.
|
|
316
|
+
const { send_template_email: notify_send_template_email } = await import("hazo_notify/template_manager");
|
|
317
|
+
const result = await notify_send_template_email({
|
|
318
|
+
template_name: template_type,
|
|
467
319
|
to,
|
|
468
|
-
|
|
320
|
+
variables,
|
|
321
|
+
scope_id: null,
|
|
469
322
|
subject,
|
|
470
|
-
|
|
471
|
-
|
|
323
|
+
from,
|
|
324
|
+
from_name,
|
|
325
|
+
}, hazo_notify_connect);
|
|
326
|
+
if (result.success) {
|
|
327
|
+
logger.info("email_sent_via_template", {
|
|
328
|
+
filename: "email_service.ts",
|
|
329
|
+
line_number: 0,
|
|
330
|
+
template_type,
|
|
331
|
+
to,
|
|
332
|
+
message_id: result.message_id,
|
|
333
|
+
});
|
|
334
|
+
return { success: true };
|
|
335
|
+
}
|
|
336
|
+
const error_message = result.error || result.message || "Unknown error";
|
|
337
|
+
logger.error("email_template_send_failed", {
|
|
338
|
+
filename: "email_service.ts",
|
|
339
|
+
line_number: 0,
|
|
340
|
+
template_type,
|
|
341
|
+
to,
|
|
342
|
+
error: error_message,
|
|
472
343
|
});
|
|
344
|
+
return { success: false, error: error_message };
|
|
473
345
|
}
|
|
474
346
|
catch (error) {
|
|
475
347
|
const error_message = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import type { SystemTemplateManifest } from "hazo_notify/template_manager";
|
|
3
|
+
/**
|
|
4
|
+
* System email templates shipped by hazo_auth.
|
|
5
|
+
*
|
|
6
|
+
* Consumers feed this into `init_template_manager({ manifests: [...hazo_auth_template_manifest] })`
|
|
7
|
+
* at boot. hazo_notify seeds/syncs the templates into its scope-aware database.
|
|
8
|
+
* Per-scope overrides are managed through the hazo_notify admin UI.
|
|
9
|
+
*/
|
|
10
|
+
export declare const hazo_auth_template_manifest: SystemTemplateManifest[];
|
|
11
|
+
//# sourceMappingURL=email_template_manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email_template_manifest.d.ts","sourceRoot":"","sources":["../../../src/lib/services/email_template_manifest.ts"],"names":[],"mappings":"AAEA,OAAO,aAAa,CAAC;AAMrB,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAiB3E;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAE,sBAAsB,EAuE/D,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// file_description: manifest of system email templates shipped by hazo_auth
|
|
2
|
+
// section: server_only_guard
|
|
3
|
+
import "server-only";
|
|
4
|
+
// section: imports
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
// section: constants
|
|
9
|
+
const TEMPLATE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "email_templates");
|
|
10
|
+
const SYSTEM_CATEGORY = "Auth";
|
|
11
|
+
// section: helpers
|
|
12
|
+
function read_template(template_name, extension) {
|
|
13
|
+
const file_path = path.join(TEMPLATE_DIR, `${template_name}.${extension}`);
|
|
14
|
+
return fs.readFileSync(file_path, "utf-8");
|
|
15
|
+
}
|
|
16
|
+
// section: manifest
|
|
17
|
+
/**
|
|
18
|
+
* System email templates shipped by hazo_auth.
|
|
19
|
+
*
|
|
20
|
+
* Consumers feed this into `init_template_manager({ manifests: [...hazo_auth_template_manifest] })`
|
|
21
|
+
* at boot. hazo_notify seeds/syncs the templates into its scope-aware database.
|
|
22
|
+
* Per-scope overrides are managed through the hazo_notify admin UI.
|
|
23
|
+
*/
|
|
24
|
+
export const hazo_auth_template_manifest = [
|
|
25
|
+
{
|
|
26
|
+
template_name: "email_verification",
|
|
27
|
+
template_label: "Email verification",
|
|
28
|
+
category: SYSTEM_CATEGORY,
|
|
29
|
+
html: read_template("email_verification", "html"),
|
|
30
|
+
text: read_template("email_verification", "txt"),
|
|
31
|
+
variables: [
|
|
32
|
+
{
|
|
33
|
+
variable_name: "user_name",
|
|
34
|
+
variable_description: "Recipient display name",
|
|
35
|
+
default_value: "",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
variable_name: "user_email",
|
|
39
|
+
variable_description: "Recipient email",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
variable_name: "verification_url",
|
|
43
|
+
variable_description: "Verification link with embedded token",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
variable_name: "token",
|
|
47
|
+
variable_description: "Raw verification token",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
template_name: "forgot_password",
|
|
53
|
+
template_label: "Forgot password",
|
|
54
|
+
category: SYSTEM_CATEGORY,
|
|
55
|
+
html: read_template("forgot_password", "html"),
|
|
56
|
+
text: read_template("forgot_password", "txt"),
|
|
57
|
+
variables: [
|
|
58
|
+
{
|
|
59
|
+
variable_name: "user_name",
|
|
60
|
+
variable_description: "Recipient display name",
|
|
61
|
+
default_value: "",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
variable_name: "user_email",
|
|
65
|
+
variable_description: "Recipient email",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
variable_name: "reset_url",
|
|
69
|
+
variable_description: "Password reset link",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
variable_name: "token",
|
|
73
|
+
variable_description: "Raw reset token",
|
|
74
|
+
},
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
template_name: "password_changed",
|
|
79
|
+
template_label: "Password changed",
|
|
80
|
+
category: SYSTEM_CATEGORY,
|
|
81
|
+
html: read_template("password_changed", "html"),
|
|
82
|
+
text: read_template("password_changed", "txt"),
|
|
83
|
+
variables: [
|
|
84
|
+
{
|
|
85
|
+
variable_name: "user_name",
|
|
86
|
+
variable_description: "Recipient display name",
|
|
87
|
+
default_value: "",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
variable_name: "user_email",
|
|
91
|
+
variable_description: "Recipient email",
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Verify Your Email</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
8
|
+
<h1 style="color: #0f172a;">Verify Your Email Address</h1>
|
|
9
|
+
<p>Thank you for registering! Please click the link below to verify your email address:</p>
|
|
10
|
+
<p style="margin: 20px 0;">
|
|
11
|
+
<a href="{{verification_url}}" style="display: inline-block; padding: 12px 24px; background-color: #0f172a; color: #ffffff; text-decoration: none; border-radius: 4px;">Verify Email Address</a>
|
|
12
|
+
</p>
|
|
13
|
+
<p>Or copy and paste this link into your browser:</p>
|
|
14
|
+
<p style="word-break: break-all; color: #666;">{{verification_url}}</p>
|
|
15
|
+
<p>This link will expire in 48 hours.</p>
|
|
16
|
+
<p style="margin-top: 30px; color: #666; font-size: 12px;">If you didn't create an account, you can safely ignore this email.</p>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Reset Your Password</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
8
|
+
<h1 style="color: #0f172a;">Reset Your Password</h1>
|
|
9
|
+
<p>We received a request to reset your password. Click the link below to reset it:</p>
|
|
10
|
+
<p style="margin: 20px 0;">
|
|
11
|
+
<a href="{{reset_url}}" style="display: inline-block; padding: 12px 24px; background-color: #0f172a; color: #ffffff; text-decoration: none; border-radius: 4px;">Reset Password</a>
|
|
12
|
+
</p>
|
|
13
|
+
<p>Or copy and paste this link into your browser:</p>
|
|
14
|
+
<p style="word-break: break-all; color: #666;">{{reset_url}}</p>
|
|
15
|
+
<p>This link will expire in 10 minutes.</p>
|
|
16
|
+
<p style="margin-top: 30px; color: #666; font-size: 12px;">If you didn't request a password reset, you can safely ignore this email.</p>
|
|
17
|
+
</body>
|
|
18
|
+
</html>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<title>Password Changed</title>
|
|
6
|
+
</head>
|
|
7
|
+
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
|
8
|
+
<h1 style="color: #0f172a;">Password Changed Successfully</h1>
|
|
9
|
+
<p>Hello {{user_name}},</p>
|
|
10
|
+
<p>This email confirms that your password has been changed successfully.</p>
|
|
11
|
+
<p>If you did not make this change, please contact support immediately to secure your account.</p>
|
|
12
|
+
<p style="margin-top: 30px; color: #666; font-size: 12px;">This is an automated notification. Please do not reply to this email.</p>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Password Changed Successfully
|
|
2
|
+
|
|
3
|
+
Hello {{user_name}},
|
|
4
|
+
|
|
5
|
+
This email confirms that your password has been changed successfully.
|
|
6
|
+
|
|
7
|
+
If you did not make this change, please contact support immediately to secure your account.
|
|
8
|
+
|
|
9
|
+
This is an automated notification. Please do not reply to this email.
|
package/dist/server-lib.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "server-only";
|
|
2
2
|
export * from "./lib/auth/index.js";
|
|
3
3
|
export * from "./lib/services/index.js";
|
|
4
|
+
export { hazo_auth_template_manifest } from "./lib/services/email_template_manifest.js";
|
|
4
5
|
export { get_config_value, get_config_number, get_config_boolean, get_config_array, read_config_section, } from "./lib/config/config_loader.server.js";
|
|
5
6
|
export { get_login_config } from "./lib/login_config.server.js";
|
|
6
7
|
export { get_register_config } from "./lib/register_config.server.js";
|
package/dist/server-lib.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-lib.d.ts","sourceRoot":"","sources":["../src/server-lib.ts"],"names":[],"mappings":"AAYA,OAAO,aAAa,CAAC;AAGrB,cAAc,kBAAkB,CAAC;AAGjC,cAAc,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"server-lib.d.ts","sourceRoot":"","sources":["../src/server-lib.ts"],"names":[],"mappings":"AAYA,OAAO,aAAa,CAAC;AAGrB,cAAc,kBAAkB,CAAC;AAGjC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,2BAA2B,EAAE,MAAM,wCAAwC,CAAC;AAGrF,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,mCAAmC,CAAC;AAG3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAC/E,OAAO,EAAE,6BAA6B,EAAE,MAAM,wCAAwC,CAAC;AACvF,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AACjF,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AAC3E,OAAO,EAAE,gCAAgC,EAAE,MAAM,2CAA2C,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAG/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,YAAY,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,cAAc,+BAA+B,CAAC"}
|
package/dist/server-lib.js
CHANGED
|
@@ -14,6 +14,7 @@ import "server-only";
|
|
|
14
14
|
export * from "./lib/auth/index.js";
|
|
15
15
|
// section: service_exports
|
|
16
16
|
export * from "./lib/services/index.js";
|
|
17
|
+
export { hazo_auth_template_manifest } from "./lib/services/email_template_manifest.js";
|
|
17
18
|
// section: config_exports
|
|
18
19
|
export { get_config_value, get_config_number, get_config_boolean, get_config_array, read_config_section, } from "./lib/config/config_loader.server.js";
|
|
19
20
|
// section: config_server_exports
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_auth",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.3.1",
|
|
4
4
|
"description": "Zero-config authentication UI components for Next.js with RBAC, OAuth, scope-based multi-tenancy, and invitations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"authentication",
|
|
@@ -193,6 +193,7 @@
|
|
|
193
193
|
"dev": "next dev",
|
|
194
194
|
"build": "next build",
|
|
195
195
|
"build:pkg": "tsc --jsx react-jsx --skipLibCheck -p tsconfig.build.json && tsx scripts/copy_assets.ts",
|
|
196
|
+
"prepare": "npm run build:pkg",
|
|
196
197
|
"prepublishOnly": "npm run build:pkg",
|
|
197
198
|
"validate": "tsx scripts/validate_setup.ts",
|
|
198
199
|
"generate-routes": "tsx scripts/generate_routes.ts",
|
|
@@ -251,7 +252,7 @@
|
|
|
251
252
|
"hazo_config": "^2.1.0",
|
|
252
253
|
"hazo_connect": "^2.4.0",
|
|
253
254
|
"hazo_logs": "^1.0.13",
|
|
254
|
-
"hazo_notify": "^
|
|
255
|
+
"hazo_notify": "^3.0.0",
|
|
255
256
|
"hazo_ui": "^2.7.0",
|
|
256
257
|
"lucide-react": "^0.553.0",
|
|
257
258
|
"next": ">=14.0.0",
|
|
@@ -375,7 +376,7 @@
|
|
|
375
376
|
"hazo_config": "^2.1.0",
|
|
376
377
|
"hazo_connect": "^2.4.0",
|
|
377
378
|
"hazo_logs": "^1.0.13",
|
|
378
|
-
"hazo_notify": "^
|
|
379
|
+
"hazo_notify": "^3.0.0",
|
|
379
380
|
"hazo_ui": "^2.7.0",
|
|
380
381
|
"jest": "^30.2.0",
|
|
381
382
|
"jest-environment-jsdom": "^30.0.0",
|
|
Binary file
|