Compare commits
15 Commits
898afd9775
...
66310adb22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66310adb22 | ||
|
|
b94516ce26 | ||
|
|
b8e6bcaf03 | ||
|
|
dea599bd9c | ||
|
|
4d5081582f | ||
|
|
1381b81bf0 | ||
|
|
686b289bdb | ||
|
|
6d8f767e45 | ||
|
|
61c9a6766d | ||
|
|
ff4295b24c | ||
|
|
9d8c0e5dd4 | ||
|
|
236a5513ee | ||
|
|
868f4b3390 | ||
|
|
aebbcf5b47 | ||
|
|
9baffc4407 |
@@ -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
|
||||
|
||||
---
|
||||
|
||||
@@ -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.
|
||||
|
||||
300
documentation/BOOTSTRAP__AI_Agent_Quickstart.md
Normal file
300
documentation/BOOTSTRAP__AI_Agent_Quickstart.md
Normal 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` |
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.');
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user