R3: split the landing into its own static container
- gateway/Dockerfile gains a `landing` target: caddy:2-alpine + the shared Vite build (identical build args keep the ui stage a single cached build); the gateway target drops landing.html from the embed. - The contour caddy routes /app/, /telegram/ and the Connect path to the gateway; the catch-all — the landing at / and any stray path — goes to the new landing service, so junk traffic is absorbed by static file serving. - deploy/landing/Caddyfile mirrors the webui caching (immutable assets, no-cache shells) and falls back unknown paths to the landing shell. - The gateway's / now 308-redirects to /app/ (keeps a local no-caddy run usable); webui placeholder landing.html removed. - CI deploy probe checks both / (landing) and /app/ (gateway). Verified: both images build; the landing container serves landing.html at / (no-cache) with junk-path fallback; the gateway image redirects / to /app/ and carries no landing content.
This commit is contained in:
+12
-4
@@ -1,7 +1,9 @@
|
||||
# Edge reverse proxy for the Scrabble contour. A single Basic-Auth gate covers
|
||||
# every operator surface under /_gm (the backend-rendered admin console and the
|
||||
# Grafana subpath); everything else (the SPA at / and /telegram/, plus the
|
||||
# Connect edge) goes to the gateway. Mirrors ../galaxy-game's /_gm model.
|
||||
# Grafana subpath); the game SPA (/app/, /telegram/) and the Connect edge go to
|
||||
# the gateway; the catch-all — notably the public landing at / — goes to the
|
||||
# static landing container (R3), so stray traffic never reaches the Go edge.
|
||||
# Mirrors ../galaxy-game's /_gm model.
|
||||
#
|
||||
# CADDY_SITE_ADDRESS is ":80" in the test contour (the host caddy terminates TLS
|
||||
# and forwards); set it to a domain in prod (Stage 18) so this caddy does its own
|
||||
@@ -36,8 +38,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
# The SPA (/, /telegram/) and the Connect edge are served by the gateway.
|
||||
handle {
|
||||
# The game SPA and the Connect edge are served by the gateway.
|
||||
@gateway path /app /app/* /telegram /telegram/* /scrabble.edge.v1.Gateway/*
|
||||
handle @gateway {
|
||||
reverse_proxy gateway:8081
|
||||
}
|
||||
|
||||
# Everything else — the public landing at / and any stray path — is static.
|
||||
handle {
|
||||
reverse_proxy landing:80
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: gateway/Dockerfile
|
||||
target: gateway
|
||||
args:
|
||||
VITE_TELEGRAM_BOT_ID: ${VITE_TELEGRAM_BOT_ID:-}
|
||||
VITE_TELEGRAM_LINK: ${VITE_TELEGRAM_LINK:-}
|
||||
@@ -100,6 +101,28 @@ services:
|
||||
# caddy owns the /_gm Basic-Auth and routes /_gm to the backend directly.
|
||||
networks: [internal]
|
||||
|
||||
# --- Landing (static) -------------------------------------------------------
|
||||
# The public landing page in its own caddy container (R3): the contour caddy
|
||||
# routes the catch-all (notably /) here, the gateway keeps only /app/,
|
||||
# /telegram/ and the Connect edge. Shares the gateway Dockerfile's UI build
|
||||
# stage — identical build args keep that stage a single cached build.
|
||||
landing:
|
||||
container_name: scrabble-landing
|
||||
image: scrabble-landing:latest
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: gateway/Dockerfile
|
||||
target: landing
|
||||
args:
|
||||
VITE_TELEGRAM_BOT_ID: ${VITE_TELEGRAM_BOT_ID:-}
|
||||
VITE_TELEGRAM_LINK: ${VITE_TELEGRAM_LINK:-}
|
||||
VITE_TELEGRAM_GAME_CHANNEL_NAME_EN: ${VITE_TELEGRAM_GAME_CHANNEL_NAME_EN:-}
|
||||
VITE_TELEGRAM_GAME_CHANNEL_NAME_RU: ${VITE_TELEGRAM_GAME_CHANNEL_NAME_RU:-}
|
||||
VITE_GATEWAY_URL: ${VITE_GATEWAY_URL:-}
|
||||
VITE_APP_VERSION: ${APP_VERSION:-dev}
|
||||
restart: unless-stopped
|
||||
networks: [internal]
|
||||
|
||||
# --- Telegram connector (egress via the VPN sidecar) -----------------------
|
||||
vpn:
|
||||
container_name: scrabble-telegram-vpn
|
||||
@@ -145,12 +168,13 @@ services:
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: http://otelcol:4317
|
||||
OTEL_EXPORTER_OTLP_INSECURE: "true"
|
||||
|
||||
# --- Edge reverse proxy (single /_gm Basic-Auth; SPA + Connect -> gateway) --
|
||||
# --- Edge reverse proxy (single /_gm Basic-Auth; SPA + Connect -> gateway;
|
||||
# the catch-all incl. the landing -> the static landing container) -------
|
||||
caddy:
|
||||
container_name: scrabble-caddy
|
||||
image: caddy:2-alpine
|
||||
restart: unless-stopped
|
||||
depends_on: [gateway, backend, grafana]
|
||||
depends_on: [gateway, backend, grafana, landing]
|
||||
environment:
|
||||
# Test: ":80" (host caddy terminates TLS). Prod: a domain for own ACME.
|
||||
CADDY_SITE_ADDRESS: ${CADDY_SITE_ADDRESS:-:80}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Static landing container (R3). Serves the public landing page and the built
|
||||
# assets it references at /; the game SPA (/app/, /telegram/) and the Connect
|
||||
# edge stay on the gateway. The contour caddy routes the catch-all here, so
|
||||
# stray public paths are absorbed by static file serving and never reach the Go
|
||||
# edge. This file is baked into the image at build time (gateway/Dockerfile,
|
||||
# target `landing`), not bind-mounted.
|
||||
{
|
||||
admin off
|
||||
}
|
||||
|
||||
:80 {
|
||||
root * /srv
|
||||
encode zstd gzip
|
||||
|
||||
# Mirror the gateway webui caching: hash-named build assets are immutable,
|
||||
# every HTML shell is no-cache so a new deploy is picked up immediately.
|
||||
header /assets/* Cache-Control "public, max-age=31536000, immutable"
|
||||
@shell not path /assets/*
|
||||
header @shell Cache-Control "no-cache"
|
||||
|
||||
# An unknown path falls back to the landing shell (the gateway's old "/"
|
||||
# behaviour); "/" itself resolves through the index below.
|
||||
try_files {path} /landing.html
|
||||
file_server {
|
||||
index landing.html
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user