mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-05-20 10:34:30 +00:00
web_server: Introduce component to handle special static file dirs
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org> Reviewed-by: Veiko Aasa <veiko17@disroot.org>
This commit is contained in:
parent
a0f6b0ea4a
commit
7a0ea38fb1
@ -14,6 +14,7 @@ Components
|
|||||||
frontpage
|
frontpage
|
||||||
domain
|
domain
|
||||||
letsencrypt
|
letsencrypt
|
||||||
|
staticfiles
|
||||||
|
|
||||||
Base Classes
|
Base Classes
|
||||||
^^^^^^^^^^^^
|
^^^^^^^^^^^^
|
||||||
|
|||||||
7
doc/dev/reference/components/staticfiles.rst
Normal file
7
doc/dev/reference/components/staticfiles.rst
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.. SPDX-License-Identifier: CC-BY-SA-4.0
|
||||||
|
|
||||||
|
StaticFiles
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: plinth.web_server.StaticFiles
|
||||||
|
:members:
|
||||||
63
plinth/tests/test_web_server.py
Normal file
63
plinth/tests/test_web_server.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||||
|
"""
|
||||||
|
Tests for CherryPy web server setup and its components.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from unittest.mock import call, patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from plinth.web_server import StaticFiles
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def fixture_cleanup_static_files():
|
||||||
|
"""Ensure that global list of static files is clean."""
|
||||||
|
StaticFiles._all_instances = {}
|
||||||
|
|
||||||
|
|
||||||
|
def test_static_files_init():
|
||||||
|
"""Test that static files component is being initialized correctly."""
|
||||||
|
component = StaticFiles('test-component')
|
||||||
|
assert component.component_id == 'test-component'
|
||||||
|
assert component.directory_map is None
|
||||||
|
|
||||||
|
directory_map = {'/a': '/b'}
|
||||||
|
component = StaticFiles('test-component', directory_map)
|
||||||
|
assert component.directory_map == directory_map
|
||||||
|
|
||||||
|
|
||||||
|
def test_static_files_list():
|
||||||
|
"""Test that static files components can be listed properly."""
|
||||||
|
component1 = StaticFiles('test-component1')
|
||||||
|
component2 = StaticFiles('test-component2')
|
||||||
|
|
||||||
|
assert set(StaticFiles.list()) == {component1, component2}
|
||||||
|
|
||||||
|
|
||||||
|
@patch('cherrypy.tree.mount')
|
||||||
|
def test_static_files_mount(mount, load_cfg):
|
||||||
|
"""Test that mounting on CherryPy works as expected."""
|
||||||
|
directory_map = {'/a': '/b', '/c': '/d'}
|
||||||
|
component = StaticFiles('test-component', directory_map)
|
||||||
|
component.mount()
|
||||||
|
|
||||||
|
calls = [
|
||||||
|
call(
|
||||||
|
None, '/plinth/a', {
|
||||||
|
'/': {
|
||||||
|
'tools.staticdir.root': '/b',
|
||||||
|
'tools.staticdir.on': True,
|
||||||
|
'tools.staticdir.dir': '.'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
call(
|
||||||
|
None, '/plinth/c', {
|
||||||
|
'/': {
|
||||||
|
'tools.staticdir.root': '/d',
|
||||||
|
'tools.staticdir.on': True,
|
||||||
|
'tools.staticdir.dir': '.'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
mount.assert_has_calls(calls)
|
||||||
@ -8,7 +8,7 @@ import os
|
|||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
from . import cfg, log, module_loader, web_framework
|
from . import app, cfg, log, module_loader, web_framework
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -72,6 +72,9 @@ def init():
|
|||||||
urlprefix = "%s%s" % (web_framework.get_static_url(), module_name)
|
urlprefix = "%s%s" % (web_framework.get_static_url(), module_name)
|
||||||
_mount_static_directory(static_dir, urlprefix)
|
_mount_static_directory(static_dir, urlprefix)
|
||||||
|
|
||||||
|
for component in StaticFiles.list():
|
||||||
|
component.mount()
|
||||||
|
|
||||||
cherrypy.engine.signal_handler.subscribe()
|
cherrypy.engine.signal_handler.subscribe()
|
||||||
|
|
||||||
|
|
||||||
@ -80,3 +83,54 @@ def run(on_web_server_stop):
|
|||||||
cherrypy.engine.start()
|
cherrypy.engine.start()
|
||||||
cherrypy.engine.subscribe('stop', on_web_server_stop)
|
cherrypy.engine.subscribe('stop', on_web_server_stop)
|
||||||
cherrypy.engine.block()
|
cherrypy.engine.block()
|
||||||
|
|
||||||
|
|
||||||
|
class StaticFiles(app.FollowerComponent):
|
||||||
|
"""Component to serve static files shipped with an app.
|
||||||
|
|
||||||
|
Any files in <app>/static directory will be automatically served on
|
||||||
|
/static/<app>/ directory by FreedomBox. This allows each app to ship custom
|
||||||
|
static files that are served by the web server.
|
||||||
|
|
||||||
|
However, in some rare circumstances, a system folder will need to be served
|
||||||
|
on a path for the app to work. This component allows declaring such
|
||||||
|
directories and the web paths they should be served on.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
_all_instances = {}
|
||||||
|
|
||||||
|
def __init__(self, component_id, directory_map=None):
|
||||||
|
"""Initialize the component.
|
||||||
|
|
||||||
|
component_id should be a unique ID across all components of an app and
|
||||||
|
across all components.
|
||||||
|
|
||||||
|
directory_map should be a dictionary with keys to be web paths and
|
||||||
|
values to be absolute path of the directory on disk to serve. The
|
||||||
|
static files from the directory are served over the given web path. The
|
||||||
|
web path will be prepended with the FreedomBox's configured base web
|
||||||
|
path. For example, {'/foo': '/usr/share/foo'} means that
|
||||||
|
'/usr/share/foo/bar.png' will be served over '/plinth/foo/bar.png' if
|
||||||
|
FreedomBox is configured to be served on '/plinth'.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super().__init__(component_id)
|
||||||
|
self.directory_map = directory_map
|
||||||
|
self._all_instances[component_id] = self
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def list(cls):
|
||||||
|
"""Return a list of all instances."""
|
||||||
|
return cls._all_instances.values()
|
||||||
|
|
||||||
|
def mount(self):
|
||||||
|
"""Perform configuration of the web server to handle static files.
|
||||||
|
|
||||||
|
Called by web server abstraction layer after web server has been setup.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if self.directory_map:
|
||||||
|
for web_path, file_path in self.directory_map.items():
|
||||||
|
web_path = '%s%s' % (cfg.server_dir, web_path)
|
||||||
|
_mount_static_directory(file_path, web_path)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user