from io import BytesIO import json import mimetypes import os from pathlib import Path from .utils import scramble class StaticFile: template_ext = ".tpl" template_filters = { b"tojson": lambda value: json.dumps(value) } def __init__(self, path): self.path = path self.mime_type = mimetypes.guess_type(self.remove_template_ext(str(path)))[0] self.mtime = None self.content = None def load_content(self): with open(self.path, "rb") as fh: return fh.read() def get(self, context_vars): content = self.load_content() if self.path.stat().st_mtime != self.mtime else self.content if self.path.suffix == self.template_ext: content = self.render(content, context_vars) self.content = content return content def render(self, content, context_vars): bio = BytesIO() pos = 0 while True: new_pos = content.find(b"{{", pos) if new_pos < 0: break bio.write(content[pos:new_pos]) end_pos = content.find(b"}}", new_pos) assert end_pos > 0 full_tag = content[new_pos + 2:end_pos].split(b"|", 1) value = context_vars[full_tag[0].strip().decode()] if len(full_tag) == 2: value = self.template_filters[full_tag[1].strip()](value) bio.write(value.encode()) pos = end_pos + 2 bio.write(content[pos:]) return bio.getvalue() def __str__(self): return f"" @classmethod def remove_template_ext(cls, filename): if filename.endswith(cls.template_ext): filename = filename[:-4] return filename class StaticResource: def __init__(self, secret, current_hubapp): mimetypes.init() self.static_files = {} self.context_vars = {} self.master_uri = "" self.setup(secret, current_hubapp) def setup(self, secret, current_hubapp): hubapp_dir = Path(__file__).parents[1] / "hubapps" / current_hubapp for file in os.listdir(hubapp_dir): uri_file = StaticFile.remove_template_ext(file) if uri_file.startswith("master."): uri = f"/{scramble(secret, uri_file)}" if uri_file == "master.html": self.master_uri = uri self.context_vars[uri_file.replace(".", "_")] = uri else: uri = f"/{uri_file}" self.static_files[uri] = StaticFile(hubapp_dir / file) if uri_file == "index.html": self.static_files["/"] = self.static_files[uri] async def on_get(self, req, resp): path = req.path index_uri = f"/index.html" if path == "/": path = index_uri res = self.static_files[path] resp.data = res.get(self.context_vars) resp.content_type = res.mime_type def add_routes(self, app): for uri_template in self.static_files: app.add_route(uri_template, self)