mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
port over base HTML layout from PHP to FastAPI+Jinja2
+ Mounted static files (at web/html) to /static. + Added AURWEB_VERSION to aurweb.config (this is used around HTML to refer back to aurweb's release on git.archlinux.org), so we need it easily accessible in the Python codebase. + Implemented basic Jinja2 partials to put together whole aurweb pages. This may be missing some things currently and is a WIP until this set is ready to be merged. + Added config [options] aurwebdir = YOUR_AUR_ROOT; this configuration option should specify the root directory of the aurweb project. It is used by various parts of the FastAPI codebase to target project directories. Added routes via aurweb.routers.html: * POST /language: Set your session language. * GET /favicon.ico: Redirect to /static/images/favicon.ico. * Some browsers always look for $ROOT/favicon.ico to get an icon for the page being loaded, regardless of a specified "shortcut icon" given in a <link> directive. * GET /: Home page; WIP. * Updated aurweb.routers.html.language passes query parameters to its next redirection. When calling aurweb.templates.render_template, the context passed should be formed via the aurweb.templates.make_context. See aurweb.routers.html.index for an example of this. Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
1ff822bb14
commit
2df90ce280
18 changed files with 339 additions and 2 deletions
|
@ -2,13 +2,26 @@ import http
|
|||
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from starlette.middleware.sessions import SessionMiddleware
|
||||
|
||||
import aurweb.config
|
||||
|
||||
from aurweb.routers import sso
|
||||
from aurweb.routers import html, sso
|
||||
|
||||
routes = set()
|
||||
|
||||
# Setup the FastAPI app.
|
||||
app = FastAPI()
|
||||
app.mount("/static/css",
|
||||
StaticFiles(directory="web/html/css"),
|
||||
name="static_css")
|
||||
app.mount("/static/js",
|
||||
StaticFiles(directory="web/html/js"),
|
||||
name="static_js")
|
||||
app.mount("/static/images",
|
||||
StaticFiles(directory="web/html/images"),
|
||||
name="static_images")
|
||||
|
||||
session_secret = aurweb.config.get("fastapi", "session_secret")
|
||||
if not session_secret:
|
||||
|
@ -17,6 +30,14 @@ if not session_secret:
|
|||
app.add_middleware(SessionMiddleware, secret_key=session_secret)
|
||||
|
||||
app.include_router(sso.router)
|
||||
app.include_router(html.router)
|
||||
|
||||
# NOTE: Always keep this dictionary updated with all routes
|
||||
# that the application contains. We use this to check for
|
||||
# parameter value verification.
|
||||
routes = {route.path for route in app.routes}
|
||||
routes.update({route.path for route in sso.router.routes})
|
||||
routes.update({route.path for route in html.router.routes})
|
||||
|
||||
|
||||
@app.exception_handler(HTTPException)
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
import configparser
|
||||
import os
|
||||
|
||||
# Publicly visible version of aurweb. This is used to display
|
||||
# aurweb versioning in the footer and must be maintained.
|
||||
# Todo: Make this dynamic/automated.
|
||||
AURWEB_VERSION = "v5.0.0"
|
||||
|
||||
_parser = None
|
||||
|
||||
|
||||
|
|
50
aurweb/routers/html.py
Normal file
50
aurweb/routers/html.py
Normal file
|
@ -0,0 +1,50 @@
|
|||
""" AURWeb's primary routing module. Define all routes via @app.app.{get,post}
|
||||
decorators in some way; more complex routes should be defined in their
|
||||
own modules and imported here. """
|
||||
from http import HTTPStatus
|
||||
from urllib.parse import unquote
|
||||
|
||||
from fastapi import APIRouter, Form, Request
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||
|
||||
from aurweb.templates import make_context, render_template
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/favicon.ico")
|
||||
async def favicon(request: Request):
|
||||
""" Some browsers attempt to find a website's favicon via root uri at
|
||||
/favicon.ico, so provide a redirection here to our static icon. """
|
||||
return RedirectResponse("/static/images/favicon.ico")
|
||||
|
||||
|
||||
@router.post("/language", response_class=RedirectResponse)
|
||||
async def language(request: Request,
|
||||
set_lang: str = Form(...),
|
||||
next: str = Form(...),
|
||||
q: str = Form(default=None)):
|
||||
""" A POST route used to set a session's language.
|
||||
|
||||
Return a 303 See Other redirect to {next}?next={next}. If we are
|
||||
setting the language on any page, we want to preserve query
|
||||
parameters across the redirect.
|
||||
"""
|
||||
from aurweb.asgi import routes
|
||||
if unquote(next) not in routes:
|
||||
return HTMLResponse(
|
||||
b"Invalid 'next' parameter.",
|
||||
status_code=400)
|
||||
|
||||
query_string = "?" + q if q else str()
|
||||
response = RedirectResponse(url=f"{next}{query_string}",
|
||||
status_code=int(HTTPStatus.SEE_OTHER))
|
||||
response.set_cookie("AURLANG", set_lang)
|
||||
return response
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def index(request: Request):
|
||||
""" Homepage route. """
|
||||
context = make_context(request, "Home")
|
||||
return render_template("index.html", context)
|
57
aurweb/templates.py
Normal file
57
aurweb/templates.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
import copy
|
||||
import os
|
||||
|
||||
from datetime import datetime
|
||||
from http import HTTPStatus
|
||||
|
||||
import jinja2
|
||||
|
||||
from fastapi import Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
import aurweb.config
|
||||
|
||||
from aurweb import l10n
|
||||
|
||||
# Prepare jinja2 objects.
|
||||
loader = jinja2.FileSystemLoader(os.path.join(
|
||||
aurweb.config.get("options", "aurwebdir"), "templates"))
|
||||
env = jinja2.Environment(loader=loader, autoescape=True,
|
||||
extensions=["jinja2.ext.i18n"])
|
||||
|
||||
# Add tr translation filter.
|
||||
env.filters["tr"] = l10n.tr
|
||||
|
||||
|
||||
def make_context(request: Request, title: str, next: str = None):
|
||||
""" Create a context for a jinja2 TemplateResponse. """
|
||||
|
||||
return {
|
||||
"request": request,
|
||||
"language": l10n.get_request_language(request),
|
||||
"languages": l10n.SUPPORTED_LANGUAGES,
|
||||
"title": title,
|
||||
# The 'now' context variable will not show proper datetimes
|
||||
# until we've implemented timezone support here.
|
||||
"now": datetime.now(),
|
||||
"config": aurweb.config,
|
||||
"next": next if next else request.url.path
|
||||
}
|
||||
|
||||
|
||||
def render_template(path: str, context: dict, status_code=int(HTTPStatus.OK)):
|
||||
""" Render a Jinja2 multi-lingual template with some context. """
|
||||
|
||||
# Create a deep copy of our jinja2 environment. The environment in
|
||||
# total by itself is 48 bytes large (according to sys.getsizeof).
|
||||
# This is done so we can install gettext translations on the template
|
||||
# environment being rendered without installing them into a global
|
||||
# which is reused in this function.
|
||||
templates = copy.copy(env)
|
||||
|
||||
translator = l10n.get_raw_translator_for_request(context.get("request"))
|
||||
templates.install_gettext_translations(translator)
|
||||
|
||||
template = templates.get_template(path)
|
||||
rendered = template.render(context)
|
||||
return HTMLResponse(rendered, status_code=status_code)
|
Loading…
Add table
Add a link
Reference in a new issue