changeset 1246:aaf28d45ae67

pages: browser code, first draft: - code for browser can now be specified if a page subdirectory named `_browser` exists - the default engine used is `Brython` (not yet implemented, so it will do nothing for now) - build_path now uses C.SITE_NAME_DEFAULT for default site instead of empty string, to avoid collision with other sites - ProtectedFile transtype path (first positional argument) to str, so a pathlib.Path can be used
author Goffi <goffi@goffi.org>
date Sun, 26 Apr 2020 22:01:13 +0200
parents 079e8eb6e327
children a6c7f07f1e4d
files libervia/server/constants.py libervia/server/pages.py libervia/server/server.py
diffstat 3 files changed, 58 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/server/constants.py	Sat Apr 25 16:08:55 2020 +0200
+++ b/libervia/server/constants.py	Sun Apr 26 22:01:13 2020 +0200
@@ -35,7 +35,9 @@
     PAGES_DIR = "pages"
     TASKS_DIR = "tasks"
     LIBERVIA_CACHE = "libervia"
+    SITE_NAME_DEFAULT = "default"
     BUILD_DIR = "__b"
+    BUILD_DIR_DYN = "dyn"
 
     TPL_RESOURCE = '_t'
 
@@ -57,6 +59,7 @@
 
     ## Libervia pages ##
     PAGES_META_FILE = "page_meta.py"
+    PAGES_BROWSER_DIR = "_browser"
     PAGES_ACCESS_NONE = (
         "none"
     )  #  no access to this page (using its path will return a 404 error)
--- a/libervia/server/pages.py	Sat Apr 25 16:08:55 2020 +0200
+++ b/libervia/server/pages.py	Sun Apr 26 22:01:13 2020 +0200
@@ -16,6 +16,16 @@
 
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import uuid
+import os.path
+import urllib.request, urllib.parse, urllib.error
+import time
+import hashlib
+import copy
+from functools import reduce
+from pathlib import Path
+
 from twisted.web import server
 from twisted.web import resource as web_resource
 from twisted.web import util as web_util
@@ -34,14 +44,6 @@
 from libervia.server.utils import quote, SubPage
 from libervia.server.classes import WebsocketMeta
 
-import uuid
-import os.path
-import urllib.request, urllib.parse, urllib.error
-import time
-import hashlib
-import copy
-from functools import reduce
-
 log = getLogger(__name__)
 
 
@@ -173,6 +175,7 @@
         self.root_dir = root_dir
         self.url = url
         self.name = name
+        self.dyn_data = {}
         if name is not None:
             if (name in self.named_pages
                 and not (replace_on_conflict and self.named_pages[name].url == url)):
@@ -331,7 +334,8 @@
             if os.path.isfile(meta_path):
                 new_path = _path + [d]
                 try:
-                    page_data, resource = cls.createPage(host, meta_path, vhost_root, new_path)
+                    page_data, resource = cls.createPage(
+                        host, meta_path, vhost_root, new_path)
                 except exceptions.ConflictError as e:
                     if _extra_pages:
                         # extra pages are discarded if there is already an existing page
@@ -369,6 +373,18 @@
                 LiberviaPage.importPages(
                     host, vhost_root, _parent=resource, _path=new_path,
                     _extra_pages=_extra_pages)
+                # now we check if there is some code for browser
+                browser_path = Path(dir_path) / C.PAGES_BROWSER_DIR
+                if browser_path.is_dir():
+                    # for now we only handle Brython
+                    dyn_data = {
+                        "path": browser_path,
+                        "url_hash": hashlib.sha256(
+                            '/'.join(new_path).encode()).hexdigest(),
+                    }
+                    vhost_root.browser_modules.setdefault(
+                        "brython", []).append(dyn_data)
+                    resource.dyn_data['brython'] = dyn_data
 
     @classmethod
     def onFileChange(cls, host, file_path, flags, site_root, site_path):
@@ -1257,6 +1273,16 @@
             template_data['locale'] = session_data.locale
         if self.vhost_root.site_name:
             template_data['site'] = self.vhost_root.site_name
