From d79899ea87d93a088b615c717f345fb786e514ac Mon Sep 17 00:00:00 2001 From: Alek Ratzloff Date: Wed, 2 Aug 2023 20:16:47 -0700 Subject: [PATCH] http: Add static file support Static files are configured using a handful of config values. Static files can be handled remotely or locally for prod or dev environments, respectively. Signed-off-by: Alek Ratzloff --- chanbans/config.py | 22 +++++++++++++++++++--- chanbans/http.py | 31 ++++++++++++++++++++++--------- chanbans/templates/base.html | 8 +++++--- chanbans/templates/index.html | 2 +- example.env | 25 ++++++++++++++++++++++++- 5 files changed, 71 insertions(+), 17 deletions(-) diff --git a/chanbans/config.py b/chanbans/config.py index 7a12755..122f418 100644 --- a/chanbans/config.py +++ b/chanbans/config.py @@ -1,5 +1,6 @@ from pathlib import Path import os +from typing import Sequence from dotenv import load_dotenv @@ -19,8 +20,16 @@ def required(name: str) -> str: return value -def default(name: str, default: str) -> str: - return os.getenv(name, default) +def default(name: str, default_value: str) -> str: + return os.getenv(name, default_value) + + +def one_of_default(name: str, values: Sequence[str], default_value: str) -> str: + value = default(name, default_value) + if value not in values: + required = ",".join(f'"{v}"' for v in values) + raise ConfigError(name, f"must be one of {required}; got {value} instead") + return value THUMBS_DIR = Path(default("THUMBS_DIR", "thumbs")) @@ -29,4 +38,11 @@ CACHE_DIR = Path(default("CACHE_DIR", "cache")) DB_SQLITE3_PATH = Path(default("DB_SQLITE3_PATH", "bans.db")) HTTP_DOMAIN = required("HTTP_DOMAIN") -HTTP_ROOT = default("HTTP_ROOT", r"/") +HTTP_ROOT = default("HTTP_ROOT", r"") + +STATIC_HANDLER = one_of_default("STATIC_HANDLER", ("remote", "local"), "local") +STATIC_LOCAL_PATH = Path(default("STATIC_LOCAL_PATH", "static")) +STATIC_LOCAL_FOLLOW_SYMLINKS = one_of_default( + "STATIC_LOCAL_FOLLOW_SYMLINKS", ("yes", "true", "1", "no", "false", "0"), "true" +) in ("yes", "true", "1") +STATIC_ROOT = default("STATIC_ROOT", "/static") diff --git a/chanbans/http.py b/chanbans/http.py index 38805a6..ba8b235 100644 --- a/chanbans/http.py +++ b/chanbans/http.py @@ -15,14 +15,15 @@ from .db import get_db, search_db def route_url(url: str): - if url in ("/" or ""): + assert url == "" or url[0] == "/", "URL must be blank or start with a slash" + if url in ("", "/"): return config.HTTP_ROOT - elif config.HTTP_ROOT in ("/" or ""): - return url - elif url.startswith("/"): - return f"{config.HTTP_ROOT}{url}" else: - return f"{config.HTTP_ROOT}/{url}" + return f"{config.HTTP_ROOT}{url}" + + +def static_url(resource: str): + return f"{config.STATIC_ROOT}/{resource}" _env = Environment( @@ -32,6 +33,7 @@ _env = Environment( _env.globals.update( { "route_url": route_url, + "static_url": static_url, } ) @@ -160,12 +162,23 @@ class IndexView(TemplateView): app = web.Application() app.add_routes( [ - web.get(route_url(r"/"), IndexView), - web.get(route_url(r"/faq"), template_view_factory("faq.html")), - web.get(route_url(r"/news"), template_view_factory("news.html")), + web.get(config.HTTP_ROOT, IndexView), + web.get(f"{config.HTTP_ROOT}/faq", template_view_factory("faq.html")), + web.get(f"{config.HTTP_ROOT}/news", template_view_factory("news.html")), ] ) +if config.STATIC_HANDLER == "local": + app.add_routes( + [ + web.static( + config.STATIC_ROOT, + config.STATIC_LOCAL_PATH, + follow_symlinks=config.STATIC_LOCAL_FOLLOW_SYMLINKS, + ) + ] + ) + def run_app(): web.run_app(app) diff --git a/chanbans/templates/base.html b/chanbans/templates/base.html index c115d2c..2168df3 100644 --- a/chanbans/templates/base.html +++ b/chanbans/templates/base.html @@ -22,8 +22,10 @@ body { } nav { + /* position: fixed; top: 0; + */ width: 100%; margin: 5px 0; text-align: left; @@ -155,6 +157,9 @@ table tr { {% endblock style %} + {% block body %}
{% block header %} @@ -172,9 +177,6 @@ table tr { {% endblock header %}
-
{% block main %} {% endblock main %} diff --git a/chanbans/templates/index.html b/chanbans/templates/index.html index d1a95fc..44b26a4 100644 --- a/chanbans/templates/index.html +++ b/chanbans/templates/index.html @@ -50,7 +50,7 @@ - + {% endif %} diff --git a/example.env b/example.env index b612346..7170322 100644 --- a/example.env +++ b/example.env @@ -7,9 +7,32 @@ # Sqlite3 database path. Defaults to "bans.db" #DB_SQLITE3_PATH=bans.db -# Domain name of the server +# Domain name of the server. This is required. HTTP_DOMAIN=domain.com # Root of the HTTP server. Defaults to "/" # If you host other things on your domain, you may want to update this to be "/bans" or something. #HTTP_ROOT=/ + +# Static files handler. Defaults to "local" +# Valid values are: +# * local +# * remote +# Locally handled static files use the STATIC_LOCAL_PATH config value. +# IN GENERAL, the "local" configuration should not be used in a production environment. +#STATIC_HANDLER=local + +# The local path to serve static files out of. Defaults to "static" +# This is only used when STATIC_HANDLER is set to "local". +#STATIC_LOCAL_PATH=static + +# Whether to follow symlinks in the local static file handler. Defaults to "true" +# This is only used when STATIC_HANDLER is set to "local". +# Must be one of: +# * 1, true, yes +# * 0, false, no +#STATIC_LOCAL_FOLLOW_SYMLINKS=true + +# This is the URL to map static files to. Defaults to /static +# This *WILL NOT* prepend the HTTP_ROOT path. +#STATIC_ROOT=/static