Skip to content
This repository was archived by the owner on May 19, 2026. It is now read-only.

Commit 2ec6778

Browse files
cursoragentkubo6472
andcommitted
Add optional Docker Compose dev stack and document it
Introduce docker-compose.yml with API (backend/Dockerfile), rundown-ui, and prompter services; env vars live in .env.docker (from example). Vite configs read DUOPUS_API_PROXY for the dev proxy target. Update PHASE1.md and AGENTS.md: remove Docker from out-of-scope, explain why Compose exists and how it relates to local dev. Co-authored-by: Jakub Doboš <kubo6472@users.noreply.github.com>
1 parent 437d3d8 commit 2ec6778

9 files changed

Lines changed: 131 additions & 5 deletions

File tree

.env.docker.example

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Used by `docker compose` (see `docker-compose.yml`).
2+
# Copy to `.env.docker` before `docker compose up`: `cp .env.docker.example .env.docker`
3+
# At minimum, set `SECRET_KEY` to a long random string.
4+
5+
SECRET_KEY=change-me-to-a-long-random-string
6+
7+
# SQLite on the named volume `duopus-sqlite`, mounted at /data in the API container.
8+
DATABASE_URL=sqlite+aiosqlite:////data/duopus.db
9+
10+
# Browser origins for the Vite apps (host machine → published ports).
11+
CORS_ORIGINS=http://localhost:5173,http://localhost:5174
12+
13+
ENVIRONMENT=development
14+
AUTOGEN_TIME_UTC=03:00
15+
16+
# Optional: seed admin password (default in docs if unset).
17+
# ADMIN_PASSWORD=

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
.env
2+
.env.docker
23
__pycache__/
34
*.py[cod]
45
.pytest_cache/

AGENTS.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
### Stack (Phase 1)
88

99
- **Backend:** Python 3.12, FastAPI, SQLModel, **SQLite** (`DATABASE_URL`). Dependencies and lockfile live under `backend/` (`pyproject.toml`, `uv.lock`). Schema is created with **`SQLModel.metadata.create_all`** (no Alembic in this phase).
10-
- **Frontend:** `frontend/rundown-ui` and `frontend/prompter` — local dev with Vite (`npm run dev`). There is **no** Docker Compose or bundled nginx in Phase 1; run the API and Vite dev servers separately.
10+
- **Frontend:** `frontend/rundown-ui` and `frontend/prompter` — local dev with Vite (`npm run dev`). For a **one-command dev/smoke-test stack**, use Docker Compose from the repo root (see below); otherwise run the API and Vite dev servers separately.
1111

1212
### Commands
1313

