Étape 21 : formulaire public d'inscription des bénévoles
Retire « constructeur de formulaire » des exclusions du CLAUDE.md (§ 5) — décision explicite prise en conversation. Un formulaire par édition (COORDINATION choisit les champs actifs parmi neuf : email, téléphone, vœux de poste — max 2, vœux d'artiste — un par soir de festival, vœu de compagnon en texte libre, disponibilité/ cotisation déclaratives, photo, commentaire ; nom/prénom toujours collectés) ouvert/fermé à volonté, avec un lien à partager (/inscription, aucun compte requis). Backend : FormTemplate/FormField (migration 0013), quatre nouveaux champs sur Volunteer (photo, companion_wish_note, self_declared_available, willing_to_pay_fee), app/api/forms.py — les deux seuls endpoints publics de toute l'API (GET .../form/public, POST .../form/public/submit), protégés par hCaptcha (app/forms/captcha.py, désactivé sans HCAPTCHA_SECRET_KEY) et un filet de sécurité global par édition (form_submission_limiter). resolve_owned_ids/set_artists/ set_wished_positions déplacés vers app/api/_common.py, partagés avec volunteers.py. Frontend : écran de configuration (/inscriptions) avec lien copiable et bascule ouvert/fermé, formulaire public (/inscription, hors RequireAuth) avec widget hCaptcha chargé dynamiquement, capture photo native (<input capture>), vœux de poste/artiste avec les mêmes règles que le back-end. Réponses du formulaire visibles et éditables depuis la fiche bénévole existante (ConfigVolunteers.tsx). Vérifié : pytest (273 passés), ruff, alembic check, tsc --noEmit, eslint, vite build, smoke (rendu SSR de chaque écran + flux de bout en bout du formulaire en mode démonstration) — tous verts. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
55c5d539a8
commit
82088f3b0c
@@ -73,6 +73,16 @@ les tables sauf les tables d'association.
|
||||
taille de t-shirt, régime, ni compétences. Porte trois listes de vœux,
|
||||
exposées par la fiche (`artist_ids`, `wished_position_ids`,
|
||||
`wished_companion_ids` ; `None`/absent = inchangé, `[]` = vider).
|
||||
Renseignables aussi via le formulaire public (§ 9) : `photo`/
|
||||
`photo_content_type` (image, servie séparément par
|
||||
`GET .../volunteers/{id}/photo`, COORD/LEAD), `companion_wish_note` (« je
|
||||
voudrais être avec » en texte libre — la personne visée n'existe pas
|
||||
forcément encore en base au moment de l'inscription, donc pas de lien
|
||||
direct vers un `VolunteerCompanionWish` ; la coordination la résout à la
|
||||
main depuis la fiche une fois les deux bénévoles inscrits),
|
||||
`self_declared_available`/`willing_to_pay_fee` (cases à cocher **purement
|
||||
informatives**, sans lien avec `Availability` ni aucun workflow de
|
||||
paiement).
|
||||
- **`Artist`** : programmation de l'édition (`name`, `performance_date`,
|
||||
`start_time`, `end_time` — `end_time <= start_time` ⇒ le passage finit le
|
||||
lendemain). Unique (`edition_id`, `name`, `performance_date`). La
|
||||
@@ -165,6 +175,27 @@ les tables sauf les tables d'association.
|
||||
pour rien : juste ReportLab) : pages recto personnalisées suivies, planche
|
||||
par planche, de la page de verso correspondante (même image répétée à
|
||||
chaque emplacement) — impression recto-verso classique.
|
||||
- **`FormTemplate`** : configuration du formulaire public d'inscription
|
||||
d'une édition (une ligne, créée à la demande). `is_open` coupe les
|
||||
nouvelles soumissions sans effacer la configuration.
|
||||
- **`FormField`** : un champ activé sur le formulaire (`field_type` ∈
|
||||
`EMAIL` / `PHONE` / `POSITION_WISHES` / `ARTIST_WISHES` / `COMPANION_WISH`
|
||||
/ `AVAILABLE` / `WILLING_TO_PAY_FEE` / `PHOTO` / `COMMENT`, au plus un par
|
||||
édition — `PUT .../form/fields` remplace tout). **Nom et prénom sont
|
||||
toujours collectés**, indispensables à la création du `Volunteer` : ils ne
|
||||
sont pas dans cette liste, contrairement aux neuf autres. Deux règles
|
||||
propres à ce formulaire (pas à la fiche bénévole classique, qui reste sans
|
||||
limite) : au plus **deux** vœux de poste, et au plus **un** artiste
|
||||
souhaité par jour de festival (`app/api/forms.py:_check_one_artist_wish_per_day`).
|
||||
`GET/PUT/PATCH /editions/{id}/form[...]` (COORDINATION) configurent ;
|
||||
`GET /editions/{id}/form/public` et `POST .../form/public/submit` sont les
|
||||
**deux seuls endpoints de toute l'API sans authentification** — protégés
|
||||
par hCaptcha (`app/forms/captcha.py`, `HCAPTCHA_SECRET_KEY` ; vérification
|
||||
désactivée si absente, comme `EMAIL_ENABLED=false`) et un filet de
|
||||
sécurité global par édition (`app/auth/rate_limit.py:form_submission_limiter`,
|
||||
hCaptcha reste la défense principale). Le bénévole créé par une soumission
|
||||
apparaît immédiatement dans la liste de la coordination, comme n'importe
|
||||
quel autre bénévole.
|
||||
|
||||
Conventions techniques :
|
||||
|
||||
@@ -202,6 +233,8 @@ Matrice appliquée par les endpoints :
|
||||
| `availability` lecture / écriture | ✔ / ✔ | ✔ / — | la sienne / — |
|
||||
| `matrix`, `import`, `accounts` | ✔ / ✔ / ✔ | ✔ / — / — | — |
|
||||
| `badges` (config + génération) | ✔ | — | — |
|
||||
| `form` (config) | ✔ | — | — |
|
||||
| `form/public`, `form/public/submit` | *(aucune authentification — voir § 2, `FormField`)* |||
|
||||
|
||||
**Authentification — deux systèmes selon le compte**, une même session JWT
|
||||
HS256 (`Authorization: Bearer <jwt>`, TTL `session_ttl_hours`) à l'arrivée.
|
||||
@@ -417,8 +450,10 @@ défilement horizontal — jamais de colonnes étroites par défaut.
|
||||
|
||||
### Hors périmètre (le modèle doit juste pouvoir les accueillir sans refonte)
|
||||
|
||||
SMS · constructeur de formulaire · pointage sur site ·
|
||||
fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
|
||||
SMS · pointage sur site · fonctions d'IA. *(L'envoi d'emails par SMTP est en
|
||||
place — voir § 2. Le formulaire public d'inscription est en place — voir § 2
|
||||
et § 9 ; ce n'était pas prévu à l'origine, retiré de cette liste à
|
||||
l'étape 21.)*
|
||||
|
||||
---
|
||||
|
||||
@@ -449,7 +484,8 @@ fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
|
||||
│ │ │ # health, auth, accounts, editions,
|
||||
│ │ │ # day_parts, imports, artists, volunteers,
|
||||
│ │ │ # availability, positions, shifts,
|
||||
│ │ │ # assignments, solver, matrix, notifications
|
||||
│ │ │ # assignments, solver, matrix, notifications,
|
||||
│ │ │ # badges, forms
|
||||
│ │ ├── auth/ # mot de passe + lien magique + JWT + rôles
|
||||
│ │ ├── email/ # SMTP (sender) + rendu des emails (templates)
|
||||
│ │ ├── importing/ # import CSV/Excel (parser, text, service)
|
||||
@@ -457,6 +493,7 @@ fonctions d'IA. *(L'envoi d'emails par SMTP est en place — voir § 2.)*
|
||||
│ │ ├── validation/ # moteur pur (models, engine) + loader (base)
|
||||
│ │ ├── solver/ # suggestions OR-Tools (models, engine, loader)
|
||||
│ │ ├── badges/ # génération PDF des planches (pdf.py, ReportLab)
|
||||
│ │ ├── forms/ # hCaptcha (captcha.py) — voir api/forms.py
|
||||
│ │ ├── bootstrap.py # instance vierge : édition + 1er compte
|
||||
│ │ ├── seed.py # jeu de démonstration (python -m app.seed)
|
||||
│ │ ├── i18n/
|
||||
@@ -529,10 +566,13 @@ Aucune dépendance locale requise hors Docker.
|
||||
- Secrets/variables Actions requis (voir `deploy/README.md`) : secrets
|
||||
`REGISTRY_TOKEN`, `SSH_KEY` ; variables `REGISTRY_HOST`,
|
||||
`REGISTRY_NAMESPACE`, `REGISTRY_USER`, `SSH_HOST`, `SSH_USER`,
|
||||
`DEPLOY_PATH`, `VITE_EDITION_ID`.
|
||||
`DEPLOY_PATH`, `VITE_EDITION_ID`, `VITE_HCAPTCHA_SITE_KEY` (optionnelle —
|
||||
clé de site hCaptcha, publique, figée au build du frontend comme
|
||||
`VITE_EDITION_ID` ; vide ⇒ formulaire public sans vérification anti-robot).
|
||||
- Le VPS porte un fichier `.env` (non versionné) avec `POSTGRES_USER`,
|
||||
`POSTGRES_PASSWORD`, `POSTGRES_DB`, `JWT_SECRET`, `PUBLIC_BASE_URL` et les
|
||||
variables `EMAIL_ENABLED` / `SMTP_*` (voir `deploy/README.md`).
|
||||
`POSTGRES_PASSWORD`, `POSTGRES_DB`, `JWT_SECRET`, `PUBLIC_BASE_URL`,
|
||||
`HCAPTCHA_SECRET_KEY` (optionnelle, secret hCaptcha — voir § 2, `FormField`)
|
||||
et les variables `EMAIL_ENABLED` / `SMTP_*` (voir `deploy/README.md`).
|
||||
- `backend` et `frontend` n'écoutent que sur `127.0.0.1` (voir
|
||||
`docker-compose.prod.yml`) : `deploy/Caddyfile.benevoles` est le bloc à
|
||||
ajouter au Caddyfile du serveur pour les exposer publiquement (`/api/*` vers
|
||||
@@ -564,6 +604,7 @@ Aucune dépendance locale requise hors Docker.
|
||||
| 18 | Gestion des référents depuis la fiche bénévole : `PATCH /accounts` (rôle LEAD ↔ VOLUNTEER), `lead_account_id` vérifié (doit être un compte référent de l'édition) | à valider |
|
||||
| 19 | Revue de sécurité avant ouverture au public : refus de démarrer en production avec un `JWT_SECRET` par défaut/vide/court, avertissement si `EMAIL_ENABLED=false` en production, en-têtes de sécurité (nginx + Caddy), pas de jeton dans les logs d'accès du frontend, limiteur de tentatives sur `/auth/login` et `/auth/magic-link` | à valider |
|
||||
| 20 | Planches de badges bénévoles (PDF) : recto personnalisable (fond + champs nom/poste(s)/catégorie/rôle, éditeur glisser-déposer), verso fixe (même image pour tous), écran « Badges » dédié (`/badges`, COORDINATION) | à valider |
|
||||
| 21 | Formulaire public d'inscription (`/inscription`, sans authentification) : champs activables par la coordination (email, téléphone, vœux de poste — max 2, vœux d'artiste — un par soir, vœu de compagnon en texte libre, disponibilité/cotisation déclaratives, photo, commentaire), captcha hCaptcha, ouverture/fermeture, lien à partager, écran de configuration dédié (`/inscriptions`, COORDINATION) — les bénévoles créés rejoignent directement la liste | à valider |
|
||||
|
||||
**Reste hors périmètre** (modèle prêt à les accueillir) : SMS, constructeur
|
||||
de formulaire, pointage sur site, IA. Frontend : squelette + 6 écrans +
|
||||
|
||||
Reference in New Issue
Block a user