feat: déploiement Docker en conteneur unique (API + UI)

Le backend FastAPI sert déjà le SPA buildé : on abandonne le duo
nginx + backend au profit d'une seule image multi-stage (build Vue
puis service par le backend). docker-compose passe à un service
unique, avec PLESNA_DATA_DIR déclarant le stockage persistant.

- backend.Dockerfile : stage node build -> copie frontend/dist,
  CMD en `uv run --no-sync` (plus de resync des deps dev au démarrage)
- docker-compose.yml : service unique `app`, volume ./data,
  PLESNA_DATA_DIR=/app/data
- supprime frontend/Dockerfile, frontend/nginx.conf, frontend/.dockerignore
- .dockerignore : ignore **/node_modules, data, data_bck
- README : section Production mise à jour

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 22:14:56 +02:00
parent c79ecc45ff
commit 0d39ea810b
7 changed files with 43 additions and 48 deletions

View File

@@ -3,9 +3,14 @@
venv
__pycache__
*.pyc
# node_modules réinstallés dans le stage de build (à tous les niveaux)
node_modules
**/node_modules
# frontend/dist est rebuildé dans l'image, jamais copié depuis l'hôte
frontend/dist
# données locales : ne jamais les embarquer dans l'image
data
data_bck
*.egg-info
.mypy_cache
.ruff_cache

View File

@@ -37,12 +37,20 @@ Ouvrir **http://localhost:5173** — le proxy Vite redirige `/api` vers le backe
## Production (Docker)
Un **conteneur unique** : l'image builde le frontend Vue puis le fait servir
par le backend FastAPI (API + interface web sur le meme port). Pas de nginx.
```bash
make docker
make docker # podman-compose up --build
# ou : docker compose up --build
```
Accessible sur **http://localhost:8080**.
Les donnees (base SQLite + documents) sont declarees via `PLESNA_DATA_DIR`
(voir `docker-compose.yml`) et persistees dans le volume `./data`, donc
conservees entre les redemarrages du conteneur.
## Application de bureau (Windows, sans ligne de commande)
L'application peut etre empaquetee en **executable Windows autonome** : un seul

View File

@@ -1,3 +1,18 @@
# syntax=docker/dockerfile:1
# --- Étape 1 : build du frontend Vue -----------------------------------------
# Le backend FastAPI sert lui-même le SPA (frontend/dist) : un seul conteneur
# suffit, pas de nginx séparé. On builde donc le frontend ici puis on copie le
# résultat dans l'image Python finale.
FROM node:22-alpine AS frontend
WORKDIR /frontend
COPY frontend/package.json frontend/package-lock.json* ./
RUN npm install
COPY frontend/ ./
RUN npm run build
# --- Étape 2 : image applicative Python --------------------------------------
FROM python:3.12-slim
# pdfplumber est en Python pur : aucune dépendance système (plus de poppler).
@@ -11,6 +26,11 @@ RUN uv sync --frozen --no-dev --no-install-project
COPY src/ src/
RUN uv sync --frozen --no-dev
# Frontend buildé : servi par le backend via le fallback SPA (api/app.py).
COPY --from=frontend /frontend/dist/ frontend/dist/
EXPOSE 8000
CMD ["uv", "run", "plesna-gerance", "serve", "--host", "0.0.0.0"]
# --no-sync : ne pas re-résoudre/installer au démarrage (l'env est déjà figé
# par les `uv sync --frozen` ci-dessus). Sinon `uv run` réinstalle les deps dev.
CMD ["uv", "run", "--no-sync", "plesna-gerance", "serve", "--host", "0.0.0.0"]

View File

@@ -1,21 +1,16 @@
services:
backend:
app:
build:
context: .
dockerfile: backend.Dockerfile
ports:
# hôte:conteneur — l'app (API + interface web) écoute sur 8000
- "8080:8000"
volumes:
# Persiste base SQLite + documents hors du conteneur.
- ./data:/app/data
environment:
- PLESNA_DB_PATH=/app/data/database.sqlite
- PLESNA_STORAGE_PATH=/app/data/documents
restart: unless-stopped
frontend:
build:
context: ./frontend
dockerfile: Dockerfile
ports:
- "8080:80"
depends_on:
- backend
# Déclare l'emplacement des données ; get_data_dir() en dérive
# database.sqlite et documents/ (cf. src/plesna_gerance/paths.py).
- PLESNA_DATA_DIR=/app/data
restart: unless-stopped

View File

@@ -1,2 +0,0 @@
node_modules
dist

View File

@@ -1,12 +0,0 @@
FROM node:22-alpine AS build
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80

View File

@@ -1,19 +0,0 @@
server {
listen 80;
root /usr/share/nginx/html;
index index.html;
# Proxy API requests to backend
location /api {
proxy_pass http://backend:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 50M;
}
# SPA fallback
location / {
try_files $uri $uri/ /index.html;
}
}