15 Commits

Author SHA1 Message Date
Scott Idem
66310adb22 More to do things. 2026-04-19 19:32:43 -04:00
Scott Idem
b94516ce26 fix(idaa): purge IDB when has_cached_session but $ae_loc has no auth
Closes a gap where $ae_loc could be reset externally (sign-out) while
$idaa_loc retained novi_verified within TTL, causing Case 2 to return
early and skip the IDB purge even though the render gate shows Access Denied.
Now Case 2 only preserves the session when $ae_loc also reflects active auth;
inconsistent state falls through to Case 1 (purge).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:53:26 -04:00
Scott Idem
b8e6bcaf03 fix(idaa): strip API calls from all +page.ts/+layout.ts, gate loading in $effect
SvelteKit load functions fire during link prefetch before Novi auth completes;
`if (browser)` guards do not prevent this. Moving all IDAA data fetching into
$effect hooks gated on `novi_verified || trusted_access` closes the IDB
pre-population race across archives, bb/[post_id], and recovery_meetings/[event_id].

Also documents the Auth-Before-Cache rule and per-route status in
AE__Permissions_and_Security.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:49:47 -04:00
Scott Idem
dea599bd9c fix(idaa): move Recovery Meetings load out of +layout.ts, gate $effect on auth
+layout.ts was firing on SvelteKit link prefetch, writing events to IDB
before Novi auth ran. Stripped to thin shell; the existing search $effect
in +page.svelte already handles SWR load+revalidation — just needed an
auth gate (novi_verified || trusted_access) at the top.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:15:41 -04:00
Scott Idem
4d5081582f fix(idaa): exempt trusted_access users from IDB purge and BB load gate
Case 1 purge in the layout was firing for manager/trusted users (no UUID),
causing a loop: db_events.event cleared → liveQuery updates → refetch →
store write → Effect 2 re-runs → clear again.

BB $effect was also blocking managers since novi_verified is always false
for non-Novi auth paths.

Both now check trusted_access before gating/purging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:12:02 -04:00
Scott Idem
1381b81bf0 fix(idaa): move BB post loading from +page.ts to $effect in +page.svelte
+page.ts runs before layout effects and fires on SvelteKit link prefetch,
causing private IDAA posts to be written to IDB before Novi auth runs.
Moving to $effect gated on novi_verified eliminates the race entirely —
$effect only runs post-mount, after the layout has verified the user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 18:06:04 -04:00
Scott Idem
686b289bdb fix(idaa): gate BB +page.ts fetch on novi_verified
Without this, +page.ts fires the API call before +layout.svelte
effects run, causing posts to be written to IDB after the purge.
Anonymous users (novi_verified=false) now return early with no fetch.
Cached verified sessions (within TTL) continue to load normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:48:03 -04:00
Scott Idem
6d8f767e45 fix(idaa): add console logs to all IDAA IDB purge paths
Three distinct log messages for each trigger:
- No UUID / no session path
- Novi auth failure (catch block)
- Reset & Retry button

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:44:49 -04:00
Scott Idem
61c9a6766d fix(idaa): purge IDAA IDB on no-UUID unauthenticated path
The previous purge only fired inside verify_novi_uuid() catch,
which requires a UUID in the URL. Unauthenticated visits without
a UUID (Case 1 in Effect 2) now also clear posts, comments,
archives, and events from IDB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:43:22 -04:00
Scott Idem
ff4295b24c fix(idaa): also purge db_events on Novi auth failure
Extends the IDB purge from the previous commit to include
db_events.event — covers cached IDAA recovery meeting records.
No module overlap in current client deployments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:37:50 -04:00
Scott Idem
9d8c0e5dd4 Updated to do list for bug fixes related to IDAA. And possibly other areas. 2026-04-19 17:27:28 -04:00
Scott Idem
236a5513ee fix(idaa): purge posts and archives IDB on Novi auth failure
When Novi UUID verification fails (or the manual Reset & Retry is
triggered), clear db_posts.post, db_posts.comment, db_archives.archive,
and db_archives.content from IndexedDB. Prevents private IDAA data
from persisting in the browser after a session ends or auth is denied.

db_events.event intentionally excluded — shared with conference modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 17:26:34 -04:00
Scott Idem
868f4b3390 Updated he directory path for general agents trash. 2026-04-19 16:55:10 -04:00
Scott Idem
aebbcf5b47 docs: add AI agent bootstrap / quickstart document
Concise onboarding doc covering: project overview, critical rules (IDAA
privacy, no-rm, svelte-check), env/deploy cheat sheet, Svelte 5 runes
patterns, V3 API patterns, naming conventions, real past mistakes, source
layout, and reading order for deeper dives.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:52:25 -04:00
Scott Idem
9baffc4407 chore(devops): clean up TODO and remove dead package.json scripts
- TODO: mark BGH file-warning and hide-draft items complete; add detailed
  Dockerfile env-file simplification task (deferred post-April 21 show);
  strip stale completed DevOps entries from the active list
- package.json: remove build:docker:test/prod (never used locally; deploys
  go through remote deploy.sh on Linode)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 16:35:31 -04:00
20 changed files with 597 additions and 268 deletions

View File

@@ -31,7 +31,7 @@
1. **Before starting:** Read `documentation/TODO__Agents.md` for active tasks
2. **Before committing:** Run `npx svelte-check` — no exceptions
3. **Commits:** Atomic — one component or fix per commit
4. **Never delete files with `rm`** — move to `~/tmp/gemini_trash`
4. **Never delete files with `rm`** — move to `~/tmp/agents_trash`
5. **Backend coordination:** Use `ae_send_message` or flag changes clearly
---

View File

