toga-ai 1.0.433 → 1.0.434
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/tools/INDEX.md +1 -0
- package/knowledge/1.0/apps/tools/architecture.md +10 -0
- package/knowledge/1.0/apps/tools/features/design-demo-admin.md +171 -0
- package/knowledge/1.0/apps/tools/workflows/deploy-to-elastic-beanstalk-al2023.md +12 -1
- package/knowledge/1.0/standards/framework-rules.md +14 -0
- package/knowledge/INDEX.md +2 -2
- package/knowledge/standalone/apps/forward/INDEX.md +1 -2
- package/knowledge/standalone/apps/forward/architecture.md +9 -3
- package/package.json +1 -1
- package/knowledge/standalone/apps/forward/features/design-demo-admin.md +0 -202
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
5
|
| [Tools (1.0 Internal-Tools App) Architecture](architecture.md) | **Tools** is a standalone 1.0 (`App_`) application that houses many small internal tools behind simple interfaces, gated by Client_True staff persona. | tools/index.php, tools/_/app/framework.php, tools/_/app/frameworkindex.php, tools/assets/img/favicon/favicon.ico, tools/assets/img/favicon/favicon-32x32.png, tools/assets/img/favicon/favicon-16x16.png, tools/assets/img/favicon/apple-touch-icon.png, tools/_/app/auth.php, tools/_/app/nav.php, tools/common/header.php, tools/common/footer.php, tools/mvc/get.php, tools/mvc/_TEMPLATE/get.php, tools/docs/ADDING_A_TOOL.md |
|
|
6
|
+
| [Design Demo Admin](features/design-demo-admin.md) | A self-serve admin UI at **`/design`** in the SSO-protected **Tools** app that lets the design team publish self-contained "Claude Design" HTML exports as **ver | tools/_/app/design/github.php, tools/mvc/design/get.php, tools/mvc/design/post.php, tools/assets/css/design.css, tools/assets/js/design.js, tools/_/app/frameworkindex.php, tools/_/app/nav.php, tools/composer.json |
|
|
6
7
|
| [Tools — Developers Folder (UUID & Password Generators)](features/developer-tools.md) | The first two tools shipped in the Tools app, both under the **Developers** folder and gated to personas **Development Team** / **TOGa Technology**. | tools/mvc/developers/uuid/get.php, tools/mvc/developers/password/get.php |
|
|
7
8
|
| [Tools MVC — Routing, CSRF & App_Database Access Patterns](features/mvc-data-access-patterns.md) | The load-bearing 1.0 (`App_`) framework conventions a developer needs when adding a page to the Tools app — URL routing, CSRF, and DB access through `App_Databa | tools/_/app/nav.php, tools/mvc/get.php |
|
|
8
9
|
| [Tools Persona-Gated Navigation (App_Nav)](features/persona-gated-navigation.md) | `App_Nav` is the Tools app's two-level, **persona-gated** navigation. | tools/_/app/nav.php, tools/mvc/get.php |
|
|
@@ -91,6 +91,16 @@ Create `mvc/<folder>/<tool>/get.php` (copy `mvc/_TEMPLATE/get.php`), add one ent
|
|
|
91
91
|
**accepted this for now** (developer decision, 2026-06-25). Future remediation: git-ignore the
|
|
92
92
|
config files and rotate all exposed credentials. (Location only — no values recorded here.)
|
|
93
93
|
- **SSO initiation + replay defense are open items** — see `features/saml-sso-auth.md`.
|
|
94
|
+
- **Framework-level Sentry error reporting is an open gap.** Prod printed "Sentry is not
|
|
95
|
+
installed" because `sentry/sentry` was missing from `composer.json` (added `^4.10`; sibling
|
|
96
|
+
apps use `^4.10`/`^4.19`). But even with Sentry in `vendor/`, the 1.0 framework does **not**
|
|
97
|
+
auto-require `vendor/autoload.php` (the `App_Talos_S3` pattern `require_once`s it explicitly),
|
|
98
|
+
so framework-level Sentry reporting for Tools still does not load. Any code path that needs a
|
|
99
|
+
composer package must `require_once` the autoloader itself.
|
|
94
100
|
|
|
95
101
|
## Change history
|
|
102
|
+
- 2026-07-24 — Added the SSO-gated **Design Demo Admin** tool at `/design` (`App_Design_Github`
|
|
103
|
+
+ `mvc/design/*` + namespaced assets + a `design` nav group), which publishes versioned demos
|
|
104
|
+
into the `forward` repo over the GitHub API (see `features/design-demo-admin.md`). Noted the
|
|
105
|
+
framework-level Sentry gap (composer autoloader not auto-required) in Known issues (jcardinal)
|
|
96
106
|
- 2026-06-26 — Documented app-local favicons under `assets/img/favicon/` (registered in `_/app/frameworkindex.php`, not the shared `resources/` set) + the per-app-asset convention; refreshed the home-route description for one-step SSO sign-in and the persona-less empty-state (jcardinal)
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Design Demo Admin
|
|
3
|
+
framework: "1.0"
|
|
4
|
+
repo: tools
|
|
5
|
+
project: Tools
|
|
6
|
+
client: shared
|
|
7
|
+
type: feature
|
|
8
|
+
status: active
|
|
9
|
+
updated: 2026-07-24
|
|
10
|
+
owners: [jcardinal]
|
|
11
|
+
files:
|
|
12
|
+
- tools/_/app/design/github.php
|
|
13
|
+
- tools/mvc/design/get.php
|
|
14
|
+
- tools/mvc/design/post.php
|
|
15
|
+
- tools/assets/css/design.css
|
|
16
|
+
- tools/assets/js/design.js
|
|
17
|
+
- tools/_/app/frameworkindex.php
|
|
18
|
+
- tools/_/app/nav.php
|
|
19
|
+
- tools/composer.json
|
|
20
|
+
related:
|
|
21
|
+
- ../architecture.md
|
|
22
|
+
- ../features/persona-gated-navigation.md
|
|
23
|
+
- ../features/saml-sso-auth.md
|
|
24
|
+
- ../workflows/deploy-to-elastic-beanstalk-al2023.md
|
|
25
|
+
- ../../../standalone/apps/forward/features/static-demo-hosting.md
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Summary
|
|
29
|
+
|
|
30
|
+
A self-serve admin UI at **`/design`** in the SSO-protected **Tools** app that lets the
|
|
31
|
+
design team publish self-contained "Claude Design" HTML exports as **versioned demos** — no
|
|
32
|
+
developer, no git, no local checkout. It writes into the **`agilantsolutions/forward`** repo
|
|
33
|
+
(branch `_main`) over the **GitHub REST API**, and the demos stay **publicly hosted by
|
|
34
|
+
`forward`** at `demo.togatech.com` (URLs unchanged). See
|
|
35
|
+
[Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md).
|
|
36
|
+
|
|
37
|
+
The tool **moved out of `forward` into `tools` this session.** Rationale: `forward` has no
|
|
38
|
+
auth (it is only a forwarder/hoster); `tools` is SSO-protected and is the home for internal
|
|
39
|
+
tools. The admin UI is now behind SSO, but the data (demos + manifests) still lives in and is
|
|
40
|
+
served from the `forward` repo.
|
|
41
|
+
|
|
42
|
+
## Key files / entry points
|
|
43
|
+
|
|
44
|
+
- `_/app/design/github.php` — class **`App_Design_Github`**: all GitHub REST + domain logic
|
|
45
|
+
ported from the old single-file tool (`ghApi` / `ghListDir` / `ghReadFile` /
|
|
46
|
+
`ghCommitFiles` / `ghDeletePaths`, project/version/tab CRUD, upload validation, redirect-
|
|
47
|
+
stub builder).
|
|
48
|
+
- `mvc/design/get.php` — route `/design` (page shell). Self-guards with
|
|
49
|
+
`App_Auth::requireAuth([...])` + `App_Page::validateCrossSiteRequestForgery()`.
|
|
50
|
+
- `mvc/design/post.php` — AJAX action dispatcher.
|
|
51
|
+
- `assets/css/design.css`, `assets/js/design.js` — namespaced under `.design-tool`,
|
|
52
|
+
registered globally in `_/app/frameworkindex.php`.
|
|
53
|
+
- `_/app/nav.php` — a `design` group, action **`Design Demos`** → `/design`, personas
|
|
54
|
+
`['TOGa Technology','Development Team']`.
|
|
55
|
+
|
|
56
|
+
## How it works
|
|
57
|
+
|
|
58
|
+
### GitHub API access (no filesystem)
|
|
59
|
+
|
|
60
|
+
The tool runs on the SSO-protected Tools EB host, not a git checkout, so **all** repo access
|
|
61
|
+
is over the GitHub API:
|
|
62
|
+
|
|
63
|
+
- **Reads / listing:** GitHub **Contents API** (`ghReadFile` uses the raw media type
|
|
64
|
+
`Accept: application/vnd.github.raw` so multi-MB exports stream up to 100 MB — the base64
|
|
65
|
+
Contents response inlines only ≤1 MB and silently returns empty content above that).
|
|
66
|
+
- **Writes:** GitHub **Git Data API** — blob → tree → commit → update-ref — so a publish is
|
|
67
|
+
an **atomic multi-file commit** (version files + regenerated stub + `project.json` land in
|
|
68
|
+
one commit).
|
|
69
|
+
- Publishing pushes to `forward`'s `_main`, which triggers `forward`'s EB auto-deploy — demos
|
|
70
|
+
go live in a few minutes; there is no staging step.
|
|
71
|
+
|
|
72
|
+
### Data model (lives in the `forward` repo)
|
|
73
|
+
|
|
74
|
+
- Per project: **`<project>/project.json`** — `{ title, latest, hidden, tabs[], versions[] }`.
|
|
75
|
+
Human detail (label/author/notes/date) lives in the manifest, not the URL. A directory is a
|
|
76
|
+
managed demo **iff it contains `project.json`**.
|
|
77
|
+
- Per version: **`<project>/vN/index.html`** — sequential `/v1`, `/v2`, `/v3` URL folders
|
|
78
|
+
(not semver, not date-based).
|
|
79
|
+
- Repo root **`tabs.json`** — `{ "tabs": [...] }`, the ordered tab list.
|
|
80
|
+
- The bare `/<project>` serves an auto-generated `index.html` **stub** that
|
|
81
|
+
meta-refresh/JS-redirects (relative `./vN/`) to the newest **visible** version; regenerated
|
|
82
|
+
on every publish/unlist.
|
|
83
|
+
|
|
84
|
+
### Multi-tab membership
|
|
85
|
+
|
|
86
|
+
The project.json `tab` **scalar** became a **`tabs` array**. It is read **backward-
|
|
87
|
+
compatibly** (an old `tab` scalar is honored) and **lazily migrated** — the scalar is dropped
|
|
88
|
+
on the next write of that project.json. Tab management via a per-pill "⋯" menu:
|
|
89
|
+
|
|
90
|
+
- **Rename / delete** — rewrite `tabs.json` **and** every affected `project.json` in one
|
|
91
|
+
**atomic** commit.
|
|
92
|
+
- **Reorder** — a single-file `tabs.json` commit, validated as a **permutation** of the
|
|
93
|
+
existing list.
|
|
94
|
+
|
|
95
|
+
### Actions & UX
|
|
96
|
+
|
|
97
|
+
- **Publish a version** — uploads a self-contained HTML export as the next `vN/index.html`,
|
|
98
|
+
appends a version entry, regenerates the stub, commits.
|
|
99
|
+
- **Remove version** — collapses the version out of the main list; unlisted versions sit
|
|
100
|
+
behind a per-project "Show N unlisted" collapsible.
|
|
101
|
+
- **Unlist a project ("Remove")** — reversible `hidden` flag; files are not deleted.
|
|
102
|
+
- **Hard-delete a whole project** — `ghDeletePaths()` reads the head tree recursively,
|
|
103
|
+
rebuilds a fresh tree **excluding the project folder** (exact folder-boundary prefix match),
|
|
104
|
+
commits, then PATCHes the ref. Offered alongside the reversible unlist.
|
|
105
|
+
- **Set latest / toggle version / edit version / toggle project / set project tabs** — commit
|
|
106
|
+
the relevant `project.json` (metadata-only edits do not rebuild the stub).
|
|
107
|
+
- **Copy URL** and **Copy Claude Design prompt** — clipboard helpers.
|
|
108
|
+
- **Single scroll region** — `#projects` is the only scroll area; header/tabbar are fixed, so
|
|
109
|
+
switching tabs never shifts layout.
|
|
110
|
+
- The Claude Design export **prompt modal** forces a dark `<pre>` background + light text with
|
|
111
|
+
`!important` (the global light `pre` theme otherwise wins).
|
|
112
|
+
|
|
113
|
+
### Hardening
|
|
114
|
+
|
|
115
|
+
- `isValidProjectName()` guards on `setLatest` / `toggleVersion` / `editVersion` /
|
|
116
|
+
`toggleProject` / `setProjectTabs`.
|
|
117
|
+
- `upload()` **errors on corrupt project.json** instead of silently resetting history.
|
|
118
|
+
- Inline `window.DESIGN_CONFIG` uses `JSON_HEX_*` flags.
|
|
119
|
+
- Input length caps, version-id regex guard, HTML content sniff on upload; no client-facing
|
|
120
|
+
leakage of GitHub/curl error detail.
|
|
121
|
+
|
|
122
|
+
## Access control
|
|
123
|
+
|
|
124
|
+
Behind Tools SSO. The page self-guards with `App_Auth::requireAuth([...])` and
|
|
125
|
+
`App_Page::validateCrossSiteRequestForgery()`; the nav action is gated to the
|
|
126
|
+
`TOGa Technology` and `Development Team` personas. This is the key improvement over the old
|
|
127
|
+
`forward` tool, which had **no** authentication.
|
|
128
|
+
|
|
129
|
+
## GitHub token
|
|
130
|
+
|
|
131
|
+
`App_Design_Github::token()` reads the token from `getenv()` **AND** `$_SERVER` **AND**
|
|
132
|
+
`$_ENV`, then falls back to a `[design] github_token` config key for local dev. The EB
|
|
133
|
+
environment property is named **`GITHUB_TOKEN`** (value not recorded here). It is never
|
|
134
|
+
logged or echoed. Reading all three superglobals is required because php-fpm on AL2023
|
|
135
|
+
defaults to `clear_env=yes` — see the deploy workflow gotcha. Prefer a **fine-grained PAT**
|
|
136
|
+
scoped to the `forward` repo's Contents R/W.
|
|
137
|
+
|
|
138
|
+
## Client variations
|
|
139
|
+
|
|
140
|
+
None — internal/shared design-team tool.
|
|
141
|
+
|
|
142
|
+
## Gotchas / known issues
|
|
143
|
+
|
|
144
|
+
- **`curl_close()` is deprecated as of PHP 8.5** (and a no-op since 8.0). The 1.0 framework
|
|
145
|
+
on PHP 8.5 promotes deprecations to `ErrorException`, which aborts the request — every
|
|
146
|
+
GitHub call threw and the tool returned "Unexpected error" (0 projects). Fix: removed all
|
|
147
|
+
`curl_close()` calls. Audit ported code for other deprecated-in-8.x calls.
|
|
148
|
+
- **Contents API inlines only ≤1 MB** — read exports with the raw media type (see above).
|
|
149
|
+
- **Publishing pushes to `forward`'s `_main`** and triggers EB auto-deploy; no staging.
|
|
150
|
+
- Never hardcode the token in tracked source — a leaked `ghp_`/PAT must be rotated.
|
|
151
|
+
|
|
152
|
+
## Change history
|
|
153
|
+
|
|
154
|
+
- 2026-07-24 — Moved the Design Demo Admin from `forward` into the SSO-protected `tools` app
|
|
155
|
+
(`App_Design_Github` + `/design` MVC route + namespaced assets + nav entry); still commits
|
|
156
|
+
to `agilantsolutions/forward` `_main` and demos stay hosted at `demo.togatech.com`. Added
|
|
157
|
+
multi-tab membership (`tabs` array, backward-compatible + lazy migration), tab
|
|
158
|
+
rename/delete/reorder via ⋯ menu (atomic multi-file commits; reorder validated as a
|
|
159
|
+
permutation), hard project delete (`ghDeletePaths` tree rebuild), unlisted-version collapse,
|
|
160
|
+
and a permanent single scroll region. Hardened project-name guards, corrupt-manifest
|
|
161
|
+
handling, and `JSON_HEX_*` inline config. Token now read from `getenv()`/`$_SERVER`/`$_ENV`
|
|
162
|
+
(`GITHUB_TOKEN` EB property) for the AL2023 php-fpm `clear_env` case; removed `curl_close()`
|
|
163
|
+
for PHP 8.5. (jcardinal)
|
|
164
|
+
|
|
165
|
+
## Related docs
|
|
166
|
+
|
|
167
|
+
- [Tools Architecture](../architecture.md)
|
|
168
|
+
- [Static Demo Hosting](../../../standalone/apps/forward/features/static-demo-hosting.md)
|
|
169
|
+
- [Deploying Tools to Elastic Beanstalk (AL2023)](../workflows/deploy-to-elastic-beanstalk-al2023.md)
|
|
170
|
+
</content>
|
|
171
|
+
</invoke>
|
|
@@ -55,7 +55,9 @@ The old approach ran `ebs/setup_phpini.php` (now superseded) which called
|
|
|
55
55
|
failed silently (it was wrapped in `ignoreErrors: true`), so `include_path` never picked up
|
|
56
56
|
`/var/www/library`, producing fatal `require_once('_.php')` errors at runtime.
|
|
57
57
|
|
|
58
|
-
The `application.ini` sets `include_path = ".:/var/www/library"`, upload/post limits
|
|
58
|
+
The `application.ini` sets `include_path = ".:/var/www/library"`, upload/post limits
|
|
59
|
+
(`upload_max_filesize=25M` / `post_max_size=30M`, raised from 16M so multi-MB design-demo HTML
|
|
60
|
+
exports up to ~25MB post through — see `features/design-demo-admin.md`),
|
|
59
61
|
`memory_limit = 2G`, `display_errors`, the `redis.so` extension, and the rediscluster session
|
|
60
62
|
handler (`session.save_path` points at the ElastiCache cluster cfg endpoint). Session GC
|
|
61
63
|
maxlifetime is 4 days.
|
|
@@ -100,6 +102,12 @@ setup — the git clone (`020`) deliberately does **not** use it (see gotchas).
|
|
|
100
102
|
|
|
101
103
|
## Gotchas (durable AL2023 / EB rules)
|
|
102
104
|
|
|
105
|
+
- **php-fpm on AL2023 defaults to `clear_env=yes`, so `getenv()` cannot see EB environment
|
|
106
|
+
properties.** An EB environment property (e.g. `GITHUB_TOKEN`) that is set correctly still
|
|
107
|
+
returns `false` from `getenv()` under php-fpm on AL2023, because the FPM pool strips the
|
|
108
|
+
environment. Read such a value from **`getenv()` AND `$_SERVER` AND `$_ENV`** (the property
|
|
109
|
+
reaches PHP via the superglobals even when `getenv()` is blind), and fall back to a config
|
|
110
|
+
key for local dev. Never log/echo the value.
|
|
103
111
|
- **Write new-deploy files to `/var/app/ondeck/<path>`, never `/var/www/html/<path>` during
|
|
104
112
|
`container_commands`.** While container_commands run, `/var/www/html` still symlinks to the
|
|
105
113
|
**old** app. After the deploy swap, `/var/app/ondeck` becomes `/var/app/current`
|
|
@@ -128,6 +136,9 @@ setup — the git clone (`020`) deliberately does **not** use it (see gotchas).
|
|
|
128
136
|
returns `200` fast. Reusable for any TOGA app behind Elastic Beanstalk.
|
|
129
137
|
|
|
130
138
|
## Change history
|
|
139
|
+
- 2026-07-24 — Added the php-fpm `clear_env=yes` gotcha (read EB env properties from
|
|
140
|
+
`getenv()`/`$_SERVER`/`$_ENV`) and raised `application.ini` upload/post limits to 25M/30M for
|
|
141
|
+
multi-MB design-demo exports (jcardinal)
|
|
131
142
|
- 2026-06-26 — CONFIRMED + RESOLVED the SAML SSO return-leg gotcha: the `tools.togatech.com` EB environment had no HTTPS (443) listener at all; adding the HTTPS:443 listener + ACM cert covering the exact return hostname fixed the SSO return leg with no code change (jcardinal)
|
|
132
143
|
- 2026-06-26 — Added the SAML SSO return-leg gotcha: a "connection timeout" on `https://<host>/sso` was a missing EB/ALB HTTPS (443) listener for the new `togatech.com` hostname (not PHP/SAML); documented the curl `http=000` vs `127.0.0.1` 200 diagnosis (jcardinal)
|
|
133
144
|
- 2026-06-26 — Documented the AL2 → AL2023 (PHP 8.5) EB migration for Tools: package renames
|
|
@@ -112,6 +112,20 @@ are adding a column that only `worker` reads, the `.sql` file goes in `dbchanges
|
|
|
112
112
|
Never create a `.sql` file inside a 1.0 application repo. The SQL that belongs in those
|
|
113
113
|
repos is only query strings embedded in PHP code — not standalone migration files.
|
|
114
114
|
|
|
115
|
+
## PHP 8.x deprecations abort the request (audit before running on 8.5)
|
|
116
|
+
|
|
117
|
+
The 1.0 framework's error handler promotes PHP **deprecation notices to `ErrorException`**,
|
|
118
|
+
which aborts the request. On PHP 8.5, a single deprecated call in a live code path is therefore
|
|
119
|
+
a hard failure, not a silent notice — it surfaces to users as a generic error.
|
|
120
|
+
|
|
121
|
+
- When porting or running 1.0 code on PHP 8.x, **audit for deprecated-in-8.x calls** in every
|
|
122
|
+
reachable code path before deploy.
|
|
123
|
+
- Known offender: **`curl_close()`** — deprecated as of PHP 8.5 and a no-op since PHP 8.0.
|
|
124
|
+
Remove all `curl_close()` calls; the cURL handle is closed automatically. (This exact bug
|
|
125
|
+
made the Design Demo Admin tool throw on every GitHub call and render 0 projects.)
|
|
126
|
+
- Do not suppress the deprecation with `@` (see the coding-style rules) — remove/replace the
|
|
127
|
+
deprecated call instead.
|
|
128
|
+
|
|
115
129
|
## Error handling in controllers
|
|
116
130
|
|
|
117
131
|
Controllers must not let exceptions bubble up to the framework unhandled. Catch specific exceptions, log them, and return an appropriate response.
|
package/knowledge/INDEX.md
CHANGED
|
@@ -13,7 +13,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
13
13
|
- **walmarttechservices** (Walmart Tech Services) — 1 doc(s) → [1.0/apps/walmarttechservices/INDEX.md](1.0/apps/walmarttechservices/INDEX.md)
|
|
14
14
|
- **test** (Test) — 13 doc(s) → [1.0/apps/test/INDEX.md](1.0/apps/test/INDEX.md)
|
|
15
15
|
- **toga** (TOGa) — 2 doc(s) → [1.0/apps/toga/INDEX.md](1.0/apps/toga/INDEX.md)
|
|
16
|
-
- **tools** (Tools) —
|
|
16
|
+
- **tools** (Tools) — 9 doc(s) → [1.0/apps/tools/INDEX.md](1.0/apps/tools/INDEX.md)
|
|
17
17
|
|
|
18
18
|
## 2.0 framework
|
|
19
19
|
|
|
@@ -37,7 +37,7 @@ _Auto-generated by `knowledge.js index`. Do not hand-edit._
|
|
|
37
37
|
|
|
38
38
|
- **togatech** (TOGA Technology Website) — 4 doc(s) → [standalone/apps/togatech/INDEX.md](standalone/apps/togatech/INDEX.md)
|
|
39
39
|
- **websocket** (WebSocket Server) — 2 doc(s) → [standalone/apps/websocket/INDEX.md](standalone/apps/websocket/INDEX.md)
|
|
40
|
-
- **forward** (Forwarder) —
|
|
40
|
+
- **forward** (Forwarder) — 3 doc(s) → [standalone/apps/forward/INDEX.md](standalone/apps/forward/INDEX.md)
|
|
41
41
|
|
|
42
42
|
## Clients
|
|
43
43
|
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
| Doc | Summary | Files |
|
|
4
4
|
|-----|---------|-------|
|
|
5
|
-
| [Forwarder Architecture](architecture.md) | Forwarder is a tiny **standalone PHP application** that powers TOGA's short, branded redirect domains. | forward/forward.ini, forward/index.php, forward/.htaccess, forward/.platform/httpd/conf.d/rewritemap.conf, forward/.ebextensions/rewritemap.config, forward/
|
|
6
|
-
| [Design Demo Admin Tool](features/design-demo-admin.md) | A standalone, dependency-free PHP tool served at `https://demo.togatech.com/design` that lets the **design team** publish self-contained "Claude Design" HTML ex | forward/design/index.php, forward/tabs.json, forward/design/.config.example.php, forward/.gitignore, forward/.htaccess, forward/.ebextensions/php-uploads.config |
|
|
5
|
+
| [Forwarder Architecture](architecture.md) | Forwarder is a tiny **standalone PHP application** that powers TOGA's short, branded redirect domains. | forward/forward.ini, forward/index.php, forward/.htaccess, forward/.platform/httpd/conf.d/rewritemap.conf, forward/.ebextensions/rewritemap.config, forward/composer.json |
|
|
7
6
|
| [Encrypted-Link Handler](features/encrypted-link-handler.md) | A `index.php` feature in [Forwarder](../architecture.md) for redirect domains whose URL path carries an **encrypted token** that must be decoded before redirect | forward/index.php |
|
|
8
7
|
| [Static Demo Hosting](features/static-demo-hosting.md) | A lightweight way to host self-contained static HTML pages (demos, exported designs, download landing pages) on the Forwarder app under a clean URL — e.g. | forward/.htaccess, forward/togadesk/index.html |
|
|
@@ -14,7 +14,6 @@ files:
|
|
|
14
14
|
- forward/.htaccess
|
|
15
15
|
- forward/.platform/httpd/conf.d/rewritemap.conf
|
|
16
16
|
- forward/.ebextensions/rewritemap.config
|
|
17
|
-
- forward/design/index.php
|
|
18
17
|
- forward/composer.json
|
|
19
18
|
related: []
|
|
20
19
|
---
|
|
@@ -76,7 +75,13 @@ The `.htaccess` at the repo root drives a plain-text lookup table:
|
|
|
76
75
|
(e.g. `sos.*` → `/sos/`), it redirects to `/<subdomain>/` and serves that dir's
|
|
77
76
|
`index.html`. Used for the bundled `sos/` Splashtop SOS download page.
|
|
78
77
|
- **404** — otherwise returns a styled `404` echoing the unmatched `host + uri`.
|
|
79
|
-
|
|
78
|
+
|
|
79
|
+
> **The Design Demo Admin tool moved out of `forward`.** As of 2026-07-24 the self-serve
|
|
80
|
+
> publishing UI lives in the SSO-protected **Tools** 1.0 app at `/design` (see
|
|
81
|
+
> `1.0/apps/tools/features/design-demo-admin.md`), and `forward/design/` was deleted. `forward`
|
|
82
|
+
> still **stores** the demo data (per-project `project.json` + `vN/index.html`, repo-root
|
|
83
|
+
> `tabs.json`) and **hosts** the demos publicly at `demo.togatech.com` (URLs unchanged); it no
|
|
84
|
+
> longer contains the admin UI. Tools writes into this repo's `_main` over the GitHub API.
|
|
80
85
|
|
|
81
86
|
## Deployment (Elastic Beanstalk / Apache)
|
|
82
87
|
|
|
@@ -92,7 +97,7 @@ The `.htaccess` at the repo root drives a plain-text lookup table:
|
|
|
92
97
|
- `composer.json` declares a `Togatech\Forward\` PSR-4 autoload over `src/` but has **no
|
|
93
98
|
dependencies** and no `src/` is shipped today — the app is effectively a single
|
|
94
99
|
`index.php` plus the Apache config.
|
|
95
|
-
- **`DirectoryIndex index.html index.php`** in `.htaccess`
|
|
100
|
+
- **`DirectoryIndex index.html index.php`** in `.htaccess` lets nested static-demo version paths `/<project>/<vN>/` serve via Apache's normal directory index. The single-segment static-demo rewrite rule does not match two-segment paths, and the `index.php` map-miss fallback does not fire for real directories — so versioned demo URLs resolve as directory-index lookups.
|
|
96
101
|
|
|
97
102
|
## The `decrypt()` scheme
|
|
98
103
|
|
|
@@ -123,3 +128,4 @@ Treat this as obfuscation only. Anything genuinely sensitive must not rely on it
|
|
|
123
128
|
- 2026-06-18 (jcardinal) — Initial architecture documentation; registered `forward`
|
|
124
129
|
(Forwarder, standalone) in the knowledge base.
|
|
125
130
|
- 2026-06-23 (jcardinal) — Added the `/design` Design Demo Admin route and a `DirectoryIndex index.html index.php` directive in `.htaccess` (enables `/design/` and nested `/<project>/<vN>/` demo paths).
|
|
131
|
+
- 2026-07-24 (jcardinal) — Removed the Design Demo Admin tool from `forward` (deleted `forward/design/`); it now lives in the SSO-protected Tools 1.0 app (`1.0/apps/tools/features/design-demo-admin.md`). `forward` still stores and hosts the demo data/`tabs.json`; Tools writes to `_main` over the GitHub API.
|
package/package.json
CHANGED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Design Demo Admin Tool
|
|
3
|
-
framework: "standalone"
|
|
4
|
-
repo: forward
|
|
5
|
-
project: Forwarder
|
|
6
|
-
client: shared
|
|
7
|
-
type: feature
|
|
8
|
-
status: active
|
|
9
|
-
updated: 2026-06-25
|
|
10
|
-
owners: ["jcardinal"]
|
|
11
|
-
files:
|
|
12
|
-
- forward/design/index.php
|
|
13
|
-
- forward/tabs.json
|
|
14
|
-
- forward/design/.config.example.php
|
|
15
|
-
- forward/.gitignore
|
|
16
|
-
- forward/.htaccess
|
|
17
|
-
- forward/.ebextensions/php-uploads.config
|
|
18
|
-
related:
|
|
19
|
-
- standalone/apps/forward/features/static-demo-hosting.md
|
|
20
|
-
- standalone/apps/forward/architecture.md
|
|
21
|
-
---
|
|
22
|
-
|
|
23
|
-
## Summary
|
|
24
|
-
|
|
25
|
-
A standalone, dependency-free PHP tool served at `https://demo.togatech.com/design` that
|
|
26
|
-
lets the **design team** publish self-contained "Claude Design" HTML exports to the
|
|
27
|
-
`forward` repo as **versioned demos** — no developer, no git, no local checkout required.
|
|
28
|
-
It sits on top of [Static Demo Hosting](static-demo-hosting.md): static hosting serves the
|
|
29
|
-
files; this tool is the self-serve UI that writes and versions them.
|
|
30
|
-
|
|
31
|
-
The tool does **all** repo access over the **GitHub REST API** — never the local
|
|
32
|
-
filesystem — because in production it runs on the Elastic Beanstalk host, not a git
|
|
33
|
-
checkout. Publishing pushes a commit to `_main`, which triggers the existing EB
|
|
34
|
-
auto-deploy ("live in a few minutes").
|
|
35
|
-
|
|
36
|
-
## Key files / entry points
|
|
37
|
-
|
|
38
|
-
- `forward/design/index.php` — the entire tool: a single-file PHP backend + HTML/JS UI.
|
|
39
|
-
`declare(strict_types=1)`. Served at `/design` (was `/admin`; renamed this session).
|
|
40
|
-
- `forward/design/.config.example.php` — template for local dev config (the real
|
|
41
|
-
`.config.php` is git-ignored).
|
|
42
|
-
- `forward/.gitignore` — ignores `design/.config.php` so a local token is never tracked.
|
|
43
|
-
- `forward/.htaccess` — `DirectoryIndex index.html index.php` so `/design/` loads
|
|
44
|
-
`index.php`, and so nested `/<project>/<vN>/` paths serve via Apache's directory index.
|
|
45
|
-
|
|
46
|
-
## How it works
|
|
47
|
-
|
|
48
|
-
### GitHub API access (no filesystem)
|
|
49
|
-
|
|
50
|
-
- **Reads / listing:** GitHub **Contents API**.
|
|
51
|
-
- **Writes:** GitHub **Git Data API** — blob → tree → commit → update-ref — so a publish
|
|
52
|
-
is an **atomic multi-file commit** (the version's files + the regenerated stub +
|
|
53
|
-
`project.json` all land in one commit).
|
|
54
|
-
- **Auth:** token from `getenv('FORWARD_GITHUB_TOKEN')` (an Elastic Beanstalk environment
|
|
55
|
-
property in prod) or an uncommitted `design/.config.php` for local dev. See gotchas — the
|
|
56
|
-
token must **never** live in the tracked `index.php`.
|
|
57
|
-
|
|
58
|
-
### Versioning model (decided this session)
|
|
59
|
-
|
|
60
|
-
- **Sequential `/v1`, `/v2`, `/v3` URL folders** — not semver, not date-based.
|
|
61
|
-
- Each project has a **`project.json` manifest** at its folder root:
|
|
62
|
-
```json
|
|
63
|
-
{
|
|
64
|
-
"title": "...", "latest": "v3", "hidden": false,
|
|
65
|
-
"versions": [
|
|
66
|
-
{ "id": "v3", "label": "...", "notes": "...", "author": "...",
|
|
67
|
-
"date": "YYYY-MM-DD", "hidden": false }
|
|
68
|
-
]
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
Human detail (label / author / notes) lives in the **manifest**, not the URL.
|
|
72
|
-
- **"latest" pointer:** the bare `/<project>` serves an auto-generated `index.html` **stub**
|
|
73
|
-
that meta-refresh / JS-redirects (relative `./vN/`) to the newest **visible** version.
|
|
74
|
-
The stub is regenerated on every publish/unlist.
|
|
75
|
-
- A directory is treated as a **managed demo iff it contains `project.json`** — this is how
|
|
76
|
-
the tool distinguishes demos from infrastructure directories.
|
|
77
|
-
|
|
78
|
-
### Actions
|
|
79
|
-
|
|
80
|
-
- **Publish a version** — uploads a self-contained HTML export as the next `vN/index.html`,
|
|
81
|
-
appends a version entry to `project.json`, regenerates the stub, commits.
|
|
82
|
-
- **Adopt** — converts a legacy `index.html`-only demo folder (e.g. `togadesk`) into a
|
|
83
|
-
managed demo: moves the existing page to `v1/index.html`, writes `project.json`,
|
|
84
|
-
regenerates the stub.
|
|
85
|
-
- **Remove = unlist only** — sets a `hidden` flag in `project.json`; files are **never
|
|
86
|
-
deleted** (explicit owner decision).
|
|
87
|
-
- **Edit version metadata** (`apiEditVersion`) — an "Edit" button on each version row opens
|
|
88
|
-
a modal pre-filled with the version's label, author ("your name"), and notes. Saving
|
|
89
|
-
commits an **updated `project.json` only** — the version id and the hosted `vN/index.html`
|
|
90
|
-
are unchanged, and the redirect stub does not depend on this metadata, so **no stub
|
|
91
|
-
rebuild**. Reuses the existing `isValidVersionId` guard and `postField` length caps (notes
|
|
92
|
-
cap 2000).
|
|
93
|
-
- **Copy URL** — a button on each project card copies the project's main live URL
|
|
94
|
-
(`https://demo.togatech.com/<project>`) to the clipboard via `navigator.clipboard`, with a
|
|
95
|
-
brief "Copied ✓" confirmation.
|
|
96
|
-
- **Copy Claude Design prompt** — one-click copy of the standard self-contained-HTML export
|
|
97
|
-
prompt (the same prompt documented in [static-demo-hosting](static-demo-hosting.md)).
|
|
98
|
-
- **Create a tab** (`apiCreateTab`, action `create-tab`) — appends a named tab to
|
|
99
|
-
`tabs.json`. Case-insensitive dedupe; name length-capped 40. Commits `tabs.json` only.
|
|
100
|
-
- **Move a project to a tab** (`apiSetProjectTab`, action `set-project-tab`) — sets/clears a
|
|
101
|
-
project's `tab` field; empty string = Unsorted (the field is `unset`). Commits that
|
|
102
|
-
project's `project.json` **only** — tab membership does not affect the redirect stub, so
|
|
103
|
-
**no stub rebuild**. `apiCreateProject` also accepts an optional `tab`.
|
|
104
|
-
|
|
105
|
-
The full action set is: `list`, `create-project`, `upload`, `adopt`, `set-latest`,
|
|
106
|
-
`toggle-version`, `edit-version`, `toggle-project`, `create-tab`, `set-project-tab` (plus
|
|
107
|
-
`bootstrap`). `list` also returns the tab list (`{ projects, tabs }`).
|
|
108
|
-
|
|
109
|
-
### Tab-based organization
|
|
110
|
-
|
|
111
|
-
Demo projects can be grouped into named **tabs** (e.g. separate Production / Pipeline /
|
|
112
|
-
Sandbox).
|
|
113
|
-
|
|
114
|
-
- **`tabs.json`** at the repo root holds the ordered tab list — `{"tabs": ["Production",
|
|
115
|
-
"Pipeline", "Sandbox"]}`. `loadTabs()` returns those three as **defaults when `tabs.json`
|
|
116
|
-
does not exist yet** (so tabs appear before any are created); the file is only written on
|
|
117
|
-
the first tab mutation (`create-tab`).
|
|
118
|
-
- Each `project.json` gains an optional **`tab`** field (string). Missing/absent =
|
|
119
|
-
**"Unsorted"**.
|
|
120
|
-
- **UI:** a filter chip bar (All / each tab / Unsorted / + New tab) with per-tab project
|
|
121
|
-
counts; clicking a chip filters the list. Each project card has a **Tab `<select>`** to
|
|
122
|
-
move the project between tabs (on the select's `change`). The New Project modal has a Tab
|
|
123
|
-
dropdown defaulting to the currently-viewed tab.
|
|
124
|
-
- The **displayed** tab list is the **union** of `tabs.json` order plus any tab names
|
|
125
|
-
referenced by projects — so removing a tab from `tabs.json` never orphans a project.
|
|
126
|
-
|
|
127
|
-
### Naming & validation
|
|
128
|
-
|
|
129
|
-
- Folder names auto-normalized to **lowercase kebab-case**, validated against a kebab regex
|
|
130
|
-
plus a reserved/excluded list.
|
|
131
|
-
- **`EXCLUDED_FOLDERS`** constant (`design`, `sos`) + `RESERVED_NAMES` are never listed or
|
|
132
|
-
managed (owner-requested; expected to grow over time).
|
|
133
|
-
|
|
134
|
-
### Hardening
|
|
135
|
-
|
|
136
|
-
Input length caps; version-id regex guard; HTML content sniff on upload; security headers;
|
|
137
|
-
no client-facing leakage of GitHub/curl error detail; `declare(strict_types=1)`.
|
|
138
|
-
|
|
139
|
-
## Access control
|
|
140
|
-
|
|
141
|
-
**No authentication** — explicit owner product decision. The `/design` URL is unlisted;
|
|
142
|
-
anyone who reaches it can publish or unlist. Do not treat the tool as protected.
|
|
143
|
-
|
|
144
|
-
## Data model
|
|
145
|
-
|
|
146
|
-
`project.json` per managed demo (see Versioning model above), plus an optional `tab` string
|
|
147
|
-
field per project (absent = Unsorted). A single repo-root **`tabs.json`** —
|
|
148
|
-
`{"tabs": [...]}` — holds the ordered tab list (defaults to `["Production", "Pipeline",
|
|
149
|
-
"Sandbox"]` until first written). No database.
|
|
150
|
-
|
|
151
|
-
## Client variations
|
|
152
|
-
|
|
153
|
-
None — internal/shared design-team tool.
|
|
154
|
-
|
|
155
|
-
## Gotchas / known issues
|
|
156
|
-
|
|
157
|
-
- **Contents API inlines only ≤1 MB.** The GitHub Contents API returns an **empty**
|
|
158
|
-
`content` field for files over ~1 MB. Design exports are routinely multi-MB (the
|
|
159
|
-
`togadesk` demo is ~4.2 MB), so reading them via the base64 Contents response silently
|
|
160
|
-
returned empty content — adopt committed a **0-byte `v1/index.html`** (blank page). Fix:
|
|
161
|
-
`ghReadFile()` uses the **raw media type** (`Accept: application/vnd.github.raw`), which
|
|
162
|
-
streams files up to 100 MB.
|
|
163
|
-
- **Never hardcode the GitHub token in the tracked `index.php`.** `design/index.php` is
|
|
164
|
-
git-**tracked**; only `design/.config.php` is git-ignored. Hardcoding a `ghp_` token in
|
|
165
|
-
`index.php` leaked a live token into pushed `_main` history of `agilantsolutions/forward`.
|
|
166
|
-
Use `getenv('FORWARD_GITHUB_TOKEN')` (EB env property) or the uncommitted `.config.php`. A
|
|
167
|
-
leaked token must be **revoked/rotated** — a later commit does not remove it from history.
|
|
168
|
-
Prefer a **fine-grained PAT** scoped to only the `forward` repo's Contents R/W.
|
|
169
|
-
- **Raise PHP upload limits for multi-MB designs (AL1 / PHP 7.3 specifics).** Default
|
|
170
|
-
`upload_max_filesize` (~2 MB) rejects real multi-MB design exports. The deploy platform
|
|
171
|
-
is **PHP 7.3 on 64-bit Amazon Linux 1**, where these values are **not** settable via the
|
|
172
|
-
`aws:elasticbeanstalk:container:php:phpini` option namespace. Instead a
|
|
173
|
-
`.ebextensions/php-uploads.config` drops a custom ini at
|
|
174
|
-
`/etc/php-7.3.d/99-design-uploads.ini` setting `upload_max_filesize=25M`,
|
|
175
|
-
`post_max_size=30M`, `memory_limit=256M`, `max_execution_time=60`.
|
|
176
|
-
- **Publishing pushes to `_main`** and triggers EB auto-deploy — changes go live in a few
|
|
177
|
-
minutes; there is no staging step.
|
|
178
|
-
|
|
179
|
-
## Change history
|
|
180
|
-
|
|
181
|
-
- 2026-06-23 — Built the Design Demo Admin tool: GitHub-API-only publishing, sequential
|
|
182
|
-
`/vN` versioning with a `project.json` manifest + auto-generated latest-pointer stub,
|
|
183
|
-
adopt/unlist actions, `/design` route. Fixed the >1 MB Contents-API empty-content bug
|
|
184
|
-
(raw media type) and the tracked-token leak (env/`.config.php`). (jcardinal)
|
|
185
|
-
- 2026-06-24 — Added **edit-version** (`apiEditVersion`): an Edit button + modal to revise a
|
|
186
|
-
version's label/author/notes, committing `project.json` only (no stub rebuild; reuses
|
|
187
|
-
`isValidVersionId` + length caps). Added a **Copy URL** button on each project card
|
|
188
|
-
(`navigator.clipboard` → `https://demo.togatech.com/<project>`). Both additive; no new
|
|
189
|
-
security surface. (jcardinal)
|
|
190
|
-
- 2026-06-25 — Added **tab-based organization**: new repo-root `tabs.json` (ordered tab
|
|
191
|
-
list, defaults Production/Pipeline/Sandbox until first written via `create-tab`), optional
|
|
192
|
-
`tab` field on `project.json` (absent = Unsorted), and actions **`create-tab`** /
|
|
193
|
-
**`set-project-tab`** (`apiCreateProject` accepts an optional `tab`). UI filter chip bar
|
|
194
|
-
with per-tab counts, per-card Tab select, and a Tab dropdown on the New Project modal. The
|
|
195
|
-
displayed tab list is the union of `tabs.json` and tabs referenced by projects, so dropping
|
|
196
|
-
a tab from `tabs.json` never orphans a project. `set-project-tab` commits `project.json`
|
|
197
|
-
only (no stub rebuild). (jcardinal)
|
|
198
|
-
|
|
199
|
-
## Related docs
|
|
200
|
-
|
|
201
|
-
- [Static Demo Hosting](static-demo-hosting.md)
|
|
202
|
-
- [Forwarder Architecture](../architecture.md)
|