Skip to content

Commit c93e114

Browse files
committed
Merge remote-tracking branch 'origin/develop' into feat/voiture
2 parents 4d9950b + 82e095b commit c93e114

314 files changed

Lines changed: 3135 additions & 100 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Sync integrabook
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths: [integrabook/**]
7+
8+
jobs:
9+
sync:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
steps:
14+
- uses: actions/checkout@v4
15+
with:
16+
sparse-checkout: integrabook
17+
18+
- uses: actions/checkout@v4
19+
with:
20+
repository: incubateur-ademe/impactco2-integrabook
21+
path: target
22+
token: ${{ secrets.INTEGRABOOK_SYNC_TOKEN }}
23+
24+
- name: Sync files
25+
env:
26+
COMMIT_MSG: ${{ github.event.head_commit.message }}
27+
run: |
28+
rsync -a --delete --exclude='.git' integrabook/ target/
29+
cd target
30+
git add -A
31+
if git diff --cached --quiet; then
32+
echo "No changes to sync"
33+
else
34+
git -c user.name="impactco2-sync" -c user.email="noreply@impactco2.fr" commit -m "sync: $COMMIT_MSG"
35+
git push
36+
fi

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const eslintConfig = [
4646
'next.config.js',
4747
'playwright.config.js',
4848
'public/**',
49+
'integrabook/**',
4950
],
5051
},
5152
]

integrabook/README.md

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# Intégrabook - Impact CO2
2+
3+
Pages HTML de test simulant un site tiers qui intègre les modules Impact CO2.
4+
Destiné aux audits de sécurité (pentest) et aux tests d'intégration cross-origin.
5+
6+
## Démarrage rapide
7+
8+
1. Modifier `config.js` pour cibler l'environnement voulu :
9+
10+
```js
11+
const INTEGRABOOK_CONFIG = {
12+
baseUrl: 'https://impactco2.fr', // ou staging, localhost:3000, etc.
13+
theme: 'default', // 'default' | 'night'
14+
language: 'fr', // 'fr' | 'en' | 'es'
15+
}
16+
```
17+
18+
2. Servir les fichiers depuis le dossier `integrabook/` sur un domaine distinct (pour tester le CORS) :
19+
20+
```bash
21+
cd integrabook
22+
python3 -m http.server 8888
23+
# ou
24+
npx serve -p 8888
25+
```
26+
27+
3. Ouvrir `http://localhost:8888` dans un navigateur.
28+
29+
## Configuration
30+
31+
### Priorité de résolution
32+
33+
La configuration suit cet ordre de priorité (le premier trouvé gagne) :
34+
35+
1. **Query string** `?baseUrl=X&theme=Y&language=Z`
36+
2. **localStorage** (persisté via la barre de config UI)
37+
3. **`config.js`** (valeurs par défaut)
38+
39+
### Via `config.js` (recommandé pour l'environnement)
40+
41+
Le fichier `config.js` à la racine définit les valeurs par défaut. C'est le seul fichier à modifier pour changer d'environnement.
42+
43+
| Clé | Description | Valeurs |
44+
|------------|--------------------------------------|----------------------------------|
45+
| `baseUrl` | URL de l'instance Impact CO2 cible | URL complète sans slash final |
46+
| `theme` | Thème par défaut des widgets | `default`, `night` |
47+
| `language` | Langue par défaut | `fr`, `en`, `es` |
48+
49+
### Via query string (recommandé pour le partage)
50+
51+
Les paramètres `baseUrl`, `theme` et `language` dans l'URL sont propagés automatiquement sur tous les liens internes. Un lien partagé configure tout le site en un clic :
52+
53+
```
54+
http://pentest.example.com/?baseUrl=https://staging.impactco2.fr&theme=night&language=en
55+
```
56+
57+
### Via l'interface
58+
59+
Chaque page expose une barre de configuration en haut. Les changements sont persistés en localStorage **et** dans le query string.
60+
61+
Pour revenir aux valeurs de `config.js`, vider le localStorage :
62+
63+
```js
64+
localStorage.removeItem('integrabook-baseUrl')
65+
localStorage.removeItem('integrabook-theme')
66+
localStorage.removeItem('integrabook-language')
67+
```
68+
69+
### Thème night
70+
71+
Quand le thème `night` est sélectionné, les zones de rendu live passent automatiquement en fond sombre avec texte clair, pour refléter le comportement des widgets en mode nuit.
72+
73+
## Pages disponibles
74+
75+
### Modules thématiques
76+
77+
| Page | Module | Intégrations |
78+
|-----------------------|-----------------------------------------|---------------------------------------|
79+
| `comparateur.html` | Comparateur CO2 + étiquettes | iframe.js, iframe direct |
80+
| `transport.html` | Transport + itinéraire | iframe.js, iframe direct, API REST |
81+
| `livraison.html` | Livraison + étiquettes | iframe.js, iframe direct |
82+
| `alimentation.html` | Alimentation | iframe.js, iframe direct, API REST |
83+
| `habillement.html` | Habillement + Osez Changer | iframe.js, iframe direct |
84+
| `chauffage.html` | Chauffage | iframe.js, iframe direct, API REST |
85+
| `quiz.html` | Quiz + quiz infographie | iframe.js, iframe direct |
86+
| `infographie.html` | Infographie + détecteur | iframe.js, iframe direct |
87+
88+
### Méthodes d'intégration transverses
89+
90+
| Page | Méthode | Description |
91+
|-----------------------|-----------------------------------------|---------------------------------------|
92+
| `detecteur.html` | Scripts de détection | `detection.js` (sync) + `detection-async.js` |
93+
| `webcomponent.html` | Web Component `<etiquette-ico2>` | Custom element natif |
94+
| `api.html` | API REST `/api/v1/*` | Tous les endpoints avec testeur |
95+
| `legacy.html` | IDs legacy | `mon-impact-transport`, `datagir-*`, `ecolab-*`, etc. |
96+
| `legacy-isolated.html`| Legacy isolé (via `?id=`) | Un seul ID legacy par page pour test indépendant |
97+
98+
## Structure des fichiers
99+
100+
```
101+
integrabook/
102+
├── config.js # Configuration (base URL, theme, langue)
103+
├── shared.css # Styles communs
104+
├── shared.js # Logique partagée (config bar, params editor, helpers)
105+
├── index.html # Hub de navigation
106+
├── comparateur.html
107+
├── transport.html
108+
├── livraison.html
109+
├── alimentation.html
110+
├── habillement.html
111+
├── chauffage.html
112+
├── quiz.html
113+
├── infographie.html
114+
├── detecteur.html
115+
├── webcomponent.html
116+
├── api.html
117+
├── legacy.html # Vue d'ensemble des 7 IDs legacy
118+
└── legacy-isolated.html # Page isolée par ID (?id=impact-co2, etc.)
119+
```
120+
121+
## Fonctionnalités par page
122+
123+
Chaque page de module contient :
124+
125+
- **Barre de configuration** : base URL, thème, langue (persisté en localStorage + query string)
126+
- **Éditeur de paramètres** : formulaire spécifique au module avec Appliquer / Reset
127+
- **Bloc de code** : snippet d'intégration copiable, mis à jour en temps réel avec les paramètres
128+
- **Zone de rendu live** : le widget tel qu'un intégrateur tiers le verrait (fond adapté au thème)
129+
130+
### Legacy IDs (spécificités)
131+
132+
Le script `iframe.js` détecte les IDs legacy via `getElementById` en cascade — seul le premier trouvé est traité. Les modules classiques (via `data-name="impact-co2"`) n'ont pas cette limitation et supportent plusieurs widgets par page.
133+
134+
`legacy.html` donne une vue d'ensemble avec les snippets de code pour chaque ID. Pour tester le rendu live de chaque ID indépendamment, utiliser les liens "Ouvrir en page isolée" qui pointent vers `legacy-isolated.html?id=<legacy-id>`.
135+
136+
### Détecteur (spécificités)
137+
138+
La page `detecteur.html` fonctionne différemment des autres : les scripts de détection scannent tout le DOM à la recherche de textes contenant des valeurs CO2 (regex sur "X kg CO2", "X tonnes CO2e", etc.) et les annotent avec des étiquettes interactives.
139+
140+
- **Sync** (`detection.js`) : se déclenche au clic sur "Charger". Le script s'exécute immédiatement.
141+
- **Async** (`detection-async.js`) : se charge au clic, puis se déclenche manuellement via `window.impactCO2Detection()`.
142+
- Les deux variantes sont **mutuellement exclusives** : la première qui tourne marque les éléments comme traités. Recharger la page entre les tests.
143+
144+
## Surfaces d'attaque couvertes
145+
146+
| Vecteur | Pages concernées |
147+
|----------------------------|-----------------------------------------------|
148+
| Injection de script tiers | Toutes les pages iframe (`iframe.js`) |
149+
| Cross-origin iframe | Toutes les pages avec iframe direct |
150+
| CORS / API REST | `api.html`, `transport.html`, `alimentation.html`, `chauffage.html` |
151+
| postMessage | Toutes les pages iframe (`iframe-resizer`) |
152+
| Custom elements | `webcomponent.html` |
153+
| DOM auto-detection | `detecteur.html`, `legacy.html` |

integrabook/alimentation.html

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<!DOCTYPE html>
2+
<html lang="fr">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Alimentation - Intégrabook</title>
8+
<link rel="stylesheet" href="shared.css">
9+
</head>
10+
11+
<body>
12+
<div class="layout">
13+
<header>
14+
<h1>Alimentation <span class="badge badge-iframe">iframe</span> <span class="badge badge-api">api</span></h1>
15+
<a class="back" href="index.html">← Intégrabook</a>
16+
</header>
17+
18+
<div id="config-bar" class="config-bar"></div>
19+
20+
<!-- Alimentation iframe -->
21+
<div class="section">
22+
<h2>Alimentation (via script iframe.js)</h2>
23+
<p class="desc">Comparateur d'impact carbone alimentaire.</p>
24+
<div id="params-alimentation"></div>
25+
<div id="code-alimentation" class="code-block"></div>
26+
<div id="render-alimentation" class="render-zone"></div>
27+
</div>
28+
29+
<!-- Iframe direct -->
30+
<div class="section">
31+
<h2>Alimentation (iframe direct)</h2>
32+
<p class="desc">Iframe brut sans script intermédiaire.</p>
33+
<div id="code-direct" class="code-block"></div>
34+
<div id="render-direct" class="render-zone"></div>
35+
</div>
36+
37+
<!-- API Alimentation -->
38+
<div class="section">
39+
<h2>API REST /api/v1/alimentation</h2>
40+
<p class="desc">Appel direct pour récupérer les données alimentaires par catégorie.</p>
41+
<div id="code-api" class="code-block"></div>
42+
<div class="api-controls">
43+
<button onclick="fetchApi('alimentation?category=group', 'api-result')">Par groupe</button>
44+
<button onclick="fetchApi('alimentation?category=rayon', 'api-result')">Par rayon</button>
45+
<button onclick="fetchApi('alimentation?category=popularity', 'api-result')">Par popularité</button>
46+
<button onclick="fetchApi('alimentation?category=group&language=en', 'api-result')">Par groupe (EN)</button>
47+
</div>
48+
<div id="api-result" class="api-result">Cliquez sur un bouton pour lancer un appel API.</div>
49+
</div>
50+
</div>
51+
52+
<script src="config.js"></script>
53+
<script src="shared.js"></script>
54+
<script>
55+
function renderAlimentation(extra) {
56+
const base = getBaseUrl()
57+
const search = buildSearch(extra)
58+
renderCodeBlock('code-alimentation',
59+
`<script
60+
data-name="impact-co2"
61+
src="${base}/iframe.js"
62+
data-type="alimentation"
63+
data-search="${search}"><\/script>`)
64+
reRenderIframeScript('render-alimentation', 'alimentation', extra)
65+
}
66+
67+
document.addEventListener('DOMContentLoaded', () => {
68+
const base = getBaseUrl()
69+
const search = buildSearch()
70+
71+
buildParamsEditor('params-alimentation', [
72+
{
73+
name: 'alimentationCategory', type: 'select', default: '', options: [
74+
{ value: 'group', label: 'Groupe' },
75+
{ value: 'rayon', label: 'Rayon' },
76+
{ value: 'popularity', label: 'Popularité' },
77+
], hint: 'Catégorie de tri'
78+
},
79+
{ name: 'alimentationEquivalents', type: 'text', default: '', placeholder: 'tomate,carotte,poulet', hint: 'Équivalents personnalisés (slugs séparés par virgule)' },
80+
], (values) => {
81+
renderAlimentation(getExtraSearchFromParams(values))
82+
})
83+
84+
renderAlimentation({})
85+
86+
const directSrc = `${base}/iframes/alimentation${search}${search ? '&' : '?'}source=${encodeURIComponent(window.location.href)}`
87+
renderCodeBlock('code-direct',
88+
`<iframe
89+
src="${directSrc}"
90+
style="border: none; width: 100%; min-height: 500px;"
91+
allowfullscreen></iframe>`)
92+
injectDirectIframe('render-direct', 'alimentation')
93+
94+
renderCodeBlock('code-api',
95+
`fetch('${base}/api/v1/alimentation?category=group')
96+
.then(res => res.json())
97+
.then(data => console.log(data))`)
98+
})
99+
</script>
100+
</body>
101+
102+
</html>

0 commit comments

Comments
 (0)