allow for serving files directly inside pages/

This commit is contained in:
Brian S. Stephan 2021-04-14 20:45:50 -05:00
parent 757b067e16
commit ced67bec8b
5 changed files with 38 additions and 15 deletions

View File

@ -6,7 +6,7 @@ import re
from flask import Blueprint, Markup, abort from flask import Blueprint, Markup, abort
from flask import current_app as app from flask import current_app as app
from flask import redirect, request from flask import redirect, request, send_from_directory
from tzlocal import get_localzone from tzlocal import get_localzone
from incorporealcms.lib import get_meta_str, init_md, render from incorporealcms.lib import get_meta_str, init_md, render
@ -21,7 +21,7 @@ bp = Blueprint('pages', __name__, url_prefix='/')
def display_page(path): def display_page(path):
"""Get the file contents of the requested path and render the file.""" """Get the file contents of the requested path and render the file."""
try: try:
resolved_path = request_path_to_instance_resource_path(path) resolved_path, render_md = request_path_to_instance_resource_path(path)
logger.debug("received request for path '%s', resolved to '%s'", path, resolved_path) logger.debug("received request for path '%s', resolved to '%s'", path, resolved_path)
except PermissionError: except PermissionError:
abort(400) abort(400)
@ -30,6 +30,9 @@ def display_page(path):
except FileNotFoundError: except FileNotFoundError:
abort(404) abort(404)
if not render_md:
return send_from_directory(app.instance_path, resolved_path)
try: try:
with app.open_instance_resource(resolved_path, 'r') as entry_file: with app.open_instance_resource(resolved_path, 'r') as entry_file:
mtime = datetime.datetime.fromtimestamp(os.path.getmtime(entry_file.name), get_localzone()) mtime = datetime.datetime.fromtimestamp(os.path.getmtime(entry_file.name), get_localzone())
@ -76,21 +79,23 @@ def request_path_to_instance_resource_path(path):
logger.info("client requested a path '%s' that is actually a directory", path) logger.info("client requested a path '%s' that is actually a directory", path)
raise IsADirectoryError raise IsADirectoryError
# derive the proper markdown file depending on if this is a dir or file # derive the proper markdown or actual file depending on if this is a dir or file
if os.path.isdir(resolved_path): if os.path.isdir(resolved_path):
absolute_resource = os.path.join(resolved_path, 'index.md') absolute_resource = os.path.join(resolved_path, 'index.md')
elif os.path.exists(resolved_path):
logger.info("final DIRECT path = '%s' for request '%s'", resolved_path, path)
return resolved_path.replace(f'{app.instance_path}{os.path.sep}', ''), False
else: else:
absolute_resource = f'{resolved_path}.md' absolute_resource = f'{resolved_path}.md'
logger.info("final path = '%s' for request '%s'", absolute_resource, path)
# does the final file actually exist? # does the final file actually exist?
if not os.path.exists(absolute_resource): if not os.path.exists(absolute_resource):
logger.warning("requested final path '%s' does not exist!", absolute_resource) logger.warning("requested final path '%s' does not exist!", absolute_resource)
raise FileNotFoundError raise FileNotFoundError
logger.info("final path = '%s' for request '%s'", absolute_resource, path)
# we checked that the file exists via absolute path, but now we need to give the path relative to instance dir # we checked that the file exists via absolute path, but now we need to give the path relative to instance dir
return absolute_resource.replace(f'{app.instance_path}{os.path.sep}', '') return absolute_resource.replace(f'{app.instance_path}{os.path.sep}', ''), True
def instance_resource_path_to_request_path(path): def instance_resource_path_to_request_path(path):

View File

@ -9,6 +9,13 @@ def test_page_that_exists(client):
assert b'<h1>test index</h1>' in response.data assert b'<h1>test index</h1>' in response.data
def test_direct_file_that_exists(client):
"""Test that the app can serve a basic file at the index."""
response = client.get('/foo.txt')
assert response.status_code == 200
assert b'test file' in response.data
def test_page_that_doesnt_exist(client): def test_page_that_doesnt_exist(client):
"""Test that the app returns 404 for nonsense requests and they use my error page.""" """Test that the app returns 404 for nonsense requests and they use my error page."""
response = client.get('/ohuesthaoeusth') response = client.get('/ohuesthaoeusth')

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -0,0 +1 @@
test file

View File

