232 lines
5.7 KiB
Markdown
232 lines
5.7 KiB
Markdown
---
|
|
description: Déploiement de dynamic.alpinux.org — app Flask, Gunicorn, Apache reverse proxy, AlpID OIDC.
|
|
---
|
|
|
|
# Déploiement de dynamic.alpinux.org
|
|
|
|
Application Flask de quiz interactifs, partiellement publique et partiellement réservée aux membres AlpID.
|
|
|
|
!!! note "Pour qui ?"
|
|
Procédure pour les mainteneurs avec accès SSH au serveur.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
```
|
|
Navigateur
|
|
│ HTTPS
|
|
▼
|
|
Apache (reverse proxy, SSL)
|
|
│ HTTP (port local dédié)
|
|
▼
|
|
Gunicorn (2 workers)
|
|
│
|
|
▼
|
|
Flask app (dynamic/)
|
|
│ │
|
|
▼ ▼
|
|
SQLite AlpID OIDC
|
|
(scores.db) (alpid.alpinux.org)
|
|
```
|
|
|
|
---
|
|
|
|
## Installation (première fois)
|
|
|
|
### 1. Cloner le dépôt sur le serveur
|
|
|
|
```bash
|
|
ssh <user>@alpinux.org
|
|
git clone https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.site.2026.git \
|
|
$APP_DIR
|
|
```
|
|
|
|
### 2. Créer l'environnement Python
|
|
|
|
```bash
|
|
cd $APP_DIR/dynamic
|
|
python3 -m venv venv
|
|
venv/bin/pip install -r requirements.txt gunicorn
|
|
```
|
|
|
|
### 3. Configurer les variables d'environnement
|
|
|
|
```bash
|
|
sudo mkdir /etc/dynamic-alpinux
|
|
sudo cp $APP_DIR/dynamic/.env.example /etc/dynamic-alpinux/config.env
|
|
sudo nano /etc/dynamic-alpinux/config.env
|
|
```
|
|
|
|
Remplissez les valeurs :
|
|
|
|
```bash
|
|
SECRET_KEY=<générer avec : python3 -c "import secrets; print(secrets.token_hex(32))">
|
|
ALPID_CLIENT_ID=<client-id-configuré-dans-keycloak>
|
|
ALPID_CLIENT_SECRET=<obtenir depuis la console Keycloak AlpID>
|
|
ALPID_DISCOVERY_URL=https://alpid.alpinux.org/realms/alpinux/.well-known/openid-configuration
|
|
DATABASE=<chemin vers le fichier scores.db>
|
|
```
|
|
|
|
```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 <répertoire données>
|
|
sudo mkdir -p <répertoire logs>
|
|
sudo chown <user>:<group> <répertoire données> <répertoire logs>
|
|
```
|
|
|
|
### 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/dynamic/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 Apache
|
|
|
|
```bash
|
|
sudo a2enmod proxy proxy_http headers ssl
|
|
|
|
sudo cp infra/dynamic/dynamic.alpinux.org.vhost.conf \
|
|
/etc/apache2/sites-available/dynamic.alpinux.org.conf
|
|
sudo a2ensite dynamic.alpinux.org
|
|
sudo apachectl configtest
|
|
sudo systemctl reload apache2
|
|
```
|
|
|
|
### 8. Obtenir le certificat SSL
|
|
|
|
```bash
|
|
sudo certbot --apache -d dynamic.alpinux.org
|
|
```
|
|
|
|
---
|
|
|
|
## Mise à jour
|
|
|
|
```bash
|
|
ssh <user>@alpinux.org
|
|
cd $APP_DIR
|
|
git pull
|
|
cd dynamic
|
|
venv/bin/pip install -r requirements.txt # si requirements.txt a changé
|
|
sudo systemctl restart dynamic-alpinux
|
|
```
|
|
|
|
---
|
|
|
|
## Gestion du service
|
|
|
|
| Action | Commande |
|
|
|---|---|
|
|
| Démarrer | `sudo systemctl start dynamic-alpinux` |
|
|
| Arrêter | `sudo systemctl stop dynamic-alpinux` |
|
|
| Redémarrer | `sudo systemctl restart dynamic-alpinux` |
|
|
| État | `sudo systemctl status dynamic-alpinux` |
|
|
| Logs en direct | `sudo journalctl -u dynamic-alpinux -f` |
|
|
|
|
---
|
|
|
|
## Structure de l'application
|
|
|
|
```
|
|
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
|
|
```
|
|
|
|
---
|
|
|
|
## Accès public vs membres
|
|
|
|
| URL | Accès |
|
|
|---|---|
|
|
| `/` | Public |
|
|
| `/quiz/` | Public (aperçu de tous les quiz) |
|
|
| `/quiz/<id>/` | Public (page intro) |
|
|
| `/quiz/<id>/jouer` | Public si `members_only: false`, sinon AlpID requis |
|
|
| `/profil/` | AlpID requis |
|
|
| `/auth/login` | Redirect → AlpID |
|
|
| `/auth/callback` | Retour OIDC (interne) |
|
|
|
|
Les quiz avancés (niveau 4) et experts (niveau 5) ont `"members_only": true` dans `data/quizzes.json`.
|
|
|
|
---
|
|
|
|
## Ajouter un quiz
|
|
|
|
Éditez `dynamic/data/quizzes.json` et ajoutez un objet au tableau :
|
|
|
|
```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
|
|
```
|