No description
  • TypeScript 57.9%
  • CSS 26.6%
  • Nunjucks 13.9%
  • Dockerfile 0.8%
  • Shell 0.6%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Julien LEQUEN 74f51436fc feat(docker): STACK_NAME pour déployer plusieurs instances
Le nom de conteneur était figé (cv-online2 / cursus), ce qui empêchait de
lancer un second déploiement sur la même machine.

STACK_NAME pilote maintenant le nom de projet Compose, le nom du conteneur
et donc le préfixe du volume (<STACK_NAME>_cv-data). Deux instances avec des
STACK_NAME + APP_PORT distincts sont totalement indépendantes, même depuis le
même dossier de sources (docker compose --env-file .env.autre up -d).

Les valeurs par défaut préservent les déploiements existants : "cv" depuis les
sources (volume cv_cv-data inchangé), "cursus" depuis l'image.

PUBLIC_BASE_URL suit désormais APP_PORT au lieu du 3000 codé en dur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-24 14:38:47 +02:00
prisma Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
public Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
scripts docs: overview Docker Hub (DOCKERHUB.md) + script de publication 2026-06-18 17:53:21 +02:00
src fix(preview): l'aperçu paginé ne coupe plus une ligne en deux 2026-07-01 17:36:18 +02:00
themes fix(themes): « Pour handicapé de Word » — fidélité à l'original + routage par colonne 2026-07-01 17:22:32 +02:00
.dockerignore Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
.env.example feat(docker): STACK_NAME pour déployer plusieurs instances 2026-08-24 14:38:47 +02:00
.gitignore Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
docker-compose.image.yml feat(docker): STACK_NAME pour déployer plusieurs instances 2026-08-24 14:38:47 +02:00
docker-compose.yml feat(docker): STACK_NAME pour déployer plusieurs instances 2026-08-24 14:38:47 +02:00
docker-entrypoint.sh Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
Dockerfile feat(themes): thèmes intégrés embarqués dans l'image (MAJ auto) 2026-06-18 17:35:28 +02:00
DOCKERHUB.md feat(docker): STACK_NAME pour déployer plusieurs instances 2026-08-24 14:38:47 +02:00
next.config.mjs Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
package-lock.json Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
package.json Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
postcss.config.mjs Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00
README.md feat(docker): STACK_NAME pour déployer plusieurs instances 2026-08-24 14:38:47 +02:00
tsconfig.json Cursus — éditeur de CV auto-hébergé 2026-06-16 15:30:51 +02:00

Cursus

Cursus est une application web auto-hébergée pour créer, héberger et exporter un CV. On édite son CV en ligne (enregistrement automatique), on le publie à une URL publique, et on l'exporte en PDF A4 au rendu identique quel que soit le navigateur — la génération est faite côté serveur par un Chromium headless.

Le logo reprend la case de l'élément Cu (n°29) du tableau périodique — clin d'œil à Cursus.


Fonctionnalités

  • ✍️ Éditeur en ligne avec enregistrement automatique (aucun bouton « Enregistrer ») et interface en barre latérale.
  • 🌐🔒 CV public et CV privé : chaque section / contact / élément a une visibilité Public, Privé ou Public et privé.
  • 🎨 7 thèmes intégrés + thèmes « fichiers » : déposez un dossier (theme.json + template.njk + styles.css) dans themes/ et il apparaît au runtime, sans recompiler (voir themes/README.md).
  • 🖨️ Export PDF déterministe (A4, marges par thème) identique à l'écran, généré par Playwright/Chromium.
  • 📄 Aperçu paginé sur le web : la page s'affiche comme le PDF (feuilles A4 empilées), avec respect de l'option « ne pas couper un bloc ».
  • 🖼️ Photo avec recadrage / zoom / rotation avant upload.
  • 🔁 Export / Import JSON du CV complet.
  • 🧩 Sections repliables, champs contextuels selon le type de section, couleur d'accent et taille de texte réglables.

