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 http
import os
import re
import sys
import typing
@ -9,18 +10,22 @@ from urllib.parse import quote_plus
from fastapi import FastAPI, HTTPException, Request, Response
from fastapi.responses import RedirectResponse
from fastapi.staticfiles import StaticFiles
from jinja2 import TemplateNotFound
from prometheus_client import multiprocess
from sqlalchemy import and_, or_
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.middleware.authentication import AuthenticationMiddleware
from starlette.middleware.sessions import SessionMiddleware
import aurweb.config
import aurweb.logging
import aurweb.pkgbase.util as pkgbaseutil
from aurweb import prometheus, util
from aurweb.auth import BasicAuthBackend
from aurweb.db import get_engine, query
from aurweb.models import AcceptedTerm, Term
from aurweb.packages.util import get_pkg_or_base
from aurweb.prometheus import instrumentator
from aurweb.routers import APP_ROUTES
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)
@app.exception_handler(HTTPException)
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: HTTPException) \
-> Response:
""" 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["exc"] = exc
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")

View file

@ -87,11 +87,14 @@ def provides_markup(provides: Providers) -> str:
def get_pkg_or_base(
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
it can't be found in the database.
: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
: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 %}