Aller au contenu

Déploiement de dynamic.alpinux.org

Application Flask de quiz interactifs, partiellement publique et partiellement réservée aux membres AlpID.

Pour qui ?

Procédure pour les mainteneurs avec accès SSH au serveur.


Architecture

Navigateur
    │  HTTPS
Apache (reverse proxy, SSL)
    │  HTTP 127.0.0.1:5001
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

ssh alpinux@alpinux.org
git clone https://gitea.alpinux.org/alpinux.cedrica5l/alpinux.site.2026.git \
    /home/alpinux/site

2. Créer l'environnement Python

cd /home/alpinux/site/dynamic
python3 -m venv venv
venv/bin/pip install -r requirements.txt gunicorn

3. Configurer les variables d'environnement

sudo mkdir /etc/dynamic-alpinux
sudo cp /home/alpinux/site/dynamic/.env.example /etc/dynamic-alpinux/config.env
sudo nano /etc/dynamic-alpinux/config.env

Remplissez les valeurs :

SECRET_KEY=<générer avec : python3 -c "import secrets; print(secrets.token_hex(32))">
ALPID_CLIENT_ID=dynamic-alpinux
ALPID_CLIENT_SECRET=<obtenir depuis la console Keycloak AlpID>
ALPID_DISCOVERY_URL=https://alpid.alpinux.org/realms/alpinux/.well-known/openid-configuration
DATABASE=/var/lib/dynamic-alpinux/scores.db
sudo chmod 600 /etc/dynamic-alpinux/config.env

4. Créer les répertoires de données et de logs

sudo mkdir -p /var/lib/dynamic-alpinux
sudo mkdir -p /var/log/dynamic-alpinux
sudo chown alpinux:alpinux /var/lib/dynamic-alpinux /var/log/dynamic-alpinux

5. Configurer AlpID (Keycloak)

Dans la console d'administration Keycloak (https://alpid.alpinux.org) :

  1. ClientsCréer un client
  2. Client ID : dynamic-alpinux
  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

sudo cp /home/alpinux/site/scripts/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

# Activer les modules nécessaires
sudo a2enmod proxy proxy_http headers ssl

# Copier le vhost
sudo cp /home/alpinux/site/scripts/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

sudo certbot --apache -d dynamic.alpinux.org

Mise à jour

ssh alpinux@alpinux.org
cd /home/alpinux/site
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
Logs accès tail -f /var/log/dynamic-alpinux/access.log
Logs erreurs tail -f /var/log/dynamic-alpinux/error.log

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 :

{
  "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 :

sudo systemctl restart dynamic-alpinux