One image for the whole app: engine, API and dashboard on one port
The backend image now builds the SPA and serves it on the API's own origin (fluksio/spa.py), so an instance is a single container with no proxy, DNS, port 80 or .env: `docker compose up -d` with the new compose.yml runs it on localhost:8080. It starts on fluksio serve (persisted secret key, first admin printed once) and brings its own Redis when REDIS_HOST is unset. Slimmer: python:3.13-slim, no dev group, uv once, fastapi without the cloud CLI. Optional features are per-feature extras (mqtt, influx, email, sentry, ml, server for all) picked with the EXTRAS build argument; numpy moved to `ml`. ffmpeg left the image for imageio-ffmpeg via the Modules screen. The operator stack runs the same image with app. and api. routed to it; the nginx frontend service, the LAN nginx proxy and DOMAIN in the backend are gone, and the MCP issuer is FRONTEND_HOST.
This commit is contained in:
@@ -17,13 +17,13 @@ COMPOSE_PROD := $(COMPOSE) -f docker/compose.yml
|
||||
# Production also runs autoheal, which restarts the backend when its deep
|
||||
# health check fails. It is profile-gated because it mounts the Docker socket.
|
||||
COMPOSE_PROD_RUN := $(COMPOSE_PROD) --profile autoheal
|
||||
# Host port `make dev-lan` publishes the frontend on.
|
||||
# Host port `make dev-lan` publishes the app on.
|
||||
APP_PORT ?= 8080
|
||||
COMPOSE_DEV := $(COMPOSE_PROD) -f docker/compose.dev.yml
|
||||
# Integrated local stack: dev stack wired onto the shared `proxy` network.
|
||||
COMPOSE_LOCAL := $(COMPOSE_DEV) -f docker/compose.local.yml
|
||||
# Same stack, with the frontend also published on APP_PORT and proxying the API
|
||||
# there, for clients that cannot resolve app.$(DOMAIN).
|
||||
# Same stack, with the app also published on APP_PORT, for clients that cannot
|
||||
# resolve app.$(DOMAIN).
|
||||
COMPOSE_LAN := $(COMPOSE_LOCAL) -f docker/compose.lan.yml
|
||||
# The frontend suite's own stack, a project of its own behind the local Traefik
|
||||
# (compose.test.yml). The hostname is fixed, never read from a file or the
|
||||
@@ -79,7 +79,7 @@ dev: ## Start the full dev stack (includes a local Traefik proxy)
|
||||
|
||||
dev-local: ## Start the local stack at http://app.localhost (also run by the root `make dev`)
|
||||
DOMAIN=$${DOMAIN:-localhost} ENVIRONMENT=$${ENVIRONMENT:-local} \
|
||||
$(COMPOSE_LOCAL) up --build -d proxy backend frontend docs mailcatcher
|
||||
$(COMPOSE_LOCAL) up --build -d --remove-orphans proxy backend docs mailcatcher
|
||||
|
||||
# The host's LAN address is the source of its default route. Without `ip`
|
||||
# (macOS) it stays empty and the instance keeps calling itself app.$(DOMAIN).
|
||||
@@ -87,21 +87,18 @@ dev-lan: ## Same, plus the app on http://<host-ip>:$(APP_PORT) (no DNS needed)
|
||||
@ip=$$(ip route get 1.1.1.1 2>/dev/null | sed -n 's/.* src \([0-9.]*\).*/\1/p'); \
|
||||
DOMAIN=$${DOMAIN:-localhost} ENVIRONMENT=$${ENVIRONMENT:-local} APP_PORT=$(APP_PORT) \
|
||||
LAN_URL=$${ip:+http://$$ip:$(APP_PORT)} \
|
||||
$(COMPOSE_LAN) up --build -d proxy backend frontend mailcatcher
|
||||
$(COMPOSE_LAN) up --build -d --remove-orphans proxy backend mailcatcher
|
||||
|
||||
# The frontend is an nginx image, so a UI change needs a rebuild. Both the
|
||||
# domain and the overlay set come off the *running* stack rather than from a
|
||||
# file: VITE_API_URL is baked in at build time, so rebuilding under a different
|
||||
# domain — or without the lan overlay a stack was started with — leaves the SPA
|
||||
# calling an API that answers elsewhere. The lan overlay is what publishes the
|
||||
# host port and builds with an empty VITE_API_URL, so dropping it takes a wall
|
||||
# panel reaching http://<host-ip>:$(APP_PORT) off the air entirely and bakes a
|
||||
# name it cannot resolve into the bundle. A published host port is how that
|
||||
# overlay is recognised, because it is the thing only that overlay adds.
|
||||
rebuild-frontend: ## Rebuild and restart the local frontend (after a UI change)
|
||||
@domain=$$(docker inspect fluksio-app 2>/dev/null \
|
||||
# The dashboard is built into the app image, so a UI change needs a rebuild;
|
||||
# only the image's frontend stage runs again. The domain and the overlay set
|
||||
# come off the *running* stack rather than from a file, so the rebuilt
|
||||
# container keeps the hostname and, on a LAN stack, the published port it was
|
||||
# started with. A published host port is how the lan overlay is recognised,
|
||||
# because it is the thing only that overlay adds.
|
||||
rebuild-frontend: ## Rebuild the app image and restart it (after a UI change)
|
||||
@domain=$$(docker inspect fluksio-api 2>/dev/null \
|
||||
| grep -o 'Host(`app\.[^`]*`)' | head -1 | sed 's/Host(`app\.//;s/`)//'); \
|
||||
port=$$(docker inspect fluksio-app \
|
||||
port=$$(docker inspect fluksio-api \
|
||||
--format '{{range $$p, $$c := .NetworkSettings.Ports}}{{range $$c}}{{.HostPort}} {{end}}{{end}}' \
|
||||
2>/dev/null | awk '{print $$1}'); \
|
||||
if [ -n "$$port" ]; then \
|
||||
@@ -112,7 +109,7 @@ rebuild-frontend: ## Rebuild and restart the local frontend (after a UI change)
|
||||
fi; \
|
||||
DOMAIN=$${domain:-$$DOMAIN} ENVIRONMENT=$${ENVIRONMENT:-local} \
|
||||
APP_PORT=$${port:-$(APP_PORT)} \
|
||||
$$compose up --build -d frontend
|
||||
$$compose up --build -d backend
|
||||
|
||||
up: ## Start the production stack
|
||||
$(COMPOSE_PROD_RUN) up --build -d
|
||||
@@ -204,14 +201,13 @@ PW_VERSION = $(shell sed -n 's/.*"@playwright\/test": "[^0-9]*\([0-9.]*\)".*/\1/
|
||||
test-stack: ## Start or update the frontend suite's own stack at http://app.test.localhost
|
||||
@docker ps -q -f name='^fluksio-app-proxy-1$$' | grep -q . \
|
||||
|| { echo " ✗ the local Traefik is not running, 'make dev' first"; exit 1; }
|
||||
$(COMPOSE_TEST) up --build -d --wait redis backend frontend
|
||||
$(COMPOSE_TEST) up --build -d --wait --remove-orphans redis backend
|
||||
|
||||
# The Playwright container, minus its network. tests/guard.ts refuses a public
|
||||
# address on top of the fixed hostname.
|
||||
PW_RUN = docker run --rm --ipc=host --user $$(id -u):$$(id -g) -e HOME=/tmp \
|
||||
-v $(COMPOSE_ROOT):/app -w /app/frontend \
|
||||
-e PLAYWRIGHT_BASE_URL=http://app.$(TEST_DOMAIN) \
|
||||
-e PLAYWRIGHT_API_URL=http://api.$(TEST_DOMAIN) \
|
||||
-e CI=$${CI:-1} \
|
||||
-e FIRST_SUPERUSER="$$(sed -n 's/^FIRST_SUPERUSER=//p' $(COMPOSE_ROOT)/.env | head -1)" \
|
||||
-e FIRST_SUPERUSER_PASSWORD="$$(sed -n 's/^FIRST_SUPERUSER_PASSWORD=//p' $(COMPOSE_ROOT)/.env | head -1)"
|
||||
@@ -225,8 +221,8 @@ test-frontend: test-stack ## Run frontend tests (Playwright e2e) against the te
|
||||
2>/dev/null | cut -d/ -f1); \
|
||||
[ -n "$$ip" ] || { echo " ✗ the local Traefik is not on the proxy network"; exit 1; }; \
|
||||
$(PW_RUN) --network proxy \
|
||||
--add-host app.$(TEST_DOMAIN):$$ip --add-host api.$(TEST_DOMAIN):$$ip \
|
||||
-e HOST_RESOLVER_RULES="MAP app.$(TEST_DOMAIN) $$ip, MAP api.$(TEST_DOMAIN) $$ip" \
|
||||
--add-host app.$(TEST_DOMAIN):$$ip \
|
||||
-e HOST_RESOLVER_RULES="MAP app.$(TEST_DOMAIN) $$ip" \
|
||||
$(PW_IMAGE) npx playwright test $(PLAYWRIGHT_ARGS)
|
||||
|
||||
# WebKit takes *.localhost to loopback through GLib and ignores /etc/hosts, so
|
||||
|
||||
Reference in New Issue
Block a user