]> git.mar77i.info Git - hublib/commitdiff
polish structure, update requirements master
authormar77i <mar77i@protonmail.ch>
Sun, 3 Mar 2024 23:53:01 +0000 (00:53 +0100)
committermar77i <mar77i@protonmail.ch>
Sun, 3 Mar 2024 23:53:01 +0000 (00:53 +0100)
hub/app.py
hub/hubapp.py
hub/utils.py
hub/websocket.py
requirements.txt

index 2a0a712f515c6984a53f56ff562094c14433508d..22a7cd481a6db610c1f1417fe2abd093c6887d78 100644 (file)
@@ -1,6 +1,8 @@
 import socket
 import sys
 from argparse import ArgumentParser
+from base64 import urlsafe_b64encode
+from hashlib import pbkdf2_hmac
 from itertools import chain
 from pathlib import Path
 from secrets import token_urlsafe
@@ -23,7 +25,7 @@ class App(FalconApp):
         self.base_dir = Path(__file__).parents[1] / "webroot"
         self.env = Environment(
             loader=FileSystemLoader(self.base_dir),
-            autoescape=select_autoescape,
+            autoescape=select_autoescape(),
             extensions=["hub.utils.StaticTag"],
         )
         self.secret = secret or token_urlsafe(64)
@@ -35,8 +37,15 @@ class App(FalconApp):
             HubApp(self, base_dir)
         self.add_error_handler(Exception, self.print_exception)
 
-    def get_hubapp_by_name(self, name):
-        return self.hubapps[name]
+    def scramble(self, value):
+        if isinstance(value, str):
+            value = value.encode()
+        secret = self.secret
+        if isinstance(secret, str):
+            secret = secret.encode()
+        return urlsafe_b64encode(
+            pbkdf2_hmac("sha512", value, secret, 221100)
+        ).rstrip(b"=").decode("ascii")
 
     async def print_exception(self, req, resp, ex, params):
         print_exception(*sys.exc_info())
index 91c9c5434847d02adebf85bccf30810a844cc455..f3c457ce04acfd434ad940be6c88846307ab64f7 100644 (file)
@@ -2,13 +2,12 @@ from pathlib import Path
 
 from falcon.constants import MEDIA_HTML, MEDIA_JS, MEDIA_TEXT
 from falcon.status_codes import HTTP_OK
+from jinja2 import Template
 
 from .websocket import WebSocketHub
-from .utils import scramble
 
 MEDIA_CSS = "text/css"
 
-
 class StaticFile:
     media_types_per_suffix = {
         ".html": MEDIA_HTML,
@@ -16,6 +15,7 @@ class StaticFile:
         ".css": MEDIA_CSS,
         ".txt": MEDIA_TEXT,
     }
+    content: str | None
 
     def __init__(self, path):
         self.path = path
@@ -41,17 +41,22 @@ class StaticFile:
 
 class StaticTemplateFile(StaticFile):
     TEMPLATE_SUFFIX = ".j2"
+    content: Template | None
 
     def __init__(self, path, hubapp):
         super().__init__(path)
         self.hubapp = hubapp
         self.context = {"hubapp": self.hubapp}
-        self.template = hubapp.app.env.get_template(
-            str(path.relative_to(hubapp.app.env.loader.searchpath[0]))
-        )
 
-    def get(self):
-        return self.template.render(self.context)
+    def get(self) -> str:
+        mtime = self.path.stat().st_mtime
+        if mtime != self.mtime:
+            env = self.hubapp.app.env
+            self.content = env.get_template(
+                str(self.path.relative_to(env.loader.searchpath[0]))
+            )
+            self.mtime = mtime
+        return self.content.render(self.context)
 
     @classmethod
     def get_media_type(cls, path):
@@ -83,25 +88,28 @@ class BaseHubApp:
             or "master" in uri_tail[:start].split(slash)
         )
 
-    def uri_from(self, path):
-        if isinstance(path, Path) and path.is_absolute():
-            uri_tail = str(path.relative_to(self.base_dir))
+    def _uri_tail(self, path, suffix):
+        if isinstance(path, Path):
+            if path.is_absolute():
+                path = path.relative_to(self.base_dir)
+            uri_tail = str(path)
         else:
             uri_tail = str(path)
