hazo_auth 5.1.8 → 5.1.10

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.
@@ -23,11 +23,12 @@ const REQUIRED_DIRECTORIES = [
23
23
  "public/profile_pictures/library",
24
24
  "public/profile_pictures/uploads",
25
25
  "data",
26
+ "config",
26
27
  ];
27
28
 
28
29
  const CONFIG_FILES = [
29
- { source: "hazo_auth_config.example.ini", target: "hazo_auth_config.ini" },
30
- { source: "hazo_notify_config.example.ini", target: "hazo_notify_config.ini" },
30
+ { source: "config/hazo_auth_config.example.ini", target: "config/hazo_auth_config.ini" },
31
+ { source: "config/hazo_notify_config.example.ini", target: "config/hazo_notify_config.ini" },
31
32
  ];
32
33
 
33
34
  // section: helpers
@@ -246,7 +247,7 @@ export function handle_init(): void {
246
247
 
247
248
  console.log("\n\x1b[32m🦊 Initialization complete!\x1b[0m");
248
249
  console.log("\nNext steps:");
249
- console.log(" 1. Edit \x1b[36mhazo_auth_config.ini\x1b[0m with your settings");
250
+ console.log(" 1. Edit \x1b[36mconfig/hazo_auth_config.ini\x1b[0m with your settings");
250
251
  console.log(" 2. Copy \x1b[36m.env.local.example\x1b[0m to \x1b[36m.env.local\x1b[0m and add your API keys");
251
252
  console.log(" 3. Run \x1b[36mnpx hazo_auth generate-routes --pages\x1b[0m to generate routes and pages");
252
253
  console.log(" 4. Run \x1b[36mnpx hazo_auth validate\x1b[0m to check your setup");
@@ -22,8 +22,8 @@ export type ValidationSummary = {
22
22
 
23
23
  // section: constants
24
24
  const REQUIRED_CONFIG_FILES = [
25
- "hazo_auth_config.ini",
26
- "hazo_notify_config.ini",
25
+ "config/hazo_auth_config.ini",
26
+ "config/hazo_notify_config.ini",
27
27
  ];
28
28
 
29
29
  const REQUIRED_ENV_VARS = [
@@ -141,7 +141,7 @@ function check_config_files(project_root: string): CheckResult[] {
141
141
  results.push({
142
142
  name: `Config file: ${config_file}`,
143
143
  status: exists ? "pass" : "fail",
144
- message: exists ? "" : `File not found. Run: cp node_modules/hazo_auth/${config_file.replace(".ini", ".example.ini")} ./${config_file}`,
144
+ message: exists ? "" : `File not found. Run: npx hazo_auth init`,
145
145
  });
146
146
  }
147
147
 
@@ -150,8 +150,8 @@ function check_config_files(project_root: string): CheckResult[] {
150
150
 
151
151
  function check_config_values(project_root: string): CheckResult[] {
152
152
  const results: CheckResult[] = [];
153
-
154
- const hazo_config_path = path.join(project_root, "hazo_auth_config.ini");
153
+
154
+ const hazo_config_path = path.join(project_root, "config", "hazo_auth_config.ini");
155
155
  const hazo_config = read_ini_file(hazo_config_path);
156
156
 
157
157
  if (hazo_config) {
@@ -224,7 +224,7 @@ function check_config_values(project_root: string): CheckResult[] {
224
224
  }
225
225
  }
226
226
 
227
- const notify_config_path = path.join(project_root, "hazo_notify_config.ini");
227
+ const notify_config_path = path.join(project_root, "config", "hazo_notify_config.ini");
228
228
  const notify_config = read_ini_file(notify_config_path);
229
229
 
230
230
  if (notify_config) {
@@ -414,15 +414,15 @@ function check_profile_pictures(project_root: string): CheckResult[] {
414
414
 
415
415
  function check_database(project_root: string): CheckResult[] {
416
416
  const results: CheckResult[] = [];
417
-
418
- const hazo_config_path = path.join(project_root, "hazo_auth_config.ini");
417
+
418
+ const hazo_config_path = path.join(project_root, "config", "hazo_auth_config.ini");
419
419
  const hazo_config = read_ini_file(hazo_config_path);
420
420
 
421
421
  if (!hazo_config) {
422
422
  results.push({
423
423
  name: "Database check",
424
424
  status: "fail",
425
- message: "Could not read hazo_auth_config.ini",
425
+ message: "Could not read config/hazo_auth_config.ini",
426
426
  });
427
427
  return results;
428
428
  }
@@ -6,7 +6,7 @@ import fs from "fs";
6
6
  import { create_app_logger } from "../app_logger.js";
7
7
 
8
8
  // section: constants
9
- const DEFAULT_CONFIG_FILE = "hazo_auth_config.ini";
9
+ const DEFAULT_CONFIG_FILE = "config/hazo_auth_config.ini";
10
10
 
11
11
  // section: helpers
12
12
  /**
@@ -261,7 +261,7 @@ export async function accept_invitation(
261
261
  if (invitation.status !== "PENDING") {
262
262
  return {
263
263
  success: false,
264
- error: `Invitation is ${invitation.status.toLowerCase()}, not pending`,
264
+ error: `Invitation is ${invitation.status}, not PENDING`,
265
265
  };
266
266
  }
267
267
 
@@ -361,7 +361,7 @@ export async function revoke_invitation(
361
361
  if (invitation_result.invitation.status !== "PENDING") {
362
362
  return {
363
363
  success: false,
364
- error: `Cannot revoke invitation with status ${invitation_result.invitation.status.toLowerCase()}`,
364
+ error: `Cannot revoke invitation with status ${invitation_result.invitation.status}`,
365
365
  };
366
366
  }
367
367
 
@@ -0,0 +1,768 @@
1
+ # file_description: configuration file for hazo_auth test application
2
+ # This file contains all configurable parameters for the application
3
+ # Commented values indicate defaults that are being used
4
+
5
+ [hazo_connect]
6
+ # Database type: sqlite, postgrest, supabase, or file
7
+ type = sqlite
8
+ ; type = postgrest
9
+
10
+ # SQLite database configuration
11
+ # Path to SQLite database file (relative to process.cwd() or absolute)
12
+ sqlite_path = __tests__/fixtures/hazo_auth.sqlite
13
+
14
+ # Enable SQLite admin UI (true/false)
15
+ enable_admin_ui = true
16
+
17
+ # SQLite read-only mode (true/false)
18
+ # read_only = false
19
+
20
+ # PostgREST configuration (uncomment if using postgrest type)
21
+ postgrest_url = http://209.38.26.241:4402
22
+ # postgrest_api_key =
23
+
24
+ [hazo_auth__ui_shell]
25
+ # Available options: test_sidebar (default) or standalone
26
+ layout_mode = test_sidebar
27
+ standalone_heading = Welcome to hazo auth
28
+ standalone_description = Reuse the packaged authentication flows while inheriting your existing app shell styles.
29
+ standalone_wrapper_class = cls_standalone_shell flex min-h-screen w-full items-center justify-center bg-background px-4 py-10
30
+ standalone_content_class = cls_standalone_shell_content w-full max-w-5xl shadow-xl rounded-2xl border bg-card
31
+ # Show/hide heading and description (true/false, default: true)
32
+ standalone_show_heading = false
33
+ standalone_show_description = false
34
+
35
+ # Enable vertical centering of auth content (default: true)
36
+ # When enabled, auth forms are vertically centered in the viewport
37
+ # vertical_center = true
38
+
39
+ [hazo_auth__navbar]
40
+ # Navbar Configuration for Auth Pages
41
+ # The navbar appears on all auth pages (login, register, forgot_password, etc.)
42
+ # when using standalone layout mode. It provides branding and a home link.
43
+ #
44
+ # Note: The navbar is automatically disabled for dev_lock pages.
45
+
46
+ # Enable/disable navbar on auth pages (default: true)
47
+ # enable_navbar = true
48
+
49
+ # Logo Configuration
50
+ # Path to logo image relative to public folder (default: /logo.png)
51
+ # logo_path = /logo.png
52
+
53
+ # Logo dimensions in pixels (default: 32x32)
54
+ # logo_width = 32
55
+ # logo_height = 32
56
+
57
+ # Company/Application Branding
58
+ # Company name displayed next to the logo (default: empty)
59
+ # Leave empty to show only the logo
60
+ # company_name = My Company
61
+
62
+ # Home Link Configuration
63
+ # Path for the home link and logo click (default: /)
64
+ # home_path = /
65
+
66
+ # Label for the home link (default: Home)
67
+ # home_label = Home
68
+
69
+ # Show/hide the home link on the right side (default: true)
70
+ # show_home_link = true
71
+
72
+ # Styling Options
73
+ # Background color for the navbar (default: empty = uses theme background)
74
+ # Leave empty to inherit from your theme's background color
75
+ # background_color = #ffffff
76
+
77
+ # Text color for the navbar (default: empty = uses theme foreground)
78
+ # Leave empty to inherit from your theme's text color
79
+ # text_color = #000000
80
+
81
+ # Navbar height in pixels (default: 64)
82
+ # height = 64
83
+
84
+ [hazo_auth__register_layout]
85
+ # Image configuration
86
+ # If image_src is commented out or empty, the default image from package assets will be used
87
+ # Default: src/assets/images/register_default.jpg
88
+ ; image_src = /auth_images/city_scape.jpg
89
+ # image_alt = Modern building representing user registration
90
+ # image_background_color = #e2e8f0
91
+
92
+ # Labels
93
+ # heading = Create your hazo account
94
+ # sub_heading = Secure your access with editable fields powered by shadcn components.
95
+ # submit_button = Register
96
+ # cancel_button = Cancel
97
+
98
+ # Field labels (defaults shown, uncomment to override)
99
+ # name_label = Full name
100
+ # name_placeholder = Enter your full name
101
+ # email_label = Email address
102
+ # email_placeholder = Enter your email address
103
+ # password_label = Password
104
+ # password_placeholder = Enter your password
105
+ # confirm_password_label = Re-enter password
106
+ # confirm_password_placeholder = Re-enter your password
107
+
108
+ # Button colors
109
+ # submit_background = #0f172a
110
+ # submit_text = #ffffff
111
+ # cancel_border = #cbd5f5
112
+ # cancel_text = #0f172a
113
+
114
+ # Show name field (true/false)
115
+ # Note: This is now deprecated, use hazo_auth__user_fields section instead
116
+ show_name_field = false
117
+
118
+ [hazo_auth__user_fields]
119
+ # Shared user fields configuration used by register and my_settings layouts
120
+ # Show name field (true/false, default: true)
121
+ # show_name_field = true
122
+
123
+ # Show email field (true/false, default: true)
124
+ # show_email_field = true
125
+
126
+ # Show password field (true/false, default: true)
127
+ # show_password_field = true
128
+
129
+ [hazo_auth__password_requirements]
130
+ # Shared password requirements used by register and reset password layouts
131
+ minimum_length = 3
132
+ # require_uppercase = false
133
+ # require_lowercase = false
134
+ # require_number = false
135
+ # require_special = false
136
+
137
+ [hazo_auth__oauth]
138
+ # OAuth Provider Configuration
139
+ # These settings control which authentication methods are available on the login page
140
+
141
+ # Enable Google OAuth login (requires HAZO_AUTH_GOOGLE_CLIENT_ID and HAZO_AUTH_GOOGLE_CLIENT_SECRET env vars)
142
+ # Default: true
143
+ enable_google = true
144
+
145
+ # Enable traditional email/password login
146
+ # Default: true
147
+ enable_email_password = true
148
+
149
+ # Auto-link accounts: When user logs in with OAuth using an email that matches
150
+ # an existing unverified email/password account, automatically link and verify the account
151
+ # Default: true
152
+ auto_link_unverified_accounts = true
153
+
154
+ # UI Labels for OAuth section
155
+ # Text displayed on the Google sign-in button
156
+ # google_button_text = Continue with Google
157
+
158
+ # Text displayed on the divider between OAuth and email/password form
159
+ # oauth_divider_text = or continue with email
160
+
161
+ [hazo_auth__login_layout]
162
+ # Image configuration
163
+ # If image_src is commented out or empty, the default image from package assets will be used
164
+ # Default: src/assets/images/login_default.jpg
165
+ # image_src = /auth_images/your_login_image.jpg
166
+ # image_alt = Secure login illustration
167
+ # image_background_color = #f1f5f9
168
+
169
+ # Labels
170
+ # heading = Sign in to your account
171
+ # sub_heading = Enter your credentials to access your secure workspace.
172
+ # submit_button = Login
173
+ # cancel_button = Cancel
174
+
175
+ # Field labels (defaults shown, uncomment to override)
176
+ # email_label = Email address
177
+ # email_placeholder = Enter your email address
178
+ # password_label = Password
179
+ # password_placeholder = Enter your password
180
+
181
+ # Button colors
182
+ # submit_background = #0f172a
183
+ # submit_text = #ffffff
184
+ # cancel_border = #cbd5f5
185
+ # cancel_text = #0f172a
186
+
187
+ # Redirect on successful login (leave empty to show success message instead)
188
+ redirect_route_on_successful_login = /
189
+
190
+ # Links shown beneath the login form
191
+ forgot_password_path = /hazo_auth/forgot_password
192
+ forgot_password_label = Forgot password?
193
+ create_account_path = /hazo_auth/register
194
+ create_account_label = Create account
195
+
196
+ # Success message (shown when no redirect route is provided)
197
+ # success_message = Successfully logged in
198
+
199
+ [hazo_auth__forgot_password_layout]
200
+ # Image configuration
201
+ # If image_src is commented out or empty, the default image from package assets will be used
202
+ # Default: src/assets/images/forgot_password_default.jpg
203
+ # image_src = /auth_images/your_forgot_password_image.jpg
204
+ # image_alt = Password recovery illustration
205
+ # image_background_color = #f1f5f9
206
+
207
+ # Labels
208
+ # heading = Forgot your password?
209
+ # sub_heading = Enter your email address and we'll send you a link to reset your password.
210
+ # submit_button = Send reset link
211
+ # cancel_button = Cancel
212
+
213
+ [hazo_auth__reset_password_layout]
214
+ # Image configuration
215
+ # If image_src is commented out or empty, the default image from package assets will be used
216
+ # Default: src/assets/images/reset_password_default.jpg
217
+ # image_src = /auth_images/your_reset_password_image.jpg
218
+ # image_alt = Reset password illustration
219
+ # image_background_color = #f1f5f9
220
+
221
+ # Labels
222
+ # heading = Reset your password
223
+ # sub_heading = Enter your new password below.
224
+ # submit_button = Reset password
225
+ # cancel_button = Cancel
226
+
227
+ # Field labels (defaults shown, uncomment to override)
228
+ # password_label = New password
229
+ # password_placeholder = Enter your new password
230
+ # confirm_password_label = Confirm new password
231
+ # confirm_password_placeholder = Re-enter your new password
232
+
233
+ # Button colors
234
+ # submit_background = #0f172a
235
+ # submit_text = #ffffff
236
+ # cancel_border = #cbd5f5
237
+ # cancel_text = #0f172a
238
+
239
+ # Error message (shown when token is invalid or missing)
240
+ # error_message = Reset password link invalid or has expired. Please go to Reset Password page to get a new link.
241
+
242
+ # Success message (shown after successful password reset)
243
+ # success_message = Password reset successfully. Redirecting to login...
244
+
245
+ # Login path (redirect target after successful reset)
246
+ # login_path = /login
247
+
248
+ # Forgot password path (link shown in error state)
249
+ # forgot_password_path = /forgot_password
250
+
251
+ # Button colors
252
+ # submit_background = #0f172a
253
+ # submit_text = #ffffff
254
+ # cancel_border = #cbd5f5
255
+ # cancel_text = #0f172a
256
+
257
+ [hazo_auth__email_verification_layout]
258
+ # Image configuration
259
+ # If image_src is commented out or empty, the default image from package assets will be used
260
+ # Default: src/assets/images/verify_email_default.jpg
261
+ # image_src = /auth_images/your_verify_email_image.jpg
262
+ # image_alt = Email verification illustration
263
+ # image_background_color = #f1f5f9
264
+
265
+ # Labels (verifying state)
266
+ # heading = Email verification
267
+ # sub_heading = Verifying your email address...
268
+ # submit_button = Resend verification email
269
+ # cancel_button = Cancel
270
+
271
+ # Success labels
272
+ # success_heading = Email verified successfully
273
+ # success_message = Your email address has been verified. You can now log in to your account.
274
+ # success_redirect_message = Redirecting to login page in
275
+ # success_go_to_login_button = Go to login
276
+
277
+ # Error labels
278
+ # error_heading = Verification failed
279
+ # error_message = The verification link is invalid or has expired.
280
+ # error_resend_form_heading = Resend verification email
281
+
282
+ # Field labels (defaults shown, uncomment to override)
283
+ # email_label = Email address
284
+ # email_placeholder = Enter your email address
285
+
286
+ # Button colors
287
+ # submit_background = #0f172a
288
+ # submit_text = #ffffff
289
+ # cancel_border = #cbd5f5
290
+ # cancel_text = #0f172a
291
+
292
+ # Redirect delay in seconds (default: 5)
293
+ # redirect_delay = 5
294
+
295
+ # Login path for redirect (default: /login)
296
+ # login_path = /login
297
+
298
+ [hazo_auth__already_logged_in]
299
+ # Message shown when user is already logged in (used across all auth pages)
300
+ # message = You're already logged in.
301
+
302
+ # Show logout button (true/false)
303
+ # show_logout_button = true
304
+
305
+ # Show return home button (true/false)
306
+ # show_return_home_button = false
307
+
308
+ # Return home button label
309
+ # return_home_button_label = Return home
310
+
311
+ # Return home path
312
+ # return_home_path = /
313
+
314
+ [hazo_auth__my_settings_layout]
315
+ # My Settings layout configuration
316
+
317
+ # Note: This layout uses shared configuration from:
318
+ # - [hazo_auth__user_fields] for field visibility (show_name_field, show_email_field, show_password_field)
319
+ # - [hazo_auth__password_requirements] for password validation rules
320
+ # - [hazo_auth__profile_picture] for profile picture settings and prioritization
321
+
322
+ # Heading
323
+ # heading = Account Settings
324
+
325
+ # Sub heading
326
+ # sub_heading = Manage your profile, password, and email preferences.
327
+
328
+ # Profile Photo section
329
+ # profile_photo_label = Profile Photo
330
+ # profile_photo_recommendation = Recommended size: 200x200px. JPG, PNG.
331
+ # Note: profile_photo_recommendation is currently not displayed in the UI (removed per user request)
332
+ # upload_photo_button_label = Upload New Photo
333
+ # remove_photo_button_label = Remove
334
+
335
+ # Profile Information section
336
+ # profile_information_label = Profile Information
337
+ # Note: Field visibility is controlled by [hazo_auth__user_fields] section:
338
+ # - show_name_field (controls Full Name field visibility)
339
+ # - show_email_field (controls Email Address field visibility)
340
+ # Note: Name and email field labels are currently hardcoded in the component
341
+
342
+ # Password section
343
+ # password_label = Password
344
+ # current_password_label = Current Password
345
+ # new_password_label = New Password
346
+ # confirm_password_label = Confirm Password
347
+ # save_password_button_label = Save Password
348
+ # Note: Field visibility is controlled by [hazo_auth__user_fields] show_password_field
349
+ # Note: Password requirements are controlled by [hazo_auth__password_requirements]
350
+
351
+ # Unauthorized message (shown when user is not logged in)
352
+ # unauthorized_message = You must be logged in to access this page.
353
+
354
+ # Login button label (shown in unauthorized message)
355
+ # login_button_label = Go to login
356
+
357
+ # Login path (redirect target in unauthorized message)
358
+ # login_path = /login
359
+
360
+ [hazo_auth__profile_picture]
361
+ # Profile picture configuration
362
+ # This configuration is used by:
363
+ # - Registration service (for default photo assignment)
364
+ # - My Settings layout (for profile picture management)
365
+
366
+ # Allow photo upload (true/false, default: false)
367
+ # When enabled, users can upload their own profile pictures
368
+ allow_photo_upload = true
369
+
370
+ # Upload photo path (required if allow_photo_upload is true)
371
+ # Path should be outside the component package (e.g., ./uploads/profile_pictures/)
372
+ # Can be relative to process.cwd() or absolute path
373
+ upload_photo_path = ./uploads/profile_pictures
374
+
375
+ # Maximum photo size in bytes (default: 51200 = 50kb)
376
+ # Photos larger than this will be compressed automatically
377
+ # max_photo_size = 51200
378
+
379
+ # Enable default photo assignment on user creation (true/false, default: true)
380
+ # When enabled, new users will automatically get a profile picture based on priority settings
381
+ # user_photo_default = true
382
+
383
+ # Default photo priority 1 (gravatar or library, default: gravatar)
384
+ # This is the first source tried when assigning a default profile picture
385
+ # Options: "gravatar" or "library"
386
+ # user_photo_default_priority1 = gravatar
387
+
388
+ # Default photo priority 2 (library or gravatar, optional)
389
+ # This is the fallback source if priority 1 fails or is not available
390
+ # Options: "library" or "gravatar"
391
+ # Leave commented to disable fallback (only priority 1 will be tried)
392
+ # user_photo_default_priority2 = library
393
+
394
+ # Library photo path relative to public directory (default: /profile_pictures/library)
395
+ # Photos should be organized in category subdirectories (e.g., /profile_pictures/library/Cars/)
396
+ # library_photo_path = /profile_pictures/library
397
+
398
+ [hazo_auth__tokens]
399
+ # Token expiry configuration (in hours)
400
+ # Default expiry times:
401
+ # - refresh: 720 hours (30 days)
402
+ # - password_reset: 0.167 hours (10 minutes)
403
+ # - email_verification: 48 hours
404
+
405
+ # Refresh token expiry (hours)
406
+ # refresh_expiry_hours = 720
407
+
408
+ # Password reset token expiry (hours, default: 10 minutes = 0.167 hours)
409
+ # password_reset_expiry_hours = 0.167
410
+
411
+ # Email verification token expiry (hours)
412
+ # email_verification_expiry_hours = 48
413
+
414
+ [hazo_auth__messages]
415
+ # User-facing messages used across the application
416
+ # Photo upload disabled message
417
+ # photo_upload_disabled_message = Photo upload is not enabled. Please contact your administrator.
418
+
419
+ # Gravatar messages
420
+ # gravatar_setup_message = To set up your Gravatar:
421
+ # gravatar_no_account_message = You don't have a Gravatar account set up for this email address.
422
+
423
+ # Library tooltip message
424
+ # library_tooltip_message = Select another tab image style to remove this image
425
+
426
+ [hazo_auth__ui_sizes]
427
+ # UI size configuration (in pixels)
428
+ # Gravatar image size (default: 200)
429
+ # gravatar_size = 200
430
+
431
+ # Profile picture size (default: 200)
432
+ # profile_picture_size = 200
433
+
434
+ # Tooltip icon sizes
435
+ # tooltip_icon_size_default = 16
436
+ # tooltip_icon_size_small = 14
437
+
438
+ # Library photo grid configuration
439
+ # library_photo_grid_columns = 4
440
+ # library_photo_preview_size = 200
441
+
442
+ # Image compression max width/height (default: 200)
443
+ # image_compression_max_dimension = 200
444
+
445
+ # Upload file hard limit in bytes (default: 10485760 = 10MB)
446
+ # upload_file_hard_limit_bytes = 10485760
447
+
448
+ [hazo_auth__file_types]
449
+ # Allowed file extensions and MIME types
450
+ # Allowed image extensions (comma-separated, default: jpg,jpeg,png)
451
+ # allowed_image_extensions = jpg,jpeg,png
452
+
453
+ # Allowed image MIME types (comma-separated, default: image/jpeg,image/jpg,image/png)
454
+ # allowed_image_mime_types = image/jpeg,image/jpg,image/png
455
+
456
+ [hazo_auth__email]
457
+ # Email configuration for verification, password reset, and password changed notification emails
458
+ # Emails are sent using hazo_notify (configured in hazo_notify_config.ini)
459
+
460
+ # Email from address (optional: overrides hazo_notify_config.ini from_email setting)
461
+ # Priority: 1. hazo_auth__email.from_email, 2. hazo_notify_config.ini from_email
462
+ # If not set, uses hazo_notify_config.ini from_email setting
463
+ from_email = noreply3@bookmemento.com
464
+ from_name = Book Memento2
465
+
466
+ # Base URL for email links (default: empty, uses relative URLs)
467
+ # If provided, this will be used to construct absolute URLs for verification and password reset links
468
+ # Priority: 1. hazo_auth__email.base_url, 2. APP_DOMAIN_NAME env var, 3. NEXT_PUBLIC_APP_URL/APP_URL env vars
469
+ # Example: https://example.com
470
+ # base_url = https://example.com
471
+
472
+ # Email template main directory (default: ./email_templates)
473
+ # This directory should contain email template files (HTML and text)
474
+ # Template files should be named: <template_type>.html and <template_type>.txt
475
+ # Example: email_verification.html, email_verification.txt, forgot_password.html, forgot_password.txt
476
+ # email_template_main_directory = ./email_templates
477
+
478
+ ### Email Template: Email Verification ###
479
+ # Email template configuration for email verification
480
+ # This config key enables custom templates for email verification emails
481
+ # If set, the service will look for email_verification.html and email_verification.txt in the template directory
482
+ # email_template__email_verification = email_verification
483
+
484
+ # Email subject for email verification (default: "Verify Your Email Address")
485
+ # email_template__email_verification__subject = Verify Your Email Address
486
+
487
+ # Template variables available in email_verification template:
488
+ # - {{token}} - The verification token
489
+ # - {{verification_url}} - Full URL for email verification
490
+ # - {{user_email}} - User's email address
491
+ # - {{user_name}} - User's name (if available)
492
+
493
+ ### Email Template: Forgot Password ###
494
+ # Email template configuration for forgot password
495
+ # This config key enables custom templates for forgot password emails
496
+ # If set, the service will look for forgot_password.html and forgot_password.txt in the template directory
497
+ # email_template__forgot_password = forgot_password
498
+
499
+ # Email subject for forgot password (default: "Reset Your Password")
500
+ # email_template__forgot_password__subject = Reset Your Password
501
+
502
+ # Template variables available in forgot_password template:
503
+ # - {{token}} - The password reset token
504
+ # - {{reset_url}} - Full URL for password reset
505
+ # - {{user_email}} - User's email address
506
+ # - {{user_name}} - User's name (if available)
507
+
508
+ ### Email Template: Password Changed ###
509
+ # Email template configuration for password changed notification
510
+ # This config key enables custom templates for password changed notification emails
511
+ # If set, the service will look for password_changed.html and password_changed.txt in the template directory
512
+ # email_template__password_changed = password_changed
513
+
514
+ # Email subject for password changed notification (default: "Password Changed Successfully")
515
+ # email_template__password_changed__subject = Password Changed Successfully
516
+
517
+ # Template variables available in password_changed template:
518
+ # - {{user_email}} - User's email address
519
+ # - {{user_name}} - User's name (if available)
520
+
521
+ [hazo_auth__user_management]
522
+ # User Management configuration
523
+ # Application permission list defaults (comma-separated)
524
+ # These permissions will be shown in the Permissions tab and can be migrated to the database
525
+ # Note: Added HRBAC permissions for scope hierarchy management and user scope assignment
526
+ application_permission_list_defaults = admin_user_management,admin_role_management,admin_permission_management,admin_scope_hierarchy_management,admin_user_scope_assignment,admin_test_access,admin_system
527
+
528
+ [hazo_auth__app_permissions]
529
+ # App Permission Declaration
530
+ # Consuming applications can declare their required permissions with descriptions here.
531
+ # This helps with debugging when permission checks fail - descriptions are included in error messages.
532
+ #
533
+ # Format: app_permission_X = permission_name:Description for debugging
534
+ #
535
+ # Example:
536
+ # app_permission_1 = view_reports:Access to view all reports
537
+ # app_permission_2 = edit_settings:Ability to modify application settings
538
+ # app_permission_3 = manage_users:Permission to manage other users
539
+ #
540
+ # API Endpoint: GET /api/hazo_auth/permissions/app
541
+ # Returns all registered app permissions with their descriptions.
542
+
543
+ [hazo_auth__scope_hierarchy]
544
+ # Hierarchical Role-Based Access Control (HRBAC) configuration
545
+ # HRBAC uses a unified hazo_scopes table with parent_id for hierarchy
546
+ #
547
+ # Scopes are membership-based: users are assigned to scopes via hazo_user_scopes
548
+ # Super admins (scope_id = super_admin_scope_id) have global access
549
+
550
+ # Enable/disable HRBAC (true/false, default: false)
551
+ # When disabled, all scope-related options in hazo_get_auth are ignored
552
+ # and no HRBAC tabs appear in User Management
553
+ enable_hrbac = true
554
+
555
+ # Cache settings for scope lookups
556
+ # TTL in minutes (default: 15)
557
+ scope_cache_ttl_minutes = 15
558
+
559
+ # Maximum entries in scope cache (default: 5000)
560
+ scope_cache_max_entries = 5000
561
+
562
+ # Super admin scope ID (special UUID for global access)
563
+ # Users assigned to this scope have access to all scopes
564
+ super_admin_scope_id = 00000000-0000-0000-0000-000000000000
565
+
566
+ # Default system scope ID (for non-multi-tenancy mode)
567
+ # Used when a single default scope is needed
568
+ default_system_scope_id = 00000000-0000-0000-0000-000000000001
569
+
570
+ # Default role for firm creators
571
+ # When a user creates a firm, they are assigned this role
572
+ default_firm_creator_role = firm_admin
573
+
574
+ [hazo_auth__invitations]
575
+ # Invitation system configuration
576
+ # Invitations allow existing users to invite new users to their scope
577
+
578
+ # Default invitation expiry in hours (default: 48)
579
+ default_expiry_hours = 48
580
+
581
+ # Maximum pending invitations per email address (default: 5)
582
+ max_pending_per_email = 5
583
+
584
+ [hazo_auth__create_firm]
585
+ # Create Firm page configuration
586
+ # Shown to new users who verify their email but have no scope/invitation
587
+
588
+ # Image source for the create firm page (default: /hazo_auth/images/new_firm_default.jpg)
589
+ image_src = /hazo_auth/images/new_firm_default.jpg
590
+
591
+ # Page heading
592
+ heading = Create Your Firm
593
+
594
+ # Page sub-heading
595
+ sub_heading = Set up your organisation to get started
596
+
597
+ # Form field labels
598
+ firm_name_label = Firm Name
599
+ org_structure_label = Organisation Structure
600
+
601
+ # Default value for organisation structure field
602
+ org_structure_default = Headquarters
603
+
604
+ # Submit button label
605
+ submit_button_label = Create Firm
606
+
607
+ [hazo_auth__initial_setup]
608
+ # Initial setup configuration for initializing users, roles, and permissions
609
+ # Email address of the super user to assign the default_super_user_role
610
+ # This user will receive all permissions from application_permission_list_defaults
611
+ default_super_user_email = pubudu79@gmail.com
612
+
613
+ [hazo_auth__auth_utility]
614
+ # Authentication utility configuration
615
+
616
+ # Cache settings
617
+ # Maximum number of users to cache (LRU eviction, default: 10000)
618
+ cache_max_users = 10000
619
+
620
+ # Cache TTL in minutes (default: 15)
621
+ cache_ttl_minutes = 15
622
+
623
+ # Force cache refresh if older than this many minutes (default: 30)
624
+ cache_max_age_minutes = 30
625
+
626
+ # Rate limiting for /api/auth/get_auth endpoint
627
+ # Per-user rate limit (requests per minute, default: 100)
628
+ rate_limit_per_user = 100
629
+
630
+ # Per-IP rate limit for unauthenticated requests (default: 200)
631
+ rate_limit_per_ip = 200
632
+
633
+ # Permission check behavior
634
+ # Log all permission denials for security audit (default: true)
635
+ log_permission_denials = true
636
+
637
+ # User-friendly error messages
638
+ # Enable mapping of technical permissions to user-friendly messages (default: true)
639
+ enable_friendly_error_messages = true
640
+
641
+ # Permission message mappings (optional, comma-separated: permission_name:user_message)
642
+ # Example: admin_user_management:You don't have access to user management,admin_role_management:You don't have access to role management
643
+ # permission_error_messages =
644
+
645
+ # Debug endpoint
646
+ # Enable debug auth endpoint /api/hazo_auth/debug_auth (default: false)
647
+ # When enabled, provides detailed authentication debug information including user details, roles, permissions, and cookies
648
+ # WARNING: Only enable this in development/staging environments, not in production
649
+ enable_debug_auth_route = true
650
+
651
+ [hazo_auth__dev_lock]
652
+ # Development Lock Screen Configuration
653
+ # This feature locks the entire application behind a password during development/testing
654
+ # When enabled, all routes (pages and APIs) require the dev lock to be unlocked first
655
+ #
656
+ # IMPORTANT: Requires environment variables:
657
+ # - HAZO_AUTH_DEV_LOCK_ENABLED=true (enables the lock in middleware)
658
+ # - HAZO_AUTH_DEV_LOCK_PASSWORD=xxx (the password users must enter)
659
+ #
660
+ # If HAZO_AUTH_DEV_LOCK_ENABLED=true but HAZO_AUTH_DEV_LOCK_PASSWORD is not set,
661
+ # the application will fail to start with a descriptive error message.
662
+
663
+ # Enable the dev lock screen (default: false)
664
+ # Note: Also requires HAZO_AUTH_DEV_LOCK_ENABLED=true env var for actual enforcement
665
+ # enable = false
666
+
667
+ # Session duration in days (default: 7)
668
+ # Once a user enters the correct password, they won't be prompted again for this duration
669
+ # session_duration_days = 7
670
+
671
+ # UI Customization
672
+
673
+ # Background color (default: #000000 - black)
674
+ # background_color = #000000
675
+
676
+ # Logo image path (default: /logo.png in public folder)
677
+ # logo_path = /logo.png
678
+
679
+ # Logo dimensions in pixels
680
+ # logo_width = 120
681
+ # logo_height = 120
682
+
683
+ # Application name displayed below the logo (default: empty)
684
+ # application_name = My Application
685
+
686
+ # Limited access text displayed with lock icon (default: "Limited Access")
687
+ # limited_access_text = Limited Access
688
+
689
+ # Password input placeholder (default: "Enter access password")
690
+ # password_placeholder = Enter access password
691
+
692
+ # Submit button text (default: "Unlock")
693
+ # submit_button_text = Unlock
694
+
695
+ # Error message for incorrect password (default: "Incorrect password")
696
+ # error_message = Incorrect password
697
+
698
+ # Text color for labels (default: #ffffff - white)
699
+ # text_color = #ffffff
700
+
701
+ # Accent color for button (default: #3b82f6 - blue)
702
+ # accent_color = #3b82f6
703
+
704
+ application_name = My App
705
+ background_color =rgb(18, 15, 15)
706
+ logo_path = /logo.png
707
+
708
+ [hazo_auth__user_types]
709
+ # User Types Feature Configuration
710
+ # This feature allows you to categorize users with customizable types
711
+ # (e.g., "Client", "Tax Agent", "Support Staff") with visual badge indicators.
712
+ #
713
+ # User types are defined in this config file and displayed as badges in the
714
+ # User Management interface. Types can be assigned to users via the UI.
715
+
716
+ # Enable user types feature (default: false)
717
+ # When enabled, a "User Type" column appears in the users table and
718
+ # a dropdown selector appears in the user detail dialog.
719
+ # enable_user_types = false
720
+
721
+ # Default user type for new users (optional, default: empty)
722
+ # When set, new registrations will automatically be assigned this type.
723
+ # The value must match one of the user_type_X key values below.
724
+ # Leave empty for no default (users start with no type assigned).
725
+ # default_user_type = standard
726
+
727
+ # User type definitions
728
+ # Format: user_type_X = key:label:badge_color
729
+ # - key: Unique identifier stored in database (lowercase, no spaces)
730
+ # - label: Display name shown in the UI
731
+ # - badge_color: Preset name (blue, green, red, yellow, purple, gray, orange, pink)
732
+ # or custom hex value (e.g., #4CAF50)
733
+ #
734
+ # Examples:
735
+ # user_type_1 = admin:Administrator:red
736
+ # user_type_2 = standard:Standard User:blue
737
+ # user_type_3 = premium:Premium User:purple
738
+ # user_type_4 = client:Client:green
739
+ # user_type_5 = agent:Tax Agent:orange
740
+ # user_type_6 = support:Support Staff:yellow
741
+ # user_type_7 = guest:Guest:gray
742
+ # user_type_8 = vip:VIP Customer:#FFD700
743
+
744
+ [hazo_auth__app_user_data]
745
+ # App User Data Schema Configuration
746
+ # Enables schema-based editing of app_user_data in User Management
747
+ #
748
+ # When enabled with a valid schema, the User Management dialog will show
749
+ # a form-based editor instead of the raw JSON tree view.
750
+
751
+ # Enable schema-based editing (default: false)
752
+ # When disabled, shows raw JSON view (read-only)
753
+ enable_schema = true
754
+
755
+ # JSON Schema defining the structure of app_user_data
756
+ # Only supports a subset of JSON Schema: type (object, string, number, boolean), properties
757
+ # The schema must be a single line of valid JSON
758
+ #
759
+ # Example schema with nested objects:
760
+ schema = {"type":"object","properties":{"electronic_funds_transfer":{"type":"object","properties":{"eft_bsb_number":{"type":"string"},"eft_account_number":{"type":"string"},"eft_account_name":{"type":"string"}}},"contact_details":{"type":"object","properties":{"daytime_phone_number":{"type":"string"},"mobile_phone_number":{"type":"string"},"email_address":{"type":"string"}}},"postal_address":{"type":"object","properties":{"postal_address_line_1":{"type":"string"},"postal_suburb_town_locality":{"type":"string"},"postal_state_territory":{"type":"string"},"postal_postcode":{"type":"string"},"postal_country_if_not_australia":{"type":"string"}}}}}
761
+
762
+ # Custom labels for top-level sections (optional)
763
+ # Format: label_<key> = Display Label
764
+ # If not provided, the key is converted from snake_case to Title Case
765
+ #
766
+ # Example:
767
+ # label_contact_details = Contact Information
768
+ # label_preferences = User Preferences
@@ -0,0 +1,32 @@
1
+ [hazo_logs]
2
+ # Directory for log files (relative to process.cwd() or absolute)
3
+ log_directory = ./logs
4
+
5
+ # Minimum log level (error, warn, info, debug)
6
+ log_level = info
7
+
8
+ # Enable console output with colors
9
+ enable_console = true
10
+
11
+ # Enable file output with daily rotation
12
+ enable_file = true
13
+
14
+ # Maximum size per log file before rotation (k, m, g)
15
+ max_file_size = 20m
16
+
17
+ # Maximum retention for log files (number of files or age)
18
+ max_files = 14d
19
+
20
+ # Date pattern for log file names
21
+ date_pattern = YYYY-MM-DD
22
+
23
+ # --- Log Viewer UI Settings ---
24
+
25
+ # Enable the log viewer UI feature
26
+ enable_log_viewer = true
27
+
28
+ # Number of log entries per page in the viewer
29
+ log_viewer_page_size = 50
30
+
31
+ # Maximum number of log entries to load for filtering/sorting
32
+ log_viewer_max_results = 1000
@@ -0,0 +1,158 @@
1
+ # file_description: configuration file for hazo_notify email service
2
+ # This file contains all configurable parameters for the hazo_notify email service
3
+ # Commented values indicate defaults that are being used
4
+
5
+ [emailer]
6
+ # Emailer module: zeptoemail_api, smtp, pop3
7
+ # Required: Yes
8
+ # Default: zeptoemail_api
9
+ # Description: Select the emailer module to use
10
+ emailer_module=zeptoemail_api
11
+
12
+ # Zeptomail API Provider Configuration
13
+ # Required when emailer_module=zeptoemail_api
14
+ # Description: API endpoint for Zeptomail email service
15
+ zeptomail_api_endpoint=https://api.zeptomail.com.au/v1.1/email
16
+
17
+ # Required when emailer_module=zeptoemail_api
18
+ # Description: Zeptomail API key for authentication
19
+ # SECURITY: Store this in .env.local file as ZEPTOMAIL_API_KEY (recommended)
20
+ # Get this from your Zeptomail account dashboard
21
+ # If not set in .env.local, you can set it here (not recommended for production)
22
+ # zeptomail_api_key=your_zeptomail_api_key
23
+
24
+ # Required: Yes
25
+ # Description: Default sender email address
26
+ # This email must be verified in your Zeptomail account
27
+ from_email=noreply1@bookmemento.com
28
+
29
+ # Required: Yes
30
+ # Description: Default sender name displayed in email clients
31
+ from_name=Book Memento1
32
+
33
+ # Optional: Reply-to email address
34
+ # Description: Email address to use for replies (if different from from_email)
35
+ # Default: Uses from_email if not specified
36
+ # reply_to_email=support@example.com
37
+
38
+ # Optional: Bounce handling email
39
+ # Description: Email address to receive bounce notifications
40
+ # bounce_email=bounce@example.com
41
+
42
+ # Optional: Return path email
43
+ # Description: Email address to use for return path
44
+ # return_path_email=returns@example.com
45
+
46
+ # SMTP Provider Configuration (placeholder - not yet implemented)
47
+ # Required when emailer_module=smtp
48
+ # Description: SMTP server hostname
49
+ # smtp_host=smtp.example.com
50
+
51
+ # Required when emailer_module=smtp
52
+ # Description: SMTP server port (typically 587 for TLS, 465 for SSL, 25 for non-secure)
53
+ # smtp_port=587
54
+
55
+ # Required when emailer_module=smtp
56
+ # Description: Use secure connection (true for SSL/TLS, false for non-secure)
57
+ # smtp_secure=false
58
+
59
+ # Required when emailer_module=smtp
60
+ # Description: SMTP authentication username (usually your email address)
61
+ # smtp_auth_user=your_email@example.com
62
+
63
+ # Required when emailer_module=smtp
64
+ # Description: SMTP authentication password or app-specific password
65
+ # smtp_auth_pass=your_password
66
+
67
+ # Optional: SMTP connection timeout in milliseconds
68
+ # Description: Timeout for SMTP connection attempts
69
+ # Default: 5000
70
+ # smtp_timeout=5000
71
+
72
+ # Optional: SMTP pool configuration
73
+ # Description: Enable connection pooling for better performance
74
+ # Default: false
75
+ # smtp_pool=false
76
+
77
+ # POP3 Provider Configuration (placeholder - not yet implemented)
78
+ # Required when emailer_module=pop3
79
+ # Description: POP3 server hostname
80
+ # pop3_host=pop3.example.com
81
+
82
+ # Required when emailer_module=pop3
83
+ # Description: POP3 server port (typically 995 for SSL, 110 for non-secure)
84
+ # pop3_port=995
85
+
86
+ # Required when emailer_module=pop3
87
+ # Description: Use secure connection (true for SSL, false for non-secure)
88
+ # pop3_secure=true
89
+
90
+ # Required when emailer_module=pop3
91
+ # Description: POP3 authentication username (usually your email address)
92
+ # pop3_auth_user=your_email@example.com
93
+
94
+ # Required when emailer_module=pop3
95
+ # Description: POP3 authentication password
96
+ # pop3_auth_pass=your_password
97
+
98
+ # Optional: POP3 connection timeout in milliseconds
99
+ # Description: Timeout for POP3 connection attempts
100
+ # Default: 5000
101
+ # pop3_timeout=5000
102
+
103
+ # Rate Limiting Configuration
104
+ # Optional: Rate limit requests per minute
105
+ # Description: Maximum number of requests allowed per minute per IP address
106
+ # Default: 10
107
+ # Recommended: 10-60 for production
108
+ rate_limit_requests=10
109
+
110
+ # Optional: Rate limit window in seconds
111
+ # Description: Time window for rate limiting
112
+ # Default: 60 (1 minute)
113
+ rate_limit_window=60
114
+
115
+ # Attachment Limits
116
+ # Optional: Maximum attachment size in bytes
117
+ # Description: Maximum size allowed per attachment (in bytes)
118
+ # Default: 10485760 (10MB)
119
+ # Recommended: 10MB for most use cases
120
+ max_attachment_size=10485760
121
+
122
+ # Optional: Maximum number of attachments
123
+ # Description: Maximum number of attachments allowed per email
124
+ # Default: 10
125
+ max_attachments=10
126
+
127
+ # Request Timeout
128
+ # Optional: Request timeout in milliseconds
129
+ # Description: Timeout for API requests to email providers
130
+ # Default: 30000 (30 seconds)
131
+ request_timeout=30000
132
+
133
+ # Input Length Limits
134
+ # Optional: Maximum subject length in characters
135
+ # Description: Maximum length for email subject line
136
+ # Default: 255 (RFC 5322 standard)
137
+ max_subject_length=255
138
+
139
+ # Optional: Maximum body length in bytes
140
+ # Description: Maximum size for email body content (text or HTML)
141
+ # Default: 1048576 (1MB)
142
+ max_body_length=1048576
143
+
144
+ # CORS Configuration
145
+ # Optional: CORS allowed origins
146
+ # Description: Comma-separated list of allowed origins for CORS
147
+ # Default: empty (same-origin only)
148
+ # Use "*" to allow all origins (not recommended for production)
149
+ # cors_allowed_origins=
150
+
151
+ [ui]
152
+ # Enable UI component and all routes (e.g., /hazo_notify/emailer_test)
153
+ # Required: No
154
+ # Default: false
155
+ # Description: Enable the entire UI component and all routes for testing services
156
+ # Set to true to enable the UI, false to disable it
157
+ enable_ui=false
158
+
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AA2IA,wBAAgB,WAAW,IAAI,IAAI,CAkHlC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AA4IA,wBAAgB,WAAW,IAAI,IAAI,CAkHlC"}
package/dist/cli/init.js CHANGED
@@ -12,10 +12,11 @@ const REQUIRED_DIRECTORIES = [
12
12
  "public/profile_pictures/library",
13
13
  "public/profile_pictures/uploads",
14
14
  "data",
15
+ "config",
15
16
  ];
16
17
  const CONFIG_FILES = [
17
- { source: "hazo_auth_config.example.ini", target: "hazo_auth_config.ini" },
18
- { source: "hazo_notify_config.example.ini", target: "hazo_notify_config.ini" },
18
+ { source: "config/hazo_auth_config.example.ini", target: "config/hazo_auth_config.ini" },
19
+ { source: "config/hazo_notify_config.example.ini", target: "config/hazo_notify_config.ini" },
19
20
  ];
20
21
  // section: helpers
21
22
  function get_project_root() {
@@ -198,7 +199,7 @@ export function handle_init() {
198
199
  }
199
200
  console.log("\n\x1b[32m🦊 Initialization complete!\x1b[0m");
200
201
  console.log("\nNext steps:");
201
- console.log(" 1. Edit \x1b[36mhazo_auth_config.ini\x1b[0m with your settings");
202
+ console.log(" 1. Edit \x1b[36mconfig/hazo_auth_config.ini\x1b[0m with your settings");
202
203
  console.log(" 2. Copy \x1b[36m.env.local.example\x1b[0m to \x1b[36m.env.local\x1b[0m and add your API keys");
203
204
  console.log(" 3. Run \x1b[36mnpx hazo_auth generate-routes --pages\x1b[0m to generate routes and pages");
204
205
  console.log(" 4. Run \x1b[36mnpx hazo_auth validate\x1b[0m to check your setup");
@@ -4,8 +4,8 @@ import * as fs from "fs";
4
4
  import * as path from "path";
5
5
  // section: constants
6
6
  const REQUIRED_CONFIG_FILES = [
7
- "hazo_auth_config.ini",
8
- "hazo_notify_config.ini",
7
+ "config/hazo_auth_config.ini",
8
+ "config/hazo_notify_config.ini",
9
9
  ];
10
10
  const REQUIRED_ENV_VARS = [
11
11
  { name: "JWT_SECRET", required: true, description: "JWT signing secret" },
@@ -110,7 +110,7 @@ function check_config_files(project_root) {
110
110
  results.push({
111
111
  name: `Config file: ${config_file}`,
112
112
  status: exists ? "pass" : "fail",
113
- message: exists ? "" : `File not found. Run: cp node_modules/hazo_auth/${config_file.replace(".ini", ".example.ini")} ./${config_file}`,
113
+ message: exists ? "" : `File not found. Run: npx hazo_auth init`,
114
114
  });
115
115
  }
116
116
  return results;
@@ -118,7 +118,7 @@ function check_config_files(project_root) {
118
118
  function check_config_values(project_root) {
119
119
  var _a, _b, _c, _d, _e, _f;
120
120
  const results = [];
121
- const hazo_config_path = path.join(project_root, "hazo_auth_config.ini");
121
+ const hazo_config_path = path.join(project_root, "config", "hazo_auth_config.ini");
122
122
  const hazo_config = read_ini_file(hazo_config_path);
123
123
  if (hazo_config) {
124
124
  const db_type = (_a = hazo_config["hazo_connect"]) === null || _a === void 0 ? void 0 : _a["type"];
@@ -193,7 +193,7 @@ function check_config_values(project_root) {
193
193
  });
194
194
  }
195
195
  }
196
- const notify_config_path = path.join(project_root, "hazo_notify_config.ini");
196
+ const notify_config_path = path.join(project_root, "config", "hazo_notify_config.ini");
197
197
  const notify_config = read_ini_file(notify_config_path);
198
198
  if (notify_config) {
199
199
  const from_email = (_e = notify_config["emailer"]) === null || _e === void 0 ? void 0 : _e["from_email"];
@@ -368,13 +368,13 @@ function check_profile_pictures(project_root) {
368
368
  function check_database(project_root) {
369
369
  var _a, _b, _c;
370
370
  const results = [];
371
- const hazo_config_path = path.join(project_root, "hazo_auth_config.ini");
371
+ const hazo_config_path = path.join(project_root, "config", "hazo_auth_config.ini");
372
372
  const hazo_config = read_ini_file(hazo_config_path);
373
373
  if (!hazo_config) {
374
374
  results.push({
375
375
  name: "Database check",
376
376
  status: "fail",
377
- message: "Could not read hazo_auth_config.ini",
377
+ message: "Could not read config/hazo_auth_config.ini",
378
378
  });
379
379
  return results;
380
380
  }
@@ -5,8 +5,10 @@ export type AppUserDataEditorProps = {
5
5
  currentData: Record<string, unknown> | null;
6
6
  /** Callback when data is saved */
7
7
  onSave?: (newData: Record<string, unknown>) => void;
8
+ /** Callback when data is cleared */
9
+ onClear?: () => void;
8
10
  /** Whether the editor is read-only (no edit button) */
9
11
  readOnly?: boolean;
10
12
  };
11
- export declare function AppUserDataEditor({ userId, currentData, onSave, readOnly, }: AppUserDataEditorProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function AppUserDataEditor({ userId, currentData, onSave, onClear, readOnly, }: AppUserDataEditorProps): import("react/jsx-runtime").JSX.Element;
12
14
  //# sourceMappingURL=app_user_data_editor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app_user_data_editor.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/app_user_data_editor.tsx"],"names":[],"mappings":"AAmCA,MAAM,MAAM,sBAAsB,GAAG;IACnC,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,kCAAkC;IAClC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAgDF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,WAAW,EACX,MAAM,EACN,QAAgB,GACjB,EAAE,sBAAsB,2CAwSxB"}
1
+ {"version":3,"file":"app_user_data_editor.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/user_management/components/app_user_data_editor.tsx"],"names":[],"mappings":"AA6CA,MAAM,MAAM,sBAAsB,GAAG;IACnC,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5C,kCAAkC;IAClC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uDAAuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAgDF,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,WAAW,EACX,MAAM,EACN,OAAO,EACP,QAAgB,GACjB,EAAE,sBAAsB,2CAkXxB"}
@@ -2,14 +2,15 @@
2
2
  // Renders form fields based on JSON schema from config
3
3
  // section: client_directive
4
4
  "use client";
5
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
6
6
  // section: imports
7
7
  import { useState, useEffect, useCallback } from "react";
8
8
  import { Button } from "../../../ui/button.js";
9
9
  import { Input } from "../../../ui/input.js";
10
10
  import { Label } from "../../../ui/label.js";
11
11
  import { Switch } from "../../../ui/switch.js";
12
- import { ChevronDown, ChevronRight, Edit, Loader2, Save, X } from "lucide-react";
12
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "../../../ui/alert-dialog.js";
13
+ import { ChevronDown, ChevronRight, Edit, Loader2, Save, Trash2, X } from "lucide-react";
13
14
  import { toast } from "sonner";
14
15
  import { useHazoAuthConfig } from "../../../../contexts/hazo_auth_provider.js";
15
16
  // section: helpers
@@ -49,13 +50,15 @@ function setNestedValue(obj, path, value) {
49
50
  return result;
50
51
  }
51
52
  // section: component
52
- export function AppUserDataEditor({ userId, currentData, onSave, readOnly = false, }) {
53
+ export function AppUserDataEditor({ userId, currentData, onSave, onClear, readOnly = false, }) {
53
54
  const { apiBasePath } = useHazoAuthConfig();
54
55
  const [schemaResponse, setSchemaResponse] = useState(null);
55
56
  const [schemaLoading, setSchemaLoading] = useState(true);
56
57
  const [isEditing, setIsEditing] = useState(false);
57
58
  const [editData, setEditData] = useState({});
58
59
  const [saving, setSaving] = useState(false);
60
+ const [clearing, setClearing] = useState(false);
61
+ const [showClearConfirmation, setShowClearConfirmation] = useState(false);
59
62
  const [expandedSections, setExpandedSections] = useState(new Set());
60
63
  // Load schema on mount
61
64
  useEffect(() => {
@@ -121,6 +124,36 @@ export function AppUserDataEditor({ userId, currentData, onSave, readOnly = fals
121
124
  setSaving(false);
122
125
  }
123
126
  }, [apiBasePath, userId, editData, onSave]);
127
+ // Clear app_user_data - uses user_management/users PATCH endpoint with null
128
+ const handleClear = useCallback(async () => {
129
+ setClearing(true);
130
+ try {
131
+ const response = await fetch(`${apiBasePath}/user_management/users`, {
132
+ method: "PATCH",
133
+ headers: { "Content-Type": "application/json" },
134
+ body: JSON.stringify({
135
+ user_id: userId,
136
+ app_user_data: null,
137
+ }),
138
+ });
139
+ const data = await response.json();
140
+ if (data.success) {
141
+ toast.success("App user data cleared");
142
+ setShowClearConfirmation(false);
143
+ onClear === null || onClear === void 0 ? void 0 : onClear();
144
+ }
145
+ else {
146
+ toast.error(data.error || "Failed to clear");
147
+ }
148
+ }
149
+ catch (error) {
150
+ toast.error("Failed to clear app user data");
151
+ console.error("Clear error:", error);
152
+ }
153
+ finally {
154
+ setClearing(false);
155
+ }
156
+ }, [apiBasePath, userId, onClear]);
124
157
  // Update field value
125
158
  const updateField = useCallback((path, value) => {
126
159
  setEditData((prev) => setNestedValue(prev, path, value));
@@ -200,5 +233,5 @@ export function AppUserDataEditor({ userId, currentData, onSave, readOnly = fals
200
233
  return (_jsx("div", { className: "text-sm", children: currentData && Object.keys(currentData).length > 0 ? (_jsx("pre", { className: "border rounded-lg p-2 bg-slate-50 overflow-x-auto text-xs", children: JSON.stringify(currentData, null, 2) })) : (_jsx("span", { className: "text-muted-foreground", children: "No app user data" })) }));
201
234
  }
202
235
  // Render schema-based editor
203
- return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "App User Data" }), !readOnly && !isEditing && (_jsxs(Button, { variant: "outline", size: "sm", onClick: handleEdit, className: "h-8", children: [_jsx(Edit, { className: "h-3.5 w-3.5 mr-1.5" }), "Edit"] }))] }), _jsx("div", { className: "flex flex-col gap-3", children: Object.entries(schemaResponse.schema.properties).map(([sectionKey, sectionSchema]) => renderSection(sectionKey, sectionSchema)) }), isEditing && (_jsxs("div", { className: "flex justify-end gap-2 pt-3 border-t", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleCancel, disabled: saving, className: "h-9", children: [_jsx(X, { className: "h-4 w-4 mr-1.5" }), "Cancel"] }), _jsxs(Button, { size: "sm", onClick: handleSave, disabled: saving, className: "h-9", children: [saving ? (_jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" })) : (_jsx(Save, { className: "h-4 w-4 mr-1.5" })), "Save"] })] }))] }));
236
+ return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("h3", { className: "text-sm font-semibold uppercase tracking-wide text-muted-foreground", children: "App User Data" }), !readOnly && !isEditing && (_jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleEdit, className: "h-8", children: [_jsx(Edit, { className: "h-3.5 w-3.5 mr-1.5" }), "Edit"] }), currentData && Object.keys(currentData).length > 0 && (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setShowClearConfirmation(true), className: "h-8 text-destructive hover:text-destructive hover:bg-destructive/10", children: [_jsx(Trash2, { className: "h-3.5 w-3.5 mr-1.5" }), "Clear"] }))] }))] }), _jsx("div", { className: "flex flex-col gap-3", children: Object.entries(schemaResponse.schema.properties).map(([sectionKey, sectionSchema]) => renderSection(sectionKey, sectionSchema)) }), isEditing && (_jsxs("div", { className: "flex justify-end gap-2 pt-3 border-t", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: handleCancel, disabled: saving, className: "h-9", children: [_jsx(X, { className: "h-4 w-4 mr-1.5" }), "Cancel"] }), _jsxs(Button, { size: "sm", onClick: handleSave, disabled: saving, className: "h-9", children: [saving ? (_jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" })) : (_jsx(Save, { className: "h-4 w-4 mr-1.5" })), "Save"] })] })), _jsx(AlertDialog, { open: showClearConfirmation, onOpenChange: setShowClearConfirmation, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "Clear App User Data?" }), _jsx(AlertDialogDescription, { children: "This will permanently delete all app user data for this user. This action cannot be undone." })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: clearing, children: "Cancel" }), _jsx(AlertDialogAction, { onClick: handleClear, disabled: clearing, className: "bg-destructive text-destructive-foreground hover:bg-destructive/90", children: clearing ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 mr-1.5 animate-spin" }), "Clearing..."] })) : ("Clear Data") })] })] }) })] }));
204
237
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AAyDA,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AAuJF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,gBAAwB,EAAE,kBAAuB,EAAE,EAAE,yBAAyB,2CAi2CrJ"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/user_management/index.tsx"],"names":[],"mappings":"AAyDA,4CAA4C;AAC5C,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,cAAc,EAAE,CAAC;CACvC,CAAC;AAuJF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,SAAS,EAAE,YAAoB,EAAE,gBAAwB,EAAE,kBAAuB,EAAE,EAAE,yBAAyB,2CA82CrJ"}
@@ -582,6 +582,11 @@ export function UserManagementLayout({ className, hrbacEnabled = false, userType
582
582
  // Also update in the users list
583
583
  setUsers((prevUsers) => prevUsers.map((u) => u.id === selectedUser.id
584
584
  ? Object.assign(Object.assign({}, u), { app_user_data: newData }) : u));
585
+ }, onClear: () => {
586
+ // Clear app_user_data in both selectedUser and users list
587
+ setSelectedUser((prev) => prev ? Object.assign(Object.assign({}, prev), { app_user_data: null }) : null);
588
+ setUsers((prevUsers) => prevUsers.map((u) => u.id === selectedUser.id
589
+ ? Object.assign(Object.assign({}, u), { app_user_data: null }) : u));
585
590
  } }) }) })] })) }), _jsx(DialogFooter, { className: "cls_user_management_user_detail_dialog_footer", children: _jsx(Button, { onClick: () => setUserDetailDialogOpen(false), variant: "outline", className: "cls_user_management_user_detail_dialog_close", children: "Close" }) })] }) }), _jsx(Dialog, { open: assignRolesDialogOpen, onOpenChange: setAssignRolesDialogOpen, children: _jsxs(DialogContent, { className: "cls_user_management_assign_roles_dialog max-w-4xl max-h-[80vh] overflow-y-auto", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Assign Roles to User" }), _jsxs(DialogDescription, { children: ["Select roles to assign to ", (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.name) || (selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.email_address), ". Check the roles you want to assign, then click Save."] })] }), _jsx("div", { className: "cls_user_management_assign_roles_dialog_content py-4", children: _jsx(RolesMatrix, { add_button_enabled: false, role_name_selection_enabled: true, permissions_read_only: true, show_save_cancel: true, user_id: selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id, onSave: (data) => {
586
591
  // Data is already saved by RolesMatrix component
587
592
  console.log("User roles saved:", data);
@@ -5,7 +5,7 @@ import path from "path";
5
5
  import fs from "fs";
6
6
  import { create_app_logger } from "../app_logger.js";
7
7
  // section: constants
8
- const DEFAULT_CONFIG_FILE = "hazo_auth_config.ini";
8
+ const DEFAULT_CONFIG_FILE = "config/hazo_auth_config.ini";
9
9
  // section: helpers
10
10
  /**
11
11
  * Gets the default config file path
@@ -183,7 +183,7 @@ export async function accept_invitation(adapter, invitation_id, user_id) {
183
183
  if (invitation.status !== "PENDING") {
184
184
  return {
185
185
  success: false,
186
- error: `Invitation is ${invitation.status.toLowerCase()}, not pending`,
186
+ error: `Invitation is ${invitation.status}, not PENDING`,
187
187
  };
188
188
  }
189
189
  // Check expiration
@@ -266,7 +266,7 @@ export async function revoke_invitation(adapter, invitation_id) {
266
266
  if (invitation_result.invitation.status !== "PENDING") {
267
267
  return {
268
268
  success: false,
269
- error: `Cannot revoke invitation with status ${invitation_result.invitation.status.toLowerCase()}`,
269
+ error: `Cannot revoke invitation with status ${invitation_result.invitation.status}`,
270
270
  };
271
271
  }
272
272
  // Mark as revoked
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "5.1.8",
3
+ "version": "5.1.10",
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",
@@ -172,8 +172,7 @@
172
172
  "cli-src/**/*",
173
173
  "public/profile_pictures/library/**/*",
174
174
  "public/hazo_auth/images/**/*",
175
- "hazo_auth_config.example.ini",
176
- "hazo_notify_config.example.ini",
175
+ "config/**/*",
177
176
  "README.md",
178
177
  "SETUP_CHECKLIST.md",
179
178
  "LICENSE"