toga-ai 1.0.1005 → 1.0.1007
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/knowledge/1.0/apps/library/features/app-cloud-aws-credentials.md +11 -2
- package/knowledge/1.0/apps/library/features/mvc-page-pattern-and-app-skeleton.md +7 -1
- package/knowledge/1.0/apps/togadesk/architecture.md +6 -1
- package/knowledge/2.0/apps/toga2-commerce/features/catalog-cache-freshness.md +5 -4
- package/package.json +1 -1
|
@@ -6,19 +6,24 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
10
|
-
owners: [jcardinal]
|
|
9
|
+
updated: 2026-10-13
|
|
10
|
+
owners: [jcardinal, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/cloud.php
|
|
13
13
|
- library/app/systemmonitor/rdsproductioncluster.php
|
|
14
14
|
- worker/ebs/cron.worker.php
|
|
15
15
|
- toga/ebs/register_instance_to_shared_application_load_balancer.php
|
|
16
16
|
- tools/ebs/check_s3fs.php
|
|
17
|
+
- library/app/model.php
|
|
18
|
+
- togadesk/ebs/check_s3fs.php
|
|
19
|
+
- togadesk/ebs/register_instance_to_shared_application_load_balancer.php
|
|
20
|
+
- togadesk/.ebextensions/020_setup_git_libraries.config
|
|
17
21
|
related:
|
|
18
22
|
- ../../../../2.0/apps/worker2/features/cross-account-aws-access.md
|
|
19
23
|
- ../../../../2.0/apps/_underscore/features/cloud-s3-helpers.md
|
|
20
24
|
- ../../tools/workflows/deploy-to-elastic-beanstalk-al2023.md
|
|
21
25
|
- ../architecture.md
|
|
26
|
+
- mvc-page-pattern-and-app-skeleton.md
|
|
22
27
|
---
|
|
23
28
|
|
|
24
29
|
How every 1.0 AWS call (App_Cloud CLI + SDK, deploy scripts, `setup_git_libraries.php`) gets credentials by assuming `WorkloadsRuntime` from the EC2 instance role; open before touching 1.0 AWS access or EB deploy hooks.
|
|
@@ -56,8 +61,12 @@ CodePipeline `Worker` (502, us-west-2) sources `_production` of **both** `agilan
|
|
|
56
61
|
- **Never put `$cmd` in an exception or log message.** It contains `$preCommands` = live temporary credentials.
|
|
57
62
|
- **CLI temp creds are visible in argv (`ps`).** Open follow-up: CLI profile with `credential_source=Ec2InstanceMetadata`.
|
|
58
63
|
- **`setup_git_libraries.php` failing is silent.** When the key died its pipeline lookup failed and **library was not updated on deploy** — the old library kept running. If a library change "didn't deploy", check this script first. It was public-read with the key inside — a likely leak path; never put a credential in it.
|
|
64
|
+
- **Triage: `Aws\S3\Exception\S3Exception` 403 `InvalidAccessKeyId` (or any S3 403) in a 1.0 app → check App_Cloud credentials and whether the current library is on that app's instances — not the app code.** Library is not in the app repo: each app's EB deploy clones it (`.ebextensions/020_setup_git_libraries.config`) and pages load it from the include path (`require_once('_.php')`). A library-only fix reaches an app only on that app's next successful deploy. TRUE-82712: TOGa Desk MSP redbox (blob-field model load — see [BLOBSTORAGE](mvc-page-pattern-and-app-skeleton.md#gotchas)) stopped once library + togadesk were released 2026-10-13; no togadesk change.
|
|
65
|
+
- **`copyFileToS3()` (and the other CLI-based calls) never report failure.** `shell_exec('aws s3 cp ...')` then `return true` — bad credentials = silent lost upload. Uploads made while the old key was dead (repair photos, KB files, `BLOBSTORAGE` saves via `App_Model::save()`, email-intake attachments) may be missing from S3. Open, not fixed.
|
|
66
|
+
- **Open, not fixed: `togadesk` and `togaview` `ebs/check_s3fs.php` + `ebs/register_instance_to_shared_application_load_balancer.php` still hardcode the old dead key** (verified on `origin/_production` 2026-10-13; TRUE-82705 did not convert them). Not on the request path; follow up with Jeff/DevOps.
|
|
59
67
|
- Verify assume-role in CloudTrail (`AssumeRole` on `WorkloadsRuntime`, by session name: `library-app-cloud`, `setup-git-libraries`, `register-instance`, `worker-deploy`).
|
|
60
68
|
- Open, not fixed (developer owns): remove `AWS_ACCESS_KEY_ID`/`AWS_SECRET_KEY` from `agilant-worker` EB env properties.
|
|
61
69
|
|
|
62
70
|
## Change history
|
|
63
71
|
- 2026-10-12 — Created: removed the static IAM key from all 1.0 code/config; App_Cloud, deploy scripts and `setup_git_libraries.php` assume `WorkloadsRuntime` (TRUE-82705) (jcardinal)
|
|
72
|
+
- 2026-10-13 — S3 403 triage, silent `copyFileToS3` failure, unconverted togadesk/togaview ebs scripts (TRUE-82712) (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: Library
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: feature
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-13
|
|
10
10
|
owners: [jcardinal, mhammontree]
|
|
11
11
|
files:
|
|
12
12
|
- library/app/framework.php
|
|
@@ -15,9 +15,11 @@ files:
|
|
|
15
15
|
- library/app/database.php
|
|
16
16
|
- library/app/model.php
|
|
17
17
|
- library/app/config.php
|
|
18
|
+
- library/app/cloud.php
|
|
18
19
|
related:
|
|
19
20
|
- ../architecture.md
|
|
20
21
|
- ../../togaview/architecture.md
|
|
22
|
+
- app-cloud-aws-credentials.md
|
|
21
23
|
---
|
|
22
24
|
|
|
23
25
|
Recipe for a new 1.0 (`App_`) app and for adding pages: folder-based MVC routing, page lifecycle, models, multi-DB config; open when standing up a 1.0 app or adding a route.
|
|
@@ -63,6 +65,8 @@ class App_Model_Toga_Client extends App_Model {
|
|
|
63
65
|
```
|
|
64
66
|
Usage: `new App_Model_X($id)` loads a row; `->save()` persists; `App_Model_X::lookupValueByField()`.
|
|
65
67
|
|
|
68
|
+
`BLOBSTORAGE` fields are not in the row — the file lives outside the DB at `<env>/<DatabaseName>/<Table>/<field>/<id>` (`<env>` = `prod` when `worker`). `buildModel()` reads it on **every** model load: dev mode → local `[internal] storage_folder`; otherwise downloads it from S3 bucket `asifiles` via `App_Cloud::getFileFromS3()`. `save()` uploads/deletes it the same way.
|
|
69
|
+
|
|
66
70
|
### Databases (multiple are first-class)
|
|
67
71
|
DB access is mysqli via `App_Database::query($sql, 'db_<name>')`. **Multiple databases are first-class:** each `[database_<name>]` section in `config.<env>.ini` is registered as registry key `db_<name>` by `App_Database::registerDatabaseConnect()`. SELECTs **auto-route to `<link>_read`** when the host is an RDS cluster (read-cluster support). Escape with `App_Database::sqlEscape()`; read results with `buildArrayOfRows()` / `fetchRow()`.
|
|
68
72
|
|
|
@@ -85,6 +89,7 @@ The app depends on the `library` core repo (bootstrapped via `_.php`).
|
|
|
85
89
|
|
|
86
90
|
- **Route folders must stay distinct.** Matching is longest-to-shortest, so overlapping route folders can produce surprise matches.
|
|
87
91
|
- **An unset `ENVIRONMENT` loads PRODUCTION config.** `getenv('ENVIRONMENT')` defaults to `'worker'`, so running a 1.0 cron locally without it loads `worker/config.worker.ini` — prod DB hosts, `test_mode=0`, `email_errors=1`. Set `ENVIRONMENT` to your dev config name (`config.dev-<name>.ini`) first. Symptom: errors mention `/var/www/cache`.
|
|
92
|
+
- **An S3 error makes a plain model load fatal.** Any model with a `BLOBSTORAGE` field calls S3 in its constructor; `getFileFromS3()` swallows only 404 and rethrows the rest, so an AWS credential/permission failure turns `new App_Model_X($id)` into a redbox on every page that loads it (TRUE-82712: TOGa Desk MSP select via `ClientMsp.logo`, and NYCDOE `Repair::addNotes` via `App_Model_Common_NYCDOETicket.rawData`). Triage: [App_Cloud credentials](app-cloud-aws-credentials.md#gotchas).
|
|
88
93
|
- **Secrets are currently stored in plaintext ini files** — a known practice, not a recommended one. Document where a credential lives, never its value.
|
|
89
94
|
|
|
90
95
|
## Related
|
|
@@ -93,3 +98,4 @@ The app depends on the `library` core repo (bootstrapped via `_.php`).
|
|
|
93
98
|
|
|
94
99
|
## Change history
|
|
95
100
|
- 2026-10-01 — Unset-`ENVIRONMENT` gotcha: it loads production config locally; added symptom and fix (TRUE-82180). (mhammontree)
|
|
101
|
+
- 2026-10-13 — `BLOBSTORAGE` load/save path + S3-error-is-fatal gotcha (TRUE-82712). (mhammontree)
|
|
@@ -6,7 +6,7 @@ project: TOGa Desk
|
|
|
6
6
|
client: shared
|
|
7
7
|
type: architecture
|
|
8
8
|
status: active
|
|
9
|
-
updated: 2026-10-
|
|
9
|
+
updated: 2026-10-13
|
|
10
10
|
owners: ["mhammontree", "jcardinal"]
|
|
11
11
|
files:
|
|
12
12
|
- index.php
|
|
@@ -25,6 +25,9 @@ files:
|
|
|
25
25
|
- desk/includes/functions.php
|
|
26
26
|
- desk/template/header.php
|
|
27
27
|
- ebs/setup_phpini.php
|
|
28
|
+
- .ebextensions/020_setup_git_libraries.config
|
|
29
|
+
- ebs/check_s3fs.php
|
|
30
|
+
- ebs/register_instance_to_shared_application_load_balancer.php
|
|
28
31
|
related:
|
|
29
32
|
- 1.0/apps/togadesk/features/per-client-host-restriction.md
|
|
30
33
|
- 1.0/apps/togaview/architecture.md
|
|
@@ -36,6 +39,7 @@ related:
|
|
|
36
39
|
- 1.0/apps/togadesk/features/notifications.md
|
|
37
40
|
- 1.0/apps/togadesk/workflows/standalone-test-scripts.md
|
|
38
41
|
- 1.0/apps/togadesk/features/staff-sso-login.md
|
|
42
|
+
- 1.0/apps/library/features/app-cloud-aws-credentials.md
|
|
39
43
|
---
|
|
40
44
|
|
|
41
45
|
1.0 (App_) support desk + field-service platform — staff at `/desk/`, public/client portal at `/`; open for the request pipeline, domain-class layer, MSP data model, crons/integrations, or system topology.
|
|
@@ -147,6 +151,7 @@ Invariants:
|
|
|
147
151
|
- The prod O365 OAuth secret used by `crons/tickets_prod.php` has a **hard expiry (~2026-12-20)**.
|
|
148
152
|
- **Cross-tenant login hole in the per-client host restriction (pre-existing, NOT fixed).** The password-login access condition in `desk/includes/functions.php` (~L627-631) is a chain of ORs; two clauses grant access regardless of which client's subdomain is in use, letting an IM Houston or Fordham user sign in on another client's branded host. Post-login filtering keys off the session `clientid` so it is not an immediate record leak, but it defeats the restriction and contradicts the comment directly above it. Deserves its own ticket.
|
|
149
153
|
- **Sessions are host-scoped, so a domain change logs everyone out.** `ebs/setup_phpini.php` sets `session.save_handler=rediscluster` but no `session.cookie_domain`, and there is no `session_set_cookie_params()` anywhere. Combined with `people.sessionid` being a **single column**, logging in on a second domain invalidates the session on the first.
|
|
154
|
+
- **Library is not in this repo — a library fix is live in Desk only after a Desk deploy.** `.ebextensions/020_setup_git_libraries.config` clones `library` on every EB deploy; both loaders `require_once('_.php')` from the include path. An S3 403 / `InvalidAccessKeyId` redbox (e.g. selecting an MSP, which loads the `ClientMsp.logo` blob field) is a library credentials problem, not Desk code — see [App_Cloud credentials](../library/features/app-cloud-aws-credentials.md#gotchas). `ebs/check_s3fs.php` and `ebs/register_instance_to_shared_application_load_balancer.php` still hardcode the old dead IAM key (open).
|
|
150
155
|
|
|
151
156
|
## Related
|
|
152
157
|
- [Staff SSO login](features/staff-sso-login.md)
|
|
@@ -101,15 +101,16 @@ Limits to state to the business:
|
|
|
101
101
|
- the effect runs in `"fields-ready"` **or** `"ready"`;
|
|
102
102
|
- it uses functional `setHomeDataState(prev => …)` spreading `prev.data`, so `bundlesData` survives when the bundles, categories and items blocks all run in one pass;
|
|
103
103
|
- it depends on `homeDataState.status`, not the whole object (avoids a render loop);
|
|
104
|
-
- the view model no longer sets the `*ToDisplay` lists — `HomePage.tsx` rebuilds them in a **`useLayoutEffect`** on `data` via `
|
|
104
|
+
- the view model no longer sets the `*ToDisplay` lists — `HomePage.tsx` rebuilds them in a **`useLayoutEffect`** on `data` via `applySearch(submittedSearchRef.current)`, keeping the shopper's **submitted** search. Not `inputValue` — the search box updates it on every keystroke, so reapplying it would filter by unsubmitted typing (CodeRabbit catch). The ref is set in `handleSearchSubmit` and cleared in `handleClearSearch`. A plain `useEffect` caused a one-frame "no results" flash.
|
|
105
105
|
|
|
106
106
|
### Item detail Add to Cart must use the refetched price (`TRUE-82427`)
|
|
107
107
|
|
|
108
|
-
`ItemViewPage.tsx` copies `itemToAddToCart` (price, lease, Item) into `itemForCart` in an effect. It depended on `[selectedItem?.partNumber, currencySymbol]`, so a refetch with a new price for the same part updated the screen but **not** what Add to Cart sent. Fix: depend on `[selectedItem, currencySymbol]`; quantity is kept.
|
|
108
|
+
`ItemViewPage.tsx` copies `itemToAddToCart` (price, lease, Item) into `itemForCart` in an effect. It depended on `[selectedItem?.partNumber, currencySymbol]`, so a refetch with a new price for the same part updated the screen but **not** what Add to Cart sent. Fix: depend on `[selectedItem, currencySymbol, user?._currencyId]`; quantity is kept. `user?._currencyId` is needed because `getItemCurrencyInfo` picks the price by currency id and two currencies can share a symbol (USD and CAD are both `$`).
|
|
109
109
|
|
|
110
110
|
- No loop: `selectedItem` is view-model state, and React Query structural sharing keeps its reference when data is unchanged.
|
|
111
111
|
- Never add `itemToAddToCart` as a dependency — it is rebuilt every render.
|
|
112
112
|
- **Open:** after Add to Cart, `handleAddItemToCart` resets `itemForCart` to price `0` / uuid `""` until `selectedItem` or currency changes; a second click in that window can add the item at price 0. Fix direction: rebuild price/lease from `itemToAddToCart` inside the reset.
|
|
113
|
+
- **Open:** `addItem` in `src/stores/useCartStoreZu.ts` keeps the existing cart line's fields when the same item uuid is added again and only increments quantity, so re-adding after a price change keeps the old price on the whole line (CodeRabbit, PR #376). Fix direction: refresh the item fields in the existing-uuid branch. Out of scope for `TRUE-82427` (cart is Zustand, a stated limit).
|
|
113
114
|
|
|
114
115
|
### Opting a single query out — `staleTime: 0` + `gcTime: 0` is not enough
|
|
115
116
|
|
|
@@ -135,7 +136,7 @@ const shouldDehydrateQuery = (query) => {
|
|
|
135
136
|
>
|
|
136
137
|
> When they meet, merge into **one** filter holding both key lists.
|
|
137
138
|
|
|
138
|
-
**Bundle queries uncached — `TRUE-81382` branch only.** There, `bundleData`, `bundleImagesData`, `fetchBundlesByPersona` spread `BUNDLE_NO_CACHE_QUERY_OPTIONS` (`{ staleTime: 0, gcTime: 0 }`) and are excluded by `shouldPersistQuery`; `useBundleBuilderZu` no longer persists the raw parent-bundle payload (cause: an inactive item was orderable from a stale persisted bundle). The constant does **not** exist on `_production` or `TRUE-82427`. **
|
|
139
|
+
**Bundle queries uncached — `TRUE-81382` branch only.** There, `bundleData`, `bundleImagesData`, `fetchBundlesByPersona` spread `BUNDLE_NO_CACHE_QUERY_OPTIONS` (`{ staleTime: 0, gcTime: 0 }`) and are excluded by `shouldPersistQuery`; `useBundleBuilderZu` no longer persists the raw parent-bundle payload (cause: an inactive item was orderable from a stale persisted bundle). The constant does **not** exist on `_production` or `TRUE-82427`. **Merging with `TRUE-82427`** (verified 2026-10-13 against `origin/TRUE-81382` `7fcecbf3`): 81382 does **not** set `refetchOnWindowFocus: false` on the bundle queries. Both branches edit the same lines in `useBundleQueryData.ts` and the Home bundles query, so git shows a conflict there. Resolve to: keep the `...BUNDLE_NO_CACHE_QUERY_OPTIONS` spread; no `refetchOnWindowFocus: false`; no `staleTime: 30_000`; no `gcTime: 1h` on the bundle queries (81382's `gcTime: 0` wins). `_sandbox-client` still carries the old `false` values (app-wide and per-query) until 82427 reaches it. In `App.tsx` keep **both** `buster` and `dehydrateOptions`.
|
|
139
140
|
|
|
140
141
|
### Decision — `gcTime` is the speed knob, `staleTime` is the correctness knob
|
|
141
142
|
|
|
@@ -182,7 +183,7 @@ Pinned 2026-09-03 on `TRUE-81610`.
|
|
|
182
183
|
- **`AuthLayout` never remounts**, so `refetchOnMount` fires once per session for queries it owns.
|
|
183
184
|
|
|
184
185
|
## Change history
|
|
185
|
-
- 2026-10-13 — `TRUE-82427` (`93f12265`, pending merge): per-query `staleTime` / `refetchOnWindowFocus: false` overrides removed, focus refetch on app-wide, guard's explicit opt-in removed, Home refetch fix, item-detail Add to Cart uses refetched price; four other refetch-ignoring pages logged as open. Countries + 5-min login queries left out of scope. 30s heavy-query shape superseded. (tcox)
|
|
186
|
+
- 2026-10-13 — `TRUE-82427` (`93f12265`, pending merge): per-query `staleTime` / `refetchOnWindowFocus: false` overrides removed, focus refetch on app-wide, guard's explicit opt-in removed, Home refetch fix, item-detail Add to Cart uses refetched price (and rebuilds on currency id change), Home refetch reapplies the submitted search (CodeRabbit fixes `61e4ffd9`, `e5eca52f`), 81382 merge resolution corrected; four other refetch-ignoring pages logged as open. Countries + 5-min login queries left out of scope. 30s heavy-query shape superseded. (tcox)
|
|
186
187
|
- 2026-10-08 — Bundle queries uncached + excluded from persistence (TRUE-81382); branch map of the two persistence filters; buster live on `_production`. (tcox)
|
|
187
188
|
- 2026-10-01 — Global default already `staleTime: 0`; per-query 24h overrides inventoried; ticket scope agreed. (apeterson)
|
|
188
189
|
- 2026-09-22 — Causes 1 (default) and 2 fixed; login resets client state. TRUE-81991 (`e546db3d`). (apeterson)
|
package/package.json
CHANGED