Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ frontend/coverage/
# Docker Config Volume
# ===================
config/
config-dev/

# ===================
# Environment & Secrets
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ set `PUID`/`PGID` (see the commented lines in `docker-compose.yml`) to run
the backend as that uid/gid — `/config` is chown'd accordingly on startup so
the database and logs end up owned by your host user instead of root.

### Development with Docker

`docker-compose.dev.yml` runs both halves from source with auto-reload
(uvicorn `--reload`, Vite HMR) — the production `docker-compose.yml` is
untouched:

```bash
docker compose -f docker-compose.dev.yml up

# Frontend (proxies /api and /ws): http://localhost:5173
# Backend API directly: http://localhost:8000
```

Dev data lives in `./config-dev` (separate from the production `./config`);
dependencies are cached in named volumes, so only the first start installs.

### Manual Installation

#### Backend
Expand Down
48 changes: 48 additions & 0 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Development compose: source is bind-mounted and both services reload on
# change (uvicorn --reload / vite HMR). Production stays docker-compose.yml.
#
# docker compose -f docker-compose.dev.yml up
#
# Frontend: http://localhost:5173 (proxies /api and /ws to the backend).
# Backend API directly: http://localhost:8000.
# Data/logs live in ./config-dev so dev never touches the production ./config.
# Dependencies are cached in named volumes; the first start installs them.

services:
backend:
image: python:3.14-slim
container_name: steamselfgifter-dev-backend
working_dir: /app
command: >
bash -c "python -m venv /opt/venv &&
/opt/venv/bin/pip install --quiet -e '.[dev]' &&
cd src &&
/opt/venv/bin/uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload"
volumes:
- ./backend:/app:z
- ./config-dev:/config:z
- backend-venv:/opt/venv
environment:
- ENVIRONMENT=development
- DEBUG=true
ports:
- "8000:8000"

frontend:
image: node:24-alpine
container_name: steamselfgifter-dev-frontend
working_dir: /app
command: sh -c "npm install --no-audit --no-fund && npm run dev -- --host 0.0.0.0"
volumes:
- ./frontend:/app:z
- frontend-node-modules:/app/node_modules
environment:
- VITE_PROXY_TARGET=http://backend:8000
ports:
- "5173:5173"
depends_on:
- backend

volumes:
backend-venv:
frontend-node-modules:
8 changes: 6 additions & 2 deletions frontend/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'

// Backend the dev server proxies to; docker-compose.dev.yml points this at
// the backend container, plain local dev keeps the localhost default.
const proxyTarget = process.env.VITE_PROXY_TARGET || 'http://localhost:8000'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
Expand All @@ -15,11 +19,11 @@ export default defineConfig({
proxy: {
// Proxy API requests to backend
'/api': {
target: 'http://localhost:8000',
target: proxyTarget,
changeOrigin: true,
},
'/ws': {
target: 'ws://localhost:8000',
target: proxyTarget.replace(/^http/, 'ws'),
ws: true,
},
},
Expand Down
Loading