import importlib import logging import os from inspect import isclass from pkgutil import iter_modules from typing import final from flask import Blueprint, Flask, g, request from flask_assets import Bundle, Environment from flask_babel import Babel from livereload import Server from starfall.config import Config from starfall.db import db, load_schema from starfall.types import SnapshotQueue from starfall.web.blueprints.base import BaseBlueprint @final class WebUI: def __init__(self): self.app: Flask | None = None self.assets: Environment | None = None self.babel: Babel | None = None self.blueprint: Blueprint = Blueprint("main", __name__) self.config: Config | None = None self.queue: SnapshotQueue | None = None self.server: Server | None = None def select_locale(self): # user = getattr(g, "user", None) # if user is not None: # return user.locale return request.accept_languages.best_match(["en", "de"]) def select_timezone(self): # user = getattr(g, "user", None) # if user is not None: # return user.timezone pass def run(self, config: Config, queue: SnapshotQueue): self.config = config self.queue = queue logging.getLogger("web").debug("Hello from %r", type(self)) self.app = Flask( import_name=__name__, root_path=os.path.realpath("."), static_folder=os.path.realpath("./web/static"), template_folder="web/templates", ) self.app.config.update( SECRET_KEY=str(self.config.get("web.secret_key")), SQLALCHEMY_DATABASE_URI=str(self.config.get("web.database_url")), TEMPLATES_AUTO_RELOAD=True, ) self.app.jinja_env.auto_reload = True self.babel = Babel( self.app, locale_selector=self.select_locale, timezone_selector=self.select_timezone, ) self.assets = Environment(self.app) self.assets.load_path = ["web/static/scss", "web/static/css"] scss = Bundle( "bootstrap/bootstrap.scss", "bootstrap/bootstrap-utilities.scss", "bootstrap/bootstrap-reboot.scss", "bootstrap/bootstrap-grid.scss", "bootstrap/bootstrap-icons.scss", "starfall/main.scss", filters="libsass", output="css/main.css", depends=["**/*.scss"], ) _ = self.assets.register("scss", scss) db.init_app(self.app) load_schema() with self.app.app_context(): db.create_all() self.import_blueprints() self.app.register_blueprint( self.blueprint, options={"queue": self.queue}, ) self.server = Server(self.app.wsgi_app) self.server.watch( filepath=os.path.join(str(self.app.template_folder), "**/*.jinja"), ) self.server.watch( filepath=os.path.join(str(self.app.static_folder), "**/*.js"), ) self.server.watch( filepath=os.path.join(str(self.app.static_folder), "**/*.scss"), ) self.server.serve( host=self.config.get("web.host"), port=self.config.get("web.port"), ) def import_blueprints(self): path = os.path.realpath(os.path.dirname(__file__) + os.sep + "blueprints") for _, module_name, _ in iter_modules([path], f"{__name__}.blueprints."): if module_name.endswith("base"): continue logging.getLogger("web").debug("Parsing module: %s" % module_name) module = importlib.import_module(module_name) for attribute_name in dir(module): attribute = getattr(module, attribute_name) if isclass(attribute) and issubclass(attribute, BaseBlueprint): globals()[attribute_name] = attribute globals()[attribute_name](self.blueprint, self.assets, self.app)