From 4ff7390d6b6f79c9c1ac2b2ba8295d71b478de95 Mon Sep 17 00:00:00 2001 From: Alpinux Date: Thu, 22 Oct 2026 08:54:28 +0200 Subject: [PATCH] =?UTF-8?q?D=C3=A9ploiement=20de=20dynamic.alpinux.org=20:?= =?UTF-8?q?=20d=C3=A9crire=20ce=20qui=20tourne=20r=C3=A9ellement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installation faite le 22/10/2026 sur owni : /opt/dynamic-alpinux, service dynamic-alpinux sur 127.0.0.1:5001, configuration dans .env, mise en ligne par scripts/deploy.sh (dépôt privé, pas de git pull sur le serveur). Ajoute l'état de la connexion AlpID (client Keycloak à créer) et renvoie au DEPLOIEMENT.md du dépôt pour le détail. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019V4mhmHoJD3b8YfQy249dC --- docs/technique/deploiement-dynamic.md | 265 +++++++++----------------- 1 file changed, 88 insertions(+), 177 deletions(-) diff --git a/docs/technique/deploiement-dynamic.md b/docs/technique/deploiement-dynamic.md index 8f10ff2..f190ccb 100644 --- a/docs/technique/deploiement-dynamic.md +++ b/docs/technique/deploiement-dynamic.md @@ -1,13 +1,16 @@ --- -description: Déploiement de dynamic.alpinux.org — app Flask, Gunicorn, Apache reverse proxy, AlpID OIDC. +description: Déploiement de dynamic.alpinux.org — application Flask derrière Apache (ISPConfig), Gunicorn, SQLite, AlpID. --- # Déploiement de dynamic.alpinux.org -Application Flask de quiz interactifs, partiellement publique et partiellement réservée aux membres AlpID. +Le site « apprendre Linux en jouant » : cours, jeux et quiz. Application Flask, publique, +avec une connexion AlpID pour enregistrer ses scores. !!! note "Pour qui ?" - Procédure pour les mainteneurs avec accès SSH au serveur. + Les mainteneurs qui ont un accès SSH au serveur. La référence détaillée est le fichier + `DEPLOIEMENT.md` du dépôt [alpinux-dynamic](https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-dynamic), + tenu à jour avec le code ; cette page en donne l'essentiel. --- @@ -17,214 +20,122 @@ Application Flask de quiz interactifs, partiellement publique et partiellement r Navigateur │ HTTPS ▼ -Apache (reverse proxy, SSL) - │ HTTP (port local dédié) +Apache (ISPConfig, proxy, Let's Encrypt) + │ HTTP, 127.0.0.1:5001 ▼ -Gunicorn (2 workers) +Gunicorn (2 workers) — service dynamic-alpinux │ ▼ -Flask app (alpinux-dynamic) - │ │ - ▼ ▼ -SQLite AlpID OIDC -(scores.db) (alpid.alpinux.org) +Flask (/opt/dynamic-alpinux) + │ │ + ▼ ▼ +SQLite AlpID (OIDC) +/var/lib/dynamic-alpinux/scores.db ``` +| | | +|---|---| +| Code | `/opt/dynamic-alpinux`, utilisateur système `dynamic-alpinux` | +| Service | `dynamic-alpinux`, Gunicorn sur `127.0.0.1:5001` | +| Configuration | `/opt/dynamic-alpinux/.env` (droits `600`) | +| Base de données | `/var/lib/dynamic-alpinux/scores.db` | +| Journaux | `sudo journalctl -u dynamic-alpinux` | + --- -## Installation (première fois) +## Mettre à jour le site -### 1. Cloner le dépôt sur le serveur +Toute modification passe par une pull request fusionnée dans `main`. Ensuite, depuis sa +copie locale du dépôt : ```bash -ssh @alpinux.org -git clone https://gitea.alpinux.org/alpinux.cedrica5l/alpinux-dynamic.git \ - $APP_DIR +scripts/deploy.sh -n # quel commit de main va partir +scripts/deploy.sh # mise en ligne ``` -### 2. Créer l'environnement Python +Le script envoie `origin/main` sur le serveur, installe les dépendances, redémarre le +service et vérifie que les pages répondent. -```bash -cd $APP_DIR -python3 -m venv venv -venv/bin/pip install -r requirements.txt gunicorn -``` +!!! info "Pourquoi pas un `git pull` sur le serveur ?" + Le dépôt est privé et le serveur n'a pas d'identifiant pour la forge. Le code part donc + du poste du mainteneur, comme pour static.alpinux.org. -### 3. Configurer les variables d'environnement - -```bash -sudo mkdir /etc/dynamic-alpinux -sudo cp $APP_DIR/.env.example /etc/dynamic-alpinux/config.env -sudo nano /etc/dynamic-alpinux/config.env -``` - -Remplissez les valeurs : - -```bash -SECRET_KEY= -ALPID_CLIENT_ID= -ALPID_CLIENT_SECRET= -ALPID_DISCOVERY_URL=https://alpid.alpinux.org/realms/alpinux/.well-known/openid-configuration -DATABASE= -``` - -```bash -sudo chmod 600 /etc/dynamic-alpinux/config.env -``` - -### 4. Créer les répertoires de données et de logs - -```bash -sudo mkdir -p -sudo mkdir -p -sudo chown : -``` - -### 5. Configurer AlpID (Keycloak) - -Dans la console d'administration Keycloak (`https://alpid.alpinux.org`) : - -1. **Clients** → **Créer un client** -2. **Client ID** : le client ID choisi pour cette app -3. **Client authentication** : activé (pour obtenir un `client_secret`) -4. **Valid redirect URIs** : `https://dynamic.alpinux.org/auth/callback` -5. **Web origins** : `https://dynamic.alpinux.org` -6. Notez le **Client secret** dans l'onglet *Credentials* - -### 6. Installer le service systemd - -Les fichiers de configuration systemd et Apache sont conservés **hors dépôt** (`infra/` local). - -```bash -sudo cp infra/services/dynamic.alpinux.org.service \ - /etc/systemd/system/dynamic-alpinux.service -sudo systemctl daemon-reload -sudo systemctl enable --now dynamic-alpinux -sudo systemctl status dynamic-alpinux -``` - -### 7. Configurer le site dans ISPConfig - -Dans **ISPConfig** (`https://owni.alpinux.org:8080`) : - -1. *Sites → Ajouter un site web* — domaine `dynamic.alpinux.org` -2. Activer **Let's Encrypt SSL** dans l'onglet SSL -3. Dans *Options → Directives Apache personnalisées (HTTPS)*, ajouter : - -```apache -ProxyPreserveHost On -ProxyPass / http://127.0.0.1:/ -ProxyPassReverse / http://127.0.0.1:/ -RequestHeader set X-Forwarded-Proto "https" -``` - -ISPConfig gère le VirtualHost et le renouvellement automatique du certificat. +Les jeux, les cours et les quiz sont lus au démarrage : modifier le contenu demande, comme +le code, une mise en ligne. --- -## Mise à jour - -```bash -ssh @alpinux.org -cd $APP_DIR -git pull -venv/bin/pip install -r requirements.txt # si requirements.txt a changé -sudo systemctl restart dynamic-alpinux -``` - ---- - -## Gestion du service +## Gérer le service | Action | Commande | |---|---| -| Démarrer | `sudo systemctl start dynamic-alpinux` | -| Arrêter | `sudo systemctl stop dynamic-alpinux` | +| État | `systemctl status dynamic-alpinux` | | Redémarrer | `sudo systemctl restart dynamic-alpinux` | -| État | `sudo systemctl status dynamic-alpinux` | -| Logs en direct | `sudo journalctl -u dynamic-alpinux -f` | +| Journal en direct | `sudo journalctl -u dynamic-alpinux -f` | +| Tester sans Apache | `curl -sI http://127.0.0.1:5001/jeux/` (sur le serveur) | --- -## Structure de l'application +## Le site dans ISPConfig +Le proxy se déclare dans *Sites → dynamic.alpinux.org → Options → Directives Apache*, et +pas dans le fichier `.vhost`, qu'ISPConfig régénère : + +```apache +ProxyPass /.well-known/ ! +ProxyPreserveHost On +ProxyPass / http://127.0.0.1:5001/ +ProxyPassReverse / http://127.0.0.1:5001/ + +RequestHeader set X-Forwarded-Proto "https" +RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}s" ``` -alpinux-dynamic/ -├── app.py ← point d'entrée Flask -├── auth_utils.py ← décorateur @login_required -├── db.py ← SQLite (scores) -├── quiz.py ← chargement du JSON -├── requirements.txt -├── .env.example -├── data/ -│ └── quizzes.json ← toutes les questions (source de vérité) -├── routes/ -│ ├── public.py ← accueil, liste, jeu, résultat -│ ├── auth.py ← /auth/login, /auth/callback, /auth/logout -│ └── protected.py ← /profil/ -├── static/ -│ ├── style.css -│ └── quiz.js -└── templates/ - ├── base.html - ├── index.html - ├── quiz/ - │ ├── intro.html - │ ├── play.html - │ └── result.html - └── profil/ - └── index.html -``` + +La première ligne laisse passer le renouvellement du certificat Let's Encrypt. --- -## Accès public vs membres +## La connexion AlpID -| URL | Accès | +L'application utilise le realm `master` d'AlpID, comme admin.alpinux.org. Son client +Keycloak, `dynamic`, se crée dans la console d'AlpID : + +1. *Clients → Create client*, **Client ID** `dynamic` ; +2. **Client authentication** activé ; +3. **Valid redirect URIs** : `https://dynamic.alpinux.org/auth/callback` ; +4. **Web origins** : `https://dynamic.alpinux.org` ; +5. le secret de l'onglet *Credentials* va dans `ALPID_CLIENT_SECRET`, dans + `/opt/dynamic-alpinux/.env`, puis on redémarre le service. + +!!! warning "État au 22/10/2026" + Le client n'est pas encore créé : les cours, les jeux et les quiz publics fonctionnent, + mais pas la connexion. Il faut aussi que l'application tienne compte du proxy HTTPS + (`ProxyFix`) avant de l'ouvrir, sinon AlpID refuse l'adresse de retour. + +--- + +## Ajouter du contenu + +Les procédures pour ajouter un jeu, un cours ou un quiz sont dans le `README.md` du dépôt. +En résumé : + +| Contenu | Fichiers | |---|---| -| `/` | Public | -| `/quiz/` | Public (aperçu de tous les quiz) | -| `/quiz//` | Public (page intro) | -| `/quiz//jouer` | Public si `members_only: false`, sinon AlpID requis | -| `/profil/` | AlpID requis | -| `/auth/login` | Redirect → AlpID | -| `/auth/callback` | Retour OIDC (interne) | +| Quiz | `data/quizzes.json` | +| Jeu | une entrée dans `data/jeux.json`, et `data/jeux/.json` | +| Cours | `data/cours/.md` | -Les quiz avancés (niveau 4) et experts (niveau 5) ont `"members_only": true` dans `data/quizzes.json`. +Puis pull request, fusion, et `scripts/deploy.sh`. --- -## Ajouter un quiz +## Réinstaller de zéro -Éditez `data/quizzes.json` et ajoutez un objet au tableau : +Le détail, commande par commande, est dans `DEPLOIEMENT.md`. Dans l'ordre : -```json -{ - "id": "mon-quiz", - "title": "Titre du quiz", - "description": "Description courte.", - "level": "Intermédiaire", - "level_id": 3, - "members_only": false, - "duration_min": 5, - "icon": "🐧", - "questions": [ - { - "id": 1, - "text": "Question ?", - "choices": ["Réponse A", "Réponse B", "Réponse C", "Réponse D"], - "answer": 0 - } - ] -} -``` - -- `level_id` : 1=Découverte, 2=Débutant, 3=Intermédiaire, 4=Avancé, 5=Expert -- `answer` : index 0-based de la bonne réponse dans `choices` -- `members_only` : `true` pour restreindre aux membres AlpID - -Après modification, redémarrez le service pour recharger le JSON : - -```bash -sudo systemctl restart dynamic-alpinux -``` +1. créer l'utilisateur système `dynamic-alpinux`, les dossiers `/opt/dynamic-alpinux` et + `/var/lib/dynamic-alpinux`, et le `venv` ; +2. écrire `/opt/dynamic-alpinux/.env` (droits `600`) ; +3. installer l'unité systemd, dont une copie de référence est dans `deploiement/` ; +4. lancer `scripts/deploy.sh` depuis sa copie locale ; +5. créer le site dans ISPConfig, avec Let's Encrypt et les directives ci-dessus.