diff --git a/cleanup_test_files.sh b/cleanup_test_files.sh new file mode 100755 index 0000000..499112e --- /dev/null +++ b/cleanup_test_files.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Script de nettoyage des fichiers de test CORS + +echo "🧹 Nettoyage des fichiers de test CORS..." + +# Supprimer les fichiers de test +rm -f test_cors_embed.py +rm -f test_embed_cors.html +rm -f cleanup_test_files.sh + +echo "✅ Fichiers de test supprimés" diff --git a/frontend/src/app/pages/agenda/agenda.html b/frontend/src/app/pages/agenda/agenda.html index 926bb33..8625554 100644 --- a/frontend/src/app/pages/agenda/agenda.html +++ b/frontend/src/app/pages/agenda/agenda.html @@ -1,10 +1,6 @@
- -
- -
@if (isLoading) {
@@ -12,31 +8,31 @@
} @else {
- + + + @if (selectedEvent) { +
+
+

Modifier l'événement

+ +
+
+ + +
}
- + } + +
+ +
-
- - @if (selectedEvent) { -
-
-

Modifier l'événement

- -
-
- - -
-
- } -
- }
- \ No newline at end of file + diff --git a/frontend/src/app/pages/agenda/agenda.scss b/frontend/src/app/pages/agenda/agenda.scss index 1ad8076..e263a6d 100644 --- a/frontend/src/app/pages/agenda/agenda.scss +++ b/frontend/src/app/pages/agenda/agenda.scss @@ -10,7 +10,7 @@ grid-template-rows: minmax(100vh, auto); gap: 0; // min-height: 100vh; - + &.is-small { grid-template-columns: 100px 1fr; } @@ -220,4 +220,4 @@ .event-edit-panel { width: 100%; } -} \ No newline at end of file +} diff --git a/frontend/src/oedb-types.ts b/frontend/src/oedb-types.ts index 2edbdab..3ab1db5 100644 --- a/frontend/src/oedb-types.ts +++ b/frontend/src/oedb-types.ts @@ -1,6 +1,6 @@ const oedb = { - presets : { - what : { + presets: { + what: { 'community': { label: 'Événement de base', description: 'Événement communautaire', @@ -22,11 +22,11 @@ const oedb = { description: 'Événement de type Culture ouvert au public', durationHours: 24, properties: { - createdate: { label: 'Createdate', writable: true }, - lastupdate: { label: 'Lastupdate', writable: true }, - start: { label: 'Start', writable: true }, - stop: { label: 'Stop', writable: true }, - type: { label: 'Type', writable: true } + createdate: {label: 'Createdate', writable: true}, + lastupdate: {label: 'Lastupdate', writable: true}, + start: {label: 'Start', writable: true}, + stop: {label: 'Stop', writable: true}, + type: {label: 'Type', writable: true} } }, 'culture.floss': { @@ -35,15 +35,15 @@ const oedb = { category: 'Autre', description: 'Événement de type Culture Floss', durationHours: 24, - properties: { -}}, 'culture.viparis': { + properties: {} + }, 'culture.viparis': { emoji: '📅', label: 'Évènements organisés par Viparis', category: 'Autre', description: 'Événement culturel par Viparis, une entreprise qui gère plusieurs grandes salles en Île de France.', durationHours: 24, - properties: { -}}, + properties: {} + }, // Culture / Arts @@ -109,8 +109,8 @@ const oedb = { description: 'Infrastructure de recharge disponible', durationHours: 300, properties: { - "capacity:vehicles": { label: 'Nombre de véhicules qui peuvent actuellement se brancher', writable: true }, - "capacity:vehicles:max": { label: 'Nombre de véhicules maximum à pouvoir se brancher', writable: true } + "capacity:vehicles": {label: 'Nombre de véhicules qui peuvent actuellement se brancher', writable: true}, + "capacity:vehicles:max": {label: 'Nombre de véhicules maximum à pouvoir se brancher', writable: true} } }, 'traffic.counter.bicycle': { @@ -162,9 +162,9 @@ const oedb = { description: 'Accident de la circulation', durationHours: 6, properties: { - severity: { label: 'Gravité', writable: true }, - lanes_closed: { label: 'Voies fermées', writable: true }, - vehicles: { label: 'Nombre de véhicules', writable: true } + severity: {label: 'Gravité', writable: true}, + lanes_closed: {label: 'Voies fermées', writable: true}, + vehicles: {label: 'Nombre de véhicules', writable: true} } }, 'traffic.incident': { @@ -193,9 +193,9 @@ const oedb = { description: 'Travaux sur la chaussée', durationHours: 72, properties: { - contractor: { label: 'Entreprise', writable: true }, - reason: { label: 'Raison', writable: true }, - lanes_affected: { label: 'Voies impactées', writable: true } + contractor: {label: 'Entreprise', writable: true}, + reason: {label: 'Raison', writable: true}, + lanes_affected: {label: 'Voies impactées', writable: true} } }, 'traffic.OperatorAction.NetworkManagement.RoadOrCarriagewayOrLaneManagement': { @@ -206,7 +206,7 @@ const oedb = { description: 'Événement de type Traffic OperatorAction NetworkManagement RoadOrCarriagewayOrLaneManagement', durationHours: 24, properties: { - source: { label: 'Source', writable: true }, + source: {label: 'Source', writable: true}, } }, @@ -218,7 +218,7 @@ const oedb = { description: 'Événement de type Traffic OperatorAction NetworkManagement RoadOrCarriagewayOrLaneManagement', durationHours: 200, properties: { - source: { label: 'Source', writable: true }, + source: {label: 'Source', writable: true}, } }, 'traffic.OperatorAction.NetworkManagement.ReroutingManagement': { @@ -229,7 +229,7 @@ const oedb = { description: 'Événement de type Traffic OperatorAction NetworkManagement ReroutingManagement', durationHours: 200, properties: { - source: { label: 'Source', writable: true }, + source: {label: 'Source', writable: true}, } }, 'traffic.OperatorAction.ConstructionWorks': { @@ -240,7 +240,7 @@ const oedb = { description: 'Événement de type Traffic OperatorAction ConstructionWorks', durationHours: 200, properties: { - source: { label: 'Source', writable: true }, + source: {label: 'Source', writable: true}, } }, 'traffic.OperatorAction.NetworkManagement.SpeedManagement': { @@ -251,75 +251,75 @@ const oedb = { description: 'Événement de type Traffic OperatorAction SpeedManagement', durationHours: 200, properties: { - source: { label: 'Source', writable: true }, + source: {label: 'Source', writable: true}, } }, // Gestion générale du réseau -'traffic.OperatorAction.NetworkManagement.GeneralNetworkManagement': { - emoji: '', + 'traffic.OperatorAction.NetworkManagement.GeneralNetworkManagement': { + emoji: '', image: 'static/cone.png', - label: 'Gestion générale du réseau', - category: 'Circulation', - description: 'Événement de type Traffic OperatorAction GeneralNetworkManagement', - durationHours: 200, - properties: { - source: { label: 'Source', writable: true }, - } -}, -'traffic.closed': { - emoji: '⛔', - label: 'Chaussée fermée', - category: 'Circulation', - description: 'Événement de type Traffic Closed', - durationHours: 200, - properties: { - source: { label: 'Source', writable: true }, - } -}, -'traffic.TrafficElement.GeneralInstructionOrMessageToRoadUsers': { - emoji: '', - image: 'static/cone.png', - label: 'Instruction ou message aux usagers', - category: 'Circulation', - description: 'Événement de type Traffic TrafficElement GeneralInstructionOrMessageToRoadUsers', - durationHours: 200, - properties: { - source: { label: 'Source', writable: true }, - } -}, -'traffic.TrafficElement.Obstruction.GeneralObstruction': { - emoji: '', - image: 'static/cone.png', - label: 'Obstruction générale', - category: 'Circulation', - description: 'Événement de type Traffic TrafficElement Obstruction GeneralObstruction', - durationHours: 200, - properties: { - source: { label: 'Source', writable: true }, - } -}, -'traffic.TrafficElement.Obstruction.InfrastructureDamageObstruction': { - emoji: '⛔', + label: 'Gestion générale du réseau', + category: 'Circulation', + description: 'Événement de type Traffic OperatorAction GeneralNetworkManagement', + durationHours: 200, + properties: { + source: {label: 'Source', writable: true}, + } + }, + 'traffic.closed': { + emoji: '⛔', + label: 'Chaussée fermée', + category: 'Circulation', + description: 'Événement de type Traffic Closed', + durationHours: 200, + properties: { + source: {label: 'Source', writable: true}, + } + }, + 'traffic.TrafficElement.GeneralInstructionOrMessageToRoadUsers': { + emoji: '', + image: 'static/cone.png', + label: 'Instruction ou message aux usagers', + category: 'Circulation', + description: 'Événement de type Traffic TrafficElement GeneralInstructionOrMessageToRoadUsers', + durationHours: 200, + properties: { + source: {label: 'Source', writable: true}, + } + }, + 'traffic.TrafficElement.Obstruction.GeneralObstruction': { + emoji: '', + image: 'static/cone.png', + label: 'Obstruction générale', + category: 'Circulation', + description: 'Événement de type Traffic TrafficElement Obstruction GeneralObstruction', + durationHours: 200, + properties: { + source: {label: 'Source', writable: true}, + } + }, + 'traffic.TrafficElement.Obstruction.InfrastructureDamageObstruction': { + emoji: '⛔', - label: 'Obstruction d\'infrastructure', - category: 'Circulation', - description: 'Événement de type Traffic TrafficElement Obstruction InfrastructureDamageObstruction', - durationHours: 200, - properties: { - source: { label: 'Source', writable: true }, - } -}, -'traffic.TrafficElement.Obstruction.EnvironmentalObstruction': { - emoji: '⛔', + label: 'Obstruction d\'infrastructure', + category: 'Circulation', + description: 'Événement de type Traffic TrafficElement Obstruction InfrastructureDamageObstruction', + durationHours: 200, + properties: { + source: {label: 'Source', writable: true}, + } + }, + 'traffic.TrafficElement.Obstruction.EnvironmentalObstruction': { + emoji: '⛔', - label: 'Obstruction environnementale', - category: 'Circulation', - description: 'Événement de type Traffic TrafficElement Obstruction EnvironmentalObstruction', - durationHours: 200, - properties: { - source: { label: 'Source', writable: true }, - } -}, + label: 'Obstruction environnementale', + category: 'Circulation', + description: 'Événement de type Traffic TrafficElement Obstruction EnvironmentalObstruction', + durationHours: 200, + properties: { + source: {label: 'Source', writable: true}, + } + }, 'traffic.obstacle.flood': { emoji: '🌊', label: 'Chaussée inondée', @@ -379,9 +379,9 @@ const oedb = { description: 'Tempête (vent fort)', durationHours: 48, properties: { - wind_speed: { label: 'Vent moyen (km/h)', writable: true }, - wind_gust: { label: 'Rafales (km/h)', writable: true }, - severity: { label: 'Sévérité', writable: true } + wind_speed: {label: 'Vent moyen (km/h)', writable: true}, + wind_gust: {label: 'Rafales (km/h)', writable: true}, + severity: {label: 'Sévérité', writable: true} } }, 'weather.thunder': { @@ -391,7 +391,7 @@ const oedb = { description: 'Activité orageuse', durationHours: 12, properties: { - lightning_count: { label: 'Nombre d’éclairs', writable: true } + lightning_count: {label: 'Nombre d’éclairs', writable: true} } }, 'weather.flood': { emoji: '🌊', @@ -400,7 +400,7 @@ const oedb = { description: 'Inondation', durationHours: 24, properties: { - flood_level: { label: 'Niveau d\'inondation', writable: true } + flood_level: {label: 'Niveau d\'inondation', writable: true} } }, 'weather.snow': { @@ -410,7 +410,7 @@ const oedb = { description: 'Neige', durationHours: 12, properties: { - snow_level: { label: 'Niveau de neige', writable: true } + snow_level: {label: 'Niveau de neige', writable: true} } }, 'weather.earthquake': { @@ -420,8 +420,8 @@ const oedb = { description: 'Séisme', durationHours: 6, properties: { - magnitude: { label: 'Magnitude (Mw)', writable: true }, - depth_km: { label: 'Profondeur (km)', writable: true } + magnitude: {label: 'Magnitude (Mw)', writable: true}, + depth_km: {label: 'Profondeur (km)', writable: true} } }, @@ -433,8 +433,8 @@ const oedb = { description: 'Interruption d\'itinéraire', durationHours: 200, properties: { - reason: { label: 'Raison', writable: true }, - route: { label: 'Itinéraire', writable: true }, + reason: {label: 'Raison', writable: true}, + route: {label: 'Itinéraire', writable: true}, } }, @@ -445,8 +445,8 @@ const oedb = { description: 'Mauvais sens de circulation', durationHours: 200, properties: { - reason: { label: 'Raison', writable: true }, - route: { label: 'Itinéraire', writable: true }, + reason: {label: 'Raison', writable: true}, + route: {label: 'Itinéraire', writable: true}, } }, @@ -457,7 +457,7 @@ const oedb = { description: 'Contestation d\'itinéraire', durationHours: 200, properties: { - route: { label: 'Itinéraire', writable: true }, + route: {label: 'Itinéraire', writable: true}, } } // ici ajouter d'autres catégories d'évènements à suggérer diff --git a/oedb/middleware/headers.py b/oedb/middleware/headers.py index 7d4f297..58fd108 100644 --- a/oedb/middleware/headers.py +++ b/oedb/middleware/headers.py @@ -2,6 +2,7 @@ Middleware components for the OpenEventDatabase. """ +import falcon from oedb.utils.logging import logger class HeaderMiddleware: @@ -9,6 +10,21 @@ class HeaderMiddleware: Middleware that adds standard headers to all responses. """ + def process_request(self, req, resp, resource, params): + """ + Handle preflight OPTIONS requests for CORS. + + Args: + req: The request object. + resp: The response object. + resource: The resource object. + params: The request parameters. + """ + if req.method == 'OPTIONS': + logger.debug("Handling CORS preflight request") + resp.status = falcon.HTTP_200 + return True # Skip further processing + def process_response(self, req, resp, resource, params): """ Add standard headers to the response. @@ -21,7 +37,15 @@ class HeaderMiddleware: """ logger.debug("Adding standard headers to response") resp.set_header('X-Powered-By', 'OpenEventDatabase') + + # CORS headers - Configuration optimisée pour embed.js resp.set_header('Access-Control-Allow-Origin', '*') - resp.set_header('Access-Control-Allow-Headers', 'X-Requested-With') - resp.set_header('Access-Control-Allow-Headers', 'Content-Type') - resp.set_header('Access-Control-Allow-Methods','GET, POST, PUT, DELETE, OPTIONS') \ No newline at end of file + resp.set_header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Authorization, Accept, Origin, User-Agent, Referer') + resp.set_header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, HEAD') + resp.set_header('Access-Control-Allow-Credentials', 'false') + resp.set_header('Access-Control-Max-Age', '86400') # 24 hours + resp.set_header('Access-Control-Expose-Headers', 'Content-Length, Content-Type, Date, Server, X-Powered-By') + + # Headers supplémentaires pour embed.js + resp.set_header('Vary', 'Origin') + resp.set_header('Cache-Control', 'public, max-age=300') # Cache de 5 minutes pour les requêtes embed \ No newline at end of file diff --git a/test_cors_embed.py b/test_cors_embed.py new file mode 100644 index 0000000..ecc9d45 --- /dev/null +++ b/test_cors_embed.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 +""" +Script de test pour vérifier la configuration CORS avec embed.js +""" + +import requests +import json + +def test_cors_headers(): + """Test des headers CORS pour l'API OEDB""" + + # URL de l'API (à adapter selon votre configuration) + api_url = "http://localhost:8080/events" + + print("🔍 Test de la configuration CORS pour embed.js") + print("=" * 50) + + # Test 1: Requête GET simple + print("\n1. Test requête GET simple...") + try: + response = requests.get(api_url, timeout=10) + print(f" Status: {response.status_code}") + + # Vérifier les headers CORS + cors_headers = { + 'Access-Control-Allow-Origin': response.headers.get('Access-Control-Allow-Origin'), + 'Access-Control-Allow-Headers': response.headers.get('Access-Control-Allow-Headers'), + 'Access-Control-Allow-Methods': response.headers.get('Access-Control-Allow-Methods'), + 'Access-Control-Max-Age': response.headers.get('Access-Control-Max-Age'), + 'Access-Control-Expose-Headers': response.headers.get('Access-Control-Expose-Headers') + } + + print(" Headers CORS:") + for header, value in cors_headers.items(): + status = "✅" if value else "❌" + print(f" {status} {header}: {value}") + + except requests.exceptions.RequestException as e: + print(f" ❌ Erreur de connexion: {e}") + return False + + # Test 2: Requête OPTIONS (preflight) + print("\n2. Test requête OPTIONS (preflight)...") + try: + headers = { + 'Origin': 'https://example.com', + 'Access-Control-Request-Method': 'GET', + 'Access-Control-Request-Headers': 'Content-Type' + } + + response = requests.options(api_url, headers=headers, timeout=10) + print(f" Status: {response.status_code}") + + if response.status_code == 200: + print(" ✅ Requête preflight réussie") + else: + print(" ❌ Requête preflight échouée") + + except requests.exceptions.RequestException as e: + print(f" ❌ Erreur de connexion: {e}") + return False + + # Test 3: Simulation d'une requête depuis embed.js + print("\n3. Test simulation embed.js...") + try: + headers = { + 'Origin': 'https://example.com', + 'User-Agent': 'Mozilla/5.0 (compatible; OEDB-Embed/1.0)', + 'Referer': 'https://example.com/page-with-embed' + } + + response = requests.get(api_url, headers=headers, timeout=10) + print(f" Status: {response.status_code}") + + # Vérifier que la réponse contient des données + if response.status_code == 200: + try: + data = response.json() + print(f" ✅ Données reçues: {len(data.get('features', []))} événements") + except json.JSONDecodeError: + print(" ⚠️ Réponse reçue mais pas de JSON valide") + else: + print(f" ❌ Erreur HTTP: {response.status_code}") + + except requests.exceptions.RequestException as e: + print(f" ❌ Erreur de connexion: {e}") + return False + + print("\n" + "=" * 50) + print("✅ Tests CORS terminés") + return True + +if __name__ == "__main__": + test_cors_headers() diff --git a/test_embed_cors.html b/test_embed_cors.html new file mode 100644 index 0000000..f00a184 --- /dev/null +++ b/test_embed_cors.html @@ -0,0 +1,180 @@ + + + + + + Test OEDB Embed avec CORS + + + +

đź§Ş Test OEDB Embed avec Configuration CORS

+ +
+

Configuration du Test

+
+ Chargement de la configuration... +
+
+ +
+

Test de Connexion CORS

+
+ Test en cours... +
+ +
+ +
+

Intégration OEDB Embed

+

Configuration utilisée :

+
+ +
+
+ Chargement du widget OEDB... +
+
+
+ +
+

Logs de Debug

+
+
🚀 Initialisation du test...
+
+
+ + + + + + +