forked from FrederikBaerentsen/BrickTracker
bricktracker
migrations
sql
views
admin
__init__.py
add.py
error.py
exceptions.py
index.py
instructions.py
login.py
minifigure.py
part.py
set.py
storage.py
upload.py
wish.py
__init__.py
app.py
config.py
configuration.py
configuration_list.py
exceptions.py
fields.py
instructions.py
instructions_list.py
login.py
metadata.py
metadata_list.py
minifigure.py
minifigure_list.py
navbar.py
parser.py
part.py
part_list.py
rebrickable.py
rebrickable_image.py
rebrickable_minifigure.py
rebrickable_part.py
rebrickable_set.py
rebrickable_set_list.py
record.py
record_list.py
reload.py
retired.py
retired_list.py
set.py
set_list.py
set_owner.py
set_owner_list.py
set_purchase_location.py
set_purchase_location_list.py
set_status.py
set_status_list.py
set_storage.py
set_storage_list.py
set_tag.py
set_tag_list.py
socket.py
socket_decorator.py
sql.py
sql_counter.py
sql_migration.py
sql_migration_list.py
sql_stats.py
theme.py
theme_list.py
version.py
wish.py
wish_list.py
wish_owner.py
wish_owner_list.py
docs
static
templates
.dockerignore
.env.sample
.gitignore
CHANGELOG.md
Dockerfile
LICENSE
README.md
__init__.py
app.py
compose.legacy.yml
compose.local.yaml
compose.yaml
entrypoint.sh
requirements.txt
test-server.sh
190 lines
4.7 KiB
Python
190 lines
4.7 KiB
Python
import logging
|
|
from sqlite3 import Error, OperationalError
|
|
import traceback
|
|
from typing import Any, Tuple
|
|
|
|
from flask import jsonify, redirect, request, render_template, url_for
|
|
from werkzeug.wrappers.response import Response
|
|
|
|
from ..exceptions import DatabaseException, ErrorException, NotFoundException
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# Get the cleaned exception
|
|
def cleaned_exception(e: Exception, /) -> str:
|
|
trace = traceback.TracebackException.from_exception(e)
|
|
|
|
cleaned: list[str] = []
|
|
|
|
# Hacky: stripped from the call to the decorator wrapper() or outer()
|
|
for line in trace.format():
|
|
if 'in wrapper' not in line and 'in outer' not in line:
|
|
cleaned.append(line)
|
|
|
|
return ''.join(cleaned)
|
|
|
|
|
|
# Generic error
|
|
def error(
|
|
error: Exception | None,
|
|
file: str,
|
|
/,
|
|
*,
|
|
json: bool = False,
|
|
post_redirect: str | None = None,
|
|
error_name: str = 'error',
|
|
**kwargs,
|
|
) -> str | Tuple[str | Response, int] | Response:
|
|
# Back to the index if no error (not sure if this can happen)
|
|
if error is None:
|
|
if json:
|
|
return json_error(
|
|
'error() called without an error',
|
|
error_name=error_name
|
|
)
|
|
else:
|
|
return redirect(url_for('index.index'))
|
|
|
|
# Convert SQLite errors
|
|
if isinstance(error, (Error, OperationalError)):
|
|
error = DatabaseException(error)
|
|
|
|
# Clear redirect if not POST or json
|
|
if json or request.method != 'POST':
|
|
post_redirect = None
|
|
|
|
# Not found
|
|
if isinstance(error, NotFoundException):
|
|
return error_404(
|
|
error,
|
|
json=json,
|
|
post_redirect=post_redirect,
|
|
error_name=error_name,
|
|
**kwargs
|
|
)
|
|
|
|
# Common error
|
|
elif isinstance(error, ErrorException):
|
|
# Error
|
|
logger.error('{title}: {error}'.format(
|
|
title=error.title,
|
|
error=str(error),
|
|
))
|
|
|
|
# Debug
|
|
logger.debug(cleaned_exception(error))
|
|
|
|
if json:
|
|
return json_error(
|
|
str(error),
|
|
error_name=error_name
|
|
)
|
|
elif post_redirect is not None:
|
|
return redirect_error(
|
|
post_redirect,
|
|
error=str(error),
|
|
error_name=error_name,
|
|
**kwargs,
|
|
)
|
|
else:
|
|
return render_template(
|
|
'error.html',
|
|
title=error.title,
|
|
error=str(error)
|
|
)
|
|
|
|
# Exception
|
|
else:
|
|
# Error
|
|
logger.error(cleaned_exception(error))
|
|
|
|
if error.__traceback__ is not None:
|
|
line = error.__traceback__.tb_lineno
|
|
else:
|
|
line = None
|
|
|
|
if json:
|
|
return json_error(
|
|
'Exception: {error}'.format(error=str(error)),
|
|
error_name=error_name,
|
|
name=type(error).__name__,
|
|
line=line,
|
|
file=file
|
|
), 500
|
|
elif post_redirect is not None:
|
|
return redirect_error(
|
|
post_redirect,
|
|
error=str(error),
|
|
error_name=error_name,
|
|
**kwargs,
|
|
)
|
|
else:
|
|
return render_template(
|
|
'exception.html',
|
|
error=str(error),
|
|
name=type(error).__name__,
|
|
line=line,
|
|
file=file,
|
|
)
|
|
|
|
|
|
# Error 404
|
|
def error_404(
|
|
error: Exception,
|
|
/,
|
|
*,
|
|
json: bool = False,
|
|
post_redirect: str | None = None,
|
|
error_name: str = 'error',
|
|
**kwargs,
|
|
) -> Response | Tuple[str | Response, int]:
|
|
# Warning
|
|
logger.warning('Not found: {error} (path: {path})'.format(
|
|
error=str(error),
|
|
path=request.path,
|
|
))
|
|
|
|
if json:
|
|
return json_error(
|
|
'Not found: {error}'.format(error=str(error)),
|
|
error_name=error_name
|
|
), 404
|
|
elif post_redirect is not None:
|
|
return redirect_error(
|
|
post_redirect,
|
|
error=str(error),
|
|
error_name=error_name,
|
|
**kwargs,
|
|
)
|
|
else:
|
|
return render_template('404.html', error=str(error)), 404
|
|
|
|
|
|
# JSON error with parametric error name
|
|
def json_error(
|
|
error: str,
|
|
error_name: str = 'error',
|
|
**parameters: Any
|
|
) -> Response:
|
|
parameters[error_name] = error
|
|
|
|
return jsonify(parameters)
|
|
|
|
|
|
# Redirect error with parametric error name
|
|
def redirect_error(
|
|
url: str,
|
|
error: str,
|
|
error_name: str = 'error',
|
|
**kwargs
|
|
) -> Response:
|
|
error_parameter: dict[str, str] = {}
|
|
error_parameter[error_name] = str(error)
|
|
|
|
return redirect(url_for(
|
|
url,
|
|
**error_parameter,
|
|
**kwargs
|
|
))
|