eptm_dashboard/Dockerfile.prod
Julien Balet 7d3b6e9136 v1.1.0 — fixes sync + UX dev/prod
Sync push_then_sync : préserve les absences 'publiee_escada' contre
écrasement/orphelines après push (PDF Escada stale). UI reconnaît le
statut (calendrier, éditeur, KPIs) au lieu d'afficher 'présent'.

Sync_esacada : timeout grille 20s → 45s + retry après reload (AUTOMAT 1
échouait à la 1re classe après changement de langue).

Telegram : ajoute liste d'erreurs + tail du log dans les notifs d'échec
même en mode normal — avant on avait juste 'a échoué (code 1)'.

UX :
- Calendrier toujours visible (même sans absences) et démarre sur le
  mois courant (pas sur le 1er mois d'absence) ; tous les jours
  cliquables pour pouvoir ajouter une absence.
- Date du jour pré-sélectionnée aussi via navigate_to (clic depuis
  /classe).
- KPIs cards taggées kpi-card/kpi-value pour CSS responsive mobile.
- Badge 'DEV' dans la sidebar (APP_ENV=dev) — invisible en prod.
- Badge 'Built with Reflex' masqué.
- KPIs retirés du dashboard /accueil.

Prod :
- Dockerfile.prod multi-stage (Reflex export bundle + runtime slim).
- docker-compose.prod.yml séparé (port 3002, projet eptm-dashboard-prod).
- .gitignore + .dockerignore nettoyés.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 09:11:39 +02:00

78 lines
3.5 KiB
Text

# Dockerfile.prod — image immuable pour la stack prod EPTM Dashboard.
#
# Multi-stage :
# 1. builder : installe deps Python + Node, exporte le frontend Reflex.
# 2. runtime : Python slim, sans Node, sans cache npm, prêt à servir.
#
# Build : docker compose -f docker-compose.prod.yml build app
# ─────────────────────────────────────────────────────────────────────────────
# Stage 1 — builder
FROM python:3.13 AS builder
WORKDIR /app
# Outils nécessaires au bundle frontend (Node + npm sont requis par `reflex export`).
RUN apt-get update && apt-get install -y --no-install-recommends \
curl gnupg unzip xvfb ca-certificates && \
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && \
apt-get install -y nodejs && \
rm -rf /var/lib/apt/lists/*
# Dépendances Python (installées dans le user-site pour pouvoir les copier
# proprement dans le stage 2 — évite de réinstaller pip dans le runtime).
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt && \
pip install --no-cache-dir \
pdfplumber sqlalchemy plotly pandas openpyxl bcrypt pyyaml pypdf \
pyotp "qrcode[pil]" reportlab playwright markdown
# Playwright : navigateur installé en system-wide pour le stage 2.
RUN playwright install --with-deps chromium
# Code applicatif + assets statiques + docs + templates AcroForm.
COPY . .
# `reflex init` prépare la conf locale (.web/, alembic, etc.).
# `reflex export --frontend-only --no-zip` génère le bundle Vite statique
# dans .web/build/ — c'est ce que servira le backend en prod.
RUN reflex init && \
reflex export --frontend-only --no-zip
# ─────────────────────────────────────────────────────────────────────────────
# Stage 2 — runtime
FROM python:3.13
WORKDIR /app
# Runtime allégé : Node n'est PAS réinstallé (`reflex export` a déjà créé le
# bundle). On garde curl pour les healthchecks et ca-certificates pour SMTP/HTTPS.
RUN apt-get update && apt-get install -y --no-install-recommends \
curl ca-certificates tzdata && \
rm -rf /var/lib/apt/lists/*
# Copie les Python deps installées dans le builder.
COPY --from=builder /usr/local/lib/python3.13/site-packages /usr/local/lib/python3.13/site-packages
COPY --from=builder /usr/local/bin /usr/local/bin
# Playwright browsers (cache global déjà téléchargé dans le builder).
COPY --from=builder /root/.cache/ms-playwright /root/.cache/ms-playwright
# Libs système requises par Chromium headless (libnspr4, libnss3, fonts, etc.).
# Sans ça, le binaire chromium-headless-shell ne charge pas et la sync Escada
# meurt avec "error while loading shared libraries: libnspr4.so".
RUN apt-get update && \
playwright install-deps chromium && \
rm -rf /var/lib/apt/lists/*
# Copie l'app entièrement déjà bundlée (avec .web/ + reflex.json + etc.).
COPY --from=builder /app /app
# Reflex 0.9+ exige un seul port en prod (frontend statique + backend granian).
# 3002 choisi pour cohabiter avec dev (3001 frontend Vite + 8001 backend).
ENV FRONTEND_PORT=3002 BACKEND_PORT=3002 \
TZ=Europe/Zurich \
PYTHONUNBUFFERED=1
EXPOSE 3002
# `reflex run --env prod` lance backend granian + sert le frontend depuis .web/build.
CMD ["reflex", "run", "--env", "prod"]