fix(FastAPI): add custom error templates for certain exceptions

Signed-off-by: Steven Guikal <void@fluix.one>
This commit is contained in:
Steven Guikal 2021-12-13 19:08:33 -05:00 committed by Kevin Morris
parent 51b60f4210
commit e126d431d7
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
4 changed files with 70 additions and 4 deletions

View file

@ -1,6 +1,7 @@
import asyncio import asyncio
import http import http
import os import os
import re
import sys import sys
import typing import typing
@ -9,18 +10,22 @@ from urllib.parse import quote_plus
from fastapi import FastAPI, HTTPException, Request, Response from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.responses import RedirectResponse from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from jinja2 import TemplateNotFound
from prometheus_client import multiprocess from prometheus_client import multiprocess
from sqlalchemy import and_, or_ from sqlalchemy import and_, or_
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.sessions import SessionMiddleware from starlette.middleware.sessions import SessionMiddleware
import aurweb.config import aurweb.config
import aurweb.logging import aurweb.logging
import aurweb.pkgbase.util as pkgbaseutil
from aurweb import prometheus, util from aurweb import prometheus, util
from aurweb.auth import BasicAuthBackend from aurweb.auth import BasicAuthBackend
from aurweb.db import get_engine, query from aurweb.db import get_engine, query
from aurweb.models import AcceptedTerm, Term from aurweb.models import AcceptedTerm, Term
from aurweb.packages.util import get_pkg_or_base
from aurweb.prometheus import instrumentator from aurweb.prometheus import instrumentator
from aurweb.routers import APP_ROUTES from aurweb.routers import APP_ROUTES
from aurweb.templates import make_context, render_template from aurweb.templates import make_context, render_template
@ -89,7 +94,7 @@ def child_exit(server, worker): # pragma: no cover
multiprocess.mark_process_dead(worker.pid) multiprocess.mark_process_dead(worker.pid)
@app.exception_handler(HTTPException) @app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: HTTPException) \ async def http_exception_handler(request: Request, exc: HTTPException) \
-> Response: -> Response:
""" Handle an HTTPException thrown in a route. """ """ Handle an HTTPException thrown in a route. """
@ -97,8 +102,24 @@ async def http_exception_handler(request: Request, exc: HTTPException) \
context = make_context(request, phrase) context = make_context(request, phrase)
context["exc"] = exc context["exc"] = exc
context["phrase"] = phrase context["phrase"] = phrase
return render_template(request, "errors/detail.html", context,
exc.status_code) # Additional context for some exceptions.
if exc.status_code == http.HTTPStatus.NOT_FOUND:
tokens = request.url.path.split("/")
matches = re.match("^([a-z0-9][a-z0-9.+_-]*?)(\\.git)?$", tokens[1])
if matches:
try:
pkgbase = get_pkg_or_base(matches.group(1))
context = pkgbaseutil.make_context(request, pkgbase)
except HTTPException:
pass
try:
return render_template(request, f"errors/{exc.status_code}.html",
context, exc.status_code)
except TemplateNotFound:
return render_template(request, "errors/detail.html",
context, exc.status_code)
@app.middleware("http") @app.middleware("http")

View file

@ -87,11 +87,14 @@ def provides_markup(provides: Providers) -> str:
def get_pkg_or_base( def get_pkg_or_base(
name: str, name: str,
cls: Union[models.Package, models.PackageBase] = models.PackageBase): cls: Union[models.Package, models.PackageBase] = models.PackageBase) \
-> Union[models.Package, models.PackageBase]:
""" Get a PackageBase instance by its name or raise a 404 if """ Get a PackageBase instance by its name or raise a 404 if
it can't be found in the database. it can't be found in the database.
:param name: {Package,PackageBase}.Name :param name: {Package,PackageBase}.Name
:param exception: Whether to raise an HTTPException or simply return None if
the package can't be found.
:raises HTTPException: With status code 404 if record doesn't exist :raises HTTPException: With status code 404 if record doesn't exist
:return: {Package,PackageBase} instance :return: {Package,PackageBase} instance
""" """

34
templates/errors/404.html Normal file
View file

@ -0,0 +1,34 @@
{% extends 'partials/layout.html' %}
{% block pageContent %}
<div id="error-page" class="box">
<h2>404 - {% trans %}Page Not Found{% endtrans %}</h2>
<p>{% trans %}Sorry, the page you've requested does not exist.{% endtrans %}</p>
{% if pkgbase %}
<ul>
{% set pkgname_strong="<strong>%s</strong>" | format(pkgbase.Name) %}
<li>
<strong>{% trans %}Note{% endtrans %}:</strong>
{% trans %}Git clone URLs are not meant to be opened in a browser.{% endtrans %}
</li>
<li>
{% set gitcmd="<code>git clone %s</code>" | format(git_clone_uri_anon | format(pkgbase.Name)) %}
{% if is_maintainer %}
{% set gitcmd="<code>git clone %s</code>" | format(git_clone_uri_priv | format(pkgbase.Name)) %}
{% endif %}
{{
"To clone the Git repository of %s, run %s."
| tr | format(pkgname_strong, gitcmd) | safe
}}
</li>
<li>
{% set pkglink='<a href="/pkgbase/%s">' | format(pkgbase.Name) %}
{{
"Click %shere%s to return to the %s details page."
| tr | format(pkglink, "</a>", pkgname_strong) | safe
}}
</li>
</ul>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,8 @@
{% extends 'partials/layout.html' %}
{% block pageContent %}
<div id="error-page" class="box">
<h2>503 - {% trans %}Service Unavailable{% endtrans %}</h2>
<p>{% trans %}Don't panic! This site is down due to maintenance. We will be back soon.{% endtrans %}</p>
</div>
{% endblock %}