114 lines
3.0 KiB
Python
114 lines
3.0 KiB
Python
from http import HTTPStatus
|
|
|
|
from flask import jsonify, render_template, request
|
|
from jinja2 import TemplateNotFound
|
|
from sqlalchemy.exc import OperationalError
|
|
from werkzeug.exceptions import HTTPException
|
|
|
|
from .extensions import db
|
|
|
|
|
|
def _safe_rollback() -> None:
|
|
try:
|
|
db.session.rollback()
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def _wants_json_response() -> bool:
|
|
if request.path.startswith("/api/"):
|
|
return True
|
|
|
|
best = request.accept_mimetypes.best_match(["application/json", "text/html"])
|
|
if not best:
|
|
return False
|
|
|
|
return (
|
|
best == "application/json"
|
|
and request.accept_mimetypes["application/json"]
|
|
>= request.accept_mimetypes["text/html"]
|
|
)
|
|
|
|
|
|
def _get_status_phrase(status_code: int) -> str:
|
|
try:
|
|
return HTTPStatus(status_code).phrase
|
|
except ValueError:
|
|
return "Blad"
|
|
|
|
|
|
def _get_status_description(status_code: int) -> str:
|
|
try:
|
|
return HTTPStatus(status_code).description
|
|
except ValueError:
|
|
return "Wystapil blad podczas przetwarzania zadania."
|
|
|
|
|
|
def _error_headers(status_code: int) -> dict[str, str]:
|
|
headers = {}
|
|
|
|
if status_code >= 500:
|
|
headers.update(
|
|
{
|
|
"Cache-Control": "no-store, no-cache, must-revalidate, max-age=0, private",
|
|
"Pragma": "no-cache",
|
|
"Expires": "0",
|
|
"Surrogate-Control": "no-store",
|
|
}
|
|
)
|
|
|
|
return headers
|
|
|
|
|
|
def _render_error(status_code: int, message: str | None = None):
|
|
phrase = _get_status_phrase(status_code)
|
|
description = message or _get_status_description(status_code)
|
|
headers = _error_headers(status_code)
|
|
|
|
payload = {
|
|
"status": status_code,
|
|
"error": phrase,
|
|
"message": description,
|
|
}
|
|
|
|
if _wants_json_response():
|
|
return jsonify(payload), status_code, headers
|
|
|
|
try:
|
|
return (
|
|
render_template(
|
|
"error.html",
|
|
error_code=status_code,
|
|
error_name=phrase,
|
|
error_message=description,
|
|
),
|
|
status_code,
|
|
headers,
|
|
)
|
|
except TemplateNotFound:
|
|
return (
|
|
f"{status_code} {phrase}: {description}",
|
|
status_code,
|
|
headers,
|
|
)
|
|
|
|
|
|
def register_error_handlers(app):
|
|
@app.errorhandler(HTTPException)
|
|
def handle_http_exception(exc):
|
|
return _render_error(exc.code or 500, exc.description)
|
|
|
|
@app.errorhandler(OperationalError)
|
|
def handle_operational_error(exc):
|
|
_safe_rollback()
|
|
app.logger.exception("Blad polaczenia z baza danych: %s", exc)
|
|
return _render_error(
|
|
503,
|
|
"Baza danych jest chwilowo niedostepna. Sprobuj ponownie za chwile.",
|
|
)
|
|
|
|
@app.errorhandler(Exception)
|
|
def handle_unexpected_error(exc):
|
|
_safe_rollback()
|
|
app.logger.exception("Nieobsluzony wyjatek: %s", exc)
|
|
return _render_error(500, "Wystapil nieoczekiwany blad serwera.") |