Prérequis

  • Docker et Docker Compose v2 (docker compose …).
  • Rien d'autre : Node, la base SQLite et le Chromium d'export PDF sont embarqués dans l'image.

Déploiement express (image Docker Hub)

Le plus simple, sans cloner les sources : l'image est publiée sur Docker Hub (jlequen1/cursus). Dans un dossier vide, récupérez docker-compose.image.yml et .env.example, puis :

cp .env.example .env
# éditez .env : générez AUTH_SECRET avec `openssl rand -hex 32`, choisissez l'admin…
docker compose -f docker-compose.image.yml up -d

Application sur http://localhost:63546. Mise à jour : docker compose -f docker-compose.image.yml pull && docker compose -f docker-compose.image.yml up -d (image en :latest).


Déploiement depuis les sources (Docker Compose, pas à pas)

1. Récupérer le projet

git clone https://forge.lequen.fr/jlequen/Cursus.git cursus
cd cursus

2. Créer le fichier de configuration .env

cp .env.example .env

Puis éditez .env et renseignez au minimum :

Variable À mettre
AUTH_SECRET Un secret long et aléatoire — générez-le avec la commande ci-dessous
ADMIN_EMAIL L'email de connexion à l'éditeur
ADMIN_PASSWORD Le mot de passe initial de ce compte
PUBLIC_BASE_URL L'URL publique finale (ex. https://cv.mon-domaine.fr)
APP_PORT Le port exposé sur l'hôte (par défaut 63546)
STACK_NAME Nom de l'instance (conteneur, projet, volume) — utile pour en déployer plusieurs

Générer un AUTH_SECRET solide :

openssl rand -base64 32

⚠️ .env contient vos secrets et n'est pas versionné (il est dans .gitignore). Ne le committez jamais.

3. Construire et lancer

docker compose up -d --build

Au premier démarrage, le conteneur applique les migrations, crée le compte admin (à partir de ADMIN_EMAIL / ADMIN_PASSWORD) puis démarre l'app. Suivez les logs :

docker compose logs -f

4. Accéder à l'application

(Remplacez le port si vous avez changé APP_PORT.)

Le CV est vide au départ : tout se remplit dans l'éditeur, ou via Import JSON.

5. Mettre à jour, arrêter, redémarrer

# Récupérer une nouvelle version puis reconstruire
git pull
docker compose up -d --build

# Arrêter (les données sont conservées dans le volume)
docker compose down

# Voir les logs
docker compose logs -f

Nouveaux thèmes intégrés à chaque version : les thèmes livrés avec l'app sont embarqués dans l'image (/app/builtin-themes, non monté). Un simple git pull && docker compose up -d --build les fait apparaître automatiquement — même si le volume ./themes (vos thèmes perso) n'est pas à jour.


Déployer plusieurs CV sur la même machine

Chaque instance est identifiée par STACK_NAME : il sert de nom de projet Compose, de nom de conteneur, et préfixe le volume de données (<STACK_NAME>_cv-data). Deux instances avec des STACK_NAME différents sont donc totalement indépendantes — y compris depuis le même dossier de sources.

Créez un fichier d'environnement par instance :

# .env.natacha
STACK_NAME=cv-natacha
APP_PORT=63547
AUTH_SECRET=…            # un secret distinct par instance
ADMIN_EMAIL=natacha@exemple.fr
ADMIN_PASSWORD=…
PUBLIC_BASE_URL=https://cv-natacha.mon-domaine.fr

puis lancez-la avec :

docker compose --env-file .env.natacha up -d --build
# arrêt / logs de cette instance uniquement :
docker compose --env-file .env.natacha logs -f
docker compose --env-file .env.natacha down

