Skip to content

fix(api): changer le statut depuis l'admin par moveRoute - #625

Merged
PaulThiberville merged 2 commits into
mainfrom
issue/611-admin-status-through-moveroute
Oct 21, 2026
Merged

PaulThiberville merged 2 commits into
mainfrom
issue/611-admin-status-through-moveroute

Conversation

@PaulThiberville

Copy link
Copy Markdown
Collaborator

Résultat et problème

Trois écritures de l'admin posaient routes.status directement, sans moveRoute (apps/api/src/editor/lifecycle.ts), le seul écrivain prévu du statut. Conséquences sur main : un parcours dépublié restait mis en avant sur les profils (D-023), unpublished_at n'était ni daté ni vidé, published_at était réécrit à chaque publication, et les transitions n'étaient pas vérifiées par canMoveRoute.

Maintenant :

  • decideRoute (moderation/decisions.ts : Publier, Demander des modifications, Refuser, Dépublier, A-02 et A-04), republishRoute (catalog/route-actions.ts : Republier, A-04) et closePlace (reports/place-signals.ts : parcours d'un lieu fermé passés needs_fix, A-03) lisent la ligne entière sous for update et appellent moveRoute(tx, route, to, 'moderator', now), dans la transaction qui écrit déjà moderation_actions et admin_actions.
  • routeTransitions (packages/shared) gagne, pour moderator seulement : unpublished → published et needs_fix → unpublished.
  • Le 409 already_decided (D-087) reste vérifié avant moveRoute, avec le statut trouvé : la concurrence entre deux membres de l'équipe garde sa réponse, le 409 conflict de moveRoute n'est jamais atteint par ces chemins.

Acteur de la fermeture d'un lieu : moderator. C'est une décision d'un membre de l'équipe, déjà journalisée à son nom (moderation_actions.actor_id, admin_actions), et la transition published → needs_fix est ouverte aux deux acteurs : system reste réservé au recalcul nocturne.

À noter : needs_fix n'est pas un statut public (publicRouteStatuses = published), donc un parcours passé à corriger par la fermeture d'un lieu ou par « Demander des modifications » sort aussi des profils, comme le veut la règle portée par moveRoute. C'était déjà le cas d'un auteur ; c'est maintenant le cas de l'admin.

Critères d'acceptation :

  • Aucune écriture de routes.status hors de moveRoute dans apps/api/src (hors seed et tests) : git grep -nE "update routes|\.update\(routes\)" -- 'apps/api/src/**/*.ts' ':!*.test.ts' ne laisse que editor/lifecycle.ts (le statut), editor/save.ts (le contenu), catalog/route-actions.ts:81 (reassignRouteToTeam, auteur seulement) et recommendation/job.ts (le score)
  • Dépublier un parcours mis en avant retire sa ligne user_featured_routes dans la même transaction (test d'intégration)
  • Dépublier date unpublished_at ; Republier le vide et garde published_at ; Publier garde la date d'une première publication (tests)
  • Fermer un lieu passe ses parcours publiés en needs_fix par moveRoute, mise en avant retirée (test)
  • canMoveRoute('unpublished', 'published', 'moderator') et canMoveRoute('needs_fix', 'unpublished', 'moderator') vrais, et faux pour tout autre acteur (test exhaustif de packages/shared)
  • Deux décisions simultanées : 409 already_decided, rien d'écrit deux fois (test existant, vert)

Fixes #611
Écrans : A-02, A-03, A-04 (aucun changement visible)
Dépendance : aucune

Comment vérifier

  • pnpm turbo run lint typecheck --force (18 tâches) et prettier --check . verts en local
  • Tests : @widoo/shared 742 verts ; @widoo/admin 199 verts ; API, les trois fichiers touchés (admin-moderation, admin-catalog, admin-signals) 54 verts. Suite API complète : 982 verts, 9 rouges dans search-access.test.ts seul, vert relancé seul (instabilité en parallèle, Stabiliser la suite de tests de l'API quand ses fichiers tournent en parallèle #623)
  • Les 4 nouveaux tests d'intégration échouent sur le code de main (vérifié en retirant le correctif) : mise en avant retirée à la dépublication et à la fermeture d'un lieu, dates de publication et de retrait
  • Six transitions refusées hors cycle de vie (publier un parcours dépublié par la décision de modération, refuser un publié, dépublier un parcours en attente ou privé…) : 409 already_decided, statut inchangé
  • Testé sur iOS / Android — sans objet (API)

Formatage mécanique : non

Risque

Niveau : 2 — règle métier et intégrité des données (statut, dates, mise en avant), réversible, sans migration.

Revue OWASP ciblée (Top 10 2025) :

  • A08 Intégrité des logiciels et des données : le statut n'a plus qu'un écrivain, vérifié contre le cycle de vie de packages/shared ; chaque changement se fait sous verrou de ligne, dans la transaction qui écrit l'historique et le journal ; les parcours d'un lieu fermé sont verrouillés dans un ordre stable (order by id). Tests ci-dessus.
  • A01 Contrôle d'accès : inchangé. Les deux transitions ajoutées sont réservées à l'acteur moderator, que seules les routes /admin/* emploient, derrière requireAdminSection (rôle lu en base, second facteur) ; un auteur ne peut ni republier ni dépublier un parcours à corriger (test exhaustif des refus).
  • A03, A05, A07, A09, A10 : non concernées, aucune entrée nouvelle, configuration, authentification, journalisation ou appel sortant.

Taille : 3 lignes dans le premier commit, 175 dans le second (dont 116 de tests).

Retour arrière

Revert de la PR : les écritures reviennent directes, aucune migration. Les dates unpublished_at posées entre-temps restent, sans effet sur l'ancien code. Les lignes user_featured_routes retirées ne reviennent pas : l'auteur remet le parcours en avant depuis son profil.

🤖 Generated with Claude Code

PaulThiberville and others added 2 commits October 21, 2026 19:37
Two moves of the admin were missing from the life cycle, for the moderator only:
unpublished → published (Republier, A-04) and needs_fix → unpublished (Dépublier).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Publish, request changes, reject and unpublish (A-02, A-04), republish (A-04) and the routes
of a closed place (A-03) wrote routes.status directly. They now lock the whole row and call
moveRoute as the moderator: a route that goes offline leaves the profiles that featured it,
the first publication keeps its date, each withdrawal is dated and a republication clears
it. The 409 already_decided stays checked before the move.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PaulThiberville PaulThiberville added the level:2 Risque niveau 2 (SECURITY.md) label Oct 21, 2026
@vercel

vercel Bot commented Oct 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
widoo-web Ready Ready Preview Oct 21, 2026 5:38pm UTC

@PaulThiberville

Copy link
Copy Markdown
Collaborator Author

Plan suivi : deux commits autonomes, d'abord les transitions de packages/shared (sans elles, Republier tomberait sur le 409 conflict de moveRoute), puis les trois écritures de l'API. Décisions : acteur moderator pour la fermeture d'un lieu (décision d'un membre de l'équipe, journalisée à son nom) ; 409 already_decided gardé avant moveRoute ; parcours d'un lieu fermé verrouillés et déplacés un par un, dans l'ordre des identifiants. Niveau 2 : la mise en production attend l'accord de l'opérateur.

@PaulThiberville
PaulThiberville merged commit 17cc6be into main Oct 21, 2026
6 checks passed
@PaulThiberville
PaulThiberville deleted the issue/611-admin-status-through-moveroute branch October 21, 2026 17:44
@PaulThiberville

Copy link
Copy Markdown
Collaborator Author

Accord de niveau 2 consigné par MANAGER : Paul (opérateur) a validé cette PR en session le 2026-10-22, après la recette locale de l'admin (#621, sur main à 7efd9150, qui l'inclut : dépublier puis republier un parcours vus conformes dans A-04). Cet accord vaut accord de l'opérateur avant production au sens de la règle du niveau 2 ; il n'autorise pas à lui seul un déploiement, qui reste manuel par Ilan ou Paul après un staging vert. #611 reste en À déployer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

level:2 Risque niveau 2 (SECURITY.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Faire passer les changements de statut de l'admin par moveRoute

1 participant