@ -60,44 +60,45 @@ def test_render_with_no_user_theme(app):
def test_request_path_to_instance_resource_path(app): def test_request_path_to_instance_resource_path(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert request_path_to_instance_resource_path('index') == 'pages/index.md' assert request_path_to_instance_resource_path('index') == ('pages/index.md', True)
def test_request_path_to_instance_resource_path_direct_file(app): def test_request_path_to_instance_resource_path_direct_file(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert request_path_to_instance_resource_path('no-title') == 'pages/no-title.md' assert request_path_to_instance_resource_path('no-title') == ('pages/no-title.md', True)
def test_request_path_to_instance_resource_path_in_subdir(app): def test_request_path_to_instance_resource_path_in_subdir(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert request_path_to_instance_resource_path('subdir/page') == 'pages/subdir/page.md' assert request_path_to_instance_resource_path('subdir/page') == ('pages/subdir/page.md', True)
def test_request_path_to_instance_resource_path_subdir_index(app): def test_request_path_to_instance_resource_path_subdir_index(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert request_path_to_instance_resource_path('subdir/') == 'pages/subdir/index.md' assert request_path_to_instance_resource_path('subdir/') == ('pages/subdir/index.md', True)
def test_request_path_to_instance_resource_path_relatives_walked(app): def test_request_path_to_instance_resource_path_relatives_walked(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert (request_path_to_instance_resource_path('subdir/more-subdir/../../more-metadata') == assert (request_path_to_instance_resource_path('subdir/more-subdir/../../more-metadata') ==
'pages/more-metadata.md') ('pages/more-metadata.md', True))
def test_request_path_to_instance_resource_path_relatives_walked_indexes_work_too(app): def test_request_path_to_instance_resource_path_relatives_walked_indexes_work_too(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert request_path_to_instance_resource_path('subdir/more-subdir/../../') == 'pages/index.md' assert request_path_to_instance_resource_path('subdir/more-subdir/../../') == ('pages/index.md', True)
def test_request_path_to_instance_resource_path_relatives_walked_into_subdirs_also_fine(app): def test_request_path_to_instance_resource_path_relatives_walked_into_subdirs_also_fine(app):
"""Test a normal URL request is transformed into the file path.""" """Test a normal URL request is transformed into the file path."""
with app.test_request_context(): with app.test_request_context():
assert request_path_to_instance_resource_path('subdir/more-subdir/../../subdir/page') == 'pages/subdir/page.md' assert (request_path_to_instance_resource_path('subdir/more-subdir/../../subdir/page') ==
('pages/subdir/page.md', True))
def test_request_path_to_instance_resource_path_permission_error_on_ref_above_pages(app): def test_request_path_to_instance_resource_path_permission_error_on_ref_above_pages(app):
@ -114,6 +115,13 @@ def test_request_path_to_instance_resource_path_isadirectory_on_file_like_req_fo
assert request_path_to_instance_resource_path('subdir') assert request_path_to_instance_resource_path('subdir')
def test_request_path_to_instance_resource_path_actual_file(app):
"""Test that a request for e.g. '/foo.png' when foo.png is a real file works."""
with app.test_request_context():
assert (request_path_to_instance_resource_path('bss-square-no-bg.png') ==
('pages/bss-square-no-bg.png', False))
def test_request_path_to_instance_resource_path_nonexistant_file_errors(app): def test_request_path_to_instance_resource_path_nonexistant_file_errors(app):
"""Test that a request for something not on disk errors.""" """Test that a request for something not on disk errors."""
with app.test_request_context(): with app.test_request_context():
@ -155,13 +163,15 @@ def test_instance_resource_path_to_request_path_on_subdir_and_page(app):
def test_request_resource_request_root(app): def test_request_resource_request_root(app):
"""Test that a request can resolve to a resource and back to a request.""" """Test that a request can resolve to a resource and back to a request."""
with app.test_request_context(): with app.test_request_context():
instance_resource_path_to_request_path(request_path_to_instance_resource_path('index')) == '' instance_path, _ = request_path_to_instance_resource_path('index')
instance_resource_path_to_request_path(instance_path) == ''
def test_request_resource_request_page(app): def test_request_resource_request_page(app):
"""Test that a request can resolve to a resource and back to a request.""" """Test that a request can resolve to a resource and back to a request."""
with app.test_request_context(): with app.test_request_context():
instance_resource_path_to_request_path(request_path_to_instance_resource_path('no-title')) == 'no-title' instance_path, _ = request_path_to_instance_resource_path('no-title')
instance_resource_path_to_request_path(instance_path) == 'no-title'
def test_request_path_to_breadcrumb_display_patterns(): def test_request_path_to_breadcrumb_display_patterns():