Repository navigation
feat(api): chercher, créer et modifier un lieu (E-13) - #594
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Rebasée sur main par l'équipe pour repasser l'audit vite 8.3.4, aucun changement de code. |
24a0f45 to
12d1f55
Compare
|
📋 MANAGER, 2026-10-21 — relecture contre #46 : verdict B (fusionnable après rebase, un petit commit et Rebase : la branche part de Découpage de #46 (la PR demandait : « l'ajouter ici ou la sortir en ticket ») : la partie mobile sort dans #622, bloqué par #46. #46 ne porte plus que l'API ; cette PR doit donc passer de Critères de #46 (version API) :
Recouvrement avec la pile admin (#398, #55, sur Contrôles et modération des lieux créés : le lieu naît Risque : niveau 2 juste. Modèle de PR : complet, labels présents. À faire avant la fusion : rebase, commit |
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12d1f55 to
f0c3e17
Compare
|
Rebasée et complétée par l'équipe : rebase sur |
|
Accord de niveau 2 consigné par MANAGER : Paul (opérateur) a donné son accord en session le 2026-10-21 (« on fusionne »), PR fusionnée le même jour. Cet accord vaut accord de l'opérateur avant production au sens de la règle du niveau 2 ; le déploiement en production reste manuel, par Ilan ou Paul, après un staging vert. |
Résultat et problème
Un créateur ne pouvait ni retrouver un lieu existant ni en ajouter un : aucune route n'écrivait ni ne cherchait les lieux (seuls le seed et les tests le faisaient). Cette PR livre la partie API de #46 (E-13), selon le wiki API › Lieux :
GET /v1/places/search?q=&near=lat,lng&category=&limit=: lieux dont le nom contient le texte ou lui ressemble (trigrammes), triés par proximité de nom puis par distance (mètres) ; les lieux fermés ne sortent pas ; au moinsqounear.POST /v1/places: crée un lieuunverifieddans la ville qui le contient. Si un lieu de nom proche (similarité ≥ 0,4) existe à moins de 50 m : 409conflictavec ce lieu dansdetails.candidate, saufforce: true. Plus de 20 créations en 24 h par compte : 429rate_limited(details.reason: place_creation).endsOn(date de fin d'un lieu éphémère, D-050) : accepté parPOST /v1/places(nul par défaut) et parPATCH, date ISOAAAA-MM-JJ, enregistré dansplaces.ends_on(commit ajouté par l'équipe).PATCH /v1/places/:id: le créateur tant que seuls ses propres parcours utilisent le lieu ; un modérateur ou un admin toujours ; les horaires sont remplacés en bloc.Aucune migration : la table
places, l'index trigramme et les horaires existent déjà.Limites restantes
outside_service_area, même avecforce. La création automatique de la ville est Ouvrir la création à toutes les villes : ville déduite de la position, recherche sans filtre de ville #340, qui dépend de cette PR.unaccentabsente) : « Café » et « Cafe » restent proches par trigrammes, pas égaux.Fixes #46
Écrans : E-13 (partie API)
Dépendance : aucune (depuis
main)Comment vérifier
Tests :
pnpm --filter api exec vitest run src/routes/places.test.ts(19 tests : recherche par texte, position, catégorie,%et_du texte pris comme lettres, lieu fermé exclu, 400 sans critère ou avec clé inconnue, 401 ; création avec horaires, doublon à 20 m,force, lieu lointain ou de nom différent non pris pour un doublon, hors ville, quota de 20 par jour propre à chaque compte, corps invalide ; modification par le créateur, refus d'un tiers, refus du créateur une fois le lieu utilisé par le parcours d'un autre, modérateur, 404, corps vide).En local (Postgres, émulateur Auth, API sur le port 8081), dans Postman :
GET /v1/places/search?q=carnavalet&near=48.85732,2.36247: 200, Musée Carnavalet,distanceM0.POST /v1/places« Café de Flore » en48.8541, 2.3327: 201,verificationStatus: "unverified".48.85428(≈ 20 m) : 409 avec le premier lieu ; avec"force": true: 201.0, 0: 422outside_service_area.PATCH /v1/places/:id{ "phone": "...", "priceRange": "medium" }: 200 ; sans jeton : 401.Vérification humaine : Marco a testé la recherche, la création et le refus hors ville dans Postman en local le 2026-10-21 (captures dans la session), et déclaré avoir tout essayé.
pnpm lint && pnpm typecheck && pnpm testverts en local (API : 787 tests, shared : 677)Formatage mécanique : non
Risque
Niveau : 2 — nouveaux endpoints authentifiés qui écrivent des données (lieux visibles de tous les créateurs), sans migration ni donnée existante modifiée.
Taille : 785 lignes au total dont 310 de tests ; commits de 122, 203, 290, 149 et 24 lignes, tous sous 400 (le troisième compte 151 lignes de tests).
Revue OWASP (Top 10:2025, ciblée)
PATCHréservé au créateur tant que seuls ses parcours utilisent le lieu, sinon rôlemoderator(lu en base) ; le lieu est verrouillé (for update) pendant la vérification. Tests : tiers 403, créateur 403 après usage par un autre, modérateur 200, inconnu 404.ILIKEéchappe%,_et\(test) ; textes en texte brut sans<,>ni caractères de contrôle, liens en https seulement, clés inconnues refusées.created_byest la seule donnée liée au compte ; la réponse ne l'expose pas.Retour arrière
Revert de la PR suffit : aucune migration. Les lieux créés pendant que la PR est en ligne restent en base (lieux
unverified, ville existante) et sont sans conséquence si les routes disparaissent.