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:
+19
-19
@@ -7,7 +7,8 @@ How to run, test and lint the `app` stack.
|
||||
**Integrated — the normal way.** From the workspace root, once: `make init` (submodules,
|
||||
secrets, the per-stack `.env` files, the shared external `proxy` docker network). Then
|
||||
`make dev` brings both stacks up detached behind a single Traefik — which is what lets the
|
||||
SPA, the API and the marketing site answer on one port. `make status` lists the containers,
|
||||
app and the marketing site answer on one port. The app is one container
|
||||
(`backend/Dockerfile`): the engine, its API and the built dashboard on port 8000. `make status` lists the containers,
|
||||
`make down` stops everything.
|
||||
|
||||
The hostname comes from `DOMAIN` in `app/.env`, which `scripts/setup.sh` copies out of the
|
||||
@@ -22,6 +23,11 @@ port 80. `make dev` starts this stack on its own Traefik with all host ports
|
||||
published, in the foreground. `make dev-utils` starts just proxy and mailcatcher — the
|
||||
useful half when the backend runs on the host.
|
||||
|
||||
**Single container.** `docker compose up -d` in this repo's root uses `compose.yml`, the
|
||||
setup the getting-started guide gives: the image on `localhost:8080` with its own Redis, no
|
||||
Traefik, no `.env`. It is a separate project from the stack below and shares nothing
|
||||
with it.
|
||||
|
||||
**No Docker.** `make install` once, then `make dev-backend` (FastAPI on :8000) and
|
||||
`make dev-frontend` (Vite on :5173) in two terminals. The backend needs nothing running: its
|
||||
database is SQLite in `DATA_DIR` (`backend/flow-data` by default), created on first start.
|
||||
@@ -45,9 +51,9 @@ already being taken would stop the whole stack from starting.
|
||||
|
||||
| URL | Service |
|
||||
|---|---|
|
||||
| http://app.localhost | dashboard SPA |
|
||||
| http://api.localhost | backend API |
|
||||
| http://api.localhost/docs | OpenAPI / Swagger UI |
|
||||
| http://app.localhost | dashboard, with the API under `/api/v1` |
|
||||
| http://api.localhost | the same container, for callers configured with the API host |
|
||||
| http://app.localhost/docs | OpenAPI / Swagger UI |
|
||||
| http://localhost | marketing site (the `index` stack) |
|
||||
|
||||
Mailcatcher has no route here, so use the container directly:
|
||||
@@ -59,8 +65,7 @@ Standalone (`cd app && make dev`) publishes the full set:
|
||||
| URL | Service |
|
||||
|---|---|
|
||||
| http://app.localhost, http://api.localhost | this stack's own Traefik on :80 |
|
||||
| http://localhost:5173 | SPA (the nginx image, not the Vite dev server) |
|
||||
| http://localhost:8000 | backend API |
|
||||
| http://localhost:8000 | dashboard and API |
|
||||
| http://localhost:8090 | Traefik dashboard |
|
||||
| http://localhost:1080 | mailcatcher web UI (SMTP on 1025) |
|
||||
|
||||
@@ -74,14 +79,9 @@ syncs the same directories, but only under `docker compose watch`, which no targ
|
||||
before the mount existed keeps serving the source baked into its image and has to be
|
||||
recreated once.
|
||||
|
||||
The frontend is built into an nginx image, so a UI change needs an explicit rebuild, from
|
||||
`app/`:
|
||||
|
||||
```sh
|
||||
docker compose -p fluksio-app --env-file "$PWD/.env" \
|
||||
-f docker/compose.yml -f docker/compose.dev.yml -f docker/compose.local.yml \
|
||||
up --build -d frontend
|
||||
```
|
||||
The dashboard is built into the app image, so a UI change needs a rebuild:
|
||||
`make rebuild-frontend` from `app/`. Only the image's frontend stage runs again, and the
|
||||
container is recreated with the domain and overlays the running one was started with.
|
||||
|
||||
## Services the dev stack adds
|
||||
|
||||
@@ -154,8 +154,8 @@ The specs are `frontend/tests/*.spec.ts`; `playwright.config.ts` defines `setup`
|
||||
in once into `playwright/.auth/user.json`, which the browser projects reuse; `mobile` runs
|
||||
`mobile.spec.ts` only, on a Pixel 5 at 393px.
|
||||
|
||||
Both origins come from `PLAYWRIGHT_BASE_URL` and `PLAYWRIGHT_API_URL`, deliberately never
|
||||
from `VITE_API_URL` — that one belongs to the app build and in a deployment checkout it
|
||||
The origin comes from `PLAYWRIGHT_BASE_URL`, with the API on it unless `PLAYWRIGHT_API_URL`
|
||||
names another, deliberately never from `VITE_API_URL` — that one belongs to the app build and in a deployment checkout it
|
||||
names the deployment, which would mean driving a browser at the local stack while sending
|
||||
teardown `DELETE`s to the live instance. `tests/guard.ts` runs first and refuses the whole
|
||||
run when either origin resolves outside loopback or the private ranges, because the suite
|
||||
@@ -166,7 +166,7 @@ creates and deletes flows, dashboards and users. `PLAYWRIGHT_ALLOW_PUBLIC=1` ove
|
||||
always happens in the Playwright container, which pins the browser by reading
|
||||
`frontend/package.json`. Running Playwright on the host is unsupported — the workspace pins a
|
||||
browser revision whose install is incomplete. The domain comes off the running stack (the
|
||||
frontend container's Traefik rule), so the shots are of whatever `make dev` last started.
|
||||
app container's Traefik rule), so the shots are of whatever `make dev` last started.
|
||||
|
||||
## Lint, hooks and the generated client
|
||||
|
||||
@@ -186,7 +186,7 @@ contract between the two frontends, and that target is the only thing checking i
|
||||
|
||||
`.gitea/workflows/` runs four jobs:
|
||||
`test-backend.yml` (pytest on Python 3.12 and 3.14, plus a check that both wheels build), `playwright.yml` (the e2e suite in
|
||||
two shards, one compose project each, the browser loading the SPA from the nginx `frontend`
|
||||
two shards, one compose project each, the browser loading the SPA from the `backend`
|
||||
service), `pre-commit.yml` (the hooks above, plus strict mypy, which they do not cover) and
|
||||
`test-compose.yml` (a smoke test that the production images actually come up). All but
|
||||
`test-compose.yml` (a smoke test that the production image actually comes up). All but
|
||||
`test-backend.yml` write `.env` from `.env.example` first. The backend suite needs none.
|
||||
|
||||
Reference in New Issue
Block a user