nginx: serve mgmt /static/ assets from disk (no Flask round-trip)
Generated management-domain server blocks now include a location /static/ aliasing webui/static/ with Cache-Control: no-cache (ETag revalidation -> 304), nosniff, and a restrictive CSP, so SPA asset requests no longer reach Flask. Flask's static route remains the dev-mode fallback. - lib/nginx.py + daemon/handlers/nginx.py: static_root render context (the handler renders the template directly, so both paths need it) - template: alias uses a trailing slash (nginx concatenates the location remainder onto the alias value) - install.sh: a+rX on webui/static plus execute-up-the-parent-chain so the nginx worker (www-data) can traverse repo-in-$HOME installs - tests: mgmt static location assertions (positive + non-mgmt negative) - docs: AGENTS.md, architecture.md, security.md static-serving notes
This commit is contained in:
@@ -337,7 +337,7 @@ The web UI is a single-page application built on **Hoover**, a custom lightweigh
|
||||
|
||||
```
|
||||
Client requests / ──→ nginx ──→ Flask (serves index.html)
|
||||
Client loads /static/app.js ──→ Hoover initializes, checkSession() (401 with valid refresh token → one refresh) → if no valid session, render #login
|
||||
Client loads /static/app.js ──→ served by nginx directly from disk (mgmt `location /static/` alias, no Flask round-trip) ──→ Hoover initializes, checkSession() (401 with valid refresh token → one refresh) → if no valid session, render #login
|
||||
Authenticated ──→ mounts #sidebar and #main render roots
|
||||
apiFetch() ──→ injects Authorization: Bearer <token> header ──→ Flask REST API
|
||||
Flask before_request ──→ validates JWT from header, checks blacklist, verifies permissions
|
||||
@@ -348,7 +348,7 @@ Token expiry ──→ refreshScheduler() ──→ POST /api/auth/refresh ─
|
||||
WS connect ──→ snapshot (full state) / versions + tick deltas (per-subsystem data) ──→ modelSet() patches model in place ──→ render engine VDOM-diffs and patches only changed DOM nodes
|
||||
```
|
||||
|
||||
The SPA entry point only serves `index.html` at `/`. All other paths return 404. Non-API, non-static paths are not served by Flask — the client-side router handles all navigation via hash changes. A dedicated `/vendor/<path>` route serves vendored JS libraries.
|
||||
The SPA entry point only serves `index.html` at `/`. All other paths return 404. Non-API, non-static paths are not served by Flask — the client-side router handles all navigation via hash changes. A dedicated `/vendor/<path>` route serves vendored JS libraries. On the management domain, nginx serves `/static/` directly from `webui/static/` via a `location /static/` alias in the generated server block, so asset requests never reach Flask in production; Flask's static route remains as the dev-mode fallback.
|
||||
|
||||
### Component Model
|
||||
|
||||
@@ -356,7 +356,7 @@ Each route is a `definePage()` component with reactive state, async data loading
|
||||
|
||||
### No Build Step
|
||||
|
||||
All JavaScript is served as ES modules. Cache invalidation is handled via HTTP cache-control headers. Dev mode (`VACUUM_WALL_DEV`) disables aggressive static asset caching.
|
||||
All JavaScript is served as ES modules. Cache invalidation is handled via HTTP cache-control headers: the management domain's `/static/` assets carry `Cache-Control: no-cache` (browsers revalidate every load; unchanged files return 304 via nginx's built-in ETag), so updates are picked up on the next page load. Dev mode (`VACUUM_WALL_DEV`) uses short TTLs instead.
|
||||
|
||||
### WebSocket Data Streaming
|
||||
|
||||
|
||||
Reference in New Issue
Block a user