Stack d'orchestration Figures Libres (Docker Compose)
Câble ensemble le générateur (dépôt generator/) et le CMS Grav d'exemple (dépôt grav-reference/, monté :ro), tous deux clonés côte à côte et non versionnés ici. Contient : docker-compose dev + surcouche prod (rsync/SSH), image Grav (docker/grav), doc de câblage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
# Dépôts séparés, clonés côte à côte (chacun son propre remote) :
|
||||
# generator/ = l'app Nuxt = le livrable (gitea figureslibres.io)
|
||||
# grav-reference/ = le CMS d'exemple = son propre dépôt, ~4,8 Go
|
||||
generator/
|
||||
grav-reference/
|
||||
|
||||
# Docs : PDF de référence + spec (déjà réalisée, désormais désynchro du code).
|
||||
# Le Dockerfile de l'image Grav reste versionné (docker/grav/).
|
||||
docs/
|
||||
|
||||
# Divers
|
||||
.DS_Store
|
||||
*.log
|
||||
.env
|
||||
46
CLAUDE.md
Normal file
46
CLAUDE.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# CLAUDE.md — stack Figures Libres (orchestration)
|
||||
|
||||
Dépôt d'**orchestration** de l'environnement du générateur de portfolios. Il ne
|
||||
contient **pas** de code applicatif : il câble ensemble trois checkouts posés
|
||||
côte à côte, via Docker Compose.
|
||||
|
||||
```
|
||||
portfolio-maker/ ← CE dépôt (stack)
|
||||
├── docker-compose.yml # grav-reference (8089) + generator (8091)
|
||||
├── docker-compose.prod.yml # surcouche déploiement (build Nitro + rsync prod)
|
||||
├── docker/grav/Dockerfile # image PHP+Apache servant grav-reference/
|
||||
├── generator/ ← dépôt À PART (l'app) — cloné ici, git-ignoré
|
||||
└── grav-reference/ ← dépôt À PART (CMS d'exemple) — cloné ici, git-ignoré
|
||||
```
|
||||
|
||||
## Règle absolue : tout tourne dans Docker
|
||||
|
||||
Node, build Nuxt, Playwright/Chromium, Ghostscript, ffmpeg, rsync — **rien sur
|
||||
l'hôte**. Exception connue : vérifier le rendu d'un PDF (`pdftoppm`) peut se faire
|
||||
sur l'hôte, les templates print étant piégeux (voir `generator/CLAUDE.md`).
|
||||
|
||||
```bash
|
||||
docker compose up -d # grav-reference + generator
|
||||
```
|
||||
|
||||
- Générateur : http://localhost:8091 — mot de passe dev `figureslibres`.
|
||||
- Grav de référence : http://localhost:8089 (CMS d'exemple, **pas** l'app à développer).
|
||||
|
||||
## Les trois composants
|
||||
|
||||
- **`generator/`** — l'app Nuxt = le livrable. Dépôt autonome (Dockerfile inclus),
|
||||
poussé sur le gitea figureslibres.io. Toute la doc app est dans `generator/CLAUDE.md`.
|
||||
- **`grav-reference/`** — le vrai site Grav, **son propre dépôt** (~4,8 Go), monté
|
||||
`:ro`. Source de contenu en lecture seule, jamais modifié.
|
||||
- **Ce dépôt** — compose, image Grav, doc de câblage. Ne versionne ni `generator/`
|
||||
ni `grav-reference/` (ils ont chacun leur dépôt).
|
||||
|
||||
## Déploiement (mode prod)
|
||||
|
||||
Contenu tiré de la prod par rsync/SSH en lecture seule vers
|
||||
`generator/data/content-cache/`, déclenché par le bouton « Rafraîchir ». Voir les
|
||||
variables `.env` (non versionné) documentées dans `docker-compose.prod.yml`.
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d generator
|
||||
```
|
||||
97
README.md
Normal file
97
README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# portfolio-maker (stack)
|
||||
|
||||
Dépôt d'**orchestration** de l'environnement du générateur de portfolios PDF de
|
||||
Figures Libres. Il ne contient pas de code applicatif : il câble ensemble, via
|
||||
Docker Compose, l'app et le CMS Grav d'exemple qui lui sert de source de contenu.
|
||||
|
||||
## Trois dépôts, un environnement
|
||||
|
||||
Ce dépôt attend deux autres checkouts **posés côte à côte** (chacun a son propre
|
||||
dépôt git et n'est pas versionné ici) :
|
||||
|
||||
```
|
||||
portfolio-maker/ # CE dépôt (stack)
|
||||
├── docker/grav/Dockerfile # Image PHP+Apache servant grav-reference/
|
||||
├── docker-compose.yml # Services grav-reference (8089) + generator (8091)
|
||||
├── docker-compose.prod.yml # Surcouche déploiement (build Nitro + rsync prod)
|
||||
├── generator/ ←──────── dépôt À PART : l'app Nuxt (Dockerfile inclus) = le livrable
|
||||
└── grav-reference/ ←──────── dépôt À PART : le vrai site Grav (~4,8 Go), monté :ro
|
||||
```
|
||||
|
||||
- **`generator/`** — le générateur (Nuxt). Détails d'architecture : voir son propre
|
||||
`README.md` / `CLAUDE.md`. Poussé sur le gitea figureslibres.io.
|
||||
- **`grav-reference/`** — CMS Grav d'exemple, en **lecture seule** (monté `:ro`).
|
||||
C'est la source de contenu, **pas** l'app à développer.
|
||||
|
||||
> ⚠️ Tout tourne dans Docker : Node, build Nuxt, Playwright/Chromium, Ghostscript,
|
||||
> ffmpeg, rsync — rien n'est installé sur l'hôte.
|
||||
|
||||
> 📦 **Non versionné dans ce dépôt** : `generator/` et `grav-reference/` (dépôts
|
||||
> séparés, à cloner côte à côte), ainsi que `docs/` (PDF de référence + spec déjà
|
||||
> réalisée). Le `Dockerfile` de l'image Grav, lui, reste versionné dans `docker/grav/`.
|
||||
|
||||
## Lancer en dev
|
||||
|
||||
```bash
|
||||
docker compose up -d # grav-reference + generator
|
||||
```
|
||||
|
||||
- Grav de référence : http://localhost:8089
|
||||
- Générateur de portfolios : http://localhost:8091 — mot de passe partagé :
|
||||
`figureslibres` (variable `PORTFOLIO_PASSWORD`)
|
||||
|
||||
Le générateur lit directement `grav-reference/user/pages/02.projets/` (mode DEV
|
||||
de `ContentSource`). Le bouton « Rafraîchir » relit le contenu local.
|
||||
|
||||
Commandes utiles (toujours via le conteneur) :
|
||||
|
||||
```bash
|
||||
docker compose run --rm --no-deps generator npm install # dépendances
|
||||
docker compose exec generator npm run build # build de prod
|
||||
docker logs -f figureslibres-portfolio-generator # logs du dev server
|
||||
```
|
||||
|
||||
## Parcours utilisateur
|
||||
|
||||
1. Se connecter (mot de passe partagé) — on arrive sur la liste des **portfolios**.
|
||||
2. Créer un **portfolio** : ajouter des projets via la modale « Ajouter un
|
||||
projet » (recherche plein texte, filtres catégorie/tag — seuls les projets
|
||||
publiés sont proposés). Chaque projet = **deux pages ou plus**, fidèles au
|
||||
gabarit `book_v3` (PDF de référence dans `docs/`, non versionné) :
|
||||
- une page **couverture** : bandeau 55 mm couleur de la catégorie (titre en
|
||||
fonte d'« utilité » — Playfair/Manifont/Avara —, pastille, présentation
|
||||
Syne 12/16 pt) + image de couverture (celle de la grille du CMS) ;
|
||||
- des pages **grille** : filet 5 mm, marges 8 mm, gouttières 3,8 mm, images
|
||||
**entières** (jamais tronquées) calées sur la grille — 8 dispositions au
|
||||
choix, présentées par icônes. Les vidéos ne sont pas sélectionnables.
|
||||
Couleur de bandeau et texte noir/blanc réglables (palette du gabarit +
|
||||
couleurs du site). Pages spéciales : couverture, sommaire, page libre.
|
||||
Aperçu WYSIWYG A3 paysage, sauvegarde automatique.
|
||||
3. **Générer le PDF** (nom de fichier personnalisable) : rendu Playwright/Chromium
|
||||
au format **A3 paysage** (1190,55 × 841,89 pt, comme le gabarit), images
|
||||
redimensionnées à 2× leur taille d'affichage (sharp), post-traitement
|
||||
Ghostscript, métadonnées. Le contenu utilisé est **figé**
|
||||
(`data/frozen/<id>/`) : régénérer redonne le même PDF ;
|
||||
« Réactualiser depuis Grav » resynchronise volontairement.
|
||||
|
||||
## Déploiement (mode PROD)
|
||||
|
||||
Le contenu est tiré de la prod par **rsync/SSH en lecture seule** vers
|
||||
`generator/data/content-cache/`, déclenché par le bouton « Rafraîchir ».
|
||||
|
||||
```bash
|
||||
# .env (non versionné)
|
||||
PORTFOLIO_PASSWORD=…
|
||||
SESSION_SECRET=…
|
||||
INTERNAL_TOKEN=…
|
||||
SYNC_REMOTE=user@prod:/chemin/vers/user/pages/02.projets/
|
||||
SYNC_SSH_KEY_FILE=/chemin/local/vers/cle_privee
|
||||
|
||||
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d generator
|
||||
```
|
||||
|
||||
## Données
|
||||
|
||||
Tout est sur disque dans `generator/data/` (sauvegarde = copie du dossier) :
|
||||
`portfolios/*.json` (compositions), `exports/*.pdf`, `frozen/<id>/` (snapshots),
|
||||
`cache/` (miniatures, posters vidéo — régénérable), `content-cache/` (rsync prod).
|
||||
23
docker-compose.prod.yml
Normal file
23
docker-compose.prod.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
# Surcouche de déploiement (spec §5.2) :
|
||||
# docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d generator
|
||||
#
|
||||
# - contenu tiré de la prod Figures Libres par rsync/SSH (bouton « Rafraîchir »)
|
||||
# - build Nuxt puis serveur Nitro (pas de serveur de dev)
|
||||
# - variables à définir dans un fichier .env (non versionné) :
|
||||
# PORTFOLIO_PASSWORD=… mot de passe partagé
|
||||
# SESSION_SECRET=… secret de session (longue chaîne aléatoire)
|
||||
# INTERNAL_TOKEN=… jeton interne du rendu print
|
||||
# SYNC_REMOTE=user@host:/chemin/vers/user/pages/02.projets/
|
||||
# SYNC_SSH_KEY_FILE=~/.ssh/portfolio_sync clé privée SSH (lecture seule)
|
||||
services:
|
||||
generator:
|
||||
command: sh -c "npm run build && npm run preview"
|
||||
environment:
|
||||
NUXT_CONTENT_SOURCE: prod
|
||||
NUXT_SYNC_REMOTE: ${SYNC_REMOTE:?SYNC_REMOTE requis en mode prod}
|
||||
NUXT_SYNC_SSH_KEY: /run/secrets/sync-ssh-key
|
||||
NITRO_HOST: 0.0.0.0
|
||||
NITRO_PORT: 3000
|
||||
volumes:
|
||||
- ./generator:/app
|
||||
- ${SYNC_SSH_KEY_FILE:?SYNC_SSH_KEY_FILE requis en mode prod}:/run/secrets/sync-ssh-key:ro
|
||||
46
docker-compose.yml
Normal file
46
docker-compose.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
services:
|
||||
# Instance Grav de référence (site "Figures Libres") servant de source de
|
||||
# contenu pour le développement du générateur de portfolios.
|
||||
# Ce n'est PAS l'app à développer : c'est le CMS d'exemple, en lecture.
|
||||
grav-reference:
|
||||
build:
|
||||
context: ./docker/grav
|
||||
args:
|
||||
PUID: "1000"
|
||||
PGID: "1000"
|
||||
container_name: figureslibres-grav-ref
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8089:80"
|
||||
volumes:
|
||||
- ./grav-reference:/var/www/html
|
||||
|
||||
# Générateur de portfolios PDF (app Nuxt) — voir docs/spec-generateur-portfolios.md.
|
||||
# Tout tourne dans ce conteneur : Node, build Nuxt, Playwright/Chromium, Ghostscript.
|
||||
generator:
|
||||
build:
|
||||
# L'app est un dépôt autonome cloné dans ./generator (Dockerfile inclus).
|
||||
context: ./generator
|
||||
image: figureslibres-portfolio-generator
|
||||
container_name: figureslibres-portfolio-generator
|
||||
restart: unless-stopped
|
||||
user: "1000:1000"
|
||||
working_dir: /app
|
||||
command: npm run dev
|
||||
environment:
|
||||
HOME: /tmp
|
||||
NUXT_SESSION_SECRET: ${SESSION_SECRET:-dev-secret-figures-libres}
|
||||
NUXT_PORTFOLIO_PASSWORD: ${PORTFOLIO_PASSWORD:-figureslibres}
|
||||
NUXT_INTERNAL_TOKEN: ${INTERNAL_TOKEN:-dev-internal-token}
|
||||
NUXT_CONTENT_SOURCE: ${CONTENT_SOURCE:-dev}
|
||||
NUXT_CONTENT_DIR: /content/02.projets
|
||||
NUXT_DATA_DIR: /app/data
|
||||
# Mode prod (voir docker-compose.prod.yml) : synchro rsync/SSH
|
||||
NUXT_SYNC_REMOTE: ${SYNC_REMOTE:-}
|
||||
NUXT_SYNC_SSH_KEY: ${SYNC_SSH_KEY:-}
|
||||
ports:
|
||||
- "8091:3000"
|
||||
volumes:
|
||||
- ./generator:/app
|
||||
# Contenu Grav monté en LECTURE SEULE (contrainte ferme, §18).
|
||||
- ./grav-reference/user/pages/02.projets:/content/02.projets:ro
|
||||
47
docker/grav/Dockerfile
Normal file
47
docker/grav/Dockerfile
Normal file
@@ -0,0 +1,47 @@
|
||||
FROM php:8.2-apache
|
||||
|
||||
# Match the container's web user to the host user so Grav can write to the
|
||||
# bind-mounted app/ folder (cache, logs, tmp, user uploads, ...).
|
||||
ARG PUID=1000
|
||||
ARG PGID=1000
|
||||
RUN groupmod -o -g "${PGID}" www-data \
|
||||
&& usermod -o -u "${PUID}" www-data
|
||||
|
||||
# System deps for Grav's PHP extensions
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libzip-dev \
|
||||
libpng-dev \
|
||||
libjpeg-dev \
|
||||
libfreetype6-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libonig-dev \
|
||||
libxml2-dev \
|
||||
unzip \
|
||||
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||
&& docker-php-ext-install -j"$(nproc)" \
|
||||
gd \
|
||||
curl \
|
||||
mbstring \
|
||||
zip \
|
||||
dom \
|
||||
xml \
|
||||
fileinfo \
|
||||
exif \
|
||||
opcache \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Grav ships its own .htaccess; enable rewrite + allow overrides
|
||||
RUN a2enmod rewrite headers
|
||||
|
||||
RUN sed -ri -e 's!AllowOverride None!AllowOverride All!g' /etc/apache2/apache2.conf
|
||||
|
||||
# Sensible PHP settings for Grav
|
||||
RUN { \
|
||||
echo 'memory_limit=256M'; \
|
||||
echo 'upload_max_filesize=32M'; \
|
||||
echo 'post_max_size=32M'; \
|
||||
echo 'max_execution_time=120'; \
|
||||
} > /usr/local/etc/php/conf.d/grav.ini
|
||||
|
||||
WORKDIR /var/www/html
|
||||
Reference in New Issue
Block a user