+        if self.dyn_data:
+            for data in self.dyn_data.values():
+                template_data_dyn = data.get('template', {})
+                try:
+                    scripts = template_data_dyn['scripts']
+                except KeyError:
+                    pass
+                else:
+                    template_data.setdefault('scripts', []).extend(scripts)
+                template_data.update(template_data_dyn)
 
         return self.host.renderer.render(
             self.template,
--- a/libervia/server/server.py	Sat Apr 25 16:08:55 2020 +0200
+++ b/libervia/server/server.py	Sun Apr 26 22:01:13 2020 +0200
@@ -125,13 +125,13 @@
 class ProtectedFile(static.File):
     """A static.File class which doesn't show directory listing"""
 
-    def __init__(self, *args, **kwargs):
+    def __init__(self, path, *args, **kwargs):
         if "defaultType" not in kwargs and len(args) < 2:
             # defaultType is second positional argument, and Twisted uses it
             # in File.createSimilarFile, so we set kwargs only if it is missing
             # in kwargs and it is not in a positional argument
             kwargs["defaultType"] = "application/octet-stream"
-        super(ProtectedFile, self).__init__(*args, **kwargs)
+        super(ProtectedFile, self).__init__(str(path), *args, **kwargs)
 
     def directoryListing(self):
         return web_resource.NoResource()
@@ -159,10 +159,19 @@
         self.site_name = site_name
         self.site_path = site_path
         self.named_pages = {}
+        self.browser_modules = {}
         self.uri_callbacks = {}
         self.pages_redirects = {}
         self.cached_urls = {}
         self.main_menu = None
+        build_path = host.getBuildPath(site_name)
+        build_path.mkdir(parents=True, exist_ok=True)
+        self.putChild(
+            C.BUILD_DIR.encode(),
+            ProtectedFile(
+                build_path,
+                defaultType="application/octet-stream"),
+        )
 
     def __str__(self):
         return ("Root resource for {host_name} using {site_name} at {site_path} and "
@@ -755,8 +764,8 @@
         # self.sat_root is official Libervia site
         root_path = os.path.join(default_site_path, C.TEMPLATE_STATIC_DIR)
         self.sat_root = default_root = LiberviaRootResource(
-            host=self, host_name='', site_name='', site_path=default_site_path,
-            path=root_path)
+            host=self, host_name='', site_name='',
+            site_path=default_site_path, path=root_path)
         if self.options['dev_mode']:
             self.files_watcher.watchDir(
                 default_site_path, auto_add=True, recursive=True,
@@ -772,6 +781,9 @@
         existing_vhosts = {b'': default_root}
 
         for host_name, site_name in self.options["vhosts_dict"].items():
+            if site_name == C.SITE_NAME_DEFAULT:
+                raise ValueError(
+                    f"{C.DEFAULT_SITE_NAME} is reserved and can't be used in vhosts_dict")
             encoded_site_name = site_name.encode('utf-8')
             try:
                 site_path = self.renderer.sites_paths[site_name]
@@ -805,12 +817,6 @@
                         site_path, auto_add=True, recursive=True,
                         callback=LiberviaPage.onFileChange, site_root=res,
                         site_path=site_path)
-                res.putChild(
-                    C.BUILD_DIR.encode('utf-8'),
-                    ProtectedFile(
-                        self.getBuildPath(site_name),
-                        defaultType="application/octet-stream"),
-                )
 
                 LiberviaPage.importPages(self, res)
                 # FIXME: default pages are accessible if not overriden by external website
@@ -1277,15 +1283,15 @@
         """Generate build path for a given site name
 
         @param site_name(unicode): name of the site
-        @return (unicode): path to the build directory
+        @return (Path): path to the build directory
         """
         build_path_elts = [
             config.getConfig(self.main_conf, "", "local_dir"),
             C.CACHE_DIR,
             C.LIBERVIA_CACHE,
-            regex.pathEscape(site_name)]
-        build_path = "/".join(build_path_elts)
-        return os.path.abspath(os.path.expanduser(build_path))
+            regex.pathEscape(site_name or C.SITE_NAME_DEFAULT)]
+        build_path = Path("/".join(build_path_elts))
+        return build_path.expanduser().resolve()
 
     def getExtBaseURLData(self, request):
         """Retrieve external base URL Data