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 <alekratz@gmail.com>
This commit is contained in:
2023-08-02 20:16:47 -07:00
parent d3e3d99b12
commit d79899ea87
5 changed files with 71 additions and 17 deletions

View File

@@ -1,5 +1,6 @@
from pathlib import Path from pathlib import Path
import os import os
from typing import Sequence
from dotenv import load_dotenv from dotenv import load_dotenv
@@ -19,8 +20,16 @@ def required(name: str) -> str:
return value return value
def default(name: str, default: str) -> str: def default(name: str, default_value: str) -> str:
return os.getenv(name, default) 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")) 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")) DB_SQLITE3_PATH = Path(default("DB_SQLITE3_PATH", "bans.db"))
HTTP_DOMAIN = required("HTTP_DOMAIN") 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")

View File

@@ -15,14 +15,15 @@ from .db import get_db, search_db
def route_url(url: str): 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 return config.HTTP_ROOT
elif config.HTTP_ROOT in ("/" or ""):
return url
elif url.startswith("/"):
return f"{config.HTTP_ROOT}{url}"
else: 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( _env = Environment(
@@ -32,6 +33,7 @@ _env = Environment(
_env.globals.update( _env.globals.update(
{ {
"route_url": route_url, "route_url": route_url,
"static_url": static_url,
} }
) )
@@ -160,12 +162,23 @@ class IndexView(TemplateView):
app = web.Application() app = web.Application()
app.add_routes( app.add_routes(
[ [
web.get(route_url(r"/"), IndexView), web.get(config.HTTP_ROOT, IndexView),
web.get(route_url(r"/faq"), template_view_factory("faq.html")), web.get(f"{config.HTTP_ROOT}/faq", template_view_factory("faq.html")),
web.get(route_url(r"/news"), template_view_factory("news.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(): def run_app():
web.run_app(app) web.run_app(app)

View File

@@ -22,8 +22,10 @@ body {
} }
nav { nav {
/*
position: fixed; position: fixed;
top: 0; top: 0;
*/
width: 100%; width: 100%;
margin: 5px 0; margin: 5px 0;
text-align: left; text-align: left;
@@ -155,6 +157,9 @@ table tr {
{% endblock style %} {% endblock style %}
</head> </head>
<body> <body>
<nav>
[ <a href="{{route_url("/")}}">Home</a> / <a href="{{route_url("/news")}}">News</a> / <a href="{{route_url("/faq")}}">FAQ</a> ]
</nav>
{% block body %} {% block body %}
<header> <header>
{% block header %} {% block header %}
@@ -172,9 +177,6 @@ table tr {
</div> </div>
{% endblock header %} {% endblock header %}
</header> </header>
<nav>
[ <a href="{{route_url("/")}}">Home</a> / <a href="{{route_url("/news")}}">News</a> / <a href="{{route_url("/faq")}}">FAQ</a> ]
</nav>
<main> <main>
{% block main %} {% block main %}
{% endblock main %} {% endblock main %}

View File

@@ -50,7 +50,7 @@
</span> </span>
</div> </div>
<span class="file-thumb"> <span class="file-thumb">
<img src="{{post['thumb_path']}}" data-md5="{{post['md5']}}" loading="lazy"> <img src="{{static_url(post['thumb_path'])}}" data-md5="{{post['md5']}}" loading="lazy">
</span> </span>
</div> </div>
{% endif %} {% endif %}

View File

@@ -7,9 +7,32 @@
# Sqlite3 database path. Defaults to "bans.db" # Sqlite3 database path. Defaults to "bans.db"
#DB_SQLITE3_PATH=bans.db #DB_SQLITE3_PATH=bans.db
# Domain name of the server # Domain name of the server. This is required.
HTTP_DOMAIN=domain.com HTTP_DOMAIN=domain.com
# Root of the HTTP server. Defaults to "/" # 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. # If you host other things on your domain, you may want to update this to be "/bans" or something.
#HTTP_ROOT=/ #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