hazo_auth 7.0.1 → 8.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 +96 -319
- package/SETUP_CHECKLIST.md +59 -248
- package/cli-src/cli/generate.ts +1 -10
- package/cli-src/cli/validate.ts +0 -4
- package/cli-src/lib/auth/auth_types.ts +15 -21
- package/cli-src/lib/auth/hazo_get_auth.server.ts +19 -0
- package/cli-src/lib/auth/hazo_get_tenant_auth.server.ts +24 -25
- package/cli-src/lib/auth/index.ts +2 -2
- package/cli-src/lib/auth/nextauth_config.ts +27 -67
- package/cli-src/lib/auth/with_auth.server.ts +15 -15
- package/cli-src/lib/config/default_config.ts +8 -0
- package/cli-src/lib/cookies_config.server.ts +1 -1
- package/cli-src/lib/email_verification_config.server.ts +34 -0
- package/cli-src/lib/forgot_password_config.server.ts +34 -0
- package/cli-src/lib/legal/legal_docs_config.server.ts +61 -0
- package/cli-src/lib/legal/legal_docs_reader.server.ts +36 -0
- package/cli-src/lib/legal/legal_docs_service.ts +196 -0
- package/cli-src/lib/legal/legal_docs_types.ts +31 -0
- package/cli-src/lib/login_config.server.ts +29 -14
- package/cli-src/lib/my_settings_config.server.ts +3 -0
- package/cli-src/lib/oauth_config.server.ts +31 -57
- package/cli-src/lib/register_config.server.ts +35 -11
- package/cli-src/lib/reset_password_config.server.ts +31 -0
- package/cli-src/lib/services/email_template_manifest.ts +0 -17
- package/cli-src/lib/services/index.ts +2 -8
- package/cli-src/lib/services/oauth_service.ts +74 -128
- package/cli-src/lib/services/otp_service.ts +7 -2
- package/cli-src/lib/services/registration_service.ts +16 -1
- package/cli-src/lib/services/session_token_service.ts +0 -2
- package/config/hazo_auth_config.example.ini +41 -76
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +1 -10
- package/dist/cli/validate.d.ts.map +1 -1
- package/dist/cli/validate.js +0 -4
- package/dist/client.d.ts +1 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/components/layouts/create_firm/index.d.ts +8 -4
- 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 +5 -4
- 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 +5 -4
- 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/index.d.ts +1 -0
- package/dist/components/layouts/index.d.ts.map +1 -1
- package/dist/components/layouts/index.js +2 -0
- package/dist/components/layouts/legal/index.d.ts +5 -0
- package/dist/components/layouts/legal/index.d.ts.map +1 -0
- package/dist/components/layouts/legal/index.js +4 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.d.ts +7 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.js +84 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts +9 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.js +11 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.d.ts +9 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.js +11 -0
- package/dist/components/layouts/legal/legal_doc_drawer.d.ts +8 -0
- package/dist/components/layouts/legal/legal_doc_drawer.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_drawer.js +55 -0
- package/dist/components/layouts/login/index.d.ts +13 -19
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +8 -11
- package/dist/components/layouts/otp/index.d.ts +5 -1
- package/dist/components/layouts/otp/index.d.ts.map +1 -1
- package/dist/components/layouts/otp/index.js +2 -2
- package/dist/components/layouts/register/hooks/use_register_form.d.ts +5 -1
- package/dist/components/layouts/register/hooks/use_register_form.d.ts.map +1 -1
- package/dist/components/layouts/register/hooks/use_register_form.js +25 -10
- package/dist/components/layouts/register/index.d.ts +11 -11
- package/dist/components/layouts/register/index.d.ts.map +1 -1
- package/dist/components/layouts/register/index.js +26 -7
- package/dist/components/layouts/reset_password/index.d.ts +5 -4
- 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 +5 -3
- 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 +2 -6
- package/dist/components/layouts/shared/components/facebook_sign_in_button.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/facebook_sign_in_button.js +11 -13
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +3 -8
- package/dist/components/layouts/shared/components/two_column_auth_layout.d.ts +6 -3
- 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 +5 -8
- package/dist/components/layouts/shared/index.d.ts +2 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +1 -0
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +84 -9
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/input-otp.d.ts +2 -2
- package/dist/components/ui/sheet.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.d.ts +14 -13
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +0 -10
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +19 -0
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts +7 -8
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_tenant_auth.server.js +22 -23
- package/dist/lib/auth/index.d.ts +2 -2
- package/dist/lib/auth/index.d.ts.map +1 -1
- package/dist/lib/auth/nextauth_config.d.ts +0 -10
- package/dist/lib/auth/nextauth_config.d.ts.map +1 -1
- package/dist/lib/auth/nextauth_config.js +23 -52
- package/dist/lib/auth/with_auth.server.d.ts +13 -13
- package/dist/lib/auth/with_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/with_auth.server.js +2 -2
- package/dist/lib/config/default_config.d.ts +16 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +8 -0
- package/dist/lib/cookies_config.server.d.ts +1 -1
- package/dist/lib/cookies_config.server.js +1 -1
- package/dist/lib/email_verification_config.server.d.ts +3 -0
- package/dist/lib/email_verification_config.server.d.ts.map +1 -1
- package/dist/lib/email_verification_config.server.js +15 -0
- package/dist/lib/forgot_password_config.server.d.ts +3 -0
- package/dist/lib/forgot_password_config.server.d.ts.map +1 -1
- package/dist/lib/forgot_password_config.server.js +15 -0
- package/dist/lib/legal/legal_docs_config.server.d.ts +22 -0
- package/dist/lib/legal/legal_docs_config.server.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_config.server.js +52 -0
- package/dist/lib/legal/legal_docs_reader.server.d.ts +15 -0
- package/dist/lib/legal/legal_docs_reader.server.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_reader.server.js +24 -0
- package/dist/lib/legal/legal_docs_service.d.ts +49 -0
- package/dist/lib/legal/legal_docs_service.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_service.js +140 -0
- package/dist/lib/legal/legal_docs_types.d.ts +25 -0
- package/dist/lib/legal/legal_docs_types.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_types.js +2 -0
- package/dist/lib/login_config.server.d.ts +3 -6
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +11 -7
- package/dist/lib/my_settings_config.server.d.ts +1 -0
- package/dist/lib/my_settings_config.server.d.ts.map +1 -1
- package/dist/lib/my_settings_config.server.js +2 -0
- package/dist/lib/oauth_config.server.d.ts +8 -17
- package/dist/lib/oauth_config.server.d.ts.map +1 -1
- package/dist/lib/oauth_config.server.js +10 -25
- package/dist/lib/register_config.server.d.ts +5 -2
- package/dist/lib/register_config.server.d.ts.map +1 -1
- package/dist/lib/register_config.server.js +15 -4
- package/dist/lib/reset_password_config.server.d.ts +3 -0
- package/dist/lib/reset_password_config.server.d.ts.map +1 -1
- package/dist/lib/reset_password_config.server.js +13 -0
- package/dist/lib/services/email_template_manifest.d.ts.map +1 -1
- package/dist/lib/services/email_template_manifest.js +0 -17
- package/dist/lib/services/index.d.ts +0 -2
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +0 -1
- package/dist/lib/services/oauth_service.d.ts +11 -22
- package/dist/lib/services/oauth_service.d.ts.map +1 -1
- package/dist/lib/services/oauth_service.js +63 -96
- package/dist/lib/services/otp_service.d.ts +1 -1
- package/dist/lib/services/otp_service.d.ts.map +1 -1
- package/dist/lib/services/otp_service.js +6 -1
- package/dist/lib/services/registration_service.d.ts +5 -0
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +6 -0
- package/dist/lib/services/session_token_service.d.ts +0 -2
- package/dist/lib/services/session_token_service.d.ts.map +1 -1
- package/dist/lib/services/session_token_service.js +0 -2
- package/dist/page_components/create_firm.d.ts +1 -13
- package/dist/page_components/create_firm.d.ts.map +1 -1
- package/dist/page_components/create_firm.js +6 -10
- package/dist/page_components/forgot_password.d.ts +4 -1
- package/dist/page_components/forgot_password.d.ts.map +1 -1
- package/dist/page_components/forgot_password.js +6 -2
- package/dist/page_components/index.d.ts +0 -5
- package/dist/page_components/index.d.ts.map +1 -1
- package/dist/page_components/index.js +0 -5
- package/dist/page_components/login.d.ts +4 -1
- package/dist/page_components/login.d.ts.map +1 -1
- package/dist/page_components/login.js +6 -2
- package/dist/page_components/register.d.ts +4 -1
- package/dist/page_components/register.d.ts.map +1 -1
- package/dist/page_components/register.js +6 -2
- package/dist/page_components/reset_password.d.ts +4 -1
- package/dist/page_components/reset_password.d.ts.map +1 -1
- package/dist/page_components/reset_password.js +6 -2
- package/dist/page_components/verify_email.d.ts +4 -1
- package/dist/page_components/verify_email.d.ts.map +1 -1
- package/dist/page_components/verify_email.js +6 -2
- package/dist/server/routes/assets.d.ts +8 -0
- package/dist/server/routes/assets.d.ts.map +1 -0
- package/dist/server/routes/assets.js +38 -0
- package/dist/server/routes/consent_me.d.ts +4 -0
- package/dist/server/routes/consent_me.d.ts.map +1 -0
- package/dist/server/routes/consent_me.js +15 -0
- package/dist/server/routes/index.d.ts +9 -4
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +13 -5
- package/dist/server/routes/legal_docs_accept.d.ts +3 -0
- package/dist/server/routes/legal_docs_accept.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_accept.js +43 -0
- package/dist/server/routes/legal_docs_get.d.ts +3 -0
- package/dist/server/routes/legal_docs_get.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_get.js +49 -0
- package/dist/server/routes/legal_docs_publish.d.ts +3 -0
- package/dist/server/routes/legal_docs_publish.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_publish.js +35 -0
- package/dist/server/routes/me.d.ts.map +1 -1
- package/dist/server/routes/me.js +1 -43
- package/dist/server/routes/oauth_facebook_callback.d.ts +1 -1
- package/dist/server/routes/oauth_facebook_callback.d.ts.map +1 -1
- package/dist/server/routes/oauth_facebook_callback.js +8 -1
- package/dist/server/routes/oauth_google_callback.js +1 -1
- package/dist/server/routes/otp/verify.js +2 -2
- package/dist/server/routes/register.d.ts.map +1 -1
- package/dist/server/routes/register.js +26 -0
- package/dist/server/routes/strings_defaults.d.ts +4 -0
- package/dist/server/routes/strings_defaults.d.ts.map +1 -0
- package/dist/server/routes/strings_defaults.js +7 -0
- package/dist/server/routes/user_management_users.d.ts +11 -0
- package/dist/server/routes/user_management_users.d.ts.map +1 -1
- package/dist/server/routes/user_management_users.js +94 -0
- package/dist/server-lib.d.ts +0 -3
- package/dist/server-lib.d.ts.map +1 -1
- package/dist/server-lib.js +0 -2
- package/dist/server_pages/forgot_password.d.ts +18 -14
- package/dist/server_pages/forgot_password.d.ts.map +1 -1
- package/dist/server_pages/forgot_password.js +14 -12
- package/dist/server_pages/forgot_password_client_wrapper.d.ts +8 -7
- 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/index.d.ts +2 -0
- package/dist/server_pages/index.d.ts.map +1 -1
- package/dist/server_pages/index.js +1 -0
- package/dist/server_pages/login.d.ts +22 -23
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +27 -14
- package/dist/server_pages/login_client_wrapper.d.ts +9 -10
- 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 +1 -3
- package/dist/server_pages/my_settings.d.ts.map +1 -1
- package/dist/server_pages/my_settings.js +2 -9
- package/dist/server_pages/register.d.ts +17 -20
- package/dist/server_pages/register.d.ts.map +1 -1
- package/dist/server_pages/register.js +20 -15
- package/dist/server_pages/register_client_wrapper.d.ts +8 -10
- 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 +16 -11
- package/dist/server_pages/reset_password.d.ts.map +1 -1
- package/dist/server_pages/reset_password.js +14 -10
- package/dist/server_pages/reset_password_client_wrapper.d.ts +8 -7
- 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 +18 -12
- package/dist/server_pages/verify_email.d.ts.map +1 -1
- package/dist/server_pages/verify_email.js +13 -11
- package/dist/server_pages/verify_email_client_wrapper.d.ts +8 -7
- 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.d.ts +2 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +3 -0
- package/dist/themes/index.d.ts +0 -1
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/index.js +1 -1
- package/package.json +30 -61
- package/dist/themes/preset_indigo_sunset.d.ts +0 -3
- package/dist/themes/preset_indigo_sunset.d.ts.map +0 -1
- package/dist/themes/preset_indigo_sunset.js +0 -20
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// file_description: service functions for the legal document acceptance system
|
|
2
|
+
// section: imports
|
|
3
|
+
import { createCrudService } from 'hazo_connect/server';
|
|
4
|
+
import type { LegalAcceptanceMap } from './legal_docs_types';
|
|
5
|
+
|
|
6
|
+
// section: helpers
|
|
7
|
+
|
|
8
|
+
function generate_id(): string {
|
|
9
|
+
return crypto.randomUUID();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// section: exports
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Write legal acceptance records. Call from:
|
|
16
|
+
* - registration_service (bundled with register POST, pre-session)
|
|
17
|
+
* - legal_docs_accept route (authenticated, post-login)
|
|
18
|
+
*
|
|
19
|
+
* Inserts one row per doc into hazo_legal_acceptances (audit history) and
|
|
20
|
+
* merges the result into the denormalised hazo_users.legal_acceptance JSONB
|
|
21
|
+
* column for fast "has this user accepted the current version?" queries.
|
|
22
|
+
*/
|
|
23
|
+
export async function write_legal_acceptance(
|
|
24
|
+
adapter: any,
|
|
25
|
+
user_id: string,
|
|
26
|
+
accepted: Record<string, { hash: string }>,
|
|
27
|
+
ip: string | null,
|
|
28
|
+
user_agent: string | null,
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
const now = new Date().toISOString();
|
|
31
|
+
|
|
32
|
+
// 1. Insert one history row per doc
|
|
33
|
+
const history_service = createCrudService(adapter, 'hazo_legal_acceptances');
|
|
34
|
+
for (const [doc_key, { hash }] of Object.entries(accepted)) {
|
|
35
|
+
await history_service.insert({
|
|
36
|
+
id: generate_id(),
|
|
37
|
+
user_id,
|
|
38
|
+
doc_key,
|
|
39
|
+
doc_hash: hash,
|
|
40
|
+
accepted_at: now,
|
|
41
|
+
ip,
|
|
42
|
+
user_agent,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 2. Merge into denormalized JSONB on hazo_users
|
|
47
|
+
const users_service = createCrudService(adapter, 'hazo_users');
|
|
48
|
+
const rows = await users_service.findBy({ id: user_id });
|
|
49
|
+
|
|
50
|
+
const existing_raw = rows[0]?.legal_acceptance;
|
|
51
|
+
let existing: LegalAcceptanceMap = {};
|
|
52
|
+
if (existing_raw) {
|
|
53
|
+
try {
|
|
54
|
+
existing = typeof existing_raw === 'string'
|
|
55
|
+
? JSON.parse(existing_raw)
|
|
56
|
+
: (existing_raw as LegalAcceptanceMap);
|
|
57
|
+
} catch { /* corrupt — start fresh */ }
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const updated: LegalAcceptanceMap = { ...existing };
|
|
61
|
+
for (const [doc_key, { hash }] of Object.entries(accepted)) {
|
|
62
|
+
updated[doc_key] = { hash, accepted_at: now, ip, user_agent };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await users_service.updateById(user_id, {
|
|
66
|
+
legal_acceptance: JSON.stringify(updated),
|
|
67
|
+
changed_at: now,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Publish a doc version as the new required version (admin action).
|
|
73
|
+
* Upserts hazo_legal_doc_versions keyed on doc_key.
|
|
74
|
+
*/
|
|
75
|
+
export async function publish_doc_version(
|
|
76
|
+
adapter: any,
|
|
77
|
+
doc_key: string,
|
|
78
|
+
required_hash: string,
|
|
79
|
+
published_by_user_id: string,
|
|
80
|
+
): Promise<{ published_at: string }> {
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
|
|
83
|
+
// createCrudService with doc_key as primary key so updateById works correctly
|
|
84
|
+
const versions_service = createCrudService(adapter, 'hazo_legal_doc_versions', {
|
|
85
|
+
primaryKeys: ['doc_key'],
|
|
86
|
+
autoId: false,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const existing = await versions_service.findBy({ doc_key });
|
|
90
|
+
|
|
91
|
+
if (existing.length > 0) {
|
|
92
|
+
await versions_service.updateById(doc_key, {
|
|
93
|
+
required_hash,
|
|
94
|
+
published_at: now,
|
|
95
|
+
published_by_user_id,
|
|
96
|
+
});
|
|
97
|
+
} else {
|
|
98
|
+
await versions_service.insert({
|
|
99
|
+
doc_key,
|
|
100
|
+
required_hash,
|
|
101
|
+
published_at: now,
|
|
102
|
+
published_by_user_id,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return { published_at: now };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Return required version info keyed by doc_key.
|
|
111
|
+
*/
|
|
112
|
+
export async function get_required_versions(
|
|
113
|
+
adapter: any,
|
|
114
|
+
doc_keys: string[],
|
|
115
|
+
): Promise<Record<string, { required_hash: string; published_at: string }>> {
|
|
116
|
+
if (doc_keys.length === 0) return {};
|
|
117
|
+
|
|
118
|
+
const versions_service = createCrudService(adapter, 'hazo_legal_doc_versions', {
|
|
119
|
+
primaryKeys: ['doc_key'],
|
|
120
|
+
autoId: false,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const rows = await versions_service.list((qb) =>
|
|
124
|
+
qb.whereIn('doc_key', doc_keys).select(['doc_key', 'required_hash', 'published_at'])
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
const result: Record<string, { required_hash: string; published_at: string }> = {};
|
|
128
|
+
for (const row of rows) {
|
|
129
|
+
result[String(row.doc_key)] = {
|
|
130
|
+
required_hash: String(row.required_hash),
|
|
131
|
+
published_at: String(row.published_at),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Return acceptance history for a user across all doc keys, newest first.
|
|
139
|
+
*/
|
|
140
|
+
export async function get_user_acceptance_history(
|
|
141
|
+
adapter: any,
|
|
142
|
+
user_id: string,
|
|
143
|
+
): Promise<Array<{
|
|
144
|
+
doc_key: string;
|
|
145
|
+
doc_hash: string;
|
|
146
|
+
accepted_at: string;
|
|
147
|
+
ip: string | null;
|
|
148
|
+
user_agent: string | null;
|
|
149
|
+
}>> {
|
|
150
|
+
const history_service = createCrudService(adapter, 'hazo_legal_acceptances');
|
|
151
|
+
|
|
152
|
+
return history_service.list((qb) =>
|
|
153
|
+
qb
|
|
154
|
+
.where('user_id', 'eq', user_id)
|
|
155
|
+
.select(['doc_key', 'doc_hash', 'accepted_at', 'ip', 'user_agent'])
|
|
156
|
+
.order('accepted_at', 'desc')
|
|
157
|
+
) as Promise<Array<{
|
|
158
|
+
doc_key: string;
|
|
159
|
+
doc_hash: string;
|
|
160
|
+
accepted_at: string;
|
|
161
|
+
ip: string | null;
|
|
162
|
+
user_agent: string | null;
|
|
163
|
+
}>>;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Count how many users have accepted the current required hash for a doc key.
|
|
168
|
+
* Returns { current, total } where current is users on the required_hash and
|
|
169
|
+
* total is all users in the system (including those with no legal_acceptance data).
|
|
170
|
+
*
|
|
171
|
+
* Note: This performs an in-process scan over all users. For large user bases
|
|
172
|
+
* consider a dedicated SQL query in a future optimisation.
|
|
173
|
+
*/
|
|
174
|
+
export async function get_compliance_count(
|
|
175
|
+
adapter: any,
|
|
176
|
+
doc_key: string,
|
|
177
|
+
required_hash: string,
|
|
178
|
+
): Promise<{ current: number; total: number }> {
|
|
179
|
+
const users_service = createCrudService(adapter, 'hazo_users');
|
|
180
|
+
const all_users = await users_service.list((qb) =>
|
|
181
|
+
qb.select(['id', 'legal_acceptance'])
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
let current = 0;
|
|
185
|
+
for (const user of all_users) {
|
|
186
|
+
let map: LegalAcceptanceMap = {};
|
|
187
|
+
try {
|
|
188
|
+
map = typeof user.legal_acceptance === 'string'
|
|
189
|
+
? JSON.parse(user.legal_acceptance as string)
|
|
190
|
+
: ((user.legal_acceptance ?? {}) as LegalAcceptanceMap);
|
|
191
|
+
} catch { /* ignore corrupt rows */ }
|
|
192
|
+
if (map[doc_key]?.hash === required_hash) current++;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { current, total: all_users.length };
|
|
196
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// file_description: shared types for the legal document acceptance system
|
|
2
|
+
|
|
3
|
+
export interface LegalDocConfig {
|
|
4
|
+
key: string;
|
|
5
|
+
title: string;
|
|
6
|
+
path: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface LegalDocsConfig {
|
|
10
|
+
docs: LegalDocConfig[];
|
|
11
|
+
display_mode: 'separate' | 'combined';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface LegalDoc {
|
|
15
|
+
key: string;
|
|
16
|
+
title: string;
|
|
17
|
+
content: string;
|
|
18
|
+
hash: string;
|
|
19
|
+
required_hash: string | null;
|
|
20
|
+
required_published_at: string | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface LegalAcceptanceRecord {
|
|
24
|
+
hash: string;
|
|
25
|
+
accepted_at: string;
|
|
26
|
+
ip: string | null;
|
|
27
|
+
user_agent: string | null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Shape of hazo_users.legal_acceptance JSONB
|
|
31
|
+
export type LegalAcceptanceMap = Record<string, LegalAcceptanceRecord>;
|
|
@@ -7,6 +7,9 @@ import { get_config_value, get_config_value_allow_empty } from "./config/config_
|
|
|
7
7
|
import { get_already_logged_in_config } from "./already_logged_in_config.server.js";
|
|
8
8
|
import { get_oauth_config, type OAuthConfig } from "./oauth_config.server.js";
|
|
9
9
|
|
|
10
|
+
// Assets served via the package's own API route — no manual copy needed.
|
|
11
|
+
const DEFAULT_LOGIN_IMAGE_PATH = "/api/hazo_auth/assets/login_default.jpg";
|
|
12
|
+
|
|
10
13
|
// section: types
|
|
11
14
|
export type LoginConfig = {
|
|
12
15
|
redirectRoute?: string;
|
|
@@ -21,14 +24,11 @@ export type LoginConfig = {
|
|
|
21
24
|
createAccountPath: string;
|
|
22
25
|
createAccountLabel: string;
|
|
23
26
|
showCreateAccountLink: boolean;
|
|
27
|
+
imageSrc: string;
|
|
28
|
+
imageAlt: string;
|
|
29
|
+
imageBackgroundColor: string;
|
|
24
30
|
/** OAuth configuration */
|
|
25
31
|
oauth: OAuthConfig;
|
|
26
|
-
/** Whether the OTP sign-in link is shown below the login form */
|
|
27
|
-
otpSigninEnabled: boolean;
|
|
28
|
-
/** Label for the OTP sign-in link */
|
|
29
|
-
otpSigninLabel: string;
|
|
30
|
-
/** href for the OTP sign-in link */
|
|
31
|
-
otpSigninHref: string;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
// section: helpers
|
|
@@ -69,14 +69,29 @@ export function get_login_config(): LoginConfig {
|
|
|
69
69
|
// Get shared already logged in config
|
|
70
70
|
const alreadyLoggedInConfig = get_already_logged_in_config();
|
|
71
71
|
|
|
72
|
+
// Read image configuration
|
|
73
|
+
// If not set in config, falls back to default path-based image
|
|
74
|
+
// Consuming apps should copy images to public/hazo_auth/images/ or configure their own image_src
|
|
75
|
+
const imageSrc = get_config_value(
|
|
76
|
+
section,
|
|
77
|
+
"image_src",
|
|
78
|
+
DEFAULT_LOGIN_IMAGE_PATH
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const imageAlt = get_config_value(
|
|
82
|
+
section,
|
|
83
|
+
"image_alt",
|
|
84
|
+
"Secure login illustration"
|
|
85
|
+
);
|
|
86
|
+
const imageBackgroundColor = get_config_value(
|
|
87
|
+
section,
|
|
88
|
+
"image_background_color",
|
|
89
|
+
"#f1f5f9"
|
|
90
|
+
);
|
|
91
|
+
|
|
72
92
|
// Get OAuth configuration
|
|
73
93
|
const oauth = get_oauth_config();
|
|
74
94
|
|
|
75
|
-
// OTP sign-in link
|
|
76
|
-
const otpSigninEnabled = get_config_value(section, "otp_signin_enabled", "false") === "true";
|
|
77
|
-
const otpSigninLabel = get_config_value(section, "otp_signin_label", "Sign in with email code");
|
|
78
|
-
const otpSigninHref = get_config_value(section, "otp_signin_href", "/hazo_auth/otp");
|
|
79
|
-
|
|
80
95
|
return {
|
|
81
96
|
redirectRoute,
|
|
82
97
|
successMessage,
|
|
@@ -90,10 +105,10 @@ export function get_login_config(): LoginConfig {
|
|
|
90
105
|
createAccountPath,
|
|
91
106
|
createAccountLabel,
|
|
92
107
|
showCreateAccountLink,
|
|
108
|
+
imageSrc,
|
|
109
|
+
imageAlt,
|
|
110
|
+
imageBackgroundColor,
|
|
93
111
|
oauth,
|
|
94
|
-
otpSigninEnabled,
|
|
95
|
-
otpSigninLabel,
|
|
96
|
-
otpSigninHref,
|
|
97
112
|
};
|
|
98
113
|
}
|
|
99
114
|
|
|
@@ -34,6 +34,7 @@ export type MySettingsConfig = {
|
|
|
34
34
|
user_photo_default_priority2?: "library" | "gravatar";
|
|
35
35
|
library_photo_path: string;
|
|
36
36
|
};
|
|
37
|
+
heading?: string;
|
|
37
38
|
subHeading?: string;
|
|
38
39
|
profilePhotoLabel?: string;
|
|
39
40
|
profilePhotoRecommendation?: string;
|
|
@@ -94,6 +95,7 @@ export function get_my_settings_config(): MySettingsConfig {
|
|
|
94
95
|
const fileTypes = get_file_types_config();
|
|
95
96
|
|
|
96
97
|
// Read optional labels with defaults
|
|
98
|
+
const heading = get_config_value(section, "heading", "Account Settings");
|
|
97
99
|
const subHeading = get_config_value(section, "sub_heading", "Manage your profile, password, and email preferences.");
|
|
98
100
|
const profilePhotoLabel = get_config_value(section, "profile_photo_label", "Profile Photo");
|
|
99
101
|
const profilePhotoRecommendation = get_config_value(section, "profile_photo_recommendation", "Recommended size: 200x200px. JPG, PNG.");
|
|
@@ -113,6 +115,7 @@ export function get_my_settings_config(): MySettingsConfig {
|
|
|
113
115
|
userFields,
|
|
114
116
|
passwordRequirements,
|
|
115
117
|
profilePicture,
|
|
118
|
+
heading,
|
|
116
119
|
subHeading,
|
|
117
120
|
profilePhotoLabel,
|
|
118
121
|
profilePhotoRecommendation,
|
|
@@ -10,20 +10,12 @@ 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;
|
|
15
13
|
/** Enable traditional email/password login */
|
|
16
14
|
enable_email_password: boolean;
|
|
17
15
|
/** Auto-link Google login to existing unverified email/password accounts */
|
|
18
16
|
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;
|
|
23
17
|
/** Text displayed on the Google sign-in button */
|
|
24
18
|
google_button_text: string;
|
|
25
|
-
/** Text displayed on the Facebook sign-in button */
|
|
26
|
-
facebook_button_text: string;
|
|
27
19
|
/** Text displayed on the divider between OAuth and email/password form */
|
|
28
20
|
oauth_divider_text: string;
|
|
29
21
|
/** NextAuth signIn page path */
|
|
@@ -38,10 +30,14 @@ export type OAuthConfig = {
|
|
|
38
30
|
skip_invitation_check: boolean;
|
|
39
31
|
/** Redirect when skip_invitation_check=true and user has no scope */
|
|
40
32
|
no_scope_redirect: string;
|
|
41
|
-
/** Facebook
|
|
42
|
-
|
|
43
|
-
/** Facebook App
|
|
44
|
-
|
|
33
|
+
/** Enable Facebook OAuth login */
|
|
34
|
+
enable_facebook_oauth: boolean;
|
|
35
|
+
/** Facebook App ID (env: HAZO_AUTH_FACEBOOK_APP_ID overrides config) */
|
|
36
|
+
facebook_app_id: string;
|
|
37
|
+
/** Facebook App Secret (env: HAZO_AUTH_FACEBOOK_APP_SECRET overrides config) */
|
|
38
|
+
facebook_app_secret: string;
|
|
39
|
+
/** Text displayed on the Facebook sign-in button */
|
|
40
|
+
facebook_button_text: string;
|
|
45
41
|
};
|
|
46
42
|
|
|
47
43
|
// section: constants
|
|
@@ -60,51 +56,24 @@ export function get_oauth_config(): OAuthConfig {
|
|
|
60
56
|
DEFAULT_OAUTH.enable_google
|
|
61
57
|
);
|
|
62
58
|
|
|
63
|
-
const enable_facebook = get_config_boolean(
|
|
64
|
-
SECTION_NAME,
|
|
65
|
-
"enable_facebook",
|
|
66
|
-
false
|
|
67
|
-
);
|
|
68
|
-
|
|
69
59
|
const enable_email_password = get_config_boolean(
|
|
70
60
|
SECTION_NAME,
|
|
71
61
|
"enable_email_password",
|
|
72
62
|
DEFAULT_OAUTH.enable_email_password
|
|
73
63
|
);
|
|
74
64
|
|
|
75
|
-
// Generic key (backward compat)
|
|
76
65
|
const auto_link_unverified_accounts = get_config_boolean(
|
|
77
66
|
SECTION_NAME,
|
|
78
67
|
"auto_link_unverified_accounts",
|
|
79
68
|
DEFAULT_OAUTH.auto_link_unverified_accounts
|
|
80
69
|
);
|
|
81
70
|
|
|
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
|
-
|
|
96
71
|
const google_button_text = get_config_value(
|
|
97
72
|
SECTION_NAME,
|
|
98
73
|
"google_button_text",
|
|
99
74
|
DEFAULT_OAUTH.google_button_text
|
|
100
75
|
);
|
|
101
76
|
|
|
102
|
-
const facebook_button_text = get_config_value(
|
|
103
|
-
SECTION_NAME,
|
|
104
|
-
"facebook_button_text",
|
|
105
|
-
"Continue with Facebook"
|
|
106
|
-
);
|
|
107
|
-
|
|
108
77
|
const oauth_divider_text = get_config_value(
|
|
109
78
|
SECTION_NAME,
|
|
110
79
|
"oauth_divider_text",
|
|
@@ -147,18 +116,31 @@ export function get_oauth_config(): OAuthConfig {
|
|
|
147
116
|
DEFAULT_OAUTH.no_scope_redirect
|
|
148
117
|
);
|
|
149
118
|
|
|
150
|
-
const
|
|
151
|
-
|
|
119
|
+
const enable_facebook_oauth = get_config_boolean(
|
|
120
|
+
SECTION_NAME,
|
|
121
|
+
"enable_facebook_oauth",
|
|
122
|
+
false
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const facebook_app_id =
|
|
126
|
+
process.env.HAZO_AUTH_FACEBOOK_APP_ID ||
|
|
127
|
+
get_config_value(SECTION_NAME, "facebook_app_id", "");
|
|
128
|
+
|
|
129
|
+
const facebook_app_secret =
|
|
130
|
+
process.env.HAZO_AUTH_FACEBOOK_APP_SECRET ||
|
|
131
|
+
get_config_value(SECTION_NAME, "facebook_app_secret", "");
|
|
132
|
+
|
|
133
|
+
const facebook_button_text = get_config_value(
|
|
134
|
+
SECTION_NAME,
|
|
135
|
+
"facebook_button_text",
|
|
136
|
+
"Continue with Facebook"
|
|
137
|
+
);
|
|
152
138
|
|
|
153
139
|
return {
|
|
154
140
|
enable_google,
|
|
155
|
-
enable_facebook,
|
|
156
141
|
enable_email_password,
|
|
157
142
|
auto_link_unverified_accounts,
|
|
158
|
-
auto_link_unverified_accounts_google,
|
|
159
|
-
auto_link_unverified_accounts_facebook,
|
|
160
143
|
google_button_text,
|
|
161
|
-
facebook_button_text,
|
|
162
144
|
oauth_divider_text,
|
|
163
145
|
sign_in_page,
|
|
164
146
|
error_page,
|
|
@@ -166,8 +148,10 @@ export function get_oauth_config(): OAuthConfig {
|
|
|
166
148
|
default_redirect,
|
|
167
149
|
skip_invitation_check,
|
|
168
150
|
no_scope_redirect,
|
|
169
|
-
|
|
170
|
-
|
|
151
|
+
enable_facebook_oauth,
|
|
152
|
+
facebook_app_id,
|
|
153
|
+
facebook_app_secret,
|
|
154
|
+
facebook_button_text,
|
|
171
155
|
};
|
|
172
156
|
}
|
|
173
157
|
|
|
@@ -190,13 +174,3 @@ export function is_email_password_enabled(): boolean {
|
|
|
190
174
|
DEFAULT_OAUTH.enable_email_password
|
|
191
175
|
);
|
|
192
176
|
}
|
|
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,6 +9,11 @@ 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
|
+
|
|
12
17
|
// section: types
|
|
13
18
|
export type RegisterConfig = {
|
|
14
19
|
showNameField: boolean;
|
|
@@ -26,14 +31,17 @@ export type RegisterConfig = {
|
|
|
26
31
|
returnHomePath: string;
|
|
27
32
|
signInPath: string;
|
|
28
33
|
signInLabel: string;
|
|
34
|
+
imageSrc: string;
|
|
35
|
+
imageAlt: string;
|
|
36
|
+
imageBackgroundColor: string;
|
|
29
37
|
/** OAuth configuration */
|
|
30
38
|
oauth: {
|
|
31
39
|
enable_google: boolean;
|
|
32
|
-
enable_facebook: boolean;
|
|
33
40
|
enable_email_password: boolean;
|
|
34
41
|
google_button_text: string;
|
|
35
|
-
facebook_button_text: string;
|
|
36
42
|
oauth_divider_text: string;
|
|
43
|
+
enable_facebook_oauth: boolean;
|
|
44
|
+
facebook_button_text: string;
|
|
37
45
|
};
|
|
38
46
|
};
|
|
39
47
|
|
|
@@ -71,6 +79,26 @@ export function get_register_config(): RegisterConfig {
|
|
|
71
79
|
"Sign in"
|
|
72
80
|
);
|
|
73
81
|
|
|
82
|
+
// Read image configuration
|
|
83
|
+
// If not set in config, falls back to default path-based image
|
|
84
|
+
// Consuming apps should copy images to public/hazo_auth/images/ or configure their own image_src
|
|
85
|
+
const imageSrc = get_config_value(
|
|
86
|
+
"hazo_auth__register_layout",
|
|
87
|
+
"image_src",
|
|
88
|
+
DEFAULT_REGISTER_IMAGE_PATH
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
const imageAlt = get_config_value(
|
|
92
|
+
"hazo_auth__register_layout",
|
|
93
|
+
"image_alt",
|
|
94
|
+
"Modern building representing user registration"
|
|
95
|
+
);
|
|
96
|
+
const imageBackgroundColor = get_config_value(
|
|
97
|
+
"hazo_auth__register_layout",
|
|
98
|
+
"image_background_color",
|
|
99
|
+
"#e2e8f0"
|
|
100
|
+
);
|
|
101
|
+
|
|
74
102
|
// Get OAuth configuration (shared with login)
|
|
75
103
|
const oauthConfig = get_oauth_config();
|
|
76
104
|
|
|
@@ -81,13 +109,6 @@ export function get_register_config(): RegisterConfig {
|
|
|
81
109
|
"Sign up with Google"
|
|
82
110
|
);
|
|
83
111
|
|
|
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
|
-
|
|
91
112
|
return {
|
|
92
113
|
showNameField,
|
|
93
114
|
passwordRequirements,
|
|
@@ -98,13 +119,16 @@ export function get_register_config(): RegisterConfig {
|
|
|
98
119
|
returnHomePath: alreadyLoggedInConfig.returnHomePath,
|
|
99
120
|
signInPath,
|
|
100
121
|
signInLabel,
|
|
122
|
+
imageSrc,
|
|
123
|
+
imageAlt,
|
|
124
|
+
imageBackgroundColor,
|
|
101
125
|
oauth: {
|
|
102
126
|
enable_google: oauthConfig.enable_google,
|
|
103
|
-
enable_facebook: oauthConfig.enable_facebook,
|
|
104
127
|
enable_email_password: oauthConfig.enable_email_password,
|
|
105
128
|
google_button_text: registerGoogleButtonText,
|
|
106
|
-
facebook_button_text: registerFacebookButtonText,
|
|
107
129
|
oauth_divider_text: oauthConfig.oauth_divider_text,
|
|
130
|
+
enable_facebook_oauth: oauthConfig.enable_facebook_oauth,
|
|
131
|
+
facebook_button_text: oauthConfig.facebook_button_text,
|
|
108
132
|
},
|
|
109
133
|
};
|
|
110
134
|
}
|
|
@@ -7,6 +7,11 @@ 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
|
+
|
|
10
15
|
// section: types
|
|
11
16
|
export type ResetPasswordConfig = {
|
|
12
17
|
errorMessage: string;
|
|
@@ -25,6 +30,9 @@ export type ResetPasswordConfig = {
|
|
|
25
30
|
require_number: boolean;
|
|
26
31
|
require_special: boolean;
|
|
27
32
|
};
|
|
33
|
+
imageSrc: string;
|
|
34
|
+
imageAlt: string;
|
|
35
|
+
imageBackgroundColor: string;
|
|
28
36
|
};
|
|
29
37
|
|
|
30
38
|
// section: helpers
|
|
@@ -62,6 +70,26 @@ export function get_reset_password_config(): ResetPasswordConfig {
|
|
|
62
70
|
// Get shared password requirements
|
|
63
71
|
const passwordRequirements = get_password_requirements_config();
|
|
64
72
|
|
|
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
|
+
|
|
65
93
|
return {
|
|
66
94
|
errorMessage,
|
|
67
95
|
successMessage,
|
|
@@ -73,6 +101,9 @@ export function get_reset_password_config(): ResetPasswordConfig {
|
|
|
73
101
|
returnHomeButtonLabel: alreadyLoggedInConfig.returnHomeButtonLabel,
|
|
74
102
|
returnHomePath: alreadyLoggedInConfig.returnHomePath,
|
|
75
103
|
passwordRequirements,
|
|
104
|
+
imageSrc,
|
|
105
|
+
imageAlt,
|
|
106
|
+
imageBackgroundColor,
|
|
76
107
|
};
|
|
77
108
|
}
|
|
78
109
|
|
|
@@ -101,21 +101,4 @@ export const hazo_auth_template_manifest: SystemTemplateManifest[] = [
|
|
|
101
101
|
},
|
|
102
102
|
],
|
|
103
103
|
},
|
|
104
|
-
{
|
|
105
|
-
template_name: "otp_signin_code",
|
|
106
|
-
template_label: "OTP sign-in code",
|
|
107
|
-
category: SYSTEM_CATEGORY,
|
|
108
|
-
html: read_template("otp_signin_code", "html"),
|
|
109
|
-
text: read_template("otp_signin_code", "txt"),
|
|
110
|
-
variables: [
|
|
111
|
-
{
|
|
112
|
-
variable_name: "otp_code",
|
|
113
|
-
variable_description: "6-digit OTP code for email sign-in (v6.1.0+)",
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
variable_name: "expires_in_minutes",
|
|
117
|
-
variable_description: "Number of minutes until the OTP code expires",
|
|
118
|
-
},
|
|
119
|
-
],
|
|
120
|
-
},
|
|
121
104
|
];
|
|
@@ -20,11 +20,5 @@ export * from "./scope_service.js";
|
|
|
20
20
|
export * from "./user_scope_service.js";
|
|
21
21
|
export * from "./oauth_service.js";
|
|
22
22
|
export * from "./branding_service.js";
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
verify_email_otp,
|
|
26
|
-
generate_otp_code,
|
|
27
|
-
hash_otp_code,
|
|
28
|
-
verify_otp_code,
|
|
29
|
-
} from "./otp_service.js";
|
|
30
|
-
export type { RequestEmailOTPResult, VerifyEmailOTPResult } from "./otp_service";
|
|
23
|
+
|
|
24
|
+
|