From f9b821aaf197a96056f5df1e95020d2636a70b2a Mon Sep 17 00:00:00 2001
From: Martin Mares <mj@ucw.cz>
Date: Sun, 24 Jan 2021 23:29:35 +0100
Subject: [PATCH] =?UTF-8?q?Obecn=C3=BD=20modul=20na=20spr=C3=A1vu=20verzov?=
 =?UTF-8?q?an=C3=BDch=20statick=C3=BDch=20soubor=C5=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Pokusil jsem se napsat ho jako flaskovou extension, aby se dal používat
i v jiných projektech.
---
 mo/ext/__init__.py |  3 +++
 mo/ext/assets.py   | 62 ++++++++++++++++++++++++++++++++++++++++++++++
 setup.py           |  2 +-
 3 files changed, 66 insertions(+), 1 deletion(-)
 create mode 100644 mo/ext/__init__.py
 create mode 100644 mo/ext/assets.py

diff --git a/mo/ext/__init__.py b/mo/ext/__init__.py
new file mode 100644
index 00000000..76dd78e6
--- /dev/null
+++ b/mo/ext/__init__.py
@@ -0,0 +1,3 @@
+# mo.ext does nothing by itself
+
+pass
diff --git a/mo/ext/assets.py b/mo/ext/assets.py
new file mode 100644
index 00000000..97a9bcdd
--- /dev/null
+++ b/mo/ext/assets.py
@@ -0,0 +1,62 @@
+# Flask extension for versioned assets
+
+from flask import send_from_directory
+import hashlib
+import os
+from typing import Sequence, Dict
+
+
+class Assets:
+
+    asset_dir: str
+    asset_dict: Dict[str, str]
+    url_prefix: str
+
+    def __init__(self, app, url_prefix: str, asset_dir: str):
+        self.app = app
+        if app is not None:
+            self.init_app(app, url_prefix, asset_dir)
+
+    def init_app(self, app, url_prefix: str, asset_dir: str):
+        self.asset_folder = asset_dir
+        self.asset_dict = {}
+        self.url_prefix = url_prefix
+        self.app = app
+        app.jinja_env.globals.update(asset_url=lambda name: self.asset_url(name))
+        app.assets = self
+
+        # This is usually needed only for development, production requests are handled by upstream proxy
+        app.add_url_rule(
+            url_prefix + "/<version>/<path:name>",
+            endpoint="assets",
+            view_func=lambda version, name: self.send_asset(name),
+        )
+
+    def add_asset(self, name: str):
+        if name in self.asset_dict:
+            return
+
+        file_name = os.path.join(self.asset_folder, name)
+        digest = hashlib.sha1()
+
+        with open(file_name, 'rb') as file:
+            while True:
+                block = file.read(4096)
+                if not block:
+                    break
+                digest.update(block)
+
+        version = digest.hexdigest()[:8]
+        self.app.logger.debug(f'Assets: Loaded {name}: version {version}')
+        self.asset_dict[name] = version
+
+    def add_assets(self, names: Sequence[str]):
+        for name in names:
+            self.add_asset(name)
+
+    def asset_url(self, name: str) -> str:
+        assert name in self.asset_dict
+        return os.path.join(self.url_prefix, self.asset_dict[name], name)
+
+    def send_asset(self, name: str):
+        return send_from_directory(self.asset_folder, name)
diff --git a/setup.py b/setup.py
index 073ae8a1..5b7705e6 100644
--- a/setup.py
+++ b/setup.py
@@ -6,7 +6,7 @@ setuptools.setup(
     name='osmo',
     version='0.1',
     description='Odevzdávací systém Matematické olympiády',
-    packages=['mo', 'mo/jobs', 'mo/web'],
+    packages=['mo', 'mo/ext', 'mo/jobs', 'mo/web'],
     scripts=[
         'bin/add-role',
         'bin/create-contests',
-- 
GitLab