hazo_auth 6.1.1 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -8
- package/SETUP_CHECKLIST.md +148 -0
- package/cli-src/lib/auth/nextauth_config.ts +101 -1
- package/cli-src/lib/email_verification_config.server.ts +0 -34
- package/cli-src/lib/forgot_password_config.server.ts +0 -34
- package/cli-src/lib/login_config.server.ts +0 -31
- package/cli-src/lib/my_settings_config.server.ts +0 -3
- package/cli-src/lib/oauth_config.server.ts +58 -0
- package/cli-src/lib/register_config.server.ts +11 -31
- package/cli-src/lib/reset_password_config.server.ts +0 -31
- package/cli-src/lib/services/oauth_service.ts +197 -0
- package/config/hazo_auth_config.example.ini +38 -41
- package/dist/components/layouts/create_firm/index.d.ts +4 -8
- package/dist/components/layouts/create_firm/index.d.ts.map +1 -1
- package/dist/components/layouts/create_firm/index.js +3 -3
- package/dist/components/layouts/email_verification/index.d.ts +4 -5
- package/dist/components/layouts/email_verification/index.d.ts.map +1 -1
- package/dist/components/layouts/email_verification/index.js +4 -4
- package/dist/components/layouts/forgot_password/index.d.ts +4 -5
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +2 -2
- package/dist/components/layouts/login/index.d.ts +13 -9
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +12 -6
- package/dist/components/layouts/otp/index.d.ts +8 -1
- package/dist/components/layouts/otp/index.d.ts.map +1 -1
- package/dist/components/layouts/otp/index.js +4 -2
- package/dist/components/layouts/register/index.d.ts +11 -7
- package/dist/components/layouts/register/index.d.ts.map +1 -1
- package/dist/components/layouts/register/index.js +8 -4
- package/dist/components/layouts/reset_password/index.d.ts +4 -5
- package/dist/components/layouts/reset_password/index.d.ts.map +1 -1
- package/dist/components/layouts/reset_password/index.js +5 -5
- package/dist/components/layouts/shared/components/already_logged_in_guard.d.ts +3 -5
- package/dist/components/layouts/shared/components/already_logged_in_guard.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/already_logged_in_guard.js +2 -2
- package/dist/components/layouts/shared/components/facebook_sign_in_button.d.ts +25 -0
- package/dist/components/layouts/shared/components/facebook_sign_in_button.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/facebook_sign_in_button.js +49 -0
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +1 -1
- package/dist/components/layouts/shared/components/two_column_auth_layout.d.ts +3 -6
- package/dist/components/layouts/shared/components/two_column_auth_layout.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/two_column_auth_layout.js +8 -5
- package/dist/consent/consent_state.d.ts +18 -0
- package/dist/consent/consent_state.d.ts.map +1 -0
- package/dist/consent/consent_state.js +29 -0
- package/dist/consent/cookie_consent_banner.d.ts +11 -0
- package/dist/consent/cookie_consent_banner.d.ts.map +1 -0
- package/dist/consent/cookie_consent_banner.js +40 -0
- package/dist/consent/gtm_mapping.d.ts +13 -0
- package/dist/consent/gtm_mapping.d.ts.map +1 -0
- package/dist/consent/gtm_mapping.js +30 -0
- package/dist/consent/index.d.ts +7 -0
- package/dist/consent/index.d.ts.map +1 -0
- package/dist/consent/index.js +7 -0
- package/dist/consent/manage_modal.d.ts +2 -0
- package/dist/consent/manage_modal.d.ts.map +1 -0
- package/dist/consent/manage_modal.js +33 -0
- package/dist/consent/read_consent.d.ts +15 -0
- package/dist/consent/read_consent.d.ts.map +1 -0
- package/dist/consent/read_consent.js +23 -0
- package/dist/consent/use_consent.d.ts +7 -0
- package/dist/consent/use_consent.d.ts.map +1 -0
- package/dist/consent/use_consent.js +55 -0
- package/dist/lib/auth/nextauth_config.d.ts +10 -0
- package/dist/lib/auth/nextauth_config.d.ts.map +1 -1
- package/dist/lib/auth/nextauth_config.js +80 -2
- package/dist/lib/email_verification_config.server.d.ts +0 -3
- package/dist/lib/email_verification_config.server.d.ts.map +1 -1
- package/dist/lib/email_verification_config.server.js +0 -15
- package/dist/lib/forgot_password_config.server.d.ts +0 -3
- package/dist/lib/forgot_password_config.server.d.ts.map +1 -1
- package/dist/lib/forgot_password_config.server.js +0 -15
- package/dist/lib/login_config.server.d.ts +0 -3
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +0 -13
- package/dist/lib/my_settings_config.server.d.ts +0 -1
- package/dist/lib/my_settings_config.server.d.ts.map +1 -1
- package/dist/lib/my_settings_config.server.js +0 -2
- package/dist/lib/oauth_config.server.d.ts +17 -0
- package/dist/lib/oauth_config.server.d.ts.map +1 -1
- package/dist/lib/oauth_config.server.js +25 -0
- package/dist/lib/register_config.server.d.ts +2 -3
- package/dist/lib/register_config.server.d.ts.map +1 -1
- package/dist/lib/register_config.server.js +4 -13
- package/dist/lib/reset_password_config.server.d.ts +0 -3
- package/dist/lib/reset_password_config.server.d.ts.map +1 -1
- package/dist/lib/reset_password_config.server.js +0 -13
- package/dist/lib/services/oauth_service.d.ts +24 -0
- package/dist/lib/services/oauth_service.d.ts.map +1 -1
- package/dist/lib/services/oauth_service.js +155 -0
- package/dist/page_components/create_firm.d.ts +13 -1
- package/dist/page_components/create_firm.d.ts.map +1 -1
- package/dist/page_components/create_firm.js +10 -6
- package/dist/page_components/forgot_password.d.ts +1 -4
- package/dist/page_components/forgot_password.d.ts.map +1 -1
- package/dist/page_components/forgot_password.js +2 -6
- package/dist/page_components/login.d.ts +1 -4
- package/dist/page_components/login.d.ts.map +1 -1
- package/dist/page_components/login.js +2 -6
- package/dist/page_components/register.d.ts +1 -4
- package/dist/page_components/register.d.ts.map +1 -1
- package/dist/page_components/register.js +2 -6
- package/dist/page_components/reset_password.d.ts +1 -4
- package/dist/page_components/reset_password.d.ts.map +1 -1
- package/dist/page_components/reset_password.js +2 -6
- package/dist/page_components/verify_email.d.ts +1 -4
- package/dist/page_components/verify_email.d.ts.map +1 -1
- package/dist/page_components/verify_email.js +2 -6
- package/dist/server/routes/index.d.ts +1 -0
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +1 -0
- package/dist/server/routes/oauth_facebook_callback.d.ts +8 -0
- package/dist/server/routes/oauth_facebook_callback.d.ts.map +1 -0
- package/dist/server/routes/oauth_facebook_callback.js +157 -0
- package/dist/server/routes/oauth_google_callback.js +1 -1
- package/dist/server_pages/forgot_password.d.ts +13 -17
- package/dist/server_pages/forgot_password.d.ts.map +1 -1
- package/dist/server_pages/forgot_password.js +12 -8
- package/dist/server_pages/forgot_password_client_wrapper.d.ts +7 -6
- package/dist/server_pages/forgot_password_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/forgot_password_client_wrapper.js +2 -2
- package/dist/server_pages/login.d.ts +22 -21
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +15 -19
- package/dist/server_pages/login_client_wrapper.d.ts +10 -6
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/dist/server_pages/my_settings.d.ts +2 -0
- package/dist/server_pages/my_settings.d.ts.map +1 -1
- package/dist/server_pages/my_settings.js +8 -2
- package/dist/server_pages/otp.d.ts +16 -2
- package/dist/server_pages/otp.d.ts.map +1 -1
- package/dist/server_pages/otp.js +10 -3
- package/dist/server_pages/register.d.ts +19 -16
- package/dist/server_pages/register.d.ts.map +1 -1
- package/dist/server_pages/register.js +15 -12
- package/dist/server_pages/register_client_wrapper.d.ts +10 -6
- package/dist/server_pages/register_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/register_client_wrapper.js +2 -2
- package/dist/server_pages/reset_password.d.ts +11 -16
- package/dist/server_pages/reset_password.d.ts.map +1 -1
- package/dist/server_pages/reset_password.js +11 -9
- package/dist/server_pages/reset_password_client_wrapper.d.ts +7 -6
- package/dist/server_pages/reset_password_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/reset_password_client_wrapper.js +2 -2
- package/dist/server_pages/verify_email.d.ts +11 -17
- package/dist/server_pages/verify_email.d.ts.map +1 -1
- package/dist/server_pages/verify_email.js +11 -8
- package/dist/server_pages/verify_email_client_wrapper.d.ts +7 -6
- package/dist/server_pages/verify_email_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/verify_email_client_wrapper.js +2 -2
- package/dist/strings/default_strings.d.ts +47 -0
- package/dist/strings/default_strings.d.ts.map +1 -0
- package/dist/strings/default_strings.js +18 -0
- package/dist/strings/index.d.ts +4 -0
- package/dist/strings/index.d.ts.map +1 -0
- package/dist/strings/index.js +3 -0
- package/dist/strings/strings_context.d.ts +12 -0
- package/dist/strings/strings_context.d.ts.map +1 -0
- package/dist/strings/strings_context.js +23 -0
- package/dist/strings/strings_provider.d.ts +26 -0
- package/dist/strings/strings_provider.d.ts.map +1 -0
- package/dist/strings/strings_provider.js +45 -0
- package/dist/theme/create_theme.d.ts +7 -0
- package/dist/theme/create_theme.d.ts.map +1 -0
- package/dist/theme/create_theme.js +97 -0
- package/dist/theme/hex_to_hsl.d.ts +16 -0
- package/dist/theme/hex_to_hsl.d.ts.map +1 -0
- package/dist/theme/hex_to_hsl.js +110 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +3 -0
- package/dist/theme/luminance.d.ts +11 -0
- package/dist/theme/luminance.d.ts.map +1 -0
- package/dist/theme/luminance.js +45 -0
- package/dist/theme/theme_provider.d.ts +14 -0
- package/dist/theme/theme_provider.d.ts.map +1 -0
- package/dist/theme/theme_provider.js +23 -0
- package/dist/theme/theme_types.d.ts +36 -0
- package/dist/theme/theme_types.d.ts.map +1 -0
- package/dist/theme/theme_types.js +1 -0
- package/dist/themes/index.d.ts +3 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +2 -0
- package/dist/themes/preset_indigo_sunset.d.ts +3 -0
- package/dist/themes/preset_indigo_sunset.d.ts.map +1 -0
- package/dist/themes/preset_indigo_sunset.js +20 -0
- package/dist/themes/preset_neutral.d.ts +3 -0
- package/dist/themes/preset_neutral.d.ts.map +1 -0
- package/dist/themes/preset_neutral.js +14 -0
- package/package.json +19 -2
|
@@ -34,7 +34,6 @@ export type MySettingsConfig = {
|
|
|
34
34
|
user_photo_default_priority2?: "library" | "gravatar";
|
|
35
35
|
library_photo_path: string;
|
|
36
36
|
};
|
|
37
|
-
heading?: string;
|
|
38
37
|
subHeading?: string;
|
|
39
38
|
profilePhotoLabel?: string;
|
|
40
39
|
profilePhotoRecommendation?: string;
|
|
@@ -95,7 +94,6 @@ export function get_my_settings_config(): MySettingsConfig {
|
|
|
95
94
|
const fileTypes = get_file_types_config();
|
|
96
95
|
|
|
97
96
|
// Read optional labels with defaults
|
|
98
|
-
const heading = get_config_value(section, "heading", "Account Settings");
|
|
99
97
|
const subHeading = get_config_value(section, "sub_heading", "Manage your profile, password, and email preferences.");
|
|
100
98
|
const profilePhotoLabel = get_config_value(section, "profile_photo_label", "Profile Photo");
|
|
101
99
|
const profilePhotoRecommendation = get_config_value(section, "profile_photo_recommendation", "Recommended size: 200x200px. JPG, PNG.");
|
|
@@ -115,7 +113,6 @@ export function get_my_settings_config(): MySettingsConfig {
|
|
|
115
113
|
userFields,
|
|
116
114
|
passwordRequirements,
|
|
117
115
|
profilePicture,
|
|
118
|
-
heading,
|
|
119
116
|
subHeading,
|
|
120
117
|
profilePhotoLabel,
|
|
121
118
|
profilePhotoRecommendation,
|
|
@@ -10,12 +10,20 @@ import { DEFAULT_OAUTH } from "./config/default_config.js";
|
|
|
10
10
|
export type OAuthConfig = {
|
|
11
11
|
/** Enable Google OAuth login */
|
|
12
12
|
enable_google: boolean;
|
|
13
|
+
/** Enable Facebook OAuth login */
|
|
14
|
+
enable_facebook: boolean;
|
|
13
15
|
/** Enable traditional email/password login */
|
|
14
16
|
enable_email_password: boolean;
|
|
15
17
|
/** Auto-link Google login to existing unverified email/password accounts */
|
|
16
18
|
auto_link_unverified_accounts: boolean;
|
|
19
|
+
/** Auto-link Google login to existing unverified email/password accounts (per-provider override) */
|
|
20
|
+
auto_link_unverified_accounts_google: boolean;
|
|
21
|
+
/** Auto-link Facebook login to existing unverified email/password accounts */
|
|
22
|
+
auto_link_unverified_accounts_facebook: boolean;
|
|
17
23
|
/** Text displayed on the Google sign-in button */
|
|
18
24
|
google_button_text: string;
|
|
25
|
+
/** Text displayed on the Facebook sign-in button */
|
|
26
|
+
facebook_button_text: string;
|
|
19
27
|
/** Text displayed on the divider between OAuth and email/password form */
|
|
20
28
|
oauth_divider_text: string;
|
|
21
29
|
/** NextAuth signIn page path */
|
|
@@ -30,6 +38,10 @@ export type OAuthConfig = {
|
|
|
30
38
|
skip_invitation_check: boolean;
|
|
31
39
|
/** Redirect when skip_invitation_check=true and user has no scope */
|
|
32
40
|
no_scope_redirect: string;
|
|
41
|
+
/** Facebook App ID from env HAZO_AUTH_FACEBOOK_APP_ID */
|
|
42
|
+
facebook_client_id: string | undefined;
|
|
43
|
+
/** Facebook App Secret from env HAZO_AUTH_FACEBOOK_APP_SECRET */
|
|
44
|
+
facebook_client_secret: string | undefined;
|
|
33
45
|
};
|
|
34
46
|
|
|
35
47
|
// section: constants
|
|
@@ -48,24 +60,51 @@ export function get_oauth_config(): OAuthConfig {
|
|
|
48
60
|
DEFAULT_OAUTH.enable_google
|
|
49
61
|
);
|
|
50
62
|
|
|
63
|
+
const enable_facebook = get_config_boolean(
|
|
64
|
+
SECTION_NAME,
|
|
65
|
+
"enable_facebook",
|
|
66
|
+
false
|
|
67
|
+
);
|
|
68
|
+
|
|
51
69
|
const enable_email_password = get_config_boolean(
|
|
52
70
|
SECTION_NAME,
|
|
53
71
|
"enable_email_password",
|
|
54
72
|
DEFAULT_OAUTH.enable_email_password
|
|
55
73
|
);
|
|
56
74
|
|
|
75
|
+
// Generic key (backward compat)
|
|
57
76
|
const auto_link_unverified_accounts = get_config_boolean(
|
|
58
77
|
SECTION_NAME,
|
|
59
78
|
"auto_link_unverified_accounts",
|
|
60
79
|
DEFAULT_OAUTH.auto_link_unverified_accounts
|
|
61
80
|
);
|
|
62
81
|
|
|
82
|
+
// Per-provider Google key (falls back to generic)
|
|
83
|
+
const auto_link_unverified_accounts_google = get_config_boolean(
|
|
84
|
+
SECTION_NAME,
|
|
85
|
+
"auto_link_unverified_accounts_google",
|
|
86
|
+
auto_link_unverified_accounts
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
// Per-provider Facebook key (defaults to false — don't auto-link Facebook unverified)
|
|
90
|
+
const auto_link_unverified_accounts_facebook = get_config_boolean(
|
|
91
|
+
SECTION_NAME,
|
|
92
|
+
"auto_link_unverified_accounts_facebook",
|
|
93
|
+
false
|
|
94
|
+
);
|
|
95
|
+
|
|
63
96
|
const google_button_text = get_config_value(
|
|
64
97
|
SECTION_NAME,
|
|
65
98
|
"google_button_text",
|
|
66
99
|
DEFAULT_OAUTH.google_button_text
|
|
67
100
|
);
|
|
68
101
|
|
|
102
|
+
const facebook_button_text = get_config_value(
|
|
103
|
+
SECTION_NAME,
|
|
104
|
+
"facebook_button_text",
|
|
105
|
+
"Continue with Facebook"
|
|
106
|
+
);
|
|
107
|
+
|
|
69
108
|
const oauth_divider_text = get_config_value(
|
|
70
109
|
SECTION_NAME,
|
|
71
110
|
"oauth_divider_text",
|
|
@@ -108,11 +147,18 @@ export function get_oauth_config(): OAuthConfig {
|
|
|
108
147
|
DEFAULT_OAUTH.no_scope_redirect
|
|
109
148
|
);
|
|
110
149
|
|
|
150
|
+
const facebook_client_id = process.env.HAZO_AUTH_FACEBOOK_APP_ID;
|
|
151
|
+
const facebook_client_secret = process.env.HAZO_AUTH_FACEBOOK_APP_SECRET;
|
|
152
|
+
|
|
111
153
|
return {
|
|
112
154
|
enable_google,
|
|
155
|
+
enable_facebook,
|
|
113
156
|
enable_email_password,
|
|
114
157
|
auto_link_unverified_accounts,
|
|
158
|
+
auto_link_unverified_accounts_google,
|
|
159
|
+
auto_link_unverified_accounts_facebook,
|
|
115
160
|
google_button_text,
|
|
161
|
+
facebook_button_text,
|
|
116
162
|
oauth_divider_text,
|
|
117
163
|
sign_in_page,
|
|
118
164
|
error_page,
|
|
@@ -120,6 +166,8 @@ export function get_oauth_config(): OAuthConfig {
|
|
|
120
166
|
default_redirect,
|
|
121
167
|
skip_invitation_check,
|
|
122
168
|
no_scope_redirect,
|
|
169
|
+
facebook_client_id,
|
|
170
|
+
facebook_client_secret,
|
|
123
171
|
};
|
|
124
172
|
}
|
|
125
173
|
|
|
@@ -142,3 +190,13 @@ export function is_email_password_enabled(): boolean {
|
|
|
142
190
|
DEFAULT_OAUTH.enable_email_password
|
|
143
191
|
);
|
|
144
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Helper to check if Facebook OAuth is enabled and credentials are present
|
|
196
|
+
* @returns true if Facebook OAuth is enabled and env vars are set
|
|
197
|
+
*/
|
|
198
|
+
export function is_facebook_oauth_enabled(): boolean {
|
|
199
|
+
const enabled = get_config_boolean(SECTION_NAME, "enable_facebook", false);
|
|
200
|
+
if (!enabled) return false;
|
|
201
|
+
return !!(process.env.HAZO_AUTH_FACEBOOK_APP_ID && process.env.HAZO_AUTH_FACEBOOK_APP_SECRET);
|
|
202
|
+
}
|
|
@@ -9,11 +9,6 @@ import { get_already_logged_in_config } from "./already_logged_in_config.server.
|
|
|
9
9
|
import { get_user_fields_config } from "./user_fields_config.server.js";
|
|
10
10
|
import { get_oauth_config } from "./oauth_config.server.js";
|
|
11
11
|
|
|
12
|
-
// Default image path - consuming apps should either:
|
|
13
|
-
// 1. Configure their own image_src in hazo_auth_config.ini
|
|
14
|
-
// 2. Copy the default images from node_modules/hazo_auth/public/hazo_auth/images/ to their public folder
|
|
15
|
-
const DEFAULT_REGISTER_IMAGE_PATH = "/hazo_auth/images/register_default.jpg";
|
|
16
|
-
|
|
17
12
|
// section: types
|
|
18
13
|
export type RegisterConfig = {
|
|
19
14
|
showNameField: boolean;
|
|
@@ -31,14 +26,13 @@ export type RegisterConfig = {
|
|
|
31
26
|
returnHomePath: string;
|
|
32
27
|
signInPath: string;
|
|
33
28
|
signInLabel: string;
|
|
34
|
-
imageSrc: string;
|
|
35
|
-
imageAlt: string;
|
|
36
|
-
imageBackgroundColor: string;
|
|
37
29
|
/** OAuth configuration */
|
|
38
30
|
oauth: {
|
|
39
31
|
enable_google: boolean;
|
|
32
|
+
enable_facebook: boolean;
|
|
40
33
|
enable_email_password: boolean;
|
|
41
34
|
google_button_text: string;
|
|
35
|
+
facebook_button_text: string;
|
|
42
36
|
oauth_divider_text: string;
|
|
43
37
|
};
|
|
44
38
|
};
|
|
@@ -77,26 +71,6 @@ export function get_register_config(): RegisterConfig {
|
|
|
77
71
|
"Sign in"
|
|
78
72
|
);
|
|
79
73
|
|
|
80
|
-
// Read image configuration
|
|
81
|
-
// If not set in config, falls back to default path-based image
|
|
82
|
-
// Consuming apps should copy images to public/hazo_auth/images/ or configure their own image_src
|
|
83
|
-
const imageSrc = get_config_value(
|
|
84
|
-
"hazo_auth__register_layout",
|
|
85
|
-
"image_src",
|
|
86
|
-
DEFAULT_REGISTER_IMAGE_PATH
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const imageAlt = get_config_value(
|
|
90
|
-
"hazo_auth__register_layout",
|
|
91
|
-
"image_alt",
|
|
92
|
-
"Modern building representing user registration"
|
|
93
|
-
);
|
|
94
|
-
const imageBackgroundColor = get_config_value(
|
|
95
|
-
"hazo_auth__register_layout",
|
|
96
|
-
"image_background_color",
|
|
97
|
-
"#e2e8f0"
|
|
98
|
-
);
|
|
99
|
-
|
|
100
74
|
// Get OAuth configuration (shared with login)
|
|
101
75
|
const oauthConfig = get_oauth_config();
|
|
102
76
|
|
|
@@ -107,6 +81,13 @@ export function get_register_config(): RegisterConfig {
|
|
|
107
81
|
"Sign up with Google"
|
|
108
82
|
);
|
|
109
83
|
|
|
84
|
+
// For the register page, default Facebook button text to "Sign up with Facebook" unless overridden
|
|
85
|
+
const registerFacebookButtonText = get_config_value(
|
|
86
|
+
"hazo_auth__oauth",
|
|
87
|
+
"facebook_button_text_register",
|
|
88
|
+
"Sign up with Facebook"
|
|
89
|
+
);
|
|
90
|
+
|
|
110
91
|
return {
|
|
111
92
|
showNameField,
|
|
112
93
|
passwordRequirements,
|
|
@@ -117,13 +98,12 @@ export function get_register_config(): RegisterConfig {
|
|
|
117
98
|
returnHomePath: alreadyLoggedInConfig.returnHomePath,
|
|
118
99
|
signInPath,
|
|
119
100
|
signInLabel,
|
|
120
|
-
imageSrc,
|
|
121
|
-
imageAlt,
|
|
122
|
-
imageBackgroundColor,
|
|
123
101
|
oauth: {
|
|
124
102
|
enable_google: oauthConfig.enable_google,
|
|
103
|
+
enable_facebook: oauthConfig.enable_facebook,
|
|
125
104
|
enable_email_password: oauthConfig.enable_email_password,
|
|
126
105
|
google_button_text: registerGoogleButtonText,
|
|
106
|
+
facebook_button_text: registerFacebookButtonText,
|
|
127
107
|
oauth_divider_text: oauthConfig.oauth_divider_text,
|
|
128
108
|
},
|
|
129
109
|
};
|
|
@@ -7,11 +7,6 @@ import { get_config_value } from "./config/config_loader.server.js";
|
|
|
7
7
|
import { get_already_logged_in_config } from "./already_logged_in_config.server.js";
|
|
8
8
|
import { get_password_requirements_config } from "./password_requirements_config.server.js";
|
|
9
9
|
|
|
10
|
-
// Default image path - consuming apps should either:
|
|
11
|
-
// 1. Configure their own image_src in hazo_auth_config.ini
|
|
12
|
-
// 2. Copy the default images from node_modules/hazo_auth/public/hazo_auth/images/ to their public folder
|
|
13
|
-
const DEFAULT_RESET_PASSWORD_IMAGE_PATH = "/hazo_auth/images/reset_password_default.jpg";
|
|
14
|
-
|
|
15
10
|
// section: types
|
|
16
11
|
export type ResetPasswordConfig = {
|
|
17
12
|
errorMessage: string;
|
|
@@ -30,9 +25,6 @@ export type ResetPasswordConfig = {
|
|
|
30
25
|
require_number: boolean;
|
|
31
26
|
require_special: boolean;
|
|
32
27
|
};
|
|
33
|
-
imageSrc: string;
|
|
34
|
-
imageAlt: string;
|
|
35
|
-
imageBackgroundColor: string;
|
|
36
28
|
};
|
|
37
29
|
|
|
38
30
|
// section: helpers
|
|
@@ -70,26 +62,6 @@ export function get_reset_password_config(): ResetPasswordConfig {
|
|
|
70
62
|
// Get shared password requirements
|
|
71
63
|
const passwordRequirements = get_password_requirements_config();
|
|
72
64
|
|
|
73
|
-
// Read image configuration
|
|
74
|
-
// If not set in config, falls back to default path-based image
|
|
75
|
-
// Consuming apps should copy images to public/hazo_auth/images/ or configure their own image_src
|
|
76
|
-
const imageSrc = get_config_value(
|
|
77
|
-
section,
|
|
78
|
-
"image_src",
|
|
79
|
-
DEFAULT_RESET_PASSWORD_IMAGE_PATH
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const imageAlt = get_config_value(
|
|
83
|
-
section,
|
|
84
|
-
"image_alt",
|
|
85
|
-
"Reset password illustration"
|
|
86
|
-
);
|
|
87
|
-
const imageBackgroundColor = get_config_value(
|
|
88
|
-
section,
|
|
89
|
-
"image_background_color",
|
|
90
|
-
"#f1f5f9"
|
|
91
|
-
);
|
|
92
|
-
|
|
93
65
|
return {
|
|
94
66
|
errorMessage,
|
|
95
67
|
successMessage,
|
|
@@ -101,9 +73,6 @@ export function get_reset_password_config(): ResetPasswordConfig {
|
|
|
101
73
|
returnHomeButtonLabel: alreadyLoggedInConfig.returnHomeButtonLabel,
|
|
102
74
|
returnHomePath: alreadyLoggedInConfig.returnHomePath,
|
|
103
75
|
passwordRequirements,
|
|
104
|
-
imageSrc,
|
|
105
|
-
imageAlt,
|
|
106
|
-
imageBackgroundColor,
|
|
107
76
|
};
|
|
108
77
|
}
|
|
109
78
|
|
|
@@ -36,6 +36,18 @@ export type OAuthLoginResult = {
|
|
|
36
36
|
error?: string;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
export type FacebookOAuthData = {
|
|
40
|
+
/** Facebook's unique user ID */
|
|
41
|
+
facebook_id: string;
|
|
42
|
+
/** User's email address from Facebook (may be null if user denied email permission) */
|
|
43
|
+
email: string | null;
|
|
44
|
+
/** User's full name from Facebook profile */
|
|
45
|
+
name?: string;
|
|
46
|
+
/** User's profile picture URL from Facebook */
|
|
47
|
+
profile_picture_url?: string;
|
|
48
|
+
// NOTE: no email_verified — we never trust Facebook's email_verified claim
|
|
49
|
+
};
|
|
50
|
+
|
|
39
51
|
export type LinkGoogleResult = {
|
|
40
52
|
success: boolean;
|
|
41
53
|
error?: string;
|
|
@@ -241,6 +253,191 @@ export async function handle_google_oauth_login(
|
|
|
241
253
|
}
|
|
242
254
|
}
|
|
243
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Handles Facebook OAuth login/registration flow
|
|
258
|
+
* 1. Check if user exists with facebook_id -> login
|
|
259
|
+
* 2. Check if user exists with email -> link Facebook account (respects auto_link_unverified)
|
|
260
|
+
* 3. Create new user with Facebook data (email_verified always false — never trust Facebook)
|
|
261
|
+
*
|
|
262
|
+
* @param adapter - The hazo_connect adapter instance
|
|
263
|
+
* @param data - Facebook OAuth user data
|
|
264
|
+
* @param opts - Options (auto_link_unverified: whether to link unverified accounts)
|
|
265
|
+
* @returns OAuth login result with user_id and status flags
|
|
266
|
+
*/
|
|
267
|
+
export async function handle_facebook_oauth_login(
|
|
268
|
+
adapter: HazoConnectAdapter,
|
|
269
|
+
data: FacebookOAuthData,
|
|
270
|
+
opts?: { auto_link_unverified?: boolean }
|
|
271
|
+
): Promise<OAuthLoginResult> {
|
|
272
|
+
const logger = create_app_logger();
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
const { facebook_id, email, name, profile_picture_url } = data;
|
|
276
|
+
|
|
277
|
+
const users_service = createCrudService(adapter, "hazo_users");
|
|
278
|
+
const now = new Date().toISOString();
|
|
279
|
+
|
|
280
|
+
// Step 1: Check if user exists with this facebook_id
|
|
281
|
+
const users_by_facebook_id = await users_service.findBy({ facebook_id });
|
|
282
|
+
|
|
283
|
+
if (Array.isArray(users_by_facebook_id) && users_by_facebook_id.length > 0) {
|
|
284
|
+
const user = users_by_facebook_id[0];
|
|
285
|
+
|
|
286
|
+
await users_service.updateById(user.id, {
|
|
287
|
+
last_logon: now,
|
|
288
|
+
changed_at: now,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
logger.info("oauth_service_facebook_login_existing_facebook_user", {
|
|
292
|
+
filename: "oauth_service.ts",
|
|
293
|
+
line_number: get_line_number(),
|
|
294
|
+
user_id: user.id,
|
|
295
|
+
email: user.email_address,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
return {
|
|
299
|
+
success: true,
|
|
300
|
+
user_id: user.id as string,
|
|
301
|
+
is_new_user: false,
|
|
302
|
+
was_linked: false,
|
|
303
|
+
email: user.email_address as string,
|
|
304
|
+
name: user.name as string | undefined,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Step 2: Check if user exists with this email
|
|
309
|
+
if (email) {
|
|
310
|
+
const users_by_email = await users_service.findBy({ email_address: email });
|
|
311
|
+
|
|
312
|
+
if (Array.isArray(users_by_email) && users_by_email.length > 0) {
|
|
313
|
+
const user = users_by_email[0];
|
|
314
|
+
const user_email_verified = user.email_verified as boolean;
|
|
315
|
+
|
|
316
|
+
if (!user_email_verified) {
|
|
317
|
+
if (!opts?.auto_link_unverified) {
|
|
318
|
+
return {
|
|
319
|
+
success: false,
|
|
320
|
+
error: "link_blocked_unverified",
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
// auto_link_unverified=true: link but do NOT change email_verified status
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Link Facebook account to existing user
|
|
327
|
+
const current_auth_providers = (user.auth_providers as string) || "email";
|
|
328
|
+
const new_auth_providers = current_auth_providers.includes("facebook")
|
|
329
|
+
? current_auth_providers
|
|
330
|
+
: `${current_auth_providers},facebook`;
|
|
331
|
+
|
|
332
|
+
const update_data: Record<string, unknown> = {
|
|
333
|
+
facebook_id,
|
|
334
|
+
auth_providers: new_auth_providers,
|
|
335
|
+
last_logon: now,
|
|
336
|
+
changed_at: now,
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// Update name if not set and Facebook provides one
|
|
340
|
+
if (!user.name && name) {
|
|
341
|
+
update_data.name = name;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Update profile picture if not set and Facebook provides one
|
|
345
|
+
if (!user.profile_picture_url && profile_picture_url) {
|
|
346
|
+
update_data.profile_picture_url = profile_picture_url;
|
|
347
|
+
update_data.profile_source = "custom";
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
await users_service.updateById(user.id, update_data);
|
|
351
|
+
|
|
352
|
+
logger.info("oauth_service_facebook_linked_to_existing", {
|
|
353
|
+
filename: "oauth_service.ts",
|
|
354
|
+
line_number: get_line_number(),
|
|
355
|
+
user_id: user.id,
|
|
356
|
+
email,
|
|
357
|
+
was_unverified: !user_email_verified,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
success: true,
|
|
362
|
+
user_id: user.id as string,
|
|
363
|
+
is_new_user: false,
|
|
364
|
+
was_linked: true,
|
|
365
|
+
email: user.email_address as string,
|
|
366
|
+
name: (update_data.name as string) || (user.name as string | undefined),
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Step 3: Create new user with Facebook data
|
|
372
|
+
const user_id = randomUUID();
|
|
373
|
+
|
|
374
|
+
const insert_data: Record<string, unknown> = {
|
|
375
|
+
id: user_id,
|
|
376
|
+
email_address: email,
|
|
377
|
+
password_hash: "", // Empty string for Facebook-only users
|
|
378
|
+
email_verified: false, // Never trust Facebook's email_verified claim
|
|
379
|
+
status: "ACTIVE",
|
|
380
|
+
login_attempts: 0,
|
|
381
|
+
facebook_id,
|
|
382
|
+
auth_providers: "facebook",
|
|
383
|
+
created_at: now,
|
|
384
|
+
changed_at: now,
|
|
385
|
+
last_logon: now,
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
if (name) {
|
|
389
|
+
insert_data.name = name;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (profile_picture_url) {
|
|
393
|
+
insert_data.profile_picture_url = profile_picture_url;
|
|
394
|
+
insert_data.profile_source = "custom";
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const inserted_users = await users_service.insert(insert_data);
|
|
398
|
+
|
|
399
|
+
if (!Array.isArray(inserted_users) || inserted_users.length === 0) {
|
|
400
|
+
return {
|
|
401
|
+
success: false,
|
|
402
|
+
error: "Failed to create user account",
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
logger.info("oauth_service_facebook_new_user_created", {
|
|
407
|
+
filename: "oauth_service.ts",
|
|
408
|
+
line_number: get_line_number(),
|
|
409
|
+
user_id,
|
|
410
|
+
email,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
return {
|
|
414
|
+
success: true,
|
|
415
|
+
user_id,
|
|
416
|
+
is_new_user: true,
|
|
417
|
+
was_linked: false,
|
|
418
|
+
email: email ?? undefined,
|
|
419
|
+
name,
|
|
420
|
+
};
|
|
421
|
+
} catch (error) {
|
|
422
|
+
const user_friendly_error = sanitize_error_for_user(error, {
|
|
423
|
+
logToConsole: true,
|
|
424
|
+
logToLogger: true,
|
|
425
|
+
logger,
|
|
426
|
+
context: {
|
|
427
|
+
filename: "oauth_service.ts",
|
|
428
|
+
line_number: get_line_number(),
|
|
429
|
+
email: data.email,
|
|
430
|
+
operation: "handle_facebook_oauth_login",
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
return {
|
|
435
|
+
success: false,
|
|
436
|
+
error: user_friendly_error,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
244
441
|
/**
|
|
245
442
|
* Links a Google account to an existing user
|
|
246
443
|
* @param adapter - The hazo_connect adapter instance
|
|
@@ -74,11 +74,9 @@ enable_admin_ui = true
|
|
|
74
74
|
# image_alt = Illustration of a globe representing secure authentication workflows
|
|
75
75
|
# image_background_color = #e2e8f0
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
# submit_button = Register
|
|
81
|
-
# cancel_button = Cancel
|
|
77
|
+
; Page text is no longer configured via INI.
|
|
78
|
+
; Use HazoAuthStringsProvider or per-page props instead.
|
|
79
|
+
; See MIGRATION.md for details.
|
|
82
80
|
|
|
83
81
|
# Field labels (defaults shown, uncomment to override)
|
|
84
82
|
# name_label = Full name
|
|
@@ -129,11 +127,9 @@ enable_admin_ui = true
|
|
|
129
127
|
# image_alt = Illustration of a globe representing secure authentication workflows
|
|
130
128
|
# image_background_color = #e2e8f0
|
|
131
129
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# submit_button = Login
|
|
136
|
-
# cancel_button = Cancel
|
|
130
|
+
; Page text is no longer configured via INI.
|
|
131
|
+
; Use HazoAuthStringsProvider or per-page props instead.
|
|
132
|
+
; See MIGRATION.md for details.
|
|
137
133
|
|
|
138
134
|
# Field labels (defaults shown, uncomment to override)
|
|
139
135
|
# email_label = Email address
|
|
@@ -170,11 +166,9 @@ otp_signin_href = /hazo_auth/otp
|
|
|
170
166
|
# image_alt = Illustration of a globe representing secure authentication workflows
|
|
171
167
|
# image_background_color = #e2e8f0
|
|
172
168
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
# submit_button = Send reset link
|
|
177
|
-
# cancel_button = Cancel
|
|
169
|
+
; Page text is no longer configured via INI.
|
|
170
|
+
; Use HazoAuthStringsProvider or per-page props instead.
|
|
171
|
+
; See MIGRATION.md for details.
|
|
178
172
|
|
|
179
173
|
[hazo_auth__reset_password_layout]
|
|
180
174
|
# Image configuration
|
|
@@ -182,11 +176,9 @@ otp_signin_href = /hazo_auth/otp
|
|
|
182
176
|
# image_alt = Illustration of a globe representing secure authentication workflows
|
|
183
177
|
# image_background_color = #e2e8f0
|
|
184
178
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
# submit_button = Reset password
|
|
189
|
-
# cancel_button = Cancel
|
|
179
|
+
; Page text is no longer configured via INI.
|
|
180
|
+
; Use HazoAuthStringsProvider or per-page props instead.
|
|
181
|
+
; See MIGRATION.md for details.
|
|
190
182
|
|
|
191
183
|
# Field labels (defaults shown, uncomment to override)
|
|
192
184
|
# password_label = New password
|
|
@@ -218,22 +210,9 @@ otp_signin_href = /hazo_auth/otp
|
|
|
218
210
|
# image_alt = Illustration of a globe representing secure authentication workflows
|
|
219
211
|
# image_background_color = #e2e8f0
|
|
220
212
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# submit_button = Resend verification email
|
|
225
|
-
# cancel_button = Cancel
|
|
226
|
-
|
|
227
|
-
# Success labels
|
|
228
|
-
# success_heading = Email verified successfully
|
|
229
|
-
# success_message = Your email address has been verified. You can now log in to your account.
|
|
230
|
-
# success_redirect_message = Redirecting to login page in
|
|
231
|
-
# success_go_to_login_button = Go to login
|
|
232
|
-
|
|
233
|
-
# Error labels
|
|
234
|
-
# error_heading = Verification failed
|
|
235
|
-
# error_message = The verification link is invalid or has expired.
|
|
236
|
-
# error_resend_form_heading = Resend verification email
|
|
213
|
+
; Page text is no longer configured via INI.
|
|
214
|
+
; Use HazoAuthStringsProvider or per-page props instead.
|
|
215
|
+
; See MIGRATION.md for details.
|
|
237
216
|
|
|
238
217
|
# Field labels (defaults shown, uncomment to override)
|
|
239
218
|
# email_label = Email address
|
|
@@ -275,11 +254,9 @@ otp_signin_href = /hazo_auth/otp
|
|
|
275
254
|
# - [hazo_auth__password_requirements] for password validation rules
|
|
276
255
|
# - [hazo_auth__profile_picture] for profile picture settings and prioritization
|
|
277
256
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
# Sub heading
|
|
282
|
-
# sub_heading = Manage your profile, password, and email preferences.
|
|
257
|
+
; Page heading text is no longer configured via INI.
|
|
258
|
+
; Use HazoAuthStringsProvider or the title prop on MySettingsPage instead.
|
|
259
|
+
; See MIGRATION.md for details.
|
|
283
260
|
|
|
284
261
|
# Profile Photo section
|
|
285
262
|
# profile_photo_label = Profile Photo
|
|
@@ -602,6 +579,26 @@ application_permission_list_defaults = admin_user_management,admin_role_manageme
|
|
|
602
579
|
# Redirect when skip_invitation_check=true and user has no scope
|
|
603
580
|
# no_scope_redirect = /
|
|
604
581
|
|
|
582
|
+
[hazo_auth__create_firm]
|
|
583
|
+
; heading, sub_heading, and submit_button_label are no longer configured via INI in v7.0.0.
|
|
584
|
+
; Use HazoAuthStringsProvider or the title/subtitle/ctaText props on CreateFirmPage instead.
|
|
585
|
+
; See MIGRATION.md for details.
|
|
586
|
+
|
|
587
|
+
; Firm name field label
|
|
588
|
+
# firm_name_label = Firm Name
|
|
589
|
+
|
|
590
|
+
; Organisation structure field label
|
|
591
|
+
# org_structure_label = Organisation Structure
|
|
592
|
+
|
|
593
|
+
; Default organisation structure value
|
|
594
|
+
# org_structure_default = Headquarters
|
|
595
|
+
|
|
596
|
+
; Success message shown after firm creation
|
|
597
|
+
# success_message = Your firm has been created successfully!
|
|
598
|
+
|
|
599
|
+
; Route to redirect to after firm creation
|
|
600
|
+
# redirect_route = /
|
|
601
|
+
|
|
605
602
|
[hazo_auth__multi_tenancy]
|
|
606
603
|
# Multi-tenancy configuration for organization hierarchy
|
|
607
604
|
# Enables hierarchical organization structures for company-wide access control
|
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
import type { StaticImageData } from "next/image";
|
|
2
1
|
import { type ButtonPaletteOverrides } from "../shared/config/layout_customization.js";
|
|
2
|
+
import type { HazoAuthTheme } from "../../../theme/theme_types";
|
|
3
3
|
export type CreateFirmLayoutProps = {
|
|
4
|
-
/**
|
|
5
|
-
|
|
6
|
-
/** Alt text for the image */
|
|
7
|
-
image_alt?: string;
|
|
8
|
-
/** Background color for the image panel */
|
|
9
|
-
image_background_color?: string;
|
|
4
|
+
/** Theme that controls visual appearance and layout mode. */
|
|
5
|
+
theme?: HazoAuthTheme;
|
|
10
6
|
/** Page heading */
|
|
11
7
|
heading?: string;
|
|
12
8
|
/** Page sub-heading */
|
|
@@ -39,6 +35,6 @@ export type CreateFirmLayoutProps = {
|
|
|
39
35
|
error: (message: string, data?: Record<string, unknown>) => void;
|
|
40
36
|
};
|
|
41
37
|
};
|
|
42
|
-
export default function CreateFirmLayout({
|
|
38
|
+
export default function CreateFirmLayout({ theme, heading, sub_heading, firm_name_label, firm_name_placeholder, org_structure_label, org_structure_placeholder, default_org_structure, submit_button_label, success_message, redirect_route, apiBasePath, onSuccess, button_colors, logger, }: CreateFirmLayoutProps): import("react/jsx-runtime").JSX.Element;
|
|
43
39
|
export { CreateFirmLayout };
|
|
44
40
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/create_firm/index.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/create_firm/index.tsx"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,sBAAsB,EAC5B,MAAM,uCAAuC,CAAC;AAM/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAGhE,MAAM,MAAM,qBAAqB,GAAG;IAClC,6DAA6D;IAC7D,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,mBAAmB;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,oCAAoC;IACpC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0CAA0C;IAC1C,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sCAAsC;IACtC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,8BAA8B;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sCAAsC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,6BAA6B;IAC7B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,2BAA2B;IAC3B,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAClE,CAAC;CACH,CAAC;AAGF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACvC,KAAK,EACL,OAA4B,EAC5B,WAAuD,EACvD,eAA6B,EAC7B,qBAA8C,EAC9C,mBAA8C,EAC9C,yBAA6D,EAC7D,qBAAsC,EACtC,mBAAmC,EACnC,eAA4D,EAC5D,cAAoB,EACpB,WAAW,EACX,SAAS,EACT,aAAa,EACb,MAAM,GACP,EAAE,qBAAqB,2CAuJvB;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|