mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +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
|
||||
domain
|
||||
letsencrypt
|
||||
staticfiles
|
||||
|
||||
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
|
||||
|
||||
from . import cfg, log, module_loader, web_framework
|
||||
from . import app, cfg, log, module_loader, web_framework
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -72,6 +72,9 @@ def init():
|
||||
urlprefix = "%s%s" % (web_framework.get_static_url(), module_name)
|
||||
_mount_static_directory(static_dir, urlprefix)
|
||||
|
||||
for component in StaticFiles.list():
|
||||
component.mount()
|
||||
|
||||
cherrypy.engine.signal_handler.subscribe()
|
||||
|
||||
|
||||
@ -80,3 +83,54 @@ def run(on_web_server_stop):
|
||||
cherrypy.engine.start()
|
||||
cherrypy.engine.subscribe('stop', on_web_server_stop)
|
||||
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