feat(idaa): enforce mandatory Novi UUID linkage for member content
CRITICAL IDENTITY FIX: Ensures all member-generated content (Meetings, Posts, Comments) is explicitly linked to the creator's Novi UUID via 'external_person_id' at the moment of creation. Changes: - Added 'external_person_id' to creation payloads in Recovery Meetings and BB Posts. - Implemented 'identity scavenging' from localStorage in submit handlers to prevent race conditions where Svelte stores are briefly null. - Refactored Post Comment edit component to robustly initialize and save creator identity. - Added 'The Novi UUID Rule' to IDAA documentation to mandate this pattern for future development. - Added Playwright test to verify creation linkage and fixed a version-mismatch bug in the test auth helper. Note: Archives and Archive Content are excluded as they do not require member ownership.
This commit is contained in:
@@ -43,9 +43,13 @@ let prom_api__post_comment_obj: any = $state();
|
||||
let post_comment_form = $state({
|
||||
content: $idaa_slct.post_comment_obj?.content ?? '',
|
||||
anonymous: $idaa_slct.post_comment_obj?.anonymous ?? false,
|
||||
external_person_id: $idaa_slct.post_comment_obj?.external_person_id ?? '',
|
||||
full_name: $idaa_slct.post_comment_obj?.full_name ?? '',
|
||||
email: $idaa_slct.post_comment_obj?.email ?? '',
|
||||
external_person_id:
|
||||
$idaa_slct.post_comment_obj?.external_person_id ||
|
||||
$idaa_loc.novi_uuid ||
|
||||
'',
|
||||
full_name:
|
||||
$idaa_slct.post_comment_obj?.full_name || $idaa_loc.novi_full_name || '',
|
||||
email: $idaa_slct.post_comment_obj?.email || $idaa_loc.novi_email || '',
|
||||
enable: $idaa_slct.post_comment_obj?.enable ?? true,
|
||||
hide: $idaa_slct.post_comment_obj?.hide ?? false,
|
||||
priority: $idaa_slct.post_comment_obj?.priority ?? false,
|
||||
@@ -94,10 +98,29 @@ async function handle_submit_form(event: any) {
|
||||
// SVELTE 5: Map values from local form state to payload
|
||||
post_comment_do['content'] = content_new_html;
|
||||
post_comment_do['anonymous'] = post_comment_form.anonymous;
|
||||
post_comment_do['external_person_id'] =
|
||||
post_comment_form.external_person_id;
|
||||
post_comment_do['full_name'] = post_comment_form.full_name;
|
||||
post_comment_do['email'] = post_comment_form.email;
|
||||
|
||||
// Robust scavenging of identity from storage if form state is missing it
|
||||
let novi_uuid = post_comment_form.external_person_id;
|
||||
let novi_full_name = post_comment_form.full_name;
|
||||
let novi_email = post_comment_form.email;
|
||||
|
||||
if (!novi_uuid && typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
const ls_val = localStorage.getItem('ae_idaa_loc');
|
||||
if (ls_val) {
|
||||
const ls_json = JSON.parse(ls_val);
|
||||
novi_uuid = ls_json.novi_uuid;
|
||||
novi_full_name = ls_json.novi_full_name;
|
||||
novi_email = ls_json.novi_email;
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
post_comment_do['external_person_id'] = novi_uuid;
|
||||
post_comment_do['full_name'] = novi_full_name;
|
||||
post_comment_do['email'] = novi_email;
|
||||
post_comment_do['notify'] = post_comment_di.notify ?? true; // Default to true if not found
|
||||
|
||||
post_comment_do['hide'] = post_comment_form.hide;
|
||||
|
||||
@@ -891,7 +891,7 @@ $effect(() => {
|
||||
name="external_person_id"
|
||||
value={$idaa_slct.post_obj?.external_person_id
|
||||
? $idaa_slct.post_obj?.external_person_id
|
||||
: ''}
|
||||
: ($idaa_loc.novi_uuid ?? '')}
|
||||
readonly={!$ae_loc.administrator_access}
|
||||
class="input preset-tonal-surface hover:preset-filled-surface-100-900 form-control w-96" />
|
||||
{/if}
|
||||
@@ -918,7 +918,7 @@ $effect(() => {
|
||||
name="full_name"
|
||||
value={$idaa_slct.post_obj?.full_name
|
||||
? $idaa_slct?.post_obj.full_name
|
||||
: $idaa_loc.novi_full_name}
|
||||
: ($idaa_loc.novi_full_name ?? '')}
|
||||
readonly={!$ae_loc.trusted_access}
|
||||
class="input preset-tonal-surface hover:preset-filled-surface-100-900 form-control w-96" />
|
||||
</label>
|
||||
|
||||
@@ -183,11 +183,30 @@ let creating = $state(false); // true while create API call is in-flight
|
||||
creating = true;
|
||||
$idaa_slct.post_obj = {};
|
||||
|
||||
// Robust scavenging of identity from storage if store is null
|
||||
let novi_uuid = $idaa_loc.novi_uuid;
|
||||
let novi_full_name = $idaa_loc.novi_full_name;
|
||||
let novi_email = $idaa_loc.novi_email;
|
||||
|
||||
if (!novi_uuid && typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
const ls_val = localStorage.getItem('ae_idaa_loc');
|
||||
if (ls_val) {
|
||||
const ls_json = JSON.parse(ls_val);
|
||||
novi_uuid = ls_json.novi_uuid;
|
||||
novi_full_name = ls_json.novi_full_name;
|
||||
novi_email = ls_json.novi_email;
|
||||
}
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
let data_kv = {
|
||||
external_person_id: $idaa_loc.novi_uuid,
|
||||
external_person_id: novi_uuid,
|
||||
title: '',
|
||||
full_name: $idaa_loc.novi_full_name,
|
||||
email: $idaa_loc.novi_email,
|
||||
full_name: novi_full_name,
|
||||
email: novi_email,
|
||||
enable: true
|
||||
};
|
||||
if (log_lvl) {
|
||||
|
||||
@@ -446,8 +446,22 @@ function prevent_default<T extends Event>(fn: (event: T) => void) {
|
||||
$idaa_slct.event_id = null;
|
||||
$idaa_slct.event_obj = {};
|
||||
|
||||
let novi_uuid = $idaa_loc.novi_uuid;
|
||||
if (!novi_uuid && typeof localStorage !== 'undefined') {
|
||||
try {
|
||||
const ls_val = localStorage.getItem('ae_idaa_loc');
|
||||
if (ls_val) {
|
||||
const ls_json = JSON.parse(ls_val);
|
||||
novi_uuid = ls_json.novi_uuid;
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore storage errors
|
||||
}
|
||||
}
|
||||
|
||||
let data_kv = {
|
||||
name: 'Change NEW Recovery Meeting Name'
|
||||
name: 'Change NEW Recovery Meeting Name',
|
||||
external_person_id: novi_uuid
|
||||
};
|
||||
if (log_lvl) {
|
||||
console.log(
|
||||
|
||||
Reference in New Issue
Block a user