fix(web): une erreur sur un point JSON ne renvoie plus une page HTML
STD-09, trouve en recroisant l'audit anterieur -- celui mene sur le miroir GitHub, jamais repasse depuis qu'on a decouvert que ce n'etait pas la bonne source. Sept gestionnaires d'erreur portaient chacun leur copie d'une liste de prefixes d'URL decidant "JSON ou page HTML". Les copies avaient derive -- trois testaient /users/coach-availability, quatre non -- et toutes manquaient les memes points. Un fetch() qui recoit une page d'erreur HTML leve en la parsant : sur le calendrier, les listes de selections et d'equipes restaient vides, sans message dans la page et sans rien dans le journal. Deux choses apprises en ecrivant le test, aucune n'etait dans le constat. L'approche par prefixe ne pouvait pas etre reparee. Trois des seize vues JSON sont a des chemins qu'aucun prefixe ne distingue des pages HTML voisines -- /matches/<id>/toggle-presence/<id> et ses deux cousins, que les gabarits appellent justement en fetch(). Les vues se declarent donc elles-memes (@json_endpoint, app/api.py), et un test parcourt la carte des URL pour verifier qu'aucune vue appelant jsonify n'a ete oubliee. Et surtout : @login_required n'atteint jamais le gestionnaire 401. Flask-Login intercepte avant et redirige. Les seize points JSON repondaient donc a une session expiree par une 302 vers un formulaire HTML, quoi que dise la liste de prefixes. Reecrire la liste seule aurait eu l'air d'un correctif sans rien changer. Au passage, le message flash de ce gestionnaire etait la seule chaine de l'application qui n'avait jamais ete traduite. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -11,6 +11,7 @@ from flask_login import current_user, login_required
|
||||
from marshmallow import ValidationError
|
||||
from sqlalchemy.orm import joinedload
|
||||
|
||||
from app.api import json_endpoint
|
||||
from app.extensions import db
|
||||
from app.forms import flash_validation_errors, form_payload
|
||||
from app.models import (
|
||||
@@ -135,6 +136,7 @@ def calendar_window(args):
|
||||
|
||||
|
||||
@matches_bp.route('/api/events')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def api_events():
|
||||
"""Calendar events for FullCalendar.
|
||||
@@ -264,6 +266,7 @@ def api_events():
|
||||
|
||||
|
||||
@matches_bp.route('/api/events/<int:tryout_id>')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def api_events_for_tryout(tryout_id):
|
||||
"""API endpoint returning calendar events for a specific tryout."""
|
||||
@@ -540,6 +543,7 @@ def edit_match(match_id):
|
||||
|
||||
|
||||
@matches_bp.route('/api/manageable-tryouts')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def api_manageable_tryouts():
|
||||
"""API endpoint returning tryouts the current user can manage."""
|
||||
@@ -637,6 +641,7 @@ def get_players_available_at_time(date_str, time_str):
|
||||
|
||||
|
||||
@matches_bp.route('/api/available_players/<date>/<time>')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def api_available_players(date, time):
|
||||
"""API endpoint to get players available at a specific date/time slot."""
|
||||
@@ -647,6 +652,7 @@ def api_available_players(date, time):
|
||||
|
||||
|
||||
@matches_bp.route('/<int:match_id>/toggle-presence/<int:participant_id>', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def toggle_presence(match_id, participant_id):
|
||||
"""Toggle attendance_confirmed for a match participant."""
|
||||
|
||||
@@ -10,6 +10,7 @@ from flask_babel import gettext as _
|
||||
from flask_login import current_user, login_required
|
||||
from marshmallow import ValidationError
|
||||
|
||||
from app.api import json_endpoint
|
||||
from app.extensions import db
|
||||
from app.forms import flash_validation_errors, form_payload
|
||||
from app.models import (
|
||||
@@ -262,6 +263,7 @@ def delete_match(match_id):
|
||||
|
||||
|
||||
@team_matches_bp.route('/api/manageable-teams')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def api_manageable_teams():
|
||||
"""API endpoint returning teams the current user can schedule matches for."""
|
||||
@@ -279,6 +281,7 @@ def api_manageable_teams():
|
||||
|
||||
|
||||
@team_matches_bp.route('/<int:match_id>/toggle-presence/<int:participant_id>', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def toggle_presence(match_id, participant_id):
|
||||
"""Toggle is_confirmed for a team match participant."""
|
||||
|
||||
@@ -9,6 +9,7 @@ from flask import Blueprint, flash, jsonify, redirect, render_template, request,
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
from app.api import json_endpoint
|
||||
from app.extensions import db
|
||||
from app.models import (
|
||||
Admin,
|
||||
@@ -474,6 +475,7 @@ def remove_player(team_id, player_id):
|
||||
|
||||
|
||||
@teams_bp.route('/<int:team_id>/toggle_status/<int:player_id>', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def toggle_player_status(team_id, player_id):
|
||||
"""Toggle a player's status between starter and substitute."""
|
||||
|
||||
@@ -11,6 +11,7 @@ from flask import flash, jsonify, redirect, render_template, request, url_for
|
||||
from flask_babel import gettext as _
|
||||
from flask_login import current_user, login_required
|
||||
|
||||
from app.api import json_endpoint
|
||||
from app.extensions import db
|
||||
from app.models import Coach, CoachAvailability, PlayerDisponibility, User
|
||||
from app.routes.users.blueprint import users_bp
|
||||
@@ -23,6 +24,7 @@ def add_30_minutes(t):
|
||||
|
||||
|
||||
@users_bp.route('/disponibilities')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def get_disponibilities():
|
||||
"""API endpoint to get all player disponibilities for scheduling."""
|
||||
@@ -52,6 +54,7 @@ def get_disponibilities():
|
||||
|
||||
|
||||
@users_bp.route('/disponibilities/my')
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def get_my_disponibilities():
|
||||
"""API endpoint for players to get their own disponibilities."""
|
||||
@@ -74,6 +77,7 @@ def get_my_disponibilities():
|
||||
|
||||
|
||||
@users_bp.route('/disponibilities/add', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def add_disponibility():
|
||||
"""Add a disponibility block for the current player."""
|
||||
@@ -107,6 +111,7 @@ def add_disponibility():
|
||||
|
||||
|
||||
@users_bp.route('/disponibilities/add_bulk', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def add_disponibilities_bulk():
|
||||
"""Add multiple disponibility blocks at once."""
|
||||
@@ -151,6 +156,7 @@ def add_disponibilities_bulk():
|
||||
|
||||
|
||||
@users_bp.route('/disponibilities/clear', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def clear_disponibilities():
|
||||
"""Clear all disponibilities for the current player."""
|
||||
@@ -160,6 +166,7 @@ def clear_disponibilities():
|
||||
|
||||
|
||||
@users_bp.route('/disponibilities/<int:disponibility_id>/delete', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def delete_disponibility(disponibility_id):
|
||||
"""Delete a disponibility block."""
|
||||
@@ -172,6 +179,7 @@ def delete_disponibility(disponibility_id):
|
||||
|
||||
|
||||
@users_bp.route('/coach-availability', methods=['GET', 'POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def manage_coach_availability():
|
||||
"""Manage coach availability for One on One sessions."""
|
||||
@@ -221,6 +229,7 @@ def manage_coach_availability():
|
||||
|
||||
|
||||
@users_bp.route('/coach-availability/clear', methods=['POST'])
|
||||
@json_endpoint
|
||||
@login_required
|
||||
def clear_coach_availability():
|
||||
"""Clear all coach availability slots."""
|
||||
|
||||
Reference in New Issue
Block a user