You've already forked snikket-web-portal
snikket_web can now be fully configured via the environment alone, no extra files needed. It is still supported to inject a python file to generate environment variables though, which may be useful for generating and reloading a secret key.
114 lines
3.0 KiB
Python
114 lines
3.0 KiB
Python
import base64
|
|
import binascii
|
|
import logging
|
|
import os
|
|
import pathlib
|
|
import typing
|
|
|
|
import quart.flask_patch
|
|
|
|
import quart
|
|
from quart import (
|
|
url_for,
|
|
)
|
|
|
|
import environ
|
|
|
|
from . import colour, infra
|
|
from ._version import version, version_info # noqa:F401
|
|
|
|
|
|
def proc() -> typing.Dict[str, typing.Any]:
|
|
def url_for_avatar(entity: str, hash_: str,
|
|
**kwargs: typing.Any) -> str:
|
|
return url_for(
|
|
"main.avatar",
|
|
from_=base64.urlsafe_b64encode(
|
|
entity.encode("utf-8"),
|
|
).decode("ascii").rstrip("="),
|
|
code=base64.urlsafe_b64encode(
|
|
binascii.a2b_hex(hash_)[:8],
|
|
).decode("ascii").rstrip("="),
|
|
**kwargs
|
|
)
|
|
|
|
return {
|
|
"url_for_avatar": url_for_avatar,
|
|
"text_to_css": colour.text_to_css,
|
|
"lang": infra.selected_locale(),
|
|
}
|
|
|
|
|
|
def autosplit(s: typing.Union[str, typing.List[str]]) -> typing.List[str]:
|
|
if isinstance(s, str):
|
|
return s.split()
|
|
return s
|
|
|
|
|
|
@environ.config(prefix="SNIKKET_WEB")
|
|
class AppConfig:
|
|
secret_key = environ.var()
|
|
prosody_endpoint = environ.var()
|
|
domain = environ.var()
|
|
avatar_cache_ttl = environ.var(1800, converter=int)
|
|
languages = environ.var(["de", "en"], converter=autosplit)
|
|
|
|
|
|
_UPPER_CASE = "".join(map(chr, range(ord("A"), ord("Z")+1)))
|
|
|
|
|
|
def create_app() -> quart.Quart:
|
|
try:
|
|
env_init = os.environ["SNIKKET_WEB_PYENV"]
|
|
except KeyError:
|
|
pass
|
|
else:
|
|
import runpy
|
|
init_vars = runpy.run_path(env_init)
|
|
for name, value in init_vars.items():
|
|
if not name:
|
|
continue
|
|
if name[0] not in _UPPER_CASE:
|
|
continue
|
|
os.environ[name] = value
|
|
|
|
config = environ.to_config(AppConfig)
|
|
|
|
app = quart.Quart(__name__)
|
|
app.config["LANGUAGES"] = config.languages
|
|
app.config["SECRET_KEY"] = config.secret_key
|
|
app.config["PROSODY_ENDPOINT"] = config.prosody_endpoint
|
|
app.config["SNIKKET_DOMAIN"] = config.domain
|
|
app.config["AVATAR_CACHE_TTL"] = config.avatar_cache_ttl
|
|
|
|
app.context_processor(proc)
|
|
|
|
logging_config = app.config.get("LOGGING_CONFIG")
|
|
if logging_config is not None:
|
|
if isinstance(logging_config, dict):
|
|
logging.config.dictConfig(logging_config)
|
|
elif isinstance(logging_config, (bytes, str, pathlib.Path)):
|
|
import toml
|
|
with open(logging_config, "r") as f:
|
|
logging_config = toml.load(f)
|
|
logging.config.dictConfig(logging_config)
|
|
|
|
else:
|
|
logging.basicConfig(level=logging.WARNING)
|
|
if app.debug:
|
|
logging.getLogger("snikket_web").setLevel(logging.DEBUG)
|
|
|
|
infra.babel.init_app(app)
|
|
infra.client.init_app(app)
|
|
infra.init_templating(app)
|
|
|
|
from .main import bp as main_bp
|
|
from .user import bp as user_bp
|
|
from .admin import bp as admin_bp
|
|
|
|
app.register_blueprint(main_bp)
|
|
app.register_blueprint(user_bp, url_prefix="/user")
|
|
app.register_blueprint(admin_bp, url_prefix="/admin")
|
|
|
|
return app
|