-        suffix = StaticTemplateFile.TEMPLATE_SUFFIX
         if uri_tail.endswith(suffix):
             uri_tail = uri_tail[:-len(suffix)]
 
         if self.is_master_uri(uri_tail):
-            uri_tail = scramble(self.app.secret, uri_tail)
+            return self.app.scramble(uri_tail)
         elif uri_tail == "index.html":
-            uri_tail = ""
+            return ""
+        return uri_tail
+
+    def uri_from(self, path) -> str | Path:
+        uri_tail = self._uri_tail(path, StaticTemplateFile.TEMPLATE_SUFFIX)
         name = self.name
         if name == "root":
             name = ""
-        if name and uri_tail:
-            name = f"{name}/"
-        return f"/{name}{uri_tail}"
+        return f"/{name}{'/' if name and uri_tail else ''}{uri_tail}"
 
     def scan_files(self, base_dir):
         stack = [base_dir.iterdir()]
index ae1fd1242684f692ccec08e4293fcaba6807214e..ae448ed8fb499a14c3c9ea486fcc1e59252fbe42 100644 (file)
@@ -1,5 +1,3 @@
-from base64 import urlsafe_b64encode
-from hashlib import pbkdf2_hmac
 from pathlib import Path
 
 from jinja2_simple_tags import StandaloneTag
@@ -12,20 +10,10 @@ class StaticTag(StandaloneTag):
         if not hubapp:
             hubapp = self.context["hubapp"]
         elif isinstance(hubapp, str):
-            hubapp = self.context["hubapp"].app.get_hubapp_by_name(hubapp)
+            hubapp = self.context["hubapp"].app.hubapps[hubapp]
         return hubapp.uri_from(Path(filename))
 
 
-def scramble(secret, value):
-    if isinstance(value, str):
-        value = value.encode()
-    if isinstance(secret, str):
-        secret = secret.encode()
-    return urlsafe_b64encode(
-        pbkdf2_hmac("sha512", value, secret, 221100)
-    ).rstrip(b"=").decode("ascii")
-
-
 def get_redis_pass(redis_conf):
     prefix = "requirepass "
     with open(redis_conf, "rt") as fh:
index 3d511168968e8386898623b9d70c6f84eba077fe..89f11928d97938ee9bd5be669ecafed79fd823ce 100644 (file)
@@ -13,14 +13,13 @@ from .utils import get_redis_pass
 
 
 class BaseWebSocketHub:
-    first = True
     client_ids_sem = asyncio.Semaphore(0)
 
     @classmethod
-    def __class_init(cls, redis):
-        if not cls.first:
+    def _class_init(cls, redis):
+        if not hasattr(cls, "_class_init"):
             return
-        cls.first = False
+        delattr(cls, "_class_init")
         asyncio.create_task(cls.initialize_client_ids(redis))
 
     @classmethod
@@ -30,7 +29,8 @@ class BaseWebSocketHub:
 
     def __init__(self):
         self.redis = StrictRedis(password=get_redis_pass("/etc/redis/redis.conf"))
-        self.__class_init(self.redis)
+        if hasattr(BaseWebSocketHub, "_class_init"):
+            BaseWebSocketHub._class_init(self.redis)
 
     def task_done(self):
         self.task = None
index a9f2dd912a66fd6277982fb9c4ca91677cd5f8ff..9646ad03f5e5e3621a82c0da82a745b65851dfc6 100644 (file)
@@ -1,23 +1,24 @@
 anyio==4.2.0
-black==23.12.1
+async-timeout==4.0.3
+black==24.2.0
 click==8.1.7
 falcon==3.1.3
 h11==0.14.0
 httptools==0.6.1
 idna==3.6
 isort==5.13.2
-Jinja2==3.1.2
+Jinja2==3.1.3
 jinja2-simple-tags==0.5.0
 MarkupSafe==2.1.3
 mypy-extensions==1.0.0
 packaging==23.2
 pathspec==0.12.1
 platformdirs==4.1.0
-python-dotenv==1.0.0
+python-dotenv==1.0.1
 PyYAML==6.0.1
-redis==5.0.1
+redis==5.0.2
 sniffio==1.3.0
-uvicorn==0.25.0
+uvicorn==0.27.1
 uvloop==0.19.0
 watchfiles==0.21.0
 websockets==12.0