Move static asset + SPA-shell ownership from backend to frontend (repo-split Stage 7a) (#22)

This commit is contained in:
emi 2026-07-21 22:48:59 +00:00
parent 20054ab198
commit 24749de0c7
2 changed files with 21 additions and 17 deletions

View file

@ -14,23 +14,25 @@
# at "/" with no sub-path prefix. Repo-split Stage 4: backend and frontend are # at "/" with no sub-path prefix. Repo-split Stage 4: backend and frontend are
# two containers now (docker-compose.yml), each on its own loopback port -- # two containers now (docker-compose.yml), each on its own loopback port --
# Caddy path-splits directly to whichever owns a given path, so the browser # Caddy path-splits directly to whichever owns a given path, so the browser
# still sees one apparent origin. The exact path list mirrors # still sees one apparent origin. Repo-split Stage 7a flipped which side owns
# backend/web/app.py's _FRONTEND_ROUTES; everything not listed (API, the # the enumerated list: frontend now owns everything (content pages, the
# interactive tool's SPA pages, static assets, /digest, /discord/interactions, # interactive tool's SPA shells, every static asset, the dynamic IndexNow key
# and the dynamic IndexNow key file) falls through to backend, which also # file) except the short, stable set below, which mirrors backend/web/app.py's
# carries its own reverse-proxy fallback for the frontend-owned paths -- so a # own routing exactly (a single catch-all proxy to frontend for everything
# gap in this list degrades to "one extra hop", never a 404. # else) -- unlike frontend's paths, backend's don't grow every time a new
# static asset filename is added. A gap in this list still just degrades to
# "one extra hop" through backend's own proxy fallback, never a 404.
thermograph.org { thermograph.org {
encode zstd gzip encode zstd gzip
@frontend_paths path / /about /privacy /glossary /glossary/* /climate /climate/* /robots.txt /sitemap.xml @backend_paths path /api/* /digest /discord/interactions
# Active health check on the same cheap /healthz route each container's own # Active health check on the same cheap /healthz route each container's own
# HEALTHCHECK uses (Dockerfile) — so a deploy that's still restarting/booting # HEALTHCHECK uses (Dockerfile) — so a deploy that's still restarting/booting
# never gets proxied into (a reload alone has no gate, hop-1 runbook hazard #10). # never gets proxied into (a reload alone has no gate, hop-1 runbook hazard #10).
handle @frontend_paths { handle @backend_paths {
reverse_proxy 127.0.0.1:8080 { reverse_proxy 127.0.0.1:8137 {
health_uri /healthz health_uri /healthz
health_interval 5s health_interval 5s
health_timeout 3s health_timeout 3s
@ -39,7 +41,7 @@ thermograph.org {
} }
handle { handle {
reverse_proxy 127.0.0.1:8137 { reverse_proxy 127.0.0.1:8080 {
health_uri /healthz health_uri /healthz
health_interval 5s health_interval 5s
health_timeout 3s health_timeout 3s

View file

@ -4,23 +4,25 @@
# #
# The app owns the domain root and runs with THERMOGRAPH_BASE=/. Repo-split # The app owns the domain root and runs with THERMOGRAPH_BASE=/. Repo-split
# Stage 4: backend (127.0.0.1:${port}) and frontend (127.0.0.1:${frontend_port}) # Stage 4: backend (127.0.0.1:${port}) and frontend (127.0.0.1:${frontend_port})
# are two containers now -- path-split directly to whichever owns a given path, # are two containers now -- path-split directly to whichever owns a given
# same list backend/web/app.py's own reverse-proxy fallback (for Caddy-less # path. Repo-split Stage 7a flipped which side owns the enumerated list:
# hosts) uses, so the two never disagree about who owns what. # backend owns the short, stable set below (mirrors backend/web/app.py's own
# routing exactly -- a single catch-all proxy to frontend for everything
# else), so the two never disagree about who owns what.
# #
# NOTE: the repo's deploy/Caddyfile additionally serves the emigriffith.dev portfolio # NOTE: the repo's deploy/Caddyfile additionally serves the emigriffith.dev portfolio
# and legacy redirects; those are host-specific and intentionally not templated here. # and legacy redirects; those are host-specific and intentionally not templated here.
${domain} { ${domain} {
encode zstd gzip encode zstd gzip
@frontend_paths path / /about /privacy /glossary /glossary/* /climate /climate/* /robots.txt /sitemap.xml @backend_paths path /api/* /digest /discord/interactions
# Active health check on the same cheap /healthz route each container's own # Active health check on the same cheap /healthz route each container's own
# HEALTHCHECK uses (Dockerfile) — so a `docker compose up -d --build` deploy # HEALTHCHECK uses (Dockerfile) — so a `docker compose up -d --build` deploy
# that's still restarting/booting never gets proxied into (a reload alone has # that's still restarting/booting never gets proxied into (a reload alone has
# no gate, hop-1 runbook hazard #10). health_uri is relative to the upstream. # no gate, hop-1 runbook hazard #10). health_uri is relative to the upstream.
handle @frontend_paths { handle @backend_paths {
reverse_proxy 127.0.0.1:${frontend_port} { reverse_proxy 127.0.0.1:${port} {
health_uri /healthz health_uri /healthz
health_interval 5s health_interval 5s
health_timeout 3s health_timeout 3s
@ -29,7 +31,7 @@ ${domain} {
} }
handle { handle {
reverse_proxy 127.0.0.1:${port} { reverse_proxy 127.0.0.1:${frontend_port} {
health_uri /healthz health_uri /healthz
health_interval 5s health_interval 5s
health_timeout 3s health_timeout 3s