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.
Files changed (40) hide show
  1. package/README.md +13 -0
  2. package/SETUP_CHECKLIST.md +46 -0
  3. package/cli-src/lib/auth/hazo_get_auth.server.ts +1 -1
  4. package/cli-src/lib/auth/index.ts +1 -1
  5. package/cli-src/lib/services/email_service.ts +136 -289
  6. package/cli-src/lib/services/email_template_manifest.ts +104 -0
  7. package/cli-src/lib/services/email_templates/email_verification.html +18 -0
  8. package/cli-src/lib/services/email_templates/email_verification.txt +9 -0
  9. package/cli-src/lib/services/email_templates/forgot_password.html +18 -0
  10. package/cli-src/lib/services/email_templates/forgot_password.txt +9 -0
  11. package/cli-src/lib/services/email_templates/password_changed.html +14 -0
  12. package/cli-src/lib/services/email_templates/password_changed.txt +9 -0
  13. package/dist/components/ui/button.d.ts +2 -2
  14. package/dist/lib/auth/hazo_get_auth.server.d.ts +6 -0
  15. package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
  16. package/dist/lib/auth/hazo_get_auth.server.js +1 -1
  17. package/dist/lib/auth/index.d.ts +1 -1
  18. package/dist/lib/auth/index.d.ts.map +1 -1
  19. package/dist/lib/auth/index.js +1 -1
  20. package/dist/lib/services/email_service.d.ts +17 -4
  21. package/dist/lib/services/email_service.d.ts.map +1 -1
  22. package/dist/lib/services/email_service.js +93 -221
  23. package/dist/lib/services/email_template_manifest.d.ts +11 -0
  24. package/dist/lib/services/email_template_manifest.d.ts.map +1 -0
  25. package/dist/lib/services/email_template_manifest.js +95 -0
  26. package/dist/lib/services/email_templates/email_verification.html +18 -0
  27. package/dist/lib/services/email_templates/email_verification.txt +9 -0
  28. package/dist/lib/services/email_templates/forgot_password.html +18 -0
  29. package/dist/lib/services/email_templates/forgot_password.txt +9 -0
  30. package/dist/lib/services/email_templates/password_changed.html +14 -0
  31. package/dist/lib/services/email_templates/password_changed.txt +9 -0
  32. package/dist/server-lib.d.ts +1 -0
  33. package/dist/server-lib.d.ts.map +1 -1
  34. package/dist/server-lib.js +1 -0
  35. package/package.json +4 -3
  36. package/cli-src/assets/images/new_firm_default.jpg +0 -0
  37. package/cli-src/lib/auth/org_cache.ts +0 -148
  38. package/cli-src/lib/index.ts +0 -48
  39. package/cli-src/lib/services/org_service.ts +0 -965
  40. 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: helpers
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
- * Gets email template directory from config
64
- * @returns Email template directory path
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 get_email_template_directory() {
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 path.isAbsolute(template_dir)
71
- ? template_dir
72
- : path.resolve(process.cwd(), template_dir);
73
- }
74
- return get_default_email_template_dir();
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
- * @param template_type - Type of email template
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 for variable substitution
441
- * @returns Promise that resolves when email is sent
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
- // Enhance data with URLs if token is provided
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
- // Get email templates
457
- const { html_body, text_body } = get_email_templates(template_type, enhanced_data);
458
- // Get email subject
459
- const subject = get_email_subject(template_type);
460
- // Get hazo_notify config instance
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
- // Send email (from_name is handled inside send_email function)
466
- return await send_email({
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
- from,
320
+ variables,
321
+ scope_id: null,
469
322
  subject,
470
- html_body,
471
- text_body,
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,9 @@
1
+ Verify Your Email Address
2
+
3
+ Thank you for registering! Please click the link below to verify your email address:
4
+
5
+ {{verification_url}}
6
+
7
+ This link will expire in 48 hours.
8
+
9
+ If you didn't create an account, you can safely ignore this email.
@@ -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,9 @@
1
+ Reset Your Password
2
+
3
+ We received a request to reset your password. Click the link below to reset it:
4
+
5
+ {{reset_url}}
6
+
7
+ This link will expire in 10 minutes.
8
+
9
+ If you didn't request a password reset, you can safely ignore this email.
@@ -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.
@@ -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";
@@ -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;AAGrC,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"}
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"}
@@ -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.2.0",
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": "^1.1.0",
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": "^1.1.0",
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",