@@ -113,6 +113,37 @@ Returns `1` if `level_a` is higher, `-1` if lower, `0` if equal. Useful for thre
- IDAA users authenticate via Novi UUID at `authenticated` level or higher.
- A prior agent accidentally exposed IDAA BB data publicly — treat any IDAA exposure as Sev-1.
#### IDAA IndexedDB (IDB) Caching — Auth-Before-Cache Rule
**Root cause discovered 2026-04:** SvelteKit `+page.ts`/`+layout.ts` load functions run *before* layout `$effect` hooks and fire during link prefetch (hover). `if (browser)` guards do NOT prevent this — they only prevent SSR. This means API calls inside these files execute before Novi auth completes, writing private IDAA data to the user's IndexedDB even for unauthenticated sessions.
**The fix — established pattern for all IDAA routes:**
1. **Load/layout `.ts` files = thin shells.** Pass URL params only. No API calls. No `if (browser)` data fetching.
2. **Data loading = `$effect` in `.svelte` files**, gated on:
```svelte
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
```
3. **Three IDB purge paths** in `(idaa)/+layout.svelte` (auth failure, anonymous no-UUID, Reset & Retry button) clear `db_posts`, `db_archives`, and `db_events` tables.
**Auth path matrix:**
| User type | `novi_verified` | `trusted_access` | Can load data? | Purge fires? |
| --- | --- | --- | --- | --- |
| Anonymous / unauthenticated | false | false | No | Yes (Case 1) |
| Novi-verified IDAA member | true | false | Yes | No |
| Manager / trusted access | false | true | Yes | No (Case 3 exemption) |
**Applied to routes (as of 2026-04-19):**
- `idaa/bb/+page.svelte` — `$effect` gate added; `bb/+page.ts` stripped
- `idaa/bb/[post_id]/+page.ts` — stripped; loading handled by trigger in `bb/+layout.svelte`
- `idaa/archives/+page.svelte` — `$effect` gate added; `archives/+layout.ts` stripped
- `idaa/archives/[archive_id]/+page.svelte` — `$effect` gate added; `[archive_id]/+page.ts` stripped
- `idaa/recovery_meetings/+page.svelte` — `$effect` gate already present; `+layout.ts` stripped
- `idaa/recovery_meetings/[event_id]/+page.svelte` — `$effect` gate added; `+page.ts` stripped
**When adding a new IDAA route:** never put API calls in `+page.ts`/`+layout.ts`. Always gate data fetching with the `$effect` pattern above.
### Journals
- Private personal data. Always authenticated. Passcode/encryption features exist.
- Never expose journal content publicly.

View File

