feat: Enhance error handling for URL resolution and unexpected exceptions
This commit is contained in:
+42
-4
@@ -28,6 +28,8 @@ Features:
|
|||||||
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file, abort
|
from flask import Flask, render_template, request, redirect, url_for, session, flash, send_from_directory, get_flashed_messages, jsonify, Response, make_response, send_file, abort
|
||||||
from werkzeug.utils import secure_filename
|
from werkzeug.utils import secure_filename
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
from werkzeug.exceptions import HTTPException
|
||||||
|
from werkzeug.routing import BuildError
|
||||||
from jinja2 import TemplateNotFound
|
from jinja2 import TemplateNotFound
|
||||||
import Web.modules.database.user as us
|
import Web.modules.database.user as us
|
||||||
import Web.modules.database.items as it
|
import Web.modules.database.items as it
|
||||||
@@ -373,7 +375,7 @@ def _enforce_module_access():
|
|||||||
if name != 'inventory' and cfg.MODULES.is_enabled('inventory'):
|
if name != 'inventory' and cfg.MODULES.is_enabled('inventory'):
|
||||||
return redirect(url_for('home'))
|
return redirect(url_for('home'))
|
||||||
elif name != 'library' and cfg.MODULES.is_enabled('library'):
|
elif name != 'library' and cfg.MODULES.is_enabled('library'):
|
||||||
return redirect(url_for('library_view'))
|
return redirect('/library')
|
||||||
|
|
||||||
return redirect(url_for('my_borrowed_items'))
|
return redirect(url_for('my_borrowed_items'))
|
||||||
|
|
||||||
@@ -433,6 +435,41 @@ def handle_not_found(e):
|
|||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(BuildError)
|
||||||
|
def handle_build_error(e):
|
||||||
|
"""Handle url_for endpoint mismatches without crashing the whole request."""
|
||||||
|
app.logger.error(f"BuildError while resolving endpoint: {e}", exc_info=True)
|
||||||
|
if request.is_json or request.path.startswith('/api/'):
|
||||||
|
return jsonify({'error': 'Route resolution failed', 'status': 500}), 500
|
||||||
|
|
||||||
|
# Known fallback for legacy library endpoint naming issues.
|
||||||
|
if 'library_view' in str(e):
|
||||||
|
return redirect('/library')
|
||||||
|
|
||||||
|
if 'username' in session:
|
||||||
|
return redirect(url_for('home'))
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
|
||||||
|
@app.errorhandler(Exception)
|
||||||
|
def handle_unexpected_exception(e):
|
||||||
|
"""Last-resort handler to avoid raw 500 pages for unhandled exceptions."""
|
||||||
|
if isinstance(e, HTTPException):
|
||||||
|
return e
|
||||||
|
|
||||||
|
app.logger.error('Unhandled exception', exc_info=True)
|
||||||
|
if request.is_json or request.path.startswith('/api/'):
|
||||||
|
return jsonify({'error': 'Internal server error', 'status': 500}), 500
|
||||||
|
|
||||||
|
if 'username' in session:
|
||||||
|
try:
|
||||||
|
return render_template('error.html', error_code=500, error_message='Interner Serverfehler.'), 500
|
||||||
|
except Exception:
|
||||||
|
return 'Internal Server Error', 500
|
||||||
|
|
||||||
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
|
|
||||||
def _csrf_error_response(message='CSRF token fehlt oder ist ungültig.'):
|
def _csrf_error_response(message='CSRF token fehlt oder ist ungültig.'):
|
||||||
if request.is_json or request.path.startswith('/api/') or request.path in {'/download_book_cover', '/proxy_image', '/log_mobile_issue'}:
|
if request.is_json or request.path.startswith('/api/') or request.path in {'/download_book_cover', '/proxy_image', '/log_mobile_issue'}:
|
||||||
return jsonify({'error': message}), 400
|
return jsonify({'error': message}), 400
|
||||||
@@ -2640,7 +2677,7 @@ def home():
|
|||||||
|
|
||||||
if not cfg.MODULES.is_enabled('inventory'):
|
if not cfg.MODULES.is_enabled('inventory'):
|
||||||
if cfg.MODULES.is_enabled('library'):
|
if cfg.MODULES.is_enabled('library'):
|
||||||
return redirect(url_for('library_view'))
|
return redirect('/library')
|
||||||
else:
|
else:
|
||||||
return "Weder Inventar- noch Bibliotheks-Modul sind aktiviert.", 403
|
return "Weder Inventar- noch Bibliotheks-Modul sind aktiviert.", 403
|
||||||
|
|
||||||
@@ -2716,8 +2753,9 @@ def tutorial_page():
|
|||||||
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS
|
student_max_borrow_days=cfg.STUDENT_MAX_BORROW_DAYS
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@app.route('/library/export', defaults={'scope': 'all'})
|
||||||
@app.route('/library/export/<scope>')
|
@app.route('/library/export/<scope>')
|
||||||
def library_export_excel(scope):
|
def library_export_excel(scope='all'):
|
||||||
if 'username' not in session:
|
if 'username' not in session:
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
|
|
||||||
@@ -2737,7 +2775,7 @@ def library_export_excel(scope):
|
|||||||
filename = f"Bibliothek_Ausgeliehen_{username}.xlsx"
|
filename = f"Bibliothek_Ausgeliehen_{username}.xlsx"
|
||||||
elif scope == 'all_borrowed':
|
elif scope == 'all_borrowed':
|
||||||
if not is_admin_user:
|
if not is_admin_user:
|
||||||
return redirect(url_for('library_view'))
|
return redirect('/library')
|
||||||
query['Verfuegbar'] = False
|
query['Verfuegbar'] = False
|
||||||
filename = "Bibliothek_Alle_Ausleihen.xlsx"
|
filename = "Bibliothek_Alle_Ausleihen.xlsx"
|
||||||
elif scope == 'schulbuecher':
|
elif scope == 'schulbuecher':
|
||||||
|
|||||||
Reference in New Issue
Block a user