App Flask complète pour https://dynamic.alpinux.org : - 10 quiz Linux, 5 niveaux (Découverte → Expert), 50+ questions - Public : Découverte, Débutant, Intermédiaire (6 quiz) - Membres AlpID : Avancé, Expert (4 quiz — Git, Admin, Sécurité, Bash) - Navigation question par question avec avance automatique après choix - Score calculé côté serveur, enregistré en SQLite si connecté - Page profil : meilleurs scores par quiz + historique des tentatives Authentification : - OIDC via authlib + AlpID (Keycloak), SSO partagé avec Gitea/Nextcloud - Décorateur @login_required, redirection post-login sur l'URL d'origine - /auth/login, /auth/callback, /auth/logout Structure : - dynamic/app.py, db.py, quiz.py, auth_utils.py - dynamic/routes/ (public.py, auth.py, protected.py) - dynamic/templates/ (base, index, quiz/*, profil/) - dynamic/static/ (style.css thème Alpinux, quiz.js vanilla) - dynamic/data/quizzes.json (source de vérité des questions) - dynamic/.env.example Infrastructure : - scripts/dynamic.alpinux.org.vhost.conf (Apache reverse proxy) - scripts/dynamic.alpinux.org.service (systemd Gunicorn) - docs/technique/deploiement-dynamic.md (procédure complète) - mkdocs.yml : page de déploiement ajoutée à la nav Technique - .gitignore : exclut venv/ et .env Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
46 lines
1.1 KiB
Python
46 lines
1.1 KiB
Python
import os
|
|
from flask import Flask
|
|
from authlib.integrations.flask_client import OAuth
|
|
|
|
from db import close_db, init_db
|
|
from routes.public import public_bp
|
|
from routes.auth import auth_bp
|
|
from routes.protected import protected_bp
|
|
|
|
|
|
def create_app():
|
|
app = Flask(__name__)
|
|
|
|
app.secret_key = os.environ['SECRET_KEY']
|
|
|
|
# OIDC AlpID
|
|
oauth = OAuth(app)
|
|
oauth.register(
|
|
name='alpid',
|
|
server_metadata_url=os.environ['ALPID_DISCOVERY_URL'],
|
|
client_id=os.environ['ALPID_CLIENT_ID'],
|
|
client_secret=os.environ['ALPID_CLIENT_SECRET'],
|
|
client_kwargs={'scope': 'openid profile email'},
|
|
)
|
|
app.extensions['oauth'] = oauth
|
|
|
|
# Filtre Jinja2 utilitaire
|
|
app.jinja_env.filters['enumerate'] = enumerate
|
|
|
|
# Blueprints
|
|
app.register_blueprint(public_bp)
|
|
app.register_blueprint(auth_bp, url_prefix='/auth')
|
|
app.register_blueprint(protected_bp)
|
|
|
|
# Base de données
|
|
app.teardown_appcontext(close_db)
|
|
with app.app_context():
|
|
init_db()
|
|
|
|
return app
|
|
|
|
|
|
app = create_app()
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=os.environ.get('FLASK_DEBUG', '0') == '1')
|