Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
{% extends 'base.html.twig' %}
{% block title %} API Documentation - E-Ticket {% endblock %}
{% block description %} Documentation de l'API E-Ticket pour les organisateurs et l'application scanner. {% endblock %}
{% block body %}
<div class="bg-[#fbfbfb] min-h-screen">
<section class="relative bg-gray-900 text-white px-4 pt-20 pb-16 border-b-8 border-[#fabf04]">
<div class="absolute inset-0 opacity-[0.05] pointer-events-none select-none overflow-hidden">
<span class="text-[8rem] md:text-[16rem] font-black uppercase leading-none block -rotate-12 translate-y-10 font-mono"> { API}</span>
</div>
<div class="max-w-5xl mx-auto relative z-10">
<div class="inline-block px-3 py-1 border-2 border-[#fabf04] bg-[#fabf04] text-gray-900 text-[10px] font-black uppercase tracking-widest mb-4">v1.0</div>
<h1 class="text-4xl md:text-6xl font-black uppercase tracking-tighter leading-[0.85] mb-4">API E-Ticket</h1>
<p class="text-lg font-bold text-gray-300 max-w-2xl">Documentation complete de l'API REST pour les organisateurs. Gestion des evenements, commandes, billets et scan.</p>
<div class="mt-8 flex flex-wrap gap-4">
<div class="border-2 border-gray-700 bg-gray-800 px-4 py-3">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Format</p>
<p class="font-mono font-bold text-sm">JSON (application/json)</p>
</div>
<div class="border-2 border-gray-700 bg-gray-800 px-4 py-3">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-500 mb-1">Authentification</p>
<p class="font-mono font-bold text-sm">ETicket-Email + ETicket-JWT</p>
</div>
</div>
2026-03-23 18:56:10 +01:00
2026-03-23 18:59:46 +01:00
<div class="mt-8">
<div class="flex items-center gap-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-500">Environnement</p>
<div class="flex border-2 border-gray-600 overflow-hidden" id="env-switcher">
<button type="button" data-env="sandbox" class="env-btn px-5 py-2 font-black uppercase text-xs tracking-widest transition-all cursor-pointer bg-orange-500 text-white">
Sandbox
</button>
<button type="button" data-env="live" class="env-btn px-5 py-2 font-black uppercase text-xs tracking-widest transition-all cursor-pointer bg-gray-800 text-gray-400 hover:text-white">
Live
</button>
2026-03-23 18:58:17 +01:00
</div>
</div>
Add Insomnia export and dynamic hostname for API doc
Insomnia export (/api/doc/insomnia.json):
- Generates Insomnia v4 export format with all API routes
- Workspace with environment variables (base_url, env, email, password, jwt_token)
- Folders per section (Auth, Events, Categories, Billets, Scanner)
- Each request with correct method, URL with Insomnia template vars, headers, body
- Auth routes use base_url directly, others use base_url/api/{env}/...
- Download button (indigo) next to Spec JSON button
Dynamic hostname:
- Insomnia export uses request.getSchemeAndHttpHost() (not hardcoded)
- Template passes host via data-host attribute
- JS env switcher reads host from data-host or falls back to location.origin
- Base URLs update dynamically when switching sandbox/live
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:35:36 +01:00
<div class="mt-3 border-2 border-gray-700 bg-gray-800 px-4 py-3 flex items-center gap-3" data-host=" {{ app .request .schemeAndHttpHost }} ">
2026-03-23 18:59:46 +01:00
<p class="text-[10px] font-black uppercase tracking-widest text-gray-500">Base URL</p>
Add Insomnia export and dynamic hostname for API doc
Insomnia export (/api/doc/insomnia.json):
- Generates Insomnia v4 export format with all API routes
- Workspace with environment variables (base_url, env, email, password, jwt_token)
- Folders per section (Auth, Events, Categories, Billets, Scanner)
- Each request with correct method, URL with Insomnia template vars, headers, body
- Auth routes use base_url directly, others use base_url/api/{env}/...
- Download button (indigo) next to Spec JSON button
Dynamic hostname:
- Insomnia export uses request.getSchemeAndHttpHost() (not hardcoded)
- Template passes host via data-host attribute
- JS env switcher reads host from data-host or falls back to location.origin
- Base URLs update dynamically when switching sandbox/live
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:35:36 +01:00
<p class="font-mono font-bold text-sm text-[#fabf04]" id="env-base-url"> {{ app .request .schemeAndHttpHost }} /api/sandbox</p>
2026-03-23 18:59:46 +01:00
</div>
<p class="mt-2 text-xs font-bold text-gray-400" id="env-description">Environnement de test. Les donnees ne sont pas modifiees.</p>
Update sandbox fixtures: 3 events (past/ongoing/upcoming), 5 scan scenarios
Events:
- #1 Brocante de Printemps (past, offline)
- #2 Convention Cosplay (ongoing, online)
- #3 Marche de Noel (upcoming, online)
6 categories, 8 billets across all events
Scan scenarios for event #2 (ongoing):
- DEMO-0001: valid, never scanned
- DEMO-0002: refused, exit_definitive (already scanned with definitive exit)
- DEMO-0003: accepted, unlimited entry/exit (already scanned once)
- DEMO-0004: accepted, invitation (never scanned)
- DEMO-0005: refused, invalid (vote type - no generated ticket)
API doc: show sandbox event IDs and scan references with colored badges
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:15:38 +01:00
<p class="mt-2 text-xs font-bold text-gray-500">L'authentification (<span class="font-mono">/api/auth/login</span>) est commune aux deux environnements (vos vrais identifiants).</p>
<div class="mt-4 border-2 border-gray-700 bg-gray-800 p-4">
<p class="text-[10px] font-black uppercase tracking-widest text-orange-400 mb-2">Donnees Sandbox</p>
<p class="text-xs font-bold text-gray-400 mb-2">La sandbox retourne des donnees fictives identiques pour tous les utilisateurs :</p>
<div class="flex flex-wrap gap-2 text-xs">
<span class="font-mono px-2 py-1 border border-gray-600 text-gray-300">Event #1 <span class="text-gray-500">(passe)</span></span>
<span class="font-mono px-2 py-1 border border-green-600 text-green-400">Event #2 <span class="text-green-600">(en cours)</span></span>
<span class="font-mono px-2 py-1 border border-indigo-600 text-indigo-400">Event #3 <span class="text-indigo-600">(a venir)</span></span>
</div>
<p class="text-xs font-bold text-gray-500 mt-3">References de scan disponibles :</p>
<div class="flex flex-wrap gap-2 mt-1 text-xs font-mono">
<span class="px-2 py-1 border border-green-600 text-green-400">ETICKET-DEMO-0001-AAAA</span>
<span class="px-2 py-1 border border-red-600 text-red-400">ETICKET-DEMO-0002-BBBB</span>
<span class="px-2 py-1 border border-green-600 text-green-400">ETICKET-DEMO-0003-CCCC</span>
<span class="px-2 py-1 border border-green-600 text-green-400">ETICKET-DEMO-0004-DDDD</span>
<span class="px-2 py-1 border border-red-600 text-red-400">ETICKET-DEMO-0005-EEEE</span>
</div>
</div>
2026-03-23 18:58:17 +01:00
</div>
Add Insomnia export and dynamic hostname for API doc
Insomnia export (/api/doc/insomnia.json):
- Generates Insomnia v4 export format with all API routes
- Workspace with environment variables (base_url, env, email, password, jwt_token)
- Folders per section (Auth, Events, Categories, Billets, Scanner)
- Each request with correct method, URL with Insomnia template vars, headers, body
- Auth routes use base_url directly, others use base_url/api/{env}/...
- Download button (indigo) next to Spec JSON button
Dynamic hostname:
- Insomnia export uses request.getSchemeAndHttpHost() (not hardcoded)
- Template passes host via data-host attribute
- JS env switcher reads host from data-host or falls back to location.origin
- Base URLs update dynamically when switching sandbox/live
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:35:36 +01:00
<div class="mt-8 flex flex-wrap gap-3">
2026-03-23 18:56:10 +01:00
<a href=" {{ path ( 'app_api_doc_json' ) }} " target="_blank" class="inline-flex items-center gap-2 px-6 py-3 border-4 border-[#fabf04] bg-[#fabf04] text-gray-900 font-black uppercase text-xs tracking-widest shadow-[6px_6px_0px_rgba(0,0,0,1)] hover:shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-y-[-2px] transition-all">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/></svg>
Add Insomnia export and dynamic hostname for API doc
Insomnia export (/api/doc/insomnia.json):
- Generates Insomnia v4 export format with all API routes
- Workspace with environment variables (base_url, env, email, password, jwt_token)
- Folders per section (Auth, Events, Categories, Billets, Scanner)
- Each request with correct method, URL with Insomnia template vars, headers, body
- Auth routes use base_url directly, others use base_url/api/{env}/...
- Download button (indigo) next to Spec JSON button
Dynamic hostname:
- Insomnia export uses request.getSchemeAndHttpHost() (not hardcoded)
- Template passes host via data-host attribute
- JS env switcher reads host from data-host or falls back to location.origin
- Base URLs update dynamically when switching sandbox/live
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:35:36 +01:00
Spec JSON
</a>
<a href=" {{ path ( 'app_api_doc_insomnia' ) }} " class="inline-flex items-center gap-2 px-6 py-3 border-4 border-indigo-600 bg-indigo-600 text-white font-black uppercase text-xs tracking-widest shadow-[6px_6px_0px_rgba(0,0,0,1)] hover:shadow-[8px_8px_0px_rgba(0,0,0,1)] hover:translate-y-[-2px] transition-all">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/></svg>
Insomnia
2026-03-23 18:56:10 +01:00
</a>
</div>
Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
</div>
</section>
<div class="max-w-5xl mx-auto px-4 py-12">
<nav class="card-brutal overflow-hidden mb-12">
<div class="bg-gray-900 text-white px-6 py-3">
<h2 class="text-[10px] font-black uppercase tracking-widest">Sommaire</h2>
</div>
<div class="p-6">
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-3">
{% for section in sections %}
<a href="#section- {{ loop .index }} " class="border-2 border-gray-900 px-4 py-3 font-black uppercase text-xs tracking-widest hover:bg-gray-900 hover:text-white transition-all">
{{ section .name }}
<span class="text-gray-400 ml-1">( {{ section .endpoints | length }} )</span>
</a>
{% endfor %}
</div>
</div>
</nav>
<div class="card-brutal overflow-hidden mb-12 border-[#fabf04]" style="border-color: #fabf04;">
<div class="bg-[#fabf04] text-gray-900 px-6 py-3">
<h2 class="text-[10px] font-black uppercase tracking-widest">Authentification</h2>
</div>
<div class="p-6">
<p class="font-bold text-sm text-gray-700 mb-4">Toutes les routes (sauf <span class="font-mono bg-gray-100 px-1">/api/auth/login</span>) necessitent deux headers :</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="bg-gray-900 text-white">
<th class="px-4 py-2 text-left font-black uppercase text-[10px] tracking-widest">Header</th>
<th class="px-4 py-2 text-left font-black uppercase text-[10px] tracking-widest">Description</th>
<th class="px-4 py-2 text-left font-black uppercase text-[10px] tracking-widest">Exemple</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-200">
<td class="px-4 py-3 font-mono font-bold text-indigo-600">ETicket-Email</td>
<td class="px-4 py-3 font-bold text-gray-600">Email de l'organisateur</td>
<td class="px-4 py-3 font-mono text-xs text-gray-500">orga@example.com</td>
</tr>
<tr>
<td class="px-4 py-3 font-mono font-bold text-indigo-600">ETicket-JWT</td>
<td class="px-4 py-3 font-bold text-gray-600">Token JWT (obtenu via /api/auth/login)</td>
<td class="px-4 py-3 font-mono text-xs text-gray-500">eyJhbGciOiJIUzI1NiIs...</td>
</tr>
</tbody>
</table>
</div>
<div class="mt-6 border-2 border-gray-900 bg-gray-900 text-white p-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-500 mb-2">Reponse standard</p>
<pre class="font-mono text-xs leading-relaxed overflow-x-auto"><code> {
"success": true,
"data": { ... },
"error": null
}</code></pre>
</div>
<div class="mt-4 border-2 border-red-800 bg-red-50 p-4">
<p class="text-[10px] font-black uppercase tracking-widest text-red-800 mb-2">Reponse erreur</p>
<pre class="font-mono text-xs leading-relaxed overflow-x-auto text-red-900"><code> {
"success": false,
"data": null,
"error": "Message d'erreur explicite"
}</code></pre>
</div>
</div>
</div>
{% for section in sections %}
<div id="section- {{ loop .index }} " class="mb-12 scroll-mt-8">
<div class="flex items-center gap-4 mb-6">
<h2 class="text-2xl font-black uppercase tracking-tighter"> {{ section .name }} </h2>
<div class="flex-1 border-t-3 border-gray-900"></div>
</div>
{% if section .description %}
<p class="font-bold text-sm text-gray-500 mb-6"> {{ section .description }} </p>
{% endif %}
{% for endpoint in section .endpoints %}
<div class="card-brutal overflow-hidden mb-6">
<div class="flex items-stretch">
{% set method_colors = {
'GET': 'bg-green-600',
'POST': 'bg-indigo-600',
'PATCH': 'bg-orange-500',
'DELETE': 'bg-red-600',
'PUT': 'bg-yellow-500'
} %}
<div class=" {{ method_colors [ endpoint .method ] ? ? 'bg-gray-600' }} text-white px-4 py-3 flex items-center min-w-[80px] justify-center">
<span class="font-black text-xs tracking-widest"> {{ endpoint .method }} </span>
</div>
2026-03-23 18:59:46 +01:00
<div class="flex-1 bg-gray-900 text-white px-4 py-3 flex items-center">
2026-03-23 18:58:17 +01:00
{% if endpoint .path starts with '/api/auth' %}
<code class="font-mono font-bold text-sm"> {{ endpoint .path }} </code>
{% else %}
2026-03-23 18:59:46 +01:00
<code class="font-mono font-bold text-sm"><span class="api-env-prefix text-orange-400">/api/sandbox</span> {{ endpoint .path | replace ( { '/api' : '' } ) }} </code>
2026-03-23 18:58:17 +01:00
{% endif %}
Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
</div>
</div>
<div class="p-6">
<h3 class="font-black uppercase text-sm tracking-tighter mb-1"> {{ endpoint .summary }} </h3>
<p class="text-sm font-bold text-gray-500 mb-4"> {{ endpoint .description }} </p>
{% if endpoint .headers | length > 0 %}
<div class="mb-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2">Headers requis</p>
<div class="flex flex-wrap gap-2">
{% for header in endpoint .headers %}
<span class="font-mono text-xs px-2 py-1 border-2 border-indigo-600 text-indigo-600 font-bold"> {{ header .name }} </span>
{% endfor %}
</div>
</div>
{% endif %}
{% if endpoint .params | length > 0 %}
<div class="mb-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2">Parametres</p>
<div class="overflow-x-auto">
<table class="w-full text-xs">
<thead>
<tr class="bg-gray-100">
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Nom</th>
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Type</th>
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Requis</th>
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Description</th>
</tr>
</thead>
<tbody>
{% for name , param in endpoint .params %}
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600"> {{ name }} </td>
<td class="px-3 py-2 font-mono text-gray-500"> {{ param .type }} </td>
<td class="px-3 py-2">
{% if param .required %}
<span class="text-red-600 font-black">oui</span>
{% else %}
<span class="text-gray-400">non {% if param .default is defined %} ( {{ param .default }} ) {% endif %} </span>
{% endif %}
</td>
<td class="px-3 py-2 font-bold text-gray-600"> {{ param .description }} </td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
{% if endpoint .request %}
<div class="mb-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2">Body (JSON)</p>
<div class="border-2 border-gray-900 bg-gray-900 text-white p-4">
<pre class="font-mono text-xs leading-relaxed overflow-x-auto"><code> {
{% for name , field in endpoint .request %}
" {{ name }} ": {{ field .example is defined ? '"' ~ field .example ~ '"' : '"..."' }} {{ not loop .last ? ',' : '' }} <span class="text-gray-500">// {{ field .type }} {% if field .required %} (requis) {% endif %} </span>
{% endfor %}
}</code></pre>
</div>
</div>
{% endif %}
{% if endpoint .response %}
<div class="mb-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2">Reponse (200)</p>
<div class="border-2 border-green-800 bg-green-50 p-4">
<pre class="font-mono text-xs leading-relaxed overflow-x-auto text-green-900"><code> {
{% for name , field in endpoint .response %}
" {{ name }} ": {{ field .example }} <span class="text-green-600">// {{ field .type }} </span> {{ not loop .last ? ',' : '' }}
{% endfor %}
}</code></pre>
</div>
</div>
{% endif %}
2026-03-23 19:06:13 +01:00
{% if endpoint .extra is defined %}
<div class="mb-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2"> {{ endpoint .extra .title }} </p>
<div class="border-2 border-red-200 bg-red-50 p-4">
<table class="w-full text-xs">
2026-04-02 14:01:35 +02:00
<thead>
<tr><th class="py-1.5 text-left font-black uppercase text-[10px] tracking-widest text-red-800 pr-4">Code</th><th class="py-1.5 text-left font-black uppercase text-[10px] tracking-widest text-red-800">Description</th></tr>
</thead>
<tbody>
2026-03-23 19:06:13 +01:00
{% for code , desc in endpoint .extra .items %}
<tr class=" {{ not loop .last ? 'border-b border-red-200' : '' }} ">
<td class="py-1.5 font-mono font-bold text-red-700 pr-4"> {{ code }} </td>
<td class="py-1.5 font-bold text-red-600"> {{ desc }} </td>
</tr>
{% endfor %}
2026-04-02 14:01:35 +02:00
</tbody>
2026-03-23 19:06:13 +01:00
</table>
</div>
</div>
{% endif %}
Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
{% if endpoint .statuses | length > 0 %}
<div>
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2">Codes de reponse</p>
<div class="flex flex-wrap gap-2">
{% for code , desc in endpoint .statuses %}
{% set code_color = code < 3 0 0 ? 'border-green-600 text-green-700 bg-green-50' : ( code < 4 0 0 ? 'border-yellow-500 text-yellow-700 bg-yellow-50' : ( code < 5 0 0 ? 'border-red-600 text-red-700 bg-red-50' : 'border-gray-600 text-gray-700 bg-gray-50' ) ) %}
<div class="border-2 {{ code_color }} px-3 py-1">
<span class="font-mono font-black text-xs"> {{ code }} </span>
<span class="font-bold text-xs ml-1"> {{ desc }} </span>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>
{% endfor %}
<div class="card-brutal overflow-hidden">
<div class="bg-gray-900 text-white px-6 py-3">
<h2 class="text-[10px] font-black uppercase tracking-widest">Rate Limiting</h2>
</div>
<div class="p-6">
<p class="font-bold text-sm text-gray-700 mb-4">L'API est limitee a <span class="font-mono bg-gray-100 px-1 text-indigo-600">60 requetes par minute</span> par cle API. En cas de depassement, un code <span class="font-mono bg-red-50 px-1 text-red-600">429</span> est retourne.</p>
<div class="border-2 border-gray-200 p-4">
<p class="text-[10px] font-black uppercase tracking-widest text-gray-400 mb-2">Headers de rate limit</p>
<table class="w-full text-xs">
2026-04-02 14:01:35 +02:00
<thead>
<tr><th class="py-2 text-left font-black uppercase text-[10px] tracking-widest text-gray-400">Header</th><th class="py-2 text-left font-black uppercase text-[10px] tracking-widest text-gray-400">Description</th></tr>
</thead>
<tbody>
Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
<tr class="border-b border-gray-100">
<td class="py-2 font-mono font-bold text-gray-600">X-RateLimit-Limit</td>
<td class="py-2 font-bold text-gray-500">Nombre max de requetes par fenetre</td>
</tr>
<tr class="border-b border-gray-100">
<td class="py-2 font-mono font-bold text-gray-600">X-RateLimit-Remaining</td>
<td class="py-2 font-bold text-gray-500">Requetes restantes dans la fenetre courante</td>
</tr>
<tr>
<td class="py-2 font-mono font-bold text-gray-600">Retry-After</td>
<td class="py-2 font-bold text-gray-500">Secondes avant la prochaine fenetre (si 429)</td>
</tr>
2026-04-02 14:01:35 +02:00
</tbody>
Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
</table>
</div>
</div>
</div>
Remove inline script from API doc, add CSP policy section
Security:
- Move env switcher logic to assets/modules/api-env-switcher.js (no inline script)
- Register in app.js via initApiEnvSwitcher()
- Compliant with CSP script-src (no unsafe-inline needed for this page)
API doc:
- Add CSP policy section showing all authorized origins per directive
- Table: script-src, connect-src, style-src, img-src, font-src, frame-src, form-action, object-src, worker-src
- Note: inline scripts not allowed, must use nonce or external file
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:02:28 +01:00
<div class="card-brutal overflow-hidden mt-12">
<div class="bg-gray-900 text-white px-6 py-3">
<h2 class="text-[10px] font-black uppercase tracking-widest">Politique de securite (CSP)</h2>
</div>
<div class="p-6">
<p class="font-bold text-sm text-gray-700 mb-4">E-Ticket applique une politique Content-Security-Policy stricte. Voici les origines autorisees par directive :</p>
<div class="overflow-x-auto">
<table class="w-full text-xs">
<thead>
<tr class="bg-gray-100">
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Directive</th>
<th class="px-3 py-2 text-left font-black uppercase tracking-widest">Origines autorisees</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">script-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' static.cloudflareinsights.com challenges.cloudflare.com cdn.jsdelivr.net js.stripe.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">connect-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' cloudflareinsights.com static.cloudflareinsights.com challenges.cloudflare.com nominatim.openstreetmap.org cdn.jsdelivr.net api.stripe.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">style-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' fonts.googleapis.com cdnjs.cloudflare.com cdn.jsdelivr.net 'unsafe-inline'</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">img-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' data: *.tile.openstreetmap.org *.basemaps.cartocdn.com cdn.jsdelivr.net</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">font-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' cdnjs.cloudflare.com fonts.googleapis.com fonts.gstatic.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">frame-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' stripe.com *.stripe.com js.stripe.com cloudflare.com *.cloudflareinsights.com challenges.cloudflare.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">form-action</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' auth.esy-web.dev *.stripe.com checkout.stripe.com</td>
</tr>
<tr class="border-b border-gray-100">
<td class="px-3 py-2 font-mono font-bold text-indigo-600">object-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'none'</td>
</tr>
<tr>
<td class="px-3 py-2 font-mono font-bold text-indigo-600">worker-src</td>
<td class="px-3 py-2 font-mono text-gray-600">'self' blob:</td>
</tr>
</tbody>
</table>
</div>
<p class="mt-4 text-xs font-bold text-gray-400">Les scripts inline ne sont pas autorises. Tous les scripts doivent etre servis depuis une origine autorisee ou utiliser un nonce CSP.</p>
</div>
</div>
Add custom API documentation page at /api/doc
- ApiDocController: serves doc page + JSON spec at /api/doc/spec.json
- Custom brutal design template matching site aesthetic
- 6 sections: Auth, Events, Orders, Scanner, Billets/Stock, Export
- Each endpoint shows: method badge (colored), path, summary, description
- Auth headers: ETicket-Email + ETicket-JWT displayed prominently
- Parameters table with type, required, default values
- Request body with JSON example and field types
- Response body with JSON example
- Status codes with colored badges (green/yellow/red)
- Rate limiting section with X-RateLimit headers
- Table of contents with anchor links
- Standard response format: {success, data, error}
- No external dependencies (no Swagger/NelmioApiDoc)
- Fully customizable via PHP spec array
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 18:51:46 +01:00
</div>
</div>
{% endblock %}