StarfallBot/starfall/web/__init__.py

102 lines
3.2 KiB
Python

import importlib
import logging
import os
from inspect import isclass
from pkgutil import iter_modules
from typing import final
from flask import Blueprint, Flask
from flask_assets import Bundle, Environment
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.config: Config | None = None
self.queue: SnapshotQueue | None = None
self.app: Flask | None = None
self.blueprint: Blueprint = Blueprint("main", __name__)
self.assets: Environment | None = None
self.server: Server | None = None
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.assets = Environment(self.app)
self.assets.load_path = ["web/static/scss", "web/static/css"]
scss = Bundle(
"main.scss",
"fonts.scss",
"bootstrap.min.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), "**/*.html"),
)
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)