Changement du refresh handler, soft reset d'ajouter sur beaucoup de formulaire de modification
CI - Security, Lint & Tests / validate (push) Failing after 1m3s

This commit is contained in:
cedrick2711
2026-09-08 11:27:53 -04:00
parent e2e2b1b07e
commit 4a2e2f053f
42 changed files with 722 additions and 333 deletions
+52 -2
View File
@@ -26,8 +26,10 @@ from sqlalchemy import text
from werkzeug.exceptions import HTTPException
from app import i18n
from app.api import json_endpoint
from app.extensions import babel, csrf, db, limiter, login_manager
from app.pagination import page_url
from app.responders import current_revision, is_ajax_submit
load_dotenv()
@@ -347,6 +349,42 @@ def create_app(config=None):
response.headers['X-Request-Id'] = g.get('request_id', '-')
return response
@app.before_request
def reject_stale_ajax_write():
"""Reject an AJAX edit whose view is stale, instead of overwriting.
The AJAX form handler in main.js sends the revision it last saw in the
X-Revision header. If another client has committed since then the
server's revision has moved on, and clobbering their change would be
silent last-write-wins. Answer 409 so the client asks the user to
reload the page first.
"""
if request.method != 'POST':
return None
if not is_ajax_submit():
return None
sent = request.headers.get('X-Revision')
if sent is None:
return None
try:
sent = int(sent)
except (TypeError, ValueError):
return None
if sent != current_revision():
return (
jsonify(
{
'error': 'conflict',
'message': _(
'This page was changed by someone else. Reload to '
'see the latest changes before saving.'
),
}
),
409,
)
return None
@app.url_defaults
def version_static_urls(endpoint, values):
"""Stamp every static URL with the file's modification time.
@@ -403,6 +441,7 @@ def create_app(config=None):
configure_logging(app)
from app.routes.admin import admin_bp
from app.routes.auth import auth_bp
from app.routes.evaluations import evaluations_bp
from app.routes.main import main_bp
@@ -411,7 +450,6 @@ def create_app(config=None):
from app.routes.teams import teams_bp
from app.routes.tryouts import tryouts_bp
from app.routes.users import users_bp
from app.routes.admin import admin_bp
app.register_blueprint(auth_bp)
app.register_blueprint(tryouts_bp)
@@ -531,6 +569,18 @@ def create_app(config=None):
return jsonify(health_data), 200
@app.route('/api/revision')
@json_endpoint
def data_revision():
"""The current dataset revision, for the AJAX form handler in main.js.
One primary-key lookup in app_settings. The client compares this with
the revision it last saw to decide whether a focus refresh is needed,
and sends it back with every AJAX mutation so a stale edit is rejected
with a 409 instead of overwriting someone else's change.
"""
return jsonify({'revision': current_revision()})
# =========================================================================
# Custom Error Handlers
# =========================================================================
@@ -725,4 +775,4 @@ def create_app(config=None):
# own host, port and debug default — `python app/app.py` bound 0.0.0.0:10000
# while `python run.py` bound 127.0.0.2:5000 with the debugger on. This
# module defines the factory; run.py starts it for development, wsgi.py for
# production (ARCH-007).
# production (ARCH-007).