- stats.html : bouton "Générer et ouvrir" avec polling async ; supprime le window.open automatique (bloqué par les navigateurs) - app.py : routes POST /stats/generate et GET /stats/status ; exécution GoAccess en thread daemon, verrou anti-doublon - .env.example : documente STATS_LOG_FILE et STATS_GENERATE_CMD - README.md : flux de publication local→git→serveur, variables d'env, procédure première installation Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| app | ||
| logo | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| favicon.ico | ||
| README.md | ||
static.alpinux.org
CDN pour les fichiers binaires et assets statiques de l'association : logos, favicons, images Open Graph.
Tableau de bord d'administration accessible sur https://static.alpinux.org (authentification AlpID).
Flux de publication
Il y a deux cibles distinctes sur le serveur, gérées par des scripts séparés :
| Quoi | Destination serveur | Script | Passe par git ? |
|---|---|---|---|
| Assets CDN (logo/, wiki/, stats/, error/…) | /var/www/clients/client1/web17/web |
push-assets.sh |
Non — rsync direct |
App Flask (app/) |
/opt/static-cdn/ + redémarrage service |
deploy-app.sh |
Non — rsync direct |
Git sert à versionner et sauvegarder le code sur Gitea. Il n'est pas dans la boucle de déploiement : les scripts rsynchent depuis le dépôt local, pas depuis Gitea.
Modifier les assets CDN (logo, wiki, stats…)
# 1. Modifier les fichiers dans static/
# 2. Versionner (optionnel mais recommandé)
git add <fichiers>
git commit -m "..."
git push # sauvegarde sur gitea.alpinux.org
# 3. Pousser sur le serveur
./scripts/push-assets.sh # aperçu + confirmation
./scripts/push-assets.sh -y # sans confirmation
push-assets.shexclut automatiquementapp/,scripts/,.git/,.env,README.md. Il envoie :logo/,wiki/,stats/,error/,favicon.ico,robots.txt,standard_index.html.
Modifier l'app Flask (app/)
# 1. Modifier les fichiers dans static/app/
# 2. Versionner
git add app/
git commit -m "..."
git push # sauvegarde sur gitea.alpinux.org
# 3. Déployer sur le serveur
./scripts/deploy-app.sh # rsync + pip install + restart service
./scripts/deploy-app.sh -n # dry-run
deploy-app.shrsyncapp/vers/opt/static-cdn/, met à jour le venv Python, et redémarre le service systemdstatic-cdn.
Contenu hébergé
| Fichier | Usage |
|---|---|
logo/alpinux-logo-512.png |
Logo 512 px — page d'accueil alpinux.org, balises OG |
logo/alpinux-logo-192.png |
Logo 192 px — PWA / Android home screen |
logo/favicon-32.png |
Favicon 32 px |
logo/favicon-16.png |
Favicon 16 px |
logo/favicon.ico |
Favicon ICO multi-taille |
wiki/ |
Images pour wiki.alpinux.org |
Configuration locale (.env)
cp .env.example .env
| Variable | Obligatoire | Description |
|---|---|---|
STATIC_HOST |
oui | Alias SSH ou nom d'hôte (alpinux.org) |
STATIC_PATH |
oui | Chemin absolu du web root sur le serveur |
LOCAL_ASSETS_DIR |
oui | Chemin absolu du dépôt local (static/) |
STATIC_USER |
non | Login SSH — laisser vide si ~/.ssh/config définit l'utilisateur |
Si l'hôte est déclaré dans ~/.ssh/config (recommandé), laisser STATIC_USER vide.
Sinon, renseigner STATIC_USER=<login> — les scripts construisent alors USER@HOST:PATH.
Scripts
# Synchroniser les assets CDN
./scripts/pull-assets.sh # serveur → local (aperçu + confirmation)
./scripts/pull-assets.sh -y # sans confirmation
./scripts/pull-assets.sh -n # dry-run
./scripts/push-assets.sh # local → serveur (aperçu + confirmation)
./scripts/push-assets.sh -y # sans confirmation
./scripts/push-assets.sh -n # dry-run
# Déployer l'app Flask
./scripts/deploy-app.sh # déploie et redémarre le service
./scripts/deploy-app.sh -n # dry-run
Tableau de bord Flask (app/)
Application Flask déployée sur le serveur via deploy-app.sh.
Accessible sur https://static.alpinux.org (proxy Apache → 127.0.0.1:5003).
Authentification SSO via AlpID (Keycloak).
Fonctionnalités
| Route | Description |
|---|---|
/ |
Tableau de bord — statistiques CDN par dossier |
/browse/ |
Navigateur de fichiers — aperçu, suppression, upload |
/search |
Recherche par nom / contenu / date |
/stats/ |
Rapport de trafic GoAccess |
Upload de fichiers
Dans le navigateur (/browse/), une zone de dépôt est affichée en bas de chaque dossier.
Elle accepte plusieurs fichiers à la fois (glisser-déposer ou sélection).
Les chemins protégés (.git, app, scripts, etc.) sont refusés avec 403.
Pour limiter la taille des uploads, ajouter dans app/app.py :
app.config['MAX_CONTENT_LENGTH'] = 50 * 1024 * 1024 # 50 Mo
Variables d'environnement de l'app
Le fichier de référence est app/.env.example. Sur le serveur : /opt/static-cdn/.env.
Auth & Flask
| Variable | Obligatoire | Description |
|---|---|---|
SECRET_KEY |
oui | Clé secrète Flask (chaîne aléatoire longue) |
ALPID_CLIENT_ID |
oui | Client Keycloak (static-cdn) |
ALPID_CLIENT_SECRET |
oui | Secret client Keycloak |
ALPID_DISCOVERY_URL |
oui | URL OIDC discovery AlpID |
ADMIN_GROUPS |
non | Groupes Keycloak autorisés (défaut : admins) |
ADMIN_EMAILS |
non | Fallback si le claim groups est absent du token |
ASSETS_ROOT |
oui | Racine du CDN (/var/www/clients/client1/web17/web en prod) |
Stats GoAccess
| Variable | Description |
|---|---|
STATS_FILE |
Rapport HTML GoAccess servi dans /stats/ (défaut : /opt/static-cdn/goaccess.html) |
STATS_JSON |
Rapport JSON GoAccess pour les badges "Vues" (défaut : /opt/static-cdn/goaccess.json) |
STATS_LOG_FILE |
Log Apache à analyser pour la génération à la demande |
STATS_GENERATE_CMD |
Commande GoAccess complète — remplace la commande par défaut si renseigné |
Génération du rapport GoAccess à la demande
Si goaccess.html est absent, la page /stats/ affiche un bouton Générer et ouvrir :
- Lance GoAccess en arrière-plan (thread daemon)
- Interroge
/stats/statustoutes les 2 s - Ouvre le rapport dans un nouvel onglet dès qu'il est prêt
Si le rapport existe, un bouton ↗ Ouvrir dans un nouvel onglet l'affiche directement.
La commande par défaut (si STATS_GENERATE_CMD est vide) :
goaccess <STATS_LOG_FILE> --log-format=COMBINED -o <STATS_FILE> [-o <STATS_JSON>]
Valeurs utilisées sur le serveur :
STATS_LOG_FILE=/var/log/ispconfig/httpd/static.alpinux.org/access.log
STATS_GENERATE_CMD=goaccess /var/log/ispconfig/httpd/static.alpinux.org/access.log \
--config-file=/var/log/ispconfig/httpd/static.alpinux.org/goaccess.conf \
-o /opt/static-cdn/goaccess.html -o /opt/static-cdn/goaccess.json
Prérequis serveur : l'utilisateur abonnelc doit appartenir au groupe client1
pour lire les logs ISPConfig (opération à faire une seule fois) :
sudo usermod -a -G client1 abonnelc
sudo systemctl restart static-cdn
Première installation (nouveau serveur)
1. Créer le site dans ISPConfig
https://owni.alpinux.org:8080 → Sites → Ajouter un site web
- Domaine :
static.alpinux.org - Activer Let's Encrypt SSL
- DocumentRoot :
/var/www/clients/client1/web17/web
2. Configurer le proxy Apache
Dans ISPConfig → onglet Directives Apache SSL du site :
# CDN public → Apache sert directement depuis DocumentRoot
ProxyPass /logo/ !
ProxyPass /wiki/ !
ProxyPass /error/ !
ProxyPass /favicon.ico !
ProxyPass /robots.txt !
# Tableau de bord → Flask
RequestHeader set X-Forwarded-Proto "https"
ProxyPreserveHost On
ProxyPass / http://127.0.0.1:5003/
ProxyPassReverse / http://127.0.0.1:5003/
3. Déployer l'app Flask
# Depuis la machine locale
./scripts/deploy-app.sh
# Sur le serveur — créer /opt/static-cdn/.env (voir app/.env.example)
ssh alpinux.org
nano /opt/static-cdn/.env
# Activer le service
sudo systemctl enable --now static-cdn
4. Pousser les assets CDN
./scripts/push-assets.sh -y
5. Droits sur les logs (pour la génération GoAccess)
sudo usermod -a -G client1 abonnelc
sudo systemctl restart static-cdn