@@ -26,6 +26,11 @@ After changing dependencies in `pyproject.toml`, run `uv lock` and commit `uv.lo
2626
- `cd frontend/rundown-ui && npm install && npm run dev`
2727
- `cd frontend/prompter && npm install && npm run dev` — prompter handoff view (`/prompter/:storyId`); polls the API as described in `docs/PHASE1.md`
2828

29+
**Docker Compose (optional, dev / smoke testing)**
30+
31+
- Copy `.env.docker.example` to `.env.docker` (gitignored) and set at least `SECRET_KEY`; add or change any API env vars there (same semantics as `.env` for the backend).
32+
- From the repo root: `docker compose up --build` — builds `backend/Dockerfile`, runs API on **8000**, rundown UI on **5173**, prompter on **5174**. SQLite uses the `duopus-sqlite` volume; see `docs/PHASE1.md` for rationale and Vite proxy details (`DUOPUS_API_PROXY`).
33+
2934
### Default dev login
3035

3136
See **Seed data** in `docs/PHASE1.md` (admin user and password for local development).

backend/.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
__pycache__/
2+
.pytest_cache/
3+
.mypy_cache/
4+
.ruff_cache/
5+
.venv/
6+
venv/
7+
*.db
8+
.git/
9+
*.egg-info/

backend/Dockerfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Dev / smoke-test API image (Python 3.12 + locked deps via uv).
2+
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim
3+
4+
WORKDIR /app
5+
6+
ENV UV_COMPILE_BYTECODE=1 \
7+
UV_LINK_MODE=copy
8+
9+
COPY pyproject.toml uv.lock ./
10+
RUN --mount=type=cache,target=/root/.cache/uv \
11+
uv sync --frozen --no-install-project --no-dev
12+
13+
COPY . .
14+
15+
EXPOSE 8000
16+
17+
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.yml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Optional dev stack: API + both Vite dev servers.
2+
# Before first run: `cp .env.docker.example .env.docker` and set secrets / overrides there.
3+
services:
4+
api:
5+
build:
6+
context: ./backend
7+
dockerfile: Dockerfile
8+
env_file:
9+
- .env.docker
10+
ports:
11+
- "8000:8000"
12+
volumes:
13+
- duopus-sqlite:/data
14+
healthcheck:
15+
test:
16+
[
17+
"CMD",
18+
"python",
19+
"-c",
20+
"import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health')",
21+
]
22+
interval: 5s
23+
timeout: 3s
24+
retries: 5
25+
start_period: 10s
26+
27+
rundown-ui:
28+
image: node:22-bookworm-slim
29+
working_dir: /app
30+
environment:
31+
DUOPUS_API_PROXY: http://api:8000
32+
volumes:
33+
- ./frontend/rundown-ui:/app
34+
- rundown-ui-node-modules:/app/node_modules
35+
command: sh -c "npm ci && npm run dev -- --host 0.0.0.0"
36+
ports:
37+
- "5173:5173"
38+
depends_on:
39+
api:
40+
condition: service_healthy
41+
42+
prompter:
43+
image: node:22-bookworm-slim
44+
working_dir: /app
45+
environment:
46+
DUOPUS_API_PROXY: http://api:8000
47+
volumes:
48+
- ./frontend/prompter:/app
49+
- prompter-node-modules:/app/node_modules
50+
command: sh -c "npm ci && npm run dev -- --host 0.0.0.0"
51+
ports:
52+
- "5174:5174"
53+
depends_on:
54+
api:
55+
condition: service_healthy
56+
57+
volumes:
58+
duopus-sqlite:
59+
rundown-ui-node-modules:
60+
prompter-node-modules:

docs/PHASE1.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ Phase 1 is a **self-contained, locally-runnable web app**. No broadcast integrat
1717
- WebSockets or Redis
1818
- CasparCG or vMix integration
1919
- Companion / Streamdeck module
20-
- Docker / docker-compose (no containerized stack in this phase)
2120
- Alembic migrations (**SQLModel `create_all` is sufficient**)
2221
- Prompter scroll velocity / mirror flip modes (use QPrompt externally)
2322
- Auth beyond simple **session-based** login (FastAPI-Users with cookies; no JWT complexity)
@@ -211,10 +210,22 @@ Prompter polling (handoff to QPrompt): **`GET /api/prompter/{story_id}`** — la
211210

212211
---
213212

214-
## Running locally (no Docker)
213+
## Running locally
214+
215+
### Without Docker (default)
215216

216217
1. **Backend:** from `backend/`, `uv sync --extra dev`, set `SECRET_KEY` (and optional `DATABASE_URL`), then `uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000`.
217218
2. **Rundown UI:** `frontend/rundown-ui``npm install` && `npm run dev` (Vite proxies `/api` to the backend per `vite.config`).
218219
3. **Prompter:** `frontend/prompter``npm run dev` on a second port if needed; configure API origin as in Vite config.
219220

220221
`CORS_ORIGINS` in `.env` must include the Vite dev origin (e.g. `http://localhost:5173`).
222+
223+
### Optional Docker Compose (dev / smoke testing)
224+
225+
The repo includes a **non-production** Compose stack for manual testing: API on port **8000**, rundown UI on **5173**, prompter on **5174**. It is **not** part of the Phase 1 product scope beyond convenience for developers and agents; behaviour matches the local stack above.
226+
227+
1. Copy **`.env.docker.example`** to **`.env.docker`** (gitignored) and set **`SECRET_KEY`** and any other variables you need. All API-related env vars for the container are read from `.env.docker` so you can tune `DATABASE_URL`, `CORS_ORIGINS`, `ADMIN_PASSWORD`, etc., without touching the compose file.
228+
2. From the repo root: **`docker compose up --build`**.
229+
3. SQLite data is stored on the named volume **`duopus-sqlite`** (path inside the API container is whatever `DATABASE_URL` points to; the example uses `/data/duopus.db` on that volume).
230+
231+
Vite dev servers in Compose proxy `/api` to the **`api`** service via the **`DUOPUS_API_PROXY`** environment variable (`http://api:8000`). For local (non-Docker) Vite, the default proxy remains `http://127.0.0.1:8000`.

frontend/prompter/vite.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import react from "@vitejs/plugin-react";
22
import { defineConfig } from "vite";
33

4+
/** Dev server proxy target (Docker Compose sets `http://api:8000`). */
5+
const apiProxy = process.env.DUOPUS_API_PROXY ?? "http://127.0.0.1:8000";
6+
47
export default defineConfig({
58
plugins: [react()],
69
base: "/prompter/",
710
server: {
811
port: 5174,
912
proxy: {
10-
"/api": "http://127.0.0.1:8000",
13+
"/api": apiProxy,
1114
},
1215
},
1316
});

frontend/rundown-ui/vite.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import react from "@vitejs/plugin-react";
22
import { defineConfig } from "vite";
33

4+
/** Dev server proxy target (Docker Compose sets `http://api:8000`). */
5+
const apiProxy = process.env.DUOPUS_API_PROXY ?? "http://127.0.0.1:8000";
6+
47
export default defineConfig({
58
plugins: [react()],
69
base: "/",
710
server: {
811
port: 5173,
912
proxy: {
10-
"/api": "http://127.0.0.1:8000",
13+
"/api": apiProxy,
1114
},
1215
},
1316
});

0 commit comments

Comments
 (0)