@@ -0,0 +1,300 @@
# Aether SvelteKit — AI Agent Bootstrap / Quickstart
> **Read this first.** This doc is the fast path to being productive on this project.
> It covers the rules, patterns, and gotchas that matter most.
> Deep dives are in the linked docs at the bottom.
---
## 1. What This Project Is
**Aether** is an event management platform built by One Sky IT (Scott Idem).
This repo is the frontend: **Svelte 5 (runes mode) + SvelteKit v2**.
The backend is a separate repo (`aether_api_fastapi`) — a FastAPI + MariaDB app
running in Docker. The frontend talks to it exclusively via the V3 REST API.
**Key clients:**
- **Conference organizers** — Presentation Management (pres_mgmt), Launcher, Badges
- **Exhibitors** — Leads capture
- **IDAA** — International Doctors in Alcoholics Anonymous (private medical/recovery community)
**Stack at a glance:**
| Layer | Technology |
|---|---|
| Framework | Svelte 5 (runes mode) + SvelteKit v2 |
| Styling | Tailwind CSS v4 + Flowbite (Skeleton UI being phased out) |
| State | `$state`/`$derived` runes + Dexie.js IndexedDB (`liveQuery`) |
| Icons | Lucide (`@lucide/svelte`) |
| Editors | CodeMirror 6 (primary), Edra/TipTap (secondary) |
| Native | Electron app for onsite launcher (`src/lib/electron/electron_relay.ts`) |
| Backend | FastAPI + MariaDB, V3 API (`/v3/crud/`, `/v3/lookup/`) |
| Auth | Custom headers: `x-aether-api-key` + `x-account-id` (no Bearer tokens) |
---
## 2. Critical Rules — Read Before Touching Any Code
### Privacy (Sev-1 class failures if violated)
- **IDAA content is ALWAYS private.** All routes under `/idaa/` require authentication.
A previous AI agent accidentally made IDAA bulletin board data publicly accessible.
This is the single most serious class of mistake on this project. When in doubt — it's private.
- **Journals** are private personal data. Always authenticated.
### File Safety
- **Never use `rm`** to delete files. Move to `~/tmp/agents_trash` instead.
- Never commit `.env` files, API keys, or passwords.
### Before Every Commit
- Run `npx svelte-check` — zero errors, zero warnings. No exceptions.
- Atomic commits: one component or one fix per commit.
### Before Starting Any Task
- Read `documentation/TODO__Agents.md` — it has active tasks, known bugs, and context
about what was recently changed and why.
### V3 API — Never Include the Object ID in PATCH Body Fields
The ID is in the URL. Including it in `data_kv` causes a `400: Unknown column in SET`.
```ts
// WRONG — causes 400 error:
update_ae_obj__event_file({ event_file_id, data_kv: { event_file_id, file_purpose: 'final' } })
// CORRECT:
update_ae_obj__event_file({ event_file_id, data_kv: { file_purpose: 'final' } })
```
---
## 3. Environment & Deploy Cheat Sheet
There are **two separate `.env` systems** — do not confuse them:
| System | File | Controls |
|---|---|---|
| `aether_container_env/.env` | Docker orchestration | Ports, `AE_CFG_ID`, replicas, paths |
| `aether_app_sveltekit/.env.*` | Vite/SvelteKit build | `PUBLIC_*` API vars baked into the JS bundle |
**The 4 commands you run and which env file each uses:**
| Command | Env file read |
|---|---|
| `npm run dev` | `aether_app_sveltekit/.env.local` (Vite dev server, localhost:5173) |
| `npm run build:docker:dev` | `aether_app_sveltekit/.env.dev` (baked into local Docker image) |
| `npm run deploy:remote:test` | `/srv/apps/test_aether_app_sveltekit/.env.test` on Linode |
| `npm run deploy:remote:prod` | `/srv/apps/prod_aether_app_sveltekit/.env.prod` on Linode |
**The `.env.*` files are gitignored** (only `.default` templates are tracked). They must be
placed manually on each server during initial setup. On the workstation you only need
`.env.local` and `.env.dev`. The Linode servers each have exactly one env file for their environment.
**What goes in every SvelteKit env file** (same 8 vars, different values per env):
```env
PUBLIC_AE_API_PROTOCOL=https
PUBLIC_AE_API_SERVER=<api server hostname>
PUBLIC_AE_API_BAK_SERVER=<bak api hostname>
PUBLIC_AE_API_PORT=443
PUBLIC_AE_API_PATH=
PUBLIC_AE_API_SECRET_KEY=<key>
PUBLIC_AE_CRUD_SUPER_KEY=<key>
PUBLIC_AE_BOOTSTRAP_KEY=<key>
PUBLIC_AE_NO_ACCOUNT_ID=No_Account_ID_Here
```
---
## 4. Svelte 5 Runes Mode — Key Patterns & Gotchas
This codebase is **fully Svelte 5 runes mode**. No Svelte 4 syntax.
### The basics
```svelte
<script lang="ts">
// Props — with optional two-way binding
interface Props { count?: number; label: string; }
let { count = $bindable(0), label }: Props = $props();
// Reactive state
let value = $state('');
let upper = $derived(value.toUpperCase());
// Side effects (replaces onMount + $: reactive)
$effect(() => {
console.log('value changed:', value);
return () => { /* cleanup */ };
});
</script>
```
### What NOT to use (Svelte 4 patterns — do not introduce)
```ts
// ❌ No writable() stores for component state
import { writable } from 'svelte/store';
// ❌ No reactive declarations
$: doubled = count * 2;
// ❌ No onDestroy for cleanup — use $effect return instead
onDestroy(() => cleanup());
```
### `$bindable()` vs `$state()`
- Use `$bindable()` when the parent needs two-way binding on a prop.
- Use `$state()` for local component state with no external binding.
### Store reactivity trap (important for `$effect`)
The app uses `svelte-persisted-store` (Svelte 4 contract) for `$ae_loc`, `$ae_api`,
`$ae_sess`, etc. In Svelte 5 `$effect`, reading **any field** of a Svelte 4 store
subscribes to the **entire store**. This means unrelated writes to `$ae_loc`
(e.g. iframe height, SWR reload) will re-trigger your effect. Be conservative about
what you read from these stores inside `$effect` blocks. See `PROJECT__Stores_Svelte5_Migration.md`
for the long-term fix plan.
### `{#await}` blocks
```svelte
{#await somePromise}
<LoadingSpinner />
{:then result}
<div>{result}</div>
{:catch error}
<ErrorMessage {error} />
{/await}
```
---
## 5. V3 API Patterns
### SWR (Stale-While-Revalidate) — the standard load pattern
Return cached Dexie data immediately, refresh from API in background.
```ts
async function load_ae_obj_id__my_obj({ api_cfg, obj_id }) {
// 1. Return stale cache immediately (fast)
const cached = await db.my_obj.get(obj_id);
if (cached) my_obj_state = cached;
// 2. Fetch fresh from API in background
_refresh_my_obj_background({ api_cfg, obj_id });
}
```
### ID convention — never use `_id_random` fields
The V3 API uses random string IDs (e.g. `event_file_id = "aBc123"`). The `*_id_random`
fields are legacy aliases. Always use the short form:
```ts
// ✅ Correct
event_file_obj.event_file_id
// ❌ Wrong — legacy alias, don't use
event_file_obj.event_file_id_random
```
### PATCH — only field values in the body
```ts
// The obj_id goes in the URL (handled by update_ae_obj__* function).
// Only the fields you want to update go in data_kv.
await events_func.update_ae_obj__event_file({
api_cfg: $ae_api,
event_file_id: 'aBc123', // → becomes the URL path param
data_kv: { file_purpose: 'final' } // → only changed fields
});
```
### Auth headers (set automatically by `api.ts`)
```
x-aether-api-key: <PUBLIC_AE_API_SECRET_KEY>
x-account-id: <account_id>
```
---
## 6. Naming Conventions
| Pattern | Example | Used for |
|---|---|---|
| `ae_comp__*` | `ae_comp__event_badge.svelte` | Route-level components |
| `ae_<module>_comp__*` | `ae_events_comp__session_list.svelte` | Module-scoped components |
| `element_*` | `element_input_files_tbl.svelte` | Reusable library primitives |
| `lq__*` | `lq__journal_obj` | Read-only liveQuery |
| `lqw__*` | `lqw__journal_obj` | Writable form snapshot liveQuery |
| `ae_<module>__<obj>.ts` | `ae_journals__journal.ts` | Object type + functions |
| `db_<module>.ts` | `db_journals.ts` | Dexie instance per module |
The **canonical pattern reference** is the Journals module (`src/lib/ae_journals/`).
When building anything new, model it after Journals.
---
## 7. Mistakes Agents Have Made on This Project
These are real incidents — know them before you start.
1. **IDAA BB exposed publicly** — an agent removed an auth guard from the bulletin board
route. All IDAA content must be behind authentication. Always check route guards when
touching `/idaa/` routes.
2. **`event_file_id` in PATCH body (400 error)** — including the object ID in `data_kv`
when calling `update_ae_obj__*`. The V3 API tries to `SET event_file_id = ...` which
fails because it's a view alias, not a DB column. See Section 2 above.
3. **Bad `.d.ts` declaration silently hid 1368 errors** — a `declare module` in `app.d.ts`
(a script-context file) replaced the entire `@lucide/svelte` type exports instead of
merging. `svelte-check` showed 0 errors, masking real problems. If `svelte-check`
suddenly drops to 0 errors, verify it's not because a bad declaration wiped a module.
4. **Coarse store reactivity loop** — an `$effect` that read `$ae_loc.some_field` was
re-triggering repeatedly because unrelated writes to `$ae_loc` (e.g. SWR config reload)
fired the effect. In Svelte 5, any read of a Svelte 4 store inside `$effect` subscribes
to the whole store. Scope what you read carefully.
5. **`file_purpose == 'admin'` not hidden in Launcher** — the `hide_draft` prop hid
`outline` and `draft` files but not `admin` files. Gaps like this happen when a new
enum value is added to a field without auditing all the places that filter on it.
6. **Deleting files with `rm`** — always move to `~/tmp/agents_trash`. A deleted file may
contain context that's not recoverable from git if it was gitignored.
---
## 8. Source Layout (Quick Reference)
```
src/lib/
ae_api/ — API helpers (V3 preferred)
ae_core/ — Account, User, Person, Site, hosted files
ae_events/ — Events, sessions, presenters, badges, locations, files
ae_journals/ — Journals (canonical/frontier model — copy patterns from here)
ae_idaa/ — IDAA custom module (PRIVATE — always authenticated)
elements/ — Reusable UI: V3 field editor, data store, CodeMirror, QR scanner
electron/ — Native Electron bridge (electron_relay.ts)
stores/ — ae_stores.ts, ae_events_stores.ts, ae_idaa_stores.ts
src/routes/
/core/ — Admin (accounts, people, sites, users)
/events/[id]/
/(pres_mgmt)/ — Presentation management
/(launcher)/ — Event launcher (kiosk display)
/(badges)/ — Badge printing
/(leads)/ — Exhibitor leads
/journals/ — Journals
/idaa/ — IDAA module (PRIVATE)
/hosted_files/ — File management
```
---
## 9. Reading Order for Deeper Dives
Start here, then go deeper as needed:
| What you need | Read |
|---|---|
| Active tasks + known bugs | `documentation/TODO__Agents.md` ← always first |
| Dev workflow + commit rules | `documentation/GUIDE__Development.md` |
| V3 API reference | `documentation/GUIDE__AE_API_V3_for_Frontend.md` |
| Dexie / liveQuery patterns | `documentation/GUIDE__SvelteKit2_Svelte5_DexieJS.md` |
| Svelte 5 patterns + pitfalls | `documentation/GEMINI__Svelte_and_Me.md` |
| Permissions + auth levels | `documentation/AE__Permissions_and_Security.md` |
| Electron / native launcher | `documentation/PROJECT__AE_Events_Launcher_Native_integration.md` |
| Store migration plan | `documentation/PROJECT__Stores_Svelte5_Migration.md` |
| Exhibitor Leads module | `documentation/MODULE__AE_Events_Exhibitor_Leads.md` |
| Naming conventions | `documentation/AE__Naming_Conventions.md` |

View File

@@ -19,7 +19,7 @@
## 2. Commit Policy
- **Atomic Commits:** One component or one logic fix per commit. Do not batch unrelated changes.
- **Safety:** Use `~/tmp/gemini_trash` for file removal; never use `rm` directly on source files.
- **Safety:** Use `~/tmp/agents_trash` for file removal; never use `rm` directly on source files.
- **Secrets:** Never commit `.env`, API keys, or passwords.
## 3. Coordination (The Handshake)

View File

@@ -124,7 +124,7 @@ the MODULE doc TODO list was stale. `duplex` is in `properties_to_save`; v2 badg
- `ae_comp__badge_print_controls.svelte` — Identity card at top, pronouns moved to attendee section,
"Staff adjustments" divider before badge_type field.
- `print_list/+page.svelte` — Updated to import v2.
- `ae_comp__badge_obj_view.svelte` (v1) — **Moved to ~/tmp/gemini_trash/**
- `ae_comp__badge_obj_view.svelte` (v1) — **Moved to ~/tmp/agents_trash/**
**Kiosk UX improvements (2026-03-12):**
- Print page header: cleaner, shows name + "Ready"/"Printed N×" status chip, event name.

View File

@@ -9,16 +9,18 @@
`load_ae_obj_li__event_location` on page load. Also fixed session query using stale
`event_location_id_random` index (should be `event_location_id`). (2026-04-19)
- [ ] **[Files] Warn/error on `.ppt` upload** — if a presenter tries to upload a `.ppt` file (legacy
PowerPoint format), show a clear warning or block the upload with an error message instructing them
to use `.pptx` instead. Modern format required for processing.
- [x] **[Files] Warn/error on `.ppt`/`.doc` upload** — warning rows shown per-file in upload table;
non-trusted users are fully blocked (`file_list_status = 'blocked_legacy'`); trusted users see
warnings but can still upload. Covers `.ppt`, `.doc` (block) and other legacy exts (warn-only).
(2026-04-19)
- [ ] **[Files] Hide draft/flagged files from list** — files marked with an X flag (draft or
purpose-excluded) should not appear in the file listing. Filter them out before display.
- [x] **[Files] Hide draft/flagged files from list** — `hide_draft` prop in `launcher_file_cont.svelte`
now hides files with `file_purpose == 'outline'`, `'draft'`, or `'admin'` when the launcher's
hide-draft toggle is enabled. (2026-04-19)
- [ ] **[Electron/Launcher] Deploy updated Aether Native Electron app to Mac laptops** — double-check
the build and deployment process; verify the latest version is installed and functional on the
onsite Mac laptops before April 21.
- [ ] **[Electron/Launcher] Deploy + test Aether Native Electron app on Mac laptops** — build,
deploy, and verify on onsite Mac laptops. Additional testing of cache/launch flow still needed
before April 21.
- [x] **[Pres Mgmt] POC column shown in "Sessions at this Location"** — wired
`hide__session_poc={!pres_mgmt_loc.current.show__session_li_poc_field}` in
@@ -107,6 +109,23 @@ suddenly jumps to 0 errors, verify it's not because a bad `.d.ts` replaced a pac
Run `npx svelte-check 2>&1 | grep ModalProps` to get the current list.
Fix pattern: replace `children` prop binding with Svelte snippet syntax per flowbite-svelte docs.
- [ ] **[IDAA] Do not cache IDAA data in IDB when access is denied (2026-04-19)**
If a user is not authenticated or receives an access-denied response, the frontend must
**not** pre-fetch or cache any IDAA content (Posts, Archives, Events/Recovery Meetings)
into the local browser IndexedDB (Dexie). Storing private IDAA data in IDB on an
unauthenticated device is a privacy violation — the data would persist in the browser
even after the session ends, accessible to the next person who opens DevTools.
**Fix pattern:**
- All IDAA SWR load functions (`load_ae_obj_li__*` in `ae_idaa/`, `ae_posts/`, and the
IDAA-specific event queries) must gate on a successful auth check before calling
`_refresh_*_background` or writing to IDB.
- If the API returns a 401/403, do not write to Dexie — return/throw early.
- On explicit logout or Novi auth invalidation, purge IDAA tables from IDB
(`db_idaa`, `db_posts`, and any IDAA event records in `db_events`).
- Audit all `+page.ts` / `+layout.ts` files under `src/routes/idaa/` to confirm
no eager prefetch runs before the auth guard resolves.
- [ ] **[IDAA] Make `contact_li_json_ext` searchable — Recovery Meeting contact search (2026-04-08)**
Members cannot search for meetings by contact name or email. `contact_li_json` data is not
included in `default_qry_str` and MariaDB cannot substring-search a JSON longtext directly.
@@ -182,21 +201,50 @@ Once the global rule is in place, remove the scoped workaround from the badge co
### [Backend/DevOps] Re-add `Access-Control-Allow-Private-Network: true` CORS header
Chrome's Private Network Access (PNA) policy blocks public-origin iframes from fetching
private-network addresses. Symptom: when `dev-api.oneskyit.com` resolves to a LAN IP
(testing from home), Chrome blocks the site domain lookup → ghost account → `site_cfg_json`
never loads → `novi_idaa_api_key` is null → IDAA Novi verifier spins forever → timeout banner.
Firefox unaffected. Production unaffected (public IPs only).
- [ ] **Re-add PNA header to API CORS config**`dev-api` Nginx or FastAPI CORS middleware
must respond with `Access-Control-Allow-Private-Network: true` when Chrome sends
`Access-Control-Request-Private-Network: true` in the preflight. This was fixed ~1 month
ago and regressed. Check Nginx site config and FastAPI `CORSMiddleware` settings.
Low urgency (dev-only, Firefox workaround available), but blocks home-network iframe testing.
### [DevOps] Remaining deployment items
- [x] **Wire AE_APP_REPLICAS:** `docker-compose.yml` line 147 already has `scale: ${AE_APP_REPLICAS:-1}`. (verified 2026-03-11)
- [x] **Archive ae_env_node_app:** Archived as tar.gz under `~/OSIT_dev/backups/`; old history/docs moved to `~/OSIT_dev/for_reference_only/`. (2026-03-11)
- [x] **Build Optimization:** Current state finalized. Local Gitea instance stood up at `git.dgrzone.com` (Docker, home server) — future: migrate repos from Bitbucket, verify Backblaze/restic backups cover Gitea data. (2026-03-11)
- [x] **Remote deploy script:** `aether_container_env/deploy.sh` — SSH-triggered from workstation via `npm run deploy:remote:test/prod`. Handles git pull (ff-only) + docker build + restart. Tested and working on test env. (2026-03-25)
- [x] **`.env.default` cleanup:** Removed 16 dead variables, added missing `AE_NETWORK_NAME`/`CONTAINER_DOZZLE`/`AE_DOZZLE_PORT`, parameterized all container names (`CONTAINER_MARIADB`, `CONTAINER_PMA`, `CONTAINER_AE_OPS`) with `:-default` fallbacks in compose. ("Dozzle" = log viewer container.) (2026-03-26)
- [x] **Prod deploy:** Run `npm run deploy:remote:prod` (off-peak). Prerequisites: both repos pushed to Bitbucket ✓; verify `.env.prod` exists in `/srv/apps/prod_aether_app_sveltekit/` on Linode before running. (2026-03-30)
- [x] **Bitbucket → SSH migration:** Switched all three repos (`aether_app_sveltekit`, `aether_container_env`, `aether_api_fastapi`) to SSH remotes (`git@bitbucket.org`) on workstation. App passwords deprecated — SSH unaffected. (2026-03-27)
- [ ] **Branch strategy cleanup:** All environments (test, prod, bak) currently pull from same branches. `deploy.sh` defaults are `ae_app_3x_llm` / `development` — acceptable for now but should establish proper branch separation (e.g. `main`/`master` for prod).
- [ ] **Tier 2 deploy (Gitea webhook):** Push-triggered deploys via Gitea webhook → listener on Linode → `deploy.sh`. Deferred until Gitea usage is more established.
- [ ] **Simplify Dockerfile env file selection** — Currently the Dockerfile uses a `BUILD_MODE` arg to
select between `.env.dev`, `.env.test`, `.env.prod` during the Docker build. This is unnecessary
complexity: each server (test Linode, prod Linode, workstation) only ever runs one environment, so
there will only ever be one env file present in that server's app directory.
**The fix:** Each server's app dir (`/srv/apps/test_aether_app_sveltekit/`, etc.) should have a
plain `.env` file (gitignored, placed manually during server setup). The Dockerfile should just
`COPY . .` and `cp .env .env.runtime` unconditionally — no `if prod / elif test / else dev`
branching for env file selection.
**What this changes:**
- `aether_app_sveltekit/Dockerfile` — remove the `BUILD_MODE`-driven `cp` block; always use `.env`
- Each Linode app dir gets a plain `.env` instead of `.env.test` / `.env.prod`
- Workstation keeps `.env.local` (for `npm run dev`) and `.env.dev` (for `build:docker:dev`) —
those stay as-is since they legitimately coexist locally
- `BUILD_MODE` arg can stay if needed for other build differences; just stop using it to pick the env file
- Update `.gitignore` in sveltekit to un-ignore `.env.test` / remove stale entries if desired
**Do not touch before the April 21 show.** Low risk but unnecessary churn right before an event.
- [ ] **Branch strategy cleanup:** All environments (test, prod, bak) currently pull from the same
branches. `deploy.sh` defaults are `ae_app_3x_llm` / `development` — acceptable for now but
should establish proper branch separation (e.g. `main`/`master` for prod).
- [ ] **Tier 2 deploy (Gitea webhook):** Push-triggered deploys via Gitea webhook → listener on
Linode → `deploy.sh`. Deferred until Gitea usage is more established.
### [General]
- [x] **Temp Cleanup:** `cleanup_tmp_files` wired in `launcher_background_sync.svelte`; called at launcher startup. Confirmed working. (2026-03-11)
- [x] **`window.print()` for badge print button:** Wired in `ae_comp__badge_print_controls.svelte` — increments count, fires `window.print()`, redirects to badge search. (done)
- **Input Field Audit:** Several input fields are missing `name`/`id` attributes or `data-testid`. Known examples: badge override fields in `ae_comp__badge_obj_view.svelte`; template name input in `ae_comp__badge_template_form.svelte`. Matters for: accessibility, autofill, label associations, and test targeting. (For tests, use `getByLabel()` rather than `input[value*=...]` which only checks the HTML attribute, not the Svelte-bound DOM property.)
## ✅ Completed (2026-04)

View File

@@ -108,7 +108,7 @@ suddenly jumps to 0 errors, verify it's not because a bad `.d.ts` replaced a pac
- [x] **[package.json] Remove orphaned ShadCN/bits-ui packages.** `shadcn-svelte` and `bits-ui`
remain in `package.json` but have no usages — `src/lib/components/ui/` was removed 2026-03-27
(trashed to `~/tmp/gemini_trash/shadcn_components_ui_2026-03-27`). Removed from `package.json` and
(trashed to `~/tmp/agents_trash/shadcn_components_ui_2026-03-27`). Removed from `package.json` and
`package-lock.json` on 2026-04-02.
### [IDAA] Jitsi config editor + live site fix

View File

@@ -19,8 +19,6 @@
"test:integration": "playwright test",
"test:unit": "vitest",
"build:docker:dev": "docker compose -f ../aether_container_env/docker-compose.yml build ae_app && docker compose -f ../aether_container_env/docker-compose.yml up -d ae_app",
"build:docker:test": "docker compose -f ../aether_container_env/docker-compose.yml build --build-arg BUILD_MODE=test ae_app && docker compose -f ../aether_container_env/docker-compose.yml up -d ae_app",
"build:docker:prod": "docker compose -f ../aether_container_env/docker-compose.yml build --build-arg BUILD_MODE=prod ae_app && docker compose -f ../aether_container_env/docker-compose.yml up -d --remove-orphans ae_app",
"compose:down": "docker compose -f ../aether_container_env/docker-compose.yml --profile database down",
"deploy:remote:test": "ssh linode.oneskyit.com 'bash /srv/env/test_aether/deploy.sh test'",
"deploy:remote:prod": "ssh linode.oneskyit.com 'bash /srv/env/prod_aether/deploy.sh prod'"

View File

@@ -18,6 +18,9 @@ import {
slct_trigger
} from '$lib/stores/ae_stores';
import { idaa_loc, idaa_sess, idaa_slct } from '$lib/stores/ae_idaa_stores';
import { db_posts } from '$lib/ae_posts/db_posts';
import { db_archives } from '$lib/ae_archives/db_archives';
import { db_events } from '$lib/ae_events/db_events';
interface Props {
/** @type {import('./$types').LayoutData} */
@@ -142,12 +145,33 @@ $effect(() => {
now - $idaa_loc.novi_verified_ts < ttl_ms;
if (has_cached_session) {
// Case 2: internal navigation — keep the verified session, nothing to do.
// BUT: only if $ae_loc also reflects active auth. If $ae_loc was reset
// externally (e.g., sign-out) while $idaa_loc retained novi_verified (within TTL),
// the state is inconsistent — fall through to Case 1 and purge.
if ($ae_loc.trusted_access || $ae_loc.authenticated_access) {
novi_verifying = false;
return;
}
// Inconsistent state — $idaa_loc says verified but $ae_loc has no auth.
// Fall through to Case 1 below to purge stale IDB data.
console.warn('IDAA Layout: has_cached_session but no auth in $ae_loc — purging (inconsistent state).');
}
// Case 3: no UUID, no cached Novi session, but user has trusted/manager access.
// They are legitimately authenticated via a non-Novi path — do NOT purge.
if ($ae_loc.trusted_access) {
novi_verifying = false;
return;
}
// Case 1: no UUID, no cached session non-Novi path, deny normally.
// Case 1: no UUID, no cached session, no elevated access — anonymous user.
// Purge any IDAA data that may have been cached from a previous session.
$idaa_loc.novi_verified = false;
novi_verifying = false;
console.log('IDAA Layout: No UUID / no session — purging IDAA IDB tables (posts, archives, events).');
db_posts.post.clear().catch(() => {});
db_posts.comment.clear().catch(() => {});
db_archives.archive.clear().catch(() => {});
db_archives.content.clear().catch(() => {});
db_events.event.clear().catch(() => {});
return;
}
@@ -322,6 +346,13 @@ async function verify_novi_uuid(
$idaa_loc.novi_email = null;
$idaa_loc.novi_full_name = null;
$idaa_loc.novi_verified = false;
// Purge private IDAA data from IDB — do not leave sensitive data cached after auth failure.
console.log('IDAA Layout: Novi auth failed — purging IDAA IDB tables (posts, archives, events).');
db_posts.post.clear().catch(() => {});
db_posts.comment.clear().catch(() => {});
db_archives.archive.clear().catch(() => {});
db_archives.content.clear().catch(() => {});
db_events.event.clear().catch(() => {});
} finally {
verify_in_flight = false;
novi_verifying = false;
@@ -356,6 +387,12 @@ async function verify_novi_uuid(
onclick={() => {
localStorage.removeItem('ae_loc');
localStorage.removeItem('ae_idaa_loc');
console.log('IDAA Layout: Reset & Retry — purging IDAA IDB tables (posts, archives, events).');
db_posts.post.clear().catch(() => {});
db_posts.comment.clear().catch(() => {});
db_archives.archive.clear().catch(() => {});
db_archives.content.clear().catch(() => {});
db_events.event.clear().catch(() => {});
location.reload();
}}>
<span class="fas fa-redo m-1"></span>

View File

@@ -1,12 +1,10 @@
/** @type {import('./$types').LayoutLoad} */
// console.log(`IDAA BB - [account_id] +layout.ts start`);
// import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { archives_func } from '$lib/ae_archives/ae_archives_functions';
// Data loading for IDAA Archives has been moved to the $effect in +page.svelte
// (gated on novi_verified / trusted_access). +layout.ts runs before layout effects and
// fires during SvelteKit link prefetch, making it unsafe for private IDAA content.
export async function load({ fetch, params, parent }) {
// route
export async function load({ parent }) {
const log_lvl: number = 0;
const data = await parent();
@@ -21,40 +19,10 @@ export async function load({ fetch, params, parent }) {
);
ae_acct = {
api: data.ae_api || {},
slct: {
account_id: account_id
}
slct: { account_id: account_id }
};
}
if (browser) {
const load_archive_obj_li = archives_func.load_ae_obj_li__archive({
api_cfg: ae_acct.api,
for_obj_type: 'account',
for_obj_id: account_id,
inc_content_li: false,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 29,
order_by_li: {
priority: 'DESC',
sort: 'DESC',
updated_on: 'DESC',
created_on: 'DESC',
name: 'ASC'
},
params: params,
try_cache: true,
log_lvl: log_lvl
});
if (log_lvl) {
console.log(`load_archive_obj_li = `, load_archive_obj_li);
}
ae_acct.slct.archive_obj_li = load_archive_obj_li;
}
// WARNING: Precaution against shared data between sites and sessions.
data[account_id] = ae_acct;
return data;
}

View File

@@ -9,6 +9,7 @@ let log_lvl: number = $state(0);
// *** Import Svelte specific
// import { page } from '$app/state';
import { untrack } from 'svelte';
import { browser } from '$app/environment';
// import { goto, invalidate, pushState, replaceState } from '$app/navigation';
@@ -36,7 +37,7 @@ import {
idaa_slct,
idaa_trig
} from '$lib/stores/ae_idaa_stores';
// import { archives_func } from '$lib/ae_archives/ae_archives_functions';
import { archives_func } from '$lib/ae_archives/ae_archives_functions';
import Comp__archive_obj_li from './ae_idaa_comp__archive_obj_li.svelte';
import Help_tech from '$lib/app_components/e_app_help_tech.svelte';
@@ -68,6 +69,34 @@ let lq__archive_obj_li = $derived(
})
);
// Initial archive list load — gated on novi_verified.
// WHY $effect and not +layout.ts: layout load functions fire on SvelteKit link prefetch,
// causing private IDAA data to be written to IDB before Novi auth runs.
// $effect only runs post-mount, after the layout has completed Novi verification.
$effect(() => {
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
untrack(() => {
archives_func.load_ae_obj_li__archive({
api_cfg: $ae_api,
for_obj_type: 'account',
for_obj_id: $ae_loc.account_id,
inc_content_li: false,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 29,
order_by_li: {
priority: 'DESC',
sort: 'DESC',
updated_on: 'DESC',
created_on: 'DESC',
name: 'ASC'
},
try_cache: true,
log_lvl: log_lvl
});
});
});
if (browser) {
console.log('Browser environment detected.');

View File

@@ -9,7 +9,7 @@ let { data }: Props = $props();
let log_lvl: number = 0;
// *** Import Svelte specific
import { onMount, onDestroy } from 'svelte';
import { onMount, onDestroy, untrack } from 'svelte';
// *** Import other supporting libraries
import { Modal } from 'flowbite-svelte';
@@ -64,6 +64,33 @@ $effect(() => {
$idaa_slct.archive_id = page.params.archive_id;
// $idaa_slct.archive_obj = ae_acct.slct.archive_obj;
// Load single archive with content — gated on auth.
// WHY $effect and not +page.ts: +page.ts runs during SvelteKit link prefetch,
// causing private IDAA data to be written to IDB before Novi auth runs.
// $effect only runs post-mount, after the layout has completed Novi verification.
$effect(() => {
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
const archive_id = $idaa_slct.archive_id;
if (!archive_id) return;
untrack(() => {
archives_func.load_ae_obj_id__archive({
api_cfg: $ae_api,
archive_id: archive_id,
inc_content_li: true,
enabled: 'enabled',
hidden: 'all',
limit: 99,
log_lvl: log_lvl
}).then((results) => {
if (!results) {
console.warn(
`IDAA Archives [archive_id] $effect: Archive ${archive_id} not found via API or Cache.`
);
}
});
});
});
// *** Functions and Logic
let lq__archive_obj = $derived(
liveQuery(async () => {

View File

@@ -1,56 +1,30 @@
import type { PageLoad } from './$types';
console.log(`ae_p_idaa_archives [archive_id] +page.ts start`);
import { browser } from '$app/environment';
import { archives_func } from '$lib/ae_archives/ae_archives_functions';
// Data loading for IDAA Archives [archive_id] has been moved to the $effect in +page.svelte
// (gated on novi_verified / trusted_access). +page.ts runs before layout effects and
// fires during SvelteKit link prefetch, making it unsafe for private IDAA content.
export const load = (async ({ params, parent }) => {
// route
const log_lvl: number = 0;
const data = await parent();
data.log_lvl = log_lvl;
const account_id = data.account_id;
const ae_acct = data[account_id];
let ae_acct = data[account_id];
const archive_id = params.archive_id;
ae_acct.slct.archive_id = archive_id;
if (browser) {
if (log_lvl) {
console.log(
`ae_idaa_archives archives [archive_id] +page.ts: archive_id = `,
archive_id
);
}
// NOTE: Fire in background — do NOT await. Newly created archives are already in Dexie
// (saved by create_ae_obj__archive), so the liveQuery renders immediately. For direct
// links or refreshes, the archive loads from the API and Dexie updates reactively.
// Awaiting here blocked the SvelteKit navigation, causing a visible "refresh" delay.
archives_func
.load_ae_obj_id__archive({
api_cfg: ae_acct.api,
archive_id: archive_id,
inc_content_li: true,
enabled: 'enabled',
hidden: 'all', // 'not_hidden' to load only visible entries
limit: 99,
log_lvl: log_lvl
})
.then((results) => {
if (!results) {
console.warn(
`ae IDAA Archives [archive_id] +page.ts: Archive ${archive_id} not found via API or Cache.`
);
}
});
if (!ae_acct) {
console.warn(
`ae IDAA Archives [archive_id] +page.ts: Account ${account_id} not found in data. Initializing ghost acct.`
);
ae_acct = {
api: data.ae_api || {},
slct: { account_id: account_id }
};
}
// WARNING: Precaution against shared data between sites and presentations.
data[account_id] = ae_acct;
ae_acct.slct.archive_id = params.archive_id;
data[account_id] = ae_acct;
return data;
}) satisfies PageLoad;

View File

@@ -10,7 +10,7 @@ let log_lvl: number = $state(0);
// *** Import Svelte specific
import { page } from '$app/state';
import { browser } from '$app/environment';
// import { untrack } from 'svelte';
import { untrack } from 'svelte';
// import { goto, invalidate, pushState, replaceState } from '$app/navigation';
// *** Import other supporting libraries
@@ -114,6 +114,28 @@ let lq__post_obj_li = $derived.by(() => {
// });
// });
// Initial post load — gated on novi_verified.
// WHY $effect and not +page.ts: +page.ts runs before layout effects and fires on SvelteKit
// link prefetch (hover), causing private IDAA data to be written to IDB before auth runs.
// $effect only runs post-mount, after the layout has completed Novi verification.
$effect(() => {
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
untrack(() => {
posts_func.load_ae_obj_li__post({
api_cfg: $ae_api,
for_obj_type: 'account',
for_obj_id: data.account_id,
inc_comment_li: false,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 19,
order_by_li: { updated_on: 'DESC', created_on: 'DESC' },
try_cache: true,
log_lvl
});
});
});
// Handle Single Post Load Trigger
$effect(() => {
if ($idaa_trig.post_id) {

View File

@@ -1,13 +1,10 @@
import type { PageLoad } from './$types';
console.log(`ae_idaa_bulletin_board [root] +page.ts start`);
import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { posts_func } from '$lib/ae_posts/ae_posts_functions';
// Data loading for IDAA BB has been moved to +page.svelte $effect (gated on novi_verified).
// +page.ts runs before layout effects and fires during SvelteKit link prefetch,
// making it unsafe for private IDAA content — see BOOTSTRAP__AI_Agent_Quickstart.md.
export const load = (async ({ params, parent }) => {
// route
const log_lvl: number = 1;
const data = await parent();
@@ -22,43 +19,10 @@ export const load = (async ({ params, parent }) => {
);
ae_acct = {
api: data.ae_api || {},
slct: {
account_id: account_id
}
slct: { account_id: account_id }
};
}
if (browser) {
const load_post_obj_li = posts_func
.load_ae_obj_li__post({
api_cfg: ae_acct.api,
for_obj_type: 'account',
for_obj_id: account_id,
inc_comment_li: false,
enabled: 'enabled',
hidden: 'not_hidden',
limit: 19,
order_by_li: { updated_on: 'DESC', created_on: 'DESC' },
try_cache: true,
log_lvl: log_lvl
})
.then((posts) => {
// REVIEW AGAIN: The backend now supports filtering out archived posts based on the 'archive_on' field.
// Workaround: V3 Search does not permit 'archive_on' field yet.
// Filter locally for posts that are not archived yet.
const now = new Date();
return (posts || []).filter(
(p: any) => !p.archive_on || new Date(p.archive_on) > now
);
});
if (log_lvl) {
console.log(`load_post_obj_li = `, load_post_obj_li);
}
ae_acct.slct.post_obj_li = load_post_obj_li;
}
// WARNING: Precaution against shared data between sites and sessions.
data[account_id] = ae_acct;
return data;
}) satisfies PageLoad;

View File

@@ -1,53 +1,31 @@
import type { PageLoad } from './$types';
console.log(`ae_idaa_bulletin_board [post_id] +page.ts start`);
import { browser } from '$app/environment';
import { posts_func } from '$lib/ae_posts/ae_posts_functions';
// Data loading for IDAA BB [post_id] has been moved to the $effect in bb/+layout.svelte
// (triggered via $idaa_trig.post_id, gated inside the auth-verified layout render).
// +page.ts runs before layout effects and fires during SvelteKit link prefetch,
// making it unsafe for private IDAA content.
export const load = (async ({ params, parent }) => {
// route
const log_lvl: number = 0;
const data = await parent();
data.log_lvl = log_lvl;
const account_id = data.account_id;
const ae_acct = data[account_id];
let ae_acct = data[account_id];
const post_id = params.post_id;
ae_acct.slct.post_id = post_id;
if (browser) {
if (log_lvl) {
console.log(
`ae_idaa_posts posts [post_id] +page.ts: post_id = `,
post_id
);
}
// NOTE: Fire in background — do NOT await. Newly created posts are already in Dexie
// (saved by create_ae_obj__post), so the liveQuery renders immediately. For direct
// links or refreshes, the post loads from the API and Dexie updates reactively.
// Awaiting here blocked the SvelteKit navigation, causing a visible "refresh" delay.
posts_func
.load_ae_obj_id__post({
api_cfg: ae_acct.api,
post_id: post_id,
inc_comment_li: true,
log_lvl: log_lvl
})
.then((results) => {
if (!results) {
console.warn(
`ae IDAA BB [post_id] +page.ts: Post ${post_id} not found via API or Cache.`
);
}
});
if (!ae_acct) {
console.warn(
`ae IDAA BB [post_id] +page.ts: Account ${account_id} not found in data. Initializing ghost acct.`
);
ae_acct = {
api: data.ae_api || {},
slct: { account_id: account_id }
};
}
// WARNING: Precaution against shared data between sites.
data[account_id] = ae_acct;
ae_acct.slct.post_id = params.post_id;
data[account_id] = ae_acct;
return data;
}) satisfies PageLoad;

View File

@@ -1,12 +1,10 @@
/** @type {import('./$types').LayoutLoad} */
// console.log(`IDAA Recovery Meetings - [account_id] +layout.ts start`);
// import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { events_func } from '$lib/ae_events/ae_events_functions';
// Data loading for IDAA Recovery Meetings has been moved to the $effect in +page.svelte
// (gated on novi_verified / trusted_access). +layout.ts runs before layout effects and
// fires during SvelteKit link prefetch, making it unsafe for private IDAA content.
export async function load({ params, parent }) {
// route
const log_lvl: number = 0;
const data = await parent();
@@ -21,38 +19,10 @@ export async function load({ params, parent }) {
);
ae_acct = {
api: data.ae_api || {},
slct: {
account_id: account_id
}
slct: { account_id: account_id }
};
}
if (browser) {
const load_event_obj_li = events_func.load_ae_obj_li__event({
api_cfg: ae_acct.api,
for_obj_id: account_id,
qry_conference: false, // IDAA Recovery Meetings are not standard conferences
enabled: 'enabled',
hidden: 'not_hidden',
limit: 499,
order_by_li: {
priority: 'DESC',
sort: 'DESC',
updated_on: 'DESC',
created_on: 'DESC',
name: 'ASC'
},
try_cache: true,
log_lvl: log_lvl
});
if (log_lvl) {
console.log(`load_event_obj_li = `, load_event_obj_li);
}
ae_acct.slct.event_obj_li = load_event_obj_li;
}
// WARNING: Precaution against shared data between sites and sessions.
data[account_id] = ae_acct;
return data;
}

View File

@@ -49,6 +49,11 @@ $effect(() => {
const account_id = $ae_loc.account_id;
if (!account_id) return; // Wait for account context
// Auth gate: do not fetch IDAA events for unauthenticated users.
// WHY $effect and not +layout.ts: layout load functions fire on SvelteKit link prefetch,
// causing private data to be written to IDB before Novi auth runs.
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
// Track filters and the search version (trigger)
const qry_params = {
v: $idaa_loc.recovery_meetings.search_version,

View File

@@ -26,7 +26,7 @@ import {
slct_trigger
} from '$lib/stores/ae_stores';
import { db_events } from '$lib/ae_events/db_events';
// import { events_func } from '$lib/ae_events/ae_events_functions';
import { events_func } from '$lib/ae_events/ae_events_functions';
import {
idaa_loc,
idaa_sess,
@@ -53,6 +53,29 @@ $effect(() => {
});
});
// Load single event — gated on auth.
// WHY $effect and not +page.ts: +page.ts runs during SvelteKit link prefetch,
// causing private IDAA data to be written to IDB before Novi auth runs.
// $effect only runs post-mount, after the layout has completed Novi verification.
$effect(() => {
if (!$idaa_loc.novi_verified && !$ae_loc.trusted_access) return;
const event_id = ae_acct?.slct?.event_id;
if (!event_id) return;
untrack(() => {
events_func.load_ae_obj_id__event({
api_cfg: $ae_api,
event_id: event_id,
log_lvl: log_lvl
}).then((results) => {
if (!results) {
console.warn(
`IDAA Recovery Meetings [event_id] $effect: Event ${event_id} not found via API or Cache.`
);
}
});
});
});
// *** Functions and Logic
let lq__event_obj = $derived(
liveQuery(async () => {

View File

@@ -1,13 +1,10 @@
import type { PageLoad } from './$types';
console.log(`ae_p_idaa_events [event_id] +page.ts start`);
// import { error } from '@sveltejs/kit';
import { browser } from '$app/environment';
import { events_func } from '$lib/ae_events/ae_events_functions';
// Data loading for IDAA Recovery Meetings [event_id] has been moved to the $effect in +page.svelte
// (gated on novi_verified / trusted_access). +page.ts runs before layout effects and
// fires during SvelteKit link prefetch, making it unsafe for private IDAA content.
export const load = (async ({ params, parent }) => {
// route
const log_lvl: number = 0;
const data = await parent();
@@ -22,54 +19,12 @@ export const load = (async ({ params, parent }) => {
);
ae_acct = {
api: data.ae_api || {},
slct: {
account_id: account_id
}
slct: { account_id: account_id }
};
}
const event_id = params.event_id;
ae_acct.slct.event_id = params.event_id;
ae_acct.slct.event_id = event_id;
if (browser) {
if (log_lvl) {
console.log(
`ae_idaa_events events [event_id] +page.ts: event_id = `,
event_id
);
}
// Load event object
const load_event_obj = await events_func.load_ae_obj_id__event({
api_cfg: ae_acct.api,
event_id: event_id,
log_lvl: log_lvl
});
// .then((results) => {
// if (!results) {
// error(404, {
// message: 'IDAA Recovery Meetings - Event not found'
// });
// } else {
// // ae_acct.slct.event_obj = results;
// }
// });
if (log_lvl) {
console.log(`load_event_obj = `, load_event_obj);
}
if (!load_event_obj) {
console.warn(
`ae IDAA Recovery Meeting [event_id] +page.ts: Event ${event_id} not found via API or Cache.`
);
} else {
ae_acct.slct.event_obj = load_event_obj;
}
}
// WARNING: Precaution against shared data between sites and presentations.
data[account_id] = ae_acct;
return data;
}) satisfies PageLoad;