Les trois valeurs à rendre uniques sont STACK_NAME, APP_PORT et PUBLIC_BASE_URL. Sans STACK_NAME, la valeur par défaut (cv depuis les sources, cursus depuis l'image) s'applique : c'est ce qui provoquait le conflit de nom de conteneur entre deux déploiements.

⚠️ Ne changez pas le STACK_NAME d'une instance déjà en service : elle repartirait sur un volume vide. Exportez d'abord le CV en JSON, ou renommez le volume.


Mise en production derrière un reverse proxy

Le conteneur écoute en interne sur le port 3000 et est exposé sur l'hôte via APP_PORT (63546 par défaut). Placez un reverse proxy (Nginx, Caddy, Traefik…) devant, en TLS, qui transmet vers http://127.0.0.1:63546, et réglez PUBLIC_BASE_URL sur l'URL HTTPS publique.


Configuration (variables d'environnement)

Variable Défaut Rôle
STACK_NAME cv (sources) / cursus (image) Nom du projet Compose, du conteneur, et préfixe du volume
APP_PORT 63546 Port exposé sur l'hôte
ADMIN_EMAIL admin@example.com Login de l'éditeur (créé au 1er démarrage)
ADMIN_PASSWORD changeme-please Mot de passe initial (à changer !)
AUTH_SECRET Secret de signature des sessions JWT (obligatoire)
PUBLIC_BASE_URL http://localhost:3000 URL publique (liens canoniques / rendu PDF)
DATABASE_URL file:/app/data/cv.db Base SQLite (sur le volume persistant)
THEMES_DIR /app/themes Vos thèmes perso (monté en volume, prioritaires)
BUILTIN_THEMES_DIR /app/builtin-themes Thèmes intégrés livrés dans l'image (non monté)
CHROMIUM_PATH /usr/bin/chromium Chromium headless pour l'export PDF (fourni par l'image)

Le mot de passe n'est créé qu'au premier démarrage. Pour le changer ensuite, utilisez Compte → Changer le mot de passe dans l'éditeur (modifier ADMIN_PASSWORD après coup n'a pas d'effet).


Données & sauvegarde

  • Toutes les données (base SQLite + photos uploadées) vivent dans un volume Docker — elles survivent à down / up / --build. Le volume est préfixé par le nom de l'instance : cv_cv-data par défaut depuis les sources, cursus_cv-data depuis l'image, <STACK_NAME>_cv-data si vous l'avez défini. Retrouvez le vôtre avec docker volume ls | grep cv-data.
  • Sauvegarde rapide du contenu : éditeur → Compte → Exporter en JSON. Restauration via Importer.
  • Sauvegarde du volume complet :
docker run --rm -v cv_cv-data:/data -v "$PWD":/backup alpine \
  tar czf /backup/cv-data-backup.tar.gz -C /data .

Thèmes

Deux possibilités :

  1. Thèmes intégrés (livrés avec l'image, dans /app/builtin-themes) : sélectionnables dans Apparence → Thème. Ils se mettent à jour avec l'image (up -d --build).
  2. Thèmes perso : déposez un dossier dans themes/ (monté en volume → /app/themes), rechargez la page — aucun rebuild. Un thème perso portant le même identifiant qu'un thème intégré le remplace. Format et variables documentés dans themes/README.md.

Sécurité

  • Changez AUTH_SECRET et ADMIN_PASSWORD avant toute mise en ligne.
  • .env, la base de données (*.db, /data) et les sauvegardes (/backups) sont exclus du dépôt (.gitignore) : aucun secret ni donnée personnelle n'est versionné.
  • Servez l'application en HTTPS derrière un reverse proxy.

Développement local (sans Docker)

npm install
cp .env.example .env          # adaptez DATABASE_URL en local si besoin
npx prisma migrate deploy
npm run seed
npm run dev                   # http://localhost:3000

L'export PDF en local nécessite un Chromium/Chrome installé ; pointez CHROMIUM_PATH dessus.


Stack technique

  • Next.js 15 (App Router) · React 19 · TypeScript · Tailwind CSS v4
  • Prisma + SQLite
  • Playwright (Chromium headless) pour l'export PDF A4 déterministe
  • Nunjucks (+ marked / sanitize-html) pour les thèmes fichiers
  • Authentification par session JWT (jose) + bcrypt