Session 26 — Scripts UI v3 · Onglet Runs · Droits JMeter/Scripts¶
Durée¶
~4h
Objectifs¶
- Enrichir Scripts UI avec les nouvelles fonctionnalités post-déploiement Phase 10
- Ajouter la persistance des runs sur disque (onglet 🏃 Runs)
- Étendre le système de droits admin avec deux nouveaux accès : JMeter et Scripts
- Corriger un XSS résiduel dans l'interface de gestion des comptes
Ce qui a été livré¶
Scripts UI — nouvelles fonctionnalités (v3)¶
Dépendances locales (zéro réseau)¶
Toutes les dépendances CDN et Google Fonts ont été remplacées par des fichiers locaux
dans scripts-ui/public/vendor/ :
| Fichier | Source | Licence |
|---|---|---|
codemirror.min.css |
cdnjs — CodeMirror 5.65.16 | MIT ✅ |
dracula.min.css |
cdnjs — thème Dracula | MIT ✅ |
codemirror.min.js |
cdnjs — CodeMirror 5.65.16 | MIT ✅ |
python.min.js |
cdnjs — mode Python | MIT ✅ |
L'application fonctionne désormais sans accès internet — cas d'usage NAS offline.
Drag & drop de l'arborescence¶
Les fichiers .robot et .py peuvent être glissés d'un dossier vers un autre
directement dans l'arborescence de gauche.
| Événement | Comportement |
|---|---|
dragstart sur un fichier |
Opacité 0.5 — feedback visuel |
dragover sur un dossier |
Fond violet + bordure drag-over |
dragleave |
Guard !row.contains(e.relatedTarget) — pas de flash enfant |
drop |
POST /api/move → loadTree() + loadCommits() |
| Dépôt sur le dossier parent | No-op silencieux (srcPath === dstPath) |
| Fichier ouvert déplacé | currentFile.path mis à jour automatiquement |
Filtrage par extension¶
Le bouton ▶ Lancer est désormais grisé pour tous les fichiers non exécutables
(.gitkeep, tout autre format). La validation est double :
- Côté UI (
openFile()) :btnRun.disabled = !(ext === 'robot' || ext === 'pytest') - Côté serveur (
POST /api/run) :path.extname().toLowerCase()→ HTTP 400 si hors.robot/.py
Bouton « ⬇️ Pull Forgejo »¶
Le bouton « 🔄 Sync Git » a été renommé en « ⬇️ Pull Forgejo » avec un tooltip explicite. 5 occurrences mises à jour (HTML, JS état running, JS restauration, toasts succès/échec).
Onglet 🏃 Runs (persistance sur disque)¶
Nouveau troisième onglet dans le panneau bas, aux côtés de « 📋 Résultat » et « 📜 Historique ».
Persistance : à la fin de chaque exécution (PASS, FAIL ou erreur), un dossier
/rf-logs/runs/<runId>/ est créé avec :
| Fichier | Contenu |
|---|---|
meta.json |
script, startedAt, endedAt, status, rc |
output.txt |
Sortie complète du run (stdout + stderr) |
result.xml |
Copie du XML JUnit (si présent dans /rf-logs/) |
Routes backend :
| Route | Description |
|---|---|
GET /api/runs |
Liste des 50 derniers runs, triés date desc |
GET /api/runs/:id |
Détail d'un run (meta + output) — validation anti-traversal |
UI :
- Chaque ligne affiche : badge PASS/FAIL/ERR · nom du script · durée · date
- Clic sur une ligne → bascule sur l'onglet Résultat avec l'output complet
accountsByIdrechargé à chaqueloadRuns()— pas de fuite mémoire
Volume nécessaire : ./test-runner/logs:/rf-logs monté sur perfshop-scripts-ui
sans :ro — ajouté dans les 3 docker-compose lors de cette session.
Droits JMeter et Scripts — nouvelle migration V31¶
Deux nouveaux droits d'accès ajoutés à la table admin_users :
| Colonne SQL | Champ Java | Rôle |
|---|---|---|
can_access_jmeter |
canAccessJmeter |
Accès interface JMeter |
can_access_scripts |
canAccessScripts |
Accès interface Scripts UI |
Migration Flyway V31__add_jmeter_scripts_rights.sql¶
ALTER TABLE admin_users
ADD COLUMN can_access_jmeter BOOLEAN NOT NULL DEFAULT FALSE,
ADD COLUMN can_access_scripts BOOLEAN NOT NULL DEFAULT FALSE;
UPDATE admin_users
SET can_access_jmeter = TRUE, can_access_scripts = TRUE
WHERE is_superadmin = TRUE;
Les comptes existants restent à FALSE par défaut (accès refusé, comportement conservateur).
Le superadmin est mis à jour automatiquement par la migration SQL et par le guard
bootstrapSuperAdmin() en Java (double filet de sécurité).
Fichiers modifiés¶
| Fichier | Modifications |
|---|---|
V31__add_jmeter_scripts_rights.sql |
Nouveau — ALTER TABLE + UPDATE superadmin |
AdminUser.java |
+2 champs @Column avec nullable = false |
AdminUserService.java |
createAdmin() +2 params · updateRights() +2 params · bootstrapSuperAdmin() guard V31 |
AdminController.java |
login() +2 clés JSON · createAccount() +2 params · updateRights() +2 params · adminToDto() +2 champs |
admin-users.html |
Tableau +2 colonnes · formulaire +2 cases à cocher |
Interface admin-users.html¶
Le tableau des comptes administrateurs affiche maintenant 9 colonnes : Email · Chaos-admin · Monitoring · Backoffice · JMeter · Scripts · Rôle · Créé le · Actions
Les nouvelles cases à cocher (Backoffice, JMeter, Scripts) sont décochées par défaut lors de la création d'un compte — principe du moindre privilège.
Correction XSS dans renderTable()¶
Bug préexistant corrigé : l'email passait dans un attribut onclick inline entre apostrophes.
Un email contenant ' (ex : o'reilly@test.fr) cassait le JavaScript.
Correction — pattern délégation + registre mémoire :
// Registre : id → account object (données jamais dans le DOM)
const accountsById = new Map();
// HTML généré : seul l'entier id transite dans data-*
`<button class="btn btn-delete" data-id="${a.id}">🗑 Supprimer</button>`
// Délégation d'événements — email lu depuis JS, jamais depuis le DOM
tbody.onclick = e => {
const btn = e.target.closest('button[data-id]');
const id = Number(btn.dataset.id);
const account = accountsById.get(id);
if (btn.classList.contains('btn-delete')) deleteAccount(id, account.email);
if (btn.classList.contains('btn-save')) openPasswordModal(id);
};
Bénéfices collatéraux : un seul listener au lieu de N handlers inline, clear() à chaque
rechargement évite toute fuite mémoire.
docker-compose — modifications (3 fichiers)¶
| Modification | Détail |
|---|---|
Service perfshop-filebrowser supprimé |
Remplacé fonctionnellement par Scripts UI |
Volume filebrowser-data supprimé |
Plus aucun consommateur |
./test-runner/logs:/rf-logs ajouté sur perfshop-scripts-ui |
Sans :ro — écriture nécessaire pour les runs |
./test-runner/logs:/rf-logs:ro conservé sur Promtail |
Inchangé |
Typo ci@perfshep.fr corrigée → ci@perfshop.fr |
Dans docker-compose.build.yml |
Déploiement¶
Scripts UI (redémarrage simple)¶
# Le volume /rf-logs étant nouveau, down/up obligatoire (pas un restart)
docker compose stop perfshop-scripts-ui
docker compose up -d perfshop-scripts-ui
docker logs -f perfshop-scripts-ui
Backend Spring Boot (rebuild + migration Flyway automatique)¶
docker compose build perfshop-app
docker compose up -d perfshop-app
# Vérifier dans les logs :
# Flyway: Successfully applied 1 migration ... V31
# [AdminUserService] Droits JMeter/Scripts ajoutés au superadmin
chaos-admin (rebuild image nginx)¶
Suppression Filebrowser (NAS)¶
docker compose stop perfshop-filebrowser
docker compose rm -f perfshop-filebrowser
docker volume rm perfshop_filebrowser-data
Validation post-déploiement¶
| Test | Attendu |
|---|---|
| Bouton « ⬇️ Pull Forgejo » visible | ✅ |
Glisser un .robot vers un sous-dossier |
✅ Déplacé dans Forgejo |
Lancer un .robot → onglet 🏃 Runs rempli après fin |
✅ |
| Clic ligne dans Runs → onglet Résultat avec output | ✅ |
| Créer un compte admin avec case JMeter cochée | ✅ |
| Email avec apostrophe dans la liste → aucun bug JS | ✅ |
Bouton ▶ Lancer grisé sur .gitkeep |
✅ |
Logs backend : [Run] run-1-xxx — PASSED (rc=0) |
✅ |
Erreurs documentées¶
Affichage fichiers : view vs cat dans la conversation¶
Contexte : pour afficher de gros fichiers dans la conversation, view produisait
un rendu JSON échappé illisible dans l'UI Claude.
Fix : Filesystem:write_file directement sur Windows — aucun affichage dans la conversation.
Règle adoptée : gros fichiers → écriture directe Windows, pas d'affichage dans la conversation.