diff --git a/TODO.md b/TODO.md index df74355c..acbe68bc 100644 --- a/TODO.md +++ b/TODO.md @@ -60,12 +60,16 @@ This is a list of tasks to be completed before the next event/show/conference. ## Core Module Improvements -- [ ] **Person Management:** - - [ ] Create dedicated page/form for creating/editing person records. - - [ ] Finalize Person-User linking. -- [ ] **Address & Contact Management:** - - [ ] Implement full V3 CRUD UI (currently placeholders). - - [ ] Create dynamic detail routes. +- [x] **Person Management:** + - [x] Create dedicated page/form for creating/editing person records. (Standardized 2026-01-15) + - [x] Finalize Person-User linking. (UI implemented in [person_id]/+page.svelte) +- [x] **Address & Contact Management:** + - [x] Implement full V3 CRUD UI (Standardized 2026-01-15). + - [x] Create dynamic detail routes. +- [x] **Logic Consolidation:** + - [x] Merged legacy logic files into modern `ae_core__*` counterparts. + - [x] Renamed Activity Log to `ae_core__activity_log.ts` for consistency. + - [x] Unified Person-User logic and ensured correct Triple-ID / V3 helper usage. --- diff --git a/src/lib/ae_core/core__activity_log.ts b/src/lib/ae_core/ae_core__activity_log.ts similarity index 99% rename from src/lib/ae_core/core__activity_log.ts rename to src/lib/ae_core/ae_core__activity_log.ts index 67868fcb..4211a6aa 100644 --- a/src/lib/ae_core/core__activity_log.ts +++ b/src/lib/ae_core/ae_core__activity_log.ts @@ -101,7 +101,7 @@ export async function create_ae_obj__activity_log({ if (!account_id) { console.log(`ERROR: Core - Activity Log - account_id required to create`); - return false; + return null; } ae_promises.create__activity_log = await api.create_ae_obj_v3({ diff --git a/src/lib/ae_core/ae_core__person.ts b/src/lib/ae_core/ae_core__person.ts index f8176ad8..cd1d5606 100644 --- a/src/lib/ae_core/ae_core__person.ts +++ b/src/lib/ae_core/ae_core__person.ts @@ -65,6 +65,7 @@ export async function load_ae_obj_li__person({ api_cfg, for_obj_type = 'account', for_obj_id, + qry_email = null, qry_user_id = null, enabled = 'enabled', hidden = 'not_hidden', @@ -84,6 +85,7 @@ export async function load_ae_obj_li__person({ api_cfg: any; for_obj_type?: string; for_obj_id?: string; + qry_email?: string | null; qry_user_id?: string | null; enabled?: 'enabled' | 'all' | 'not_enabled'; hidden?: 'hidden' | 'all' | 'not_hidden'; @@ -101,11 +103,19 @@ export async function load_ae_obj_li__person({ let promise; - if (qry_user_id) { + if (qry_user_id || qry_email) { const search_query: any = { - and: [{ field: 'user_id_random', op: 'eq', value: qry_user_id }] + and: [] }; + if (qry_user_id) { + search_query.and.push({ field: 'user_id_random', op: 'eq', value: qry_user_id }); + } + + if (qry_email) { + search_query.and.push({ field: 'primary_email', op: 'eq', value: qry_email }); + } + if (for_obj_id) { search_query.and.push({ field: `${for_obj_type}_id_random`, @@ -352,7 +362,18 @@ const properties_to_save = [ 'contact_id', 'contact_id_random', 'contact_name', - 'contact_email' + 'contact_email', + 'contact_cc_email', + 'contact_phone_mobile', + 'contact_phone_home', + 'contact_phone_office', + 'contact_phone_land', + 'contact_phone_fax', + 'contact_phone_other', + 'address_id', + 'address_id_random', + 'address_city', + 'address_country_alpha_2_code' ]; async function _process_generic_props>({ diff --git a/src/lib/ae_core/ae_core__user.ts b/src/lib/ae_core/ae_core__user.ts index d4c2d6b0..bf17061d 100644 --- a/src/lib/ae_core/ae_core__user.ts +++ b/src/lib/ae_core/ae_core__user.ts @@ -253,6 +253,322 @@ export async function delete_ae_obj_id__user({ return result; } +/* + * *** LEGACY AUTHENTICATION HEADER LOGIC *** + * + * The functions in this section interact with legacy Aether API authentication endpoints + * (e.g., /user/authenticate, /user/lookup_email). + * + * Unlike V3 endpoints which handle context automatically or via standard headers, + * these legacy endpoints have specific requirements: + * + * 1. They often require the `x-account-id` header to be explicitly set to the target + * account ID to find the user within that specific account context. + * 2. The standard API wrapper logic might strip `x-account-id` if `x-no-account-id` + * is present (Bootstrap Paradox logic). We must explicitly remove `x-no-account-id` + * and set `x-account-id` to ensure the request is routed correctly. + * 3. Some endpoints accept `account_id` as a query parameter, while others (like email sending) + * may crash (500 Error) if unexpected parameters are passed. + */ + +// Updated 2025-04-04 +// This function handles username/password authentication. +// It explicitly sets the x-account-id header to ensure the user is looked up in the correct account. +export async function auth_ae_obj__username_password({ + api_cfg, + account_id, + null_account_id = false, + username, + password, + params = {}, + try_cache = true, + log_lvl = 0 +}: { + api_cfg: any; + account_id: string; + null_account_id?: boolean; + username: string; + password: string; + params?: key_val; + try_cache?: boolean; + log_lvl?: number; +}) { + if (log_lvl) { + console.log( + `*** auth_ae_obj__username_password() *** account_id=${account_id} username=${username} password=${password}` + ); + } + + const endpoint = '/user/authenticate'; + + // Prepare API config with correct headers to override global guest settings + const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; + if (account_id) { + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; + params['account_id'] = account_id; + } + + if (null_account_id) { + params['null_account_id'] = true; + } + params['username'] = username; // Required + params['password'] = password; // Required + params['inc_jwt'] = true; // Request a JWT in the response + if (log_lvl > 1) { + console.log(`auth_ae_obj__username_password() - params:`, params); + } + + ae_promises.auth__username_password = await api + .get_object({ + api_cfg: use_api_cfg, + endpoint: endpoint, + params: params, + // data: {}, + log_lvl: log_lvl + }) + .then(async function (user_obj_get_result) { + if (user_obj_get_result) { + return user_obj_get_result; + } else { + console.log('No results returned.'); + return null; + } + }) + .catch(function (error: any) { + console.log('No results returned or failed.', error); + }); + + if (log_lvl) { + console.log('ae_promises.auth__username_password:', ae_promises.auth__username_password); + } + return ae_promises.auth__username_password; +} + +// Updated 2025-04-04 +// This function handles authentication using a User ID and a one-time auth key. +export async function auth_ae_obj__user_id_user_auth_key({ + api_cfg, + account_id, + user_id, + user_auth_key, + params = {}, + try_cache = true, + log_lvl = 0 +}: { + api_cfg: any; + account_id: string; + user_id: string; + user_auth_key: string; + params?: key_val; + try_cache?: boolean; + log_lvl?: number; +}) { + if (log_lvl) { + console.log( + `*** auth_ae_obj__user_id_user_auth_key() *** account_id=${account_id} user_id=${user_id}` + ); + } + + const endpoint = '/user/authenticate'; + + // Prepare API config with correct headers to override global guest settings + const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; + if (account_id) { + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; + params['account_id'] = account_id; + } + + params['user_id'] = user_id; // Required + params['auth_key'] = user_auth_key; // Required + params['inc_jwt'] = true; // Request a JWT in the response + if (log_lvl > 1) { + console.log(`auth_ae_obj__user_id_user_auth_key() - params:`, params); + } + + ae_promises.auth__user_id_user_key = await api + .get_object({ + api_cfg: use_api_cfg, + endpoint: endpoint, + params: params, + log_lvl: log_lvl + }) + .then(async function (user_obj_get_result) { + if (user_obj_get_result) { + return user_obj_get_result; + } else { + console.log('No results returned.'); + return null; + } + }) + .catch(function (error: any) { + console.log('No results returned or failed.', error); + }); + + if (log_lvl) { + console.log('ae_promises.auth__user_id_user_key:', ae_promises.auth__user_id_user_key); + } + return ae_promises.auth__user_id_user_key; +} + +// Send an email to the user with a new one time use authentication key. +// Updated 2025-04-08 +// NOTE: This legacy endpoint is sensitive to extra query parameters and will 500 if account_id is passed in the URL. +export async function send_email_auth_ae_obj__user_id({ + api_cfg, + account_id, + user_id, + base_url, + key_param_name = 'user_key', // API defaults to 'auth_key' + params = {}, + log_lvl = 0 +}: { + api_cfg: any; + account_id: string; + user_id: string; + base_url?: string; + key_param_name?: string; + params?: key_val; + log_lvl?: number; +}) { + if (log_lvl) { + console.log( + `*** send_email_auth_ae_obj__user_id() *** account_id=${account_id} user_id=${user_id}` + ); + } + + const email_auth_key_endpoint = `/user/${user_id}/email_auth_key_url`; + params = { + root_url: base_url, + key_param_name: key_param_name + }; + + // Prepare API config with correct headers + const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; + if (account_id) { + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; + // WARNING: Do NOT add account_id to params here, as it causes a 500 error on the legacy backend. + } + + ae_promises.auth_key__send_email = await api.get_object({ + api_cfg: use_api_cfg, + endpoint: email_auth_key_endpoint, + params: params, + log_lvl: log_lvl + }); + + return ae_promises.auth_key__send_email; +} + +// Look up user based on email address provided +// Updated 2025-04-08 +export async function qry_ae_obj_li__user_email({ + api_cfg, + account_id, + null_account_id = false, + email, + params = {}, + try_cache = true, + log_lvl = 0 +}: { + api_cfg: any; + account_id: string; + null_account_id?: boolean; + email: string; + params?: key_val; + try_cache?: boolean; + log_lvl?: number; +}) { + if (log_lvl) { + console.log(`*** qry_ae_obj_li__user_email() *** account_id=${account_id} email=${email}`); + } + + const endpoint = '/user/lookup_email'; + + // Prepare API config with correct headers + const use_api_cfg = { ...api_cfg, headers: { ...api_cfg.headers } }; + if (account_id) { + use_api_cfg.headers['x-account-id'] = account_id; + delete use_api_cfg.headers['x-no-account-id']; + params['account_id'] = account_id; + } + + params['email'] = email; // Required + params['null_account_id'] = null_account_id || false; + + ae_promises.qry__user_email = await api + .get_object({ + api_cfg: use_api_cfg, + endpoint: endpoint, + params: params, + log_lvl: log_lvl + }) + .then(async function (user_obj_get_result) { + if (user_obj_get_result) { + return user_obj_get_result; + } else { + console.log('No results returned.'); + return null; + } + }) + .catch(function (error: any) { + console.log('No results returned or failed.', error); + }); + + return ae_promises.qry__user_email; +} + +// Change user password +// Updated 2025-04-11 +export async function auth_ae_obj__user_id_change_password({ + api_cfg, + account_id, + user_id, + password, + params = {}, + log_lvl = 0 +}: { + api_cfg: any; + account_id: string; + user_id: string; + password: string; + params?: key_val; + log_lvl?: number; +}) { + if (log_lvl) { + console.log( + `*** auth_ae_obj__user_id_change_password() *** account_id=${account_id} user_id=${user_id}` + ); + } + + const endpoint = `/user/${user_id}/change_password`; + + params['user_id'] = user_id; // Required + ae_promises.change_password__user_id = await api + .patch_object({ + api_cfg: api_cfg, + endpoint: endpoint, + params: params, + data: { password: password }, + log_lvl: log_lvl + }) + .then(async function (change_password_result) { + if (change_password_result) { + return change_password_result; + } else { + console.log('No results returned.'); + return null; + } + }) + .catch(function (error: any) { + console.log('No results returned or failed.', error); + }); + + return ae_promises.change_password__user_id; +} + const properties_to_save = [ 'id', 'user_id', diff --git a/src/lib/ae_core/ae_core_functions.ts b/src/lib/ae_core/ae_core_functions.ts index 63e0e70d..571534c8 100644 --- a/src/lib/ae_core/ae_core_functions.ts +++ b/src/lib/ae_core/ae_core_functions.ts @@ -7,8 +7,7 @@ import { load_ae_obj_li__activity_log, create_ae_obj__activity_log, update_ae_obj__activity_log - // db_save_ae_obj_li__activity_log -} from '$lib/ae_core/core__activity_log'; +} from '$lib/ae_core/ae_core__activity_log'; import { load_ae_obj_id__person, @@ -16,7 +15,6 @@ import { create_ae_obj__person, delete_ae_obj_id__person, update_ae_obj__person - // db_save_ae_obj_li__person } from '$lib/ae_core/ae_core__person'; import { @@ -25,12 +23,7 @@ import { send_email_auth_ae_obj__user_id, qry_ae_obj_li__user_email, auth_ae_obj__user_id_change_password - // handle_load_ae_obj_id__user, - // handle_load_ae_obj_li__user, - // handle_create_ae_obj__user, - // handle_update_ae_obj__user, - // handle_db_save_ae_obj_li__user -} from '$lib/ae_core/core__user'; +} from '$lib/ae_core/ae_core__user'; import { generate_qr_code, js_generate_qr_code } from '$lib/ae_core/core__qr_code'; diff --git a/src/lib/ae_core/core__account.ts b/src/lib/ae_core/core__account.ts.legacy similarity index 100% rename from src/lib/ae_core/core__account.ts rename to src/lib/ae_core/core__account.ts.legacy diff --git a/src/lib/ae_core/core__crud_generic.ts b/src/lib/ae_core/core__crud_generic.ts index 533dceab..f5846157 100644 --- a/src/lib/ae_core/core__crud_generic.ts +++ b/src/lib/ae_core/core__crud_generic.ts @@ -189,7 +189,7 @@ import { load_ae_obj_id__journal } from '$lib/ae_journals/ae_journals__journal'; import { load_ae_obj_id__journal_entry } from '$lib/ae_journals/ae_journals__journal_entry'; import { load_ae_obj_id__post } from '$lib/ae_posts/ae_posts__post'; import { load_ae_obj_id__post_comment } from '$lib/ae_posts/ae_posts__post_comment'; -import { load_ae_obj_id__person } from '$lib/ae_core/core__person'; +import { load_ae_obj_id__person } from '$lib/ae_core/ae_core__person'; export async function update_ae_obj_id_crud_v2({ api_cfg, diff --git a/src/lib/ae_core/core__person.ts b/src/lib/ae_core/core__person.ts.legacy similarity index 100% rename from src/lib/ae_core/core__person.ts rename to src/lib/ae_core/core__person.ts.legacy diff --git a/src/lib/ae_core/core__site.ts b/src/lib/ae_core/core__site.ts.legacy similarity index 100% rename from src/lib/ae_core/core__site.ts rename to src/lib/ae_core/core__site.ts.legacy diff --git a/src/lib/ae_core/core__user.ts b/src/lib/ae_core/core__user.ts.legacy similarity index 100% rename from src/lib/ae_core/core__user.ts rename to src/lib/ae_core/core__user.ts.legacy diff --git a/src/routes/core/activity_logs/+page.svelte b/src/routes/core/activity_logs/+page.svelte index 7961adc8..d42c3c7a 100644 --- a/src/routes/core/activity_logs/+page.svelte +++ b/src/routes/core/activity_logs/+page.svelte @@ -1,7 +1,7 @@