style: formater le depot avec ruff format
QUA-002, premiere moitie. **Ce commit ne fait que reformater** : aucun changement de comportement, aucune ligne de logique touchee. 72 fichiers, 4 restaient deja conformes. Il est isole exprès, pour que `git log -p` sur les commits voisins reste lisible. `quote-style = "preserve"` etait deja pose dans pyproject.toml, ce qui evite le brassage guillemets simples / doubles : le diff porte sur les retours a la ligne, l indentation des appels longs et les virgules finales, pas sur le style de chaine. Verification : 263 tests passent avant et apres, ruff check propre. L activation en CI arrive dans le commit suivant, separement, pour que ce diff-ci ne contienne rien d autre. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+64
-52
@@ -100,19 +100,21 @@ def build_csp(*, allow_inline_script, nonce=None):
|
||||
else:
|
||||
script_src = f"'self' 'nonce-{nonce}' https://cdn.jsdelivr.net"
|
||||
|
||||
return '; '.join([
|
||||
"default-src 'self'",
|
||||
f'script-src {script_src}',
|
||||
# style-src is a separate migration: inline style="" attributes are
|
||||
# spread across the templates and are not an XSS vector on their own.
|
||||
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net",
|
||||
"font-src 'self' https://cdnjs.cloudflare.com",
|
||||
"img-src 'self' data: https://cdn.discordapp.com",
|
||||
"connect-src 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
])
|
||||
return '; '.join(
|
||||
[
|
||||
"default-src 'self'",
|
||||
f'script-src {script_src}',
|
||||
# style-src is a separate migration: inline style="" attributes are
|
||||
# spread across the templates and are not an XSS vector on their own.
|
||||
"style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com https://cdn.jsdelivr.net",
|
||||
"font-src 'self' https://cdnjs.cloudflare.com",
|
||||
"img-src 'self' data: https://cdn.discordapp.com",
|
||||
"connect-src 'self'",
|
||||
"frame-ancestors 'none'",
|
||||
"base-uri 'self'",
|
||||
"form-action 'self'",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def create_app(config=None):
|
||||
@@ -170,12 +172,8 @@ def create_app(config=None):
|
||||
|
||||
# Side effects of create_app(), both on by default so that production and
|
||||
# development behave exactly as before. Tests turn them off.
|
||||
app.config['AUTO_CREATE_TABLES'] = (
|
||||
os.getenv('AUTO_CREATE_TABLES', 'true').lower() == 'true'
|
||||
)
|
||||
app.config['ENABLE_DISCORD_BOT'] = (
|
||||
os.getenv('ENABLE_DISCORD_BOT', 'true').lower() == 'true'
|
||||
)
|
||||
app.config['AUTO_CREATE_TABLES'] = os.getenv('AUTO_CREATE_TABLES', 'true').lower() == 'true'
|
||||
app.config['ENABLE_DISCORD_BOT'] = os.getenv('ENABLE_DISCORD_BOT', 'true').lower() == 'true'
|
||||
|
||||
# --- caller overrides win ---------------------------------------------
|
||||
if config:
|
||||
@@ -198,7 +196,9 @@ def create_app(config=None):
|
||||
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
|
||||
|
||||
# Secure session cookie settings
|
||||
app.config['SESSION_COOKIE_SECURE'] = os.getenv('SESSION_COOKIE_SECURE', 'true').lower() == 'true'
|
||||
app.config['SESSION_COOKIE_SECURE'] = (
|
||||
os.getenv('SESSION_COOKIE_SECURE', 'true').lower() == 'true'
|
||||
)
|
||||
app.config['SESSION_COOKIE_HTTPONLY'] = True
|
||||
app.config['SESSION_COOKIE_SAMESITE'] = 'Lax'
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = 3600 # 1 hour session timeout
|
||||
@@ -245,12 +245,14 @@ def create_app(config=None):
|
||||
|
||||
@app.context_processor
|
||||
def inject_csp_nonce():
|
||||
return {'csp_nonce': '' if app.config['CSP_ALLOW_INLINE_SCRIPT']
|
||||
else g.get('csp_nonce', '')}
|
||||
return {
|
||||
'csp_nonce': '' if app.config['CSP_ALLOW_INLINE_SCRIPT'] else g.get('csp_nonce', '')
|
||||
}
|
||||
|
||||
@app.context_processor
|
||||
def inject_locales():
|
||||
from flask_babel import get_locale
|
||||
|
||||
return {
|
||||
'current_locale': str(get_locale() or i18n.DEFAULT_LOCALE),
|
||||
'supported_locales': i18n.SUPPORTED_LOCALES,
|
||||
@@ -259,6 +261,7 @@ def create_app(config=None):
|
||||
|
||||
# Configure structured logging
|
||||
from app.logging_config import configure_logging
|
||||
|
||||
configure_logging(app)
|
||||
|
||||
from app.routes.auth import auth_bp
|
||||
@@ -303,8 +306,7 @@ def create_app(config=None):
|
||||
response.headers['X-Frame-Options'] = 'DENY'
|
||||
response.headers['Referrer-Policy'] = 'strict-origin-when-cross-origin'
|
||||
response.headers['Permissions-Policy'] = (
|
||||
'camera=(), microphone=(), geolocation=(), '
|
||||
'interest-cohort=(), payment=(), usb=()'
|
||||
'camera=(), microphone=(), geolocation=(), interest-cohort=(), payment=(), usb=()'
|
||||
)
|
||||
response.headers['Cross-Origin-Opener-Policy'] = 'same-origin'
|
||||
response.headers['Content-Security-Policy'] = build_csp(
|
||||
@@ -383,9 +385,11 @@ def create_app(config=None):
|
||||
Returns:
|
||||
Response: Rendered error page or JSON for API requests.
|
||||
"""
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/coach-availability') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
if (
|
||||
request.path.startswith('/users/disponibilities')
|
||||
or request.path.startswith('/users/coach-availability')
|
||||
or request.path.startswith('/users/api/')
|
||||
):
|
||||
return jsonify({'error': 'Bad request', 'message': str(error)}), 400
|
||||
return render_template('errors/400.html', error=error), 400
|
||||
|
||||
@@ -399,10 +403,12 @@ def create_app(config=None):
|
||||
Returns:
|
||||
Response: Redirect to login for pages, JSON for API.
|
||||
"""
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
if request.path.startswith('/users/disponibilities') or request.path.startswith(
|
||||
'/users/api/'
|
||||
):
|
||||
return jsonify({'error': 'Unauthorized'}), 401
|
||||
from flask import flash as _flash
|
||||
|
||||
_flash('Please log in to access this page.', 'warning')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
@@ -416,8 +422,9 @@ def create_app(config=None):
|
||||
Returns:
|
||||
Response: Rendered error page or JSON for API requests.
|
||||
"""
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
if request.path.startswith('/users/disponibilities') or request.path.startswith(
|
||||
'/users/api/'
|
||||
):
|
||||
return jsonify({'error': 'Forbidden', 'message': str(error)}), 403
|
||||
return render_template('errors/403.html', error=error), 403
|
||||
|
||||
@@ -431,8 +438,9 @@ def create_app(config=None):
|
||||
Returns:
|
||||
Response: Rendered error page or JSON for API requests.
|
||||
"""
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
if request.path.startswith('/users/disponibilities') or request.path.startswith(
|
||||
'/users/api/'
|
||||
):
|
||||
return jsonify({'error': 'Not found'}), 404
|
||||
return render_template('errors/404.html', error=error), 404
|
||||
|
||||
@@ -446,12 +454,12 @@ def create_app(config=None):
|
||||
Returns:
|
||||
Response: JSON error for API or rendered page.
|
||||
"""
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
return jsonify({
|
||||
'error': 'Too many requests',
|
||||
'message': 'Please try again later.'
|
||||
}), 429
|
||||
if request.path.startswith('/users/disponibilities') or request.path.startswith(
|
||||
'/users/api/'
|
||||
):
|
||||
return jsonify(
|
||||
{'error': 'Too many requests', 'message': 'Please try again later.'}
|
||||
), 429
|
||||
return render_template('errors/429.html', error=error), 429
|
||||
|
||||
@app.errorhandler(500)
|
||||
@@ -472,12 +480,15 @@ def create_app(config=None):
|
||||
# Roll back any failed database session
|
||||
db.session.rollback()
|
||||
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
return jsonify({
|
||||
'error': 'Internal server error',
|
||||
'message': 'An unexpected error occurred. Please try again later.'
|
||||
}), 500
|
||||
if request.path.startswith('/users/disponibilities') or request.path.startswith(
|
||||
'/users/api/'
|
||||
):
|
||||
return jsonify(
|
||||
{
|
||||
'error': 'Internal server error',
|
||||
'message': 'An unexpected error occurred. Please try again later.',
|
||||
}
|
||||
), 500
|
||||
return render_template('errors/500.html'), 500
|
||||
|
||||
@app.errorhandler(HTTPException)
|
||||
@@ -490,13 +501,12 @@ def create_app(config=None):
|
||||
Returns:
|
||||
Response: JSON error for API, re-raises for others.
|
||||
"""
|
||||
if request.path.startswith('/users/disponibilities') or \
|
||||
request.path.startswith('/users/api/'):
|
||||
return jsonify({
|
||||
'error': error.name,
|
||||
'message': error.description,
|
||||
'code': error.code
|
||||
}), error.code
|
||||
if request.path.startswith('/users/disponibilities') or request.path.startswith(
|
||||
'/users/api/'
|
||||
):
|
||||
return jsonify(
|
||||
{'error': error.name, 'message': error.description, 'code': error.code}
|
||||
), error.code
|
||||
return error
|
||||
|
||||
# =========================================================================
|
||||
@@ -504,6 +514,7 @@ def create_app(config=None):
|
||||
# =========================================================================
|
||||
with app.app_context():
|
||||
import app.models as models # noqa: F401 — registers all models with SQLAlchemy
|
||||
|
||||
# NOTE: create_all() only ever creates missing tables. It never adds a
|
||||
# column to an existing one, so a model change is silently absent from
|
||||
# any database that already has the table. Replacing this with Alembic
|
||||
@@ -515,6 +526,7 @@ def create_app(config=None):
|
||||
if app.config['ENABLE_DISCORD_BOT']:
|
||||
try:
|
||||
from app.discord_bot import start_bot
|
||||
|
||||
start_bot(flask_app=app)
|
||||
except Exception as e:
|
||||
app.logger.warning('Could not start Discord bot: %s', e)
|
||||
|
||||
Reference in New Issue
Block a user