62 lines
1.5 KiB
Python
62 lines
1.5 KiB
Python
import mimetypes
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from werkzeug.wrappers import Response
|
|
from werkzeug.wsgi import wrap_file
|
|
|
|
import frappe
|
|
from frappe.website.page_renderers.base_renderer import BaseRenderer
|
|
from frappe.website.utils import is_binary_file
|
|
|
|
UNSUPPORTED_STATIC_PAGE_TYPES = (
|
|
"css",
|
|
"html",
|
|
"js",
|
|
"json",
|
|
"md",
|
|
"py",
|
|
"pyc",
|
|
"pyo",
|
|
"txt",
|
|
"xml",
|
|
)
|
|
|
|
|
|
class StaticPage(BaseRenderer):
|
|
__slots__ = ("file_path", "path")
|
|
|
|
def __init__(self, path, http_status_code=None):
|
|
super().__init__(path=path, http_status_code=http_status_code)
|
|
self.set_file_path()
|
|
|
|
def set_file_path(self):
|
|
self.file_path = ""
|
|
if not self.is_valid_file_path():
|
|
return
|
|
for app in frappe.get_installed_apps():
|
|
app_path = Path(frappe.get_app_path(app, "www"))
|
|
requested_path = (app_path / self.path).resolve()
|
|
if (
|
|
requested_path.is_relative_to(app_path)
|
|
and requested_path.is_file()
|
|
and is_binary_file(requested_path)
|
|
):
|
|
self.file_path = requested_path
|
|
break
|
|
|
|
def can_render(self):
|
|
return self.is_valid_file_path() and self.file_path
|
|
|
|
def is_valid_file_path(self):
|
|
extension = self.path.rsplit(".", 1)[-1] if "." in self.path else ""
|
|
if extension in UNSUPPORTED_STATIC_PAGE_TYPES:
|
|
return False
|
|
return True
|
|
|
|
def render(self):
|
|
# file descriptor to be left open, closed by middleware
|
|
f = open(self.file_path, "rb")
|
|
response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True)
|
|
response.mimetype = mimetypes.guess_type(self.file_path)[0] or "application/octet-stream"
|
|
return response
|