uhhh lots of things
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	
This commit is contained in:
		
							
								
								
									
										12
									
								
								config.toml
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								config.toml
									
									
									
									
									
								
							| @@ -3,7 +3,11 @@ content_dir = "/home/dubey/projects/foldsite/example/content" | |||||||
| templates_dir = "/home/dubey/projects/foldsite/example/templates" | templates_dir = "/home/dubey/projects/foldsite/example/templates" | ||||||
| styles_dir = "/home/dubey/projects/foldsite/example/styles" | styles_dir = "/home/dubey/projects/foldsite/example/styles" | ||||||
|  |  | ||||||
| [secrets] | [server] | ||||||
| password = "YiaysZ4g8QX1R8R" | listen_address = "0.0.0.0" | ||||||
| aws_secret_key = "ybCvAq1GQpYg0kEeXc2LqfJl9y6/EXAMPLEKEY" | listen_port = 8080 | ||||||
| aws_key_id = "AKIASQ5ZB43T69DWV8BQ" | enable_admin_browser = false | ||||||
|  | admin_password = "password" | ||||||
|  | max_threads = 4 | ||||||
|  | debug = false | ||||||
|  | access_log = true | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								main.py
									
									
									
									
									
								
							| @@ -21,7 +21,13 @@ def main(): | |||||||
|     r = RouteManager(c) |     r = RouteManager(c) | ||||||
|     t = TemplateHelpers(c) |     t = TemplateHelpers(c) | ||||||
|  |  | ||||||
|     server = Server() |     server = Server( | ||||||
|  |         debug=c.debug, | ||||||
|  |         host=c.listen_address, | ||||||
|  |         port=c.listen_port, | ||||||
|  |         access_log=c.access_log, | ||||||
|  |         workers=c.max_threads, | ||||||
|  |     ) | ||||||
|  |  | ||||||
|     server.register_template_function("get_sibling_content_files", t.get_sibling_content_files) |     server.register_template_function("get_sibling_content_files", t.get_sibling_content_files) | ||||||
|     server.register_template_function("get_text_document_preview", t.get_text_document_preview) |     server.register_template_function("get_text_document_preview", t.get_text_document_preview) | ||||||
| @@ -33,7 +39,8 @@ def main(): | |||||||
|     server.register_route("/", r.default_route, defaults={"path": ""}) |     server.register_route("/", r.default_route, defaults={"path": ""}) | ||||||
|     server.register_route("/<path:path>", r.default_route) |     server.register_route("/<path:path>", r.default_route) | ||||||
|  |  | ||||||
|     file_manager_bp = create_filemanager_blueprint(c.content_dir, url_prefix='/admin', auth_password="password") |     if c.admin_browser: | ||||||
|  |         file_manager_bp = create_filemanager_blueprint(c.content_dir, url_prefix='/admin', auth_password=c.admin_password) | ||||||
|         server.app.register_blueprint(file_manager_bp) |         server.app.register_blueprint(file_manager_bp) | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|   | |||||||
| @@ -9,10 +9,19 @@ class Configuration: | |||||||
|  |  | ||||||
|     def __init__(self, config_path): |     def __init__(self, config_path): | ||||||
|         self.config_path = config_path |         self.config_path = config_path | ||||||
|  |  | ||||||
|         self.content_dir: Path = None |         self.content_dir: Path = None | ||||||
|         self.templates_dir: Path = None |         self.templates_dir: Path = None | ||||||
|         self.styles_dir: Path = None |         self.styles_dir: Path = None | ||||||
|  |  | ||||||
|  |         self.listen_address: str = "127.0.0.1" | ||||||
|  |         self.listen_port: int = 8080 | ||||||
|  |         self.debug: bool = False | ||||||
|  |         self.access_log: bool = True | ||||||
|  |         self.max_threads: int = 4 | ||||||
|  |         self.admin_browser: bool = False | ||||||
|  |         self.admin_password: str = None | ||||||
|  |  | ||||||
|     def load_config(self): |     def load_config(self): | ||||||
|         try: |         try: | ||||||
|             with open(self.config_path, "rb") as f: |             with open(self.config_path, "rb") as f: | ||||||
| @@ -41,6 +50,18 @@ class Configuration: | |||||||
|             raise ValueError("Config file does not contain styles_dir path") |             raise ValueError("Config file does not contain styles_dir path") | ||||||
|         self.styles_dir = Path(self.styles_dir) |         self.styles_dir = Path(self.styles_dir) | ||||||
|  |  | ||||||
|  |         server = self.config_data.get("server", {}) | ||||||
|  |         if not server: | ||||||
|  |             raise ValueError("Config file does not contain server section") | ||||||
|  |          | ||||||
|  |         self.listen_address = server.get("listen_address", self.listen_address) | ||||||
|  |         self.listen_port = server.get("listen_port", self.listen_port) | ||||||
|  |         self.debug = server.get("debug", self.debug) | ||||||
|  |         self.access_log = server.get("access_log", self.access_log) | ||||||
|  |         self.max_threads = server.get("max_threads", self.max_threads) | ||||||
|  |         self.admin_browser = server.get("admin_browser", self.admin_browser) | ||||||
|  |         self.admin_password = server.get("admin_password", self.admin_password) | ||||||
|  |          | ||||||
|     def set_globals(self): |     def set_globals(self): | ||||||
|         global CONTENT_DIR, TEMPLATES_DIR, STYLES_DIR |         global CONTENT_DIR, TEMPLATES_DIR, STYLES_DIR | ||||||
|         CONTENT_DIR = self.content_dir |         CONTENT_DIR = self.content_dir | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
| from src.config.config import Configuration | from src.config.config import Configuration | ||||||
| from src.rendering import GENERIC_FILE_MAPPING | from src.rendering import GENERIC_FILE_MAPPING | ||||||
| from src.rendering.markdown import render_markdown, read_raw_markdown, rendered_markdown_to_plain_text | from src.rendering.markdown import ( | ||||||
|  |     render_markdown, | ||||||
|  |     read_raw_markdown, | ||||||
|  |     rendered_markdown_to_plain_text, | ||||||
|  | ) | ||||||
| from enum import Enum | from enum import Enum | ||||||
|  |  | ||||||
| from PIL import Image | from PIL import Image | ||||||
| @@ -16,9 +20,22 @@ class ImageMetadata: | |||||||
|     alt: str |     alt: str | ||||||
|     exif: dict |     exif: dict | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass | @dataclass | ||||||
| class MarkdownMetadata: | class MarkdownMetadata: | ||||||
|     fontmatter: dict |     """ | ||||||
|  |     A class to represent metadata for a Markdown file. | ||||||
|  |  | ||||||
|  |     Attributes: | ||||||
|  |     ---------- | ||||||
|  |     frontmatter : dict | ||||||
|  |         A dictionary containing the front matter of the Markdown file. | ||||||
|  |     content : str | ||||||
|  |         The main content of the Markdown file. | ||||||
|  |     preview : str | ||||||
|  |         A preview or summary of the Markdown content. | ||||||
|  |     """ | ||||||
|  |     frontmatter: dict | ||||||
|     content: str |     content: str | ||||||
|     preview: str |     preview: str | ||||||
|  |  | ||||||
| @@ -30,6 +47,24 @@ class FileMetadata: | |||||||
|  |  | ||||||
| @dataclass | @dataclass | ||||||
| class TemplateFile: | class TemplateFile: | ||||||
|  |     """ | ||||||
|  |     A class to represent a template file with its associated metadata. | ||||||
|  |  | ||||||
|  |     Attributes: | ||||||
|  |     ---------- | ||||||
|  |         name (str): The name of the file. | ||||||
|  |         path (str): The file path. | ||||||
|  |         proper_name (str): The proper name of the file. | ||||||
|  |         extension (str): The file extension. | ||||||
|  |         categories (list[str]): A list of categories associated with the file. | ||||||
|  |         date_modified (str): The date the file was last modified. | ||||||
|  |         date_created (str): The date the file was created. | ||||||
|  |         size_kb (int): The size of the file in kilobytes. | ||||||
|  |         metadata (ImageMetadata | FileMetadata | None): Metadata associated with the file, | ||||||
|  |             which can be either image metadata, file metadata, or None. | ||||||
|  |         dir_item_count (int): The number of items in the directory if the file is a directory. | ||||||
|  |         is_dir (bool): A flag indicating whether the file is a directory. | ||||||
|  |     """ | ||||||
|     name: str |     name: str | ||||||
|     path: str |     path: str | ||||||
|     proper_name: str |     proper_name: str | ||||||
| @@ -51,9 +86,10 @@ class TemplateHelpers: | |||||||
|     def __init__(self, config: Configuration): |     def __init__(self, config: Configuration): | ||||||
|         self.config: Configuration = config |         self.config: Configuration = config | ||||||
|  |  | ||||||
|  |     def _filter_hidden_files(self, files): | ||||||
|  |         return [f for f in files if not f.name.startswith("___")] | ||||||
|  |  | ||||||
|     def build_metadata_for_file(self, path: str, categories: list[str] = []): |     def _build_metadata_for_file(self, path: str, categories: list[str] = []): | ||||||
|         """Builds metadata for a file""" |  | ||||||
|         file_path = self.config.content_dir / path |         file_path = self.config.content_dir / path | ||||||
|         for k in categories: |         for k in categories: | ||||||
|             if k == "image": |             if k == "image": | ||||||
| @@ -75,19 +111,37 @@ class TemplateHelpers: | |||||||
|                     ret = FileMetadata(None) |                     ret = FileMetadata(None) | ||||||
|                 if file_path.suffix[1:].lower() == "md": |                 if file_path.suffix[1:].lower() == "md": | ||||||
|                     ret.typeMeta = MarkdownMetadata({}, "", "") |                     ret.typeMeta = MarkdownMetadata({}, "", "") | ||||||
|                     ret.typeMeta.fontmatter = frontmatter.load(file_path) |                     ret.typeMeta.frontmatter = frontmatter.load(file_path) | ||||||
|                     ret.typeMeta.content = render_markdown(file_path) |                     ret.typeMeta.content = render_markdown(file_path) | ||||||
|                     ret.typeMeta.rawContent = read_raw_markdown(file_path) |                     ret.typeMeta.rawContent = read_raw_markdown(file_path) | ||||||
|                     ret.typeMeta.rawText = rendered_markdown_to_plain_text(ret.typeMeta.content) |                     ret.typeMeta.rawText = rendered_markdown_to_plain_text( | ||||||
|  |                         ret.typeMeta.content | ||||||
|  |                     ) | ||||||
|                     ret.typeMeta.preview = ret.typeMeta.rawText[:500] + "..." |                     ret.typeMeta.preview = ret.typeMeta.rawText[:500] + "..." | ||||||
|                 return ret |                 return ret | ||||||
|         return None |         return None | ||||||
|  |  | ||||||
|  |  | ||||||
|     def get_folder_contents(self, path: str = ""): |     def get_folder_contents(self, path: str = ""): | ||||||
|         """Returns the contents of a folder as a list of TemplateFile objects |         """ | ||||||
|  |         Retrieve the contents of a folder and return a list of TemplateFile objects. | ||||||
|  |  | ||||||
|         The metadata field is populated with the appropriate metadata object |         Args: | ||||||
|  |             path (str): The relative path to the folder within the content directory. Defaults to an empty string, | ||||||
|  |                 which refers to the root content directory. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |             list: A list of TemplateFile objects representing the files and directories within the specified folder. | ||||||
|  |  | ||||||
|  |         The function performs the following steps: | ||||||
|  |         1. Constructs the full path to the folder by combining the content directory with the provided path. | ||||||
|  |         2. Retrieves all files and directories within the specified folder. | ||||||
|  |         3. Iterates over each file and directory, creating a TemplateFile object with metadata such as name, | ||||||
|  |             path, proper name, extension, categories, date modified, date created, size in KB, metadata, directory | ||||||
|  |             item count, and whether it is a directory. | ||||||
|  |         4. If the item is a file, it assigns categories based on the file extension using a predefined mapping. | ||||||
|  |         5. Builds additional metadata for each file. | ||||||
|  |         6. Filters out hidden files from the list. | ||||||
|  |         7. Returns the list of TemplateFile objects. | ||||||
|         """ |         """ | ||||||
|         search_contnet_path = self.config.content_dir / path |         search_contnet_path = self.config.content_dir / path | ||||||
|         files = search_contnet_path.glob("*") |         files = search_contnet_path.glob("*") | ||||||
| @@ -110,30 +164,70 @@ class TemplateHelpers: | |||||||
|                 for k, v in GENERIC_FILE_MAPPING.items(): |                 for k, v in GENERIC_FILE_MAPPING.items(): | ||||||
|                     if f.suffix[1:].lower() in v: |                     if f.suffix[1:].lower() in v: | ||||||
|                         t.categories.append(k) |                         t.categories.append(k) | ||||||
|             t.metadata = self.build_metadata_for_file(f, t.categories) |             t.metadata = self._build_metadata_for_file(f, t.categories) | ||||||
|             ret.append(t) |             ret.append(t) | ||||||
|  |         ret = self._filter_hidden_files(ret) | ||||||
|         return ret |         return ret | ||||||
|  |  | ||||||
|     def get_sibling_content_files(self, path: str = ""): |     def get_sibling_content_files(self, path: str = ""): | ||||||
|  |         """ | ||||||
|  |         Retrieves a list of sibling content files in the specified directory. | ||||||
|  |  | ||||||
|  |         Args: | ||||||
|  |             path (str): The relative path within the content directory to search for files. | ||||||
|  |                 Defaults to an empty string, which means the root of the content directory. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |             list: A list of tuples, where each tuple contains the file name and its relative path  | ||||||
|  |                 to the content directory. Only files that do not start with "___" are included. | ||||||
|  |         """ | ||||||
|         search_contnet_path = self.config.content_dir / path |         search_contnet_path = self.config.content_dir / path | ||||||
|         files = search_contnet_path.glob("*") |         files = search_contnet_path.glob("*") | ||||||
|         return [ |         return [ | ||||||
|             (file.name, str(file.relative_to(self.config.content_dir))) |             (file.name, str(file.relative_to(self.config.content_dir))) | ||||||
|             for file in files |             for file in files | ||||||
|             if file.is_file() |             if file.is_file() and not file.name.startswith("___") | ||||||
|         ] |         ] | ||||||
|  |  | ||||||
|     def get_text_document_preview(self, path: str): |     def get_text_document_preview(self, path: str): | ||||||
|  |         """ | ||||||
|  |         Generates a preview of the text document located at the given path. | ||||||
|  |  | ||||||
|  |         This method reads the first 100 characters from the specified text file | ||||||
|  |         and returns it as a string. The file path is constructed by combining | ||||||
|  |         the content directory from the configuration with the provided path. | ||||||
|  |  | ||||||
|  |         Args: | ||||||
|  |             path (str): The relative path to the text document within the content directory. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |             str: A string containing the first 100 characters of the text document. | ||||||
|  |  | ||||||
|  |         Raises: | ||||||
|  |             FileNotFoundError: If the file at the specified path does not exist. | ||||||
|  |             IOError: If an I/O error occurs while reading the file. | ||||||
|  |         """ | ||||||
|         file_path = self.config.content_dir / path |         file_path = self.config.content_dir / path | ||||||
|         with open(file_path, "r") as f: |         with open(file_path, "r") as f: | ||||||
|             content = f.read(100) |             content = f.read(100) | ||||||
|         return content |         return content | ||||||
|  |  | ||||||
|     def get_sibling_content_folders(self, path: str = ""): |     def get_sibling_content_folders(self, path: str = ""): | ||||||
|  |         """ | ||||||
|  |         Retrieves a list of sibling content folders within a specified directory. | ||||||
|  |  | ||||||
|  |         Args: | ||||||
|  |             path (str): A relative path from the content directory to search within. Defaults to an empty string,  | ||||||
|  |                         which means the search will be conducted in the content directory itself. | ||||||
|  |  | ||||||
|  |         Returns: | ||||||
|  |             list of tuple: A list of tuples where each tuple contains the folder name and its relative path  | ||||||
|  |                            to the content directory. Only directories that do not start with "___" are included. | ||||||
|  |         """ | ||||||
|         search_contnet_path = self.config.content_dir / path |         search_contnet_path = self.config.content_dir / path | ||||||
|         files = search_contnet_path.glob("*") |         files = search_contnet_path.glob("*") | ||||||
|         return [ |         return [ | ||||||
|             (file.name, str(file.relative_to(self.config.content_dir))) |             (file.name, str(file.relative_to(self.config.content_dir))) | ||||||
|             for file in files |             for file in files | ||||||
|             if file.is_dir() |             if file.is_dir() and not file.name.startswith("___") | ||||||
|         ] |         ] | ||||||
|   | |||||||
| @@ -95,6 +95,30 @@ def render_page( | |||||||
|     template_path: Path = Path("./"), |     template_path: Path = Path("./"), | ||||||
|     style_path: Path = Path("./"), |     style_path: Path = Path("./"), | ||||||
| ): | ): | ||||||
|  |     """ | ||||||
|  |     Renders a web page based on the provided path and optional base, template, and style paths. | ||||||
|  |  | ||||||
|  |     Args: | ||||||
|  |         path (Path): The path to the target file or directory to render. | ||||||
|  |         base_path (Path, optional): The base path to use for relative paths. Defaults to Path("./"). | ||||||
|  |         template_path (Path, optional): The path to the directory containing HTML templates. Defaults to Path("./"). | ||||||
|  |         style_path (Path, optional): The path to the directory containing CSS styles. Defaults to Path("./"). | ||||||
|  |  | ||||||
|  |     Returns: | ||||||
|  |         str: The rendered HTML content of the page, or an error page if the target path does not exist or no suitable template is found. | ||||||
|  |  | ||||||
|  |     Raises: | ||||||
|  |         Exception: If the base template (base.html) is not found in the template_path. | ||||||
|  |  | ||||||
|  |     Notes: | ||||||
|  |         - If the target path does not exist, a 404 error page is rendered. | ||||||
|  |         - If the target path is a file, the function attempts to determine its type, category, and extension. | ||||||
|  |         - The function generates a list of possible CSS styles based on the target path and its type/category. | ||||||
|  |         - The function searches for suitable HTML templates based on the target path and its type/category. | ||||||
|  |         - If no suitable template is found, the function either sends the file directly (if it's a file) or renders a 404 error page. | ||||||
|  |         - If the target file is a document, its content is rendered as Markdown. | ||||||
|  |         - The function ensures that the base template (base.html) exists before rendering the final content. | ||||||
|  |     """ | ||||||
|     if not path.exists(): |     if not path.exists(): | ||||||
|         return render_error_page( |         return render_error_page( | ||||||
|             error_code=404, |             error_code=404, | ||||||
| @@ -111,7 +135,18 @@ def render_page( | |||||||
|     relative_path = target_file.relative_to(base_path) |     relative_path = target_file.relative_to(base_path) | ||||||
|     relative_dir = target_path.relative_to(base_path) |     relative_dir = target_path.relative_to(base_path) | ||||||
|  |  | ||||||
|     # Generate the possible paths for style |     """ | ||||||
|  |     The styles are ordered in the following manner: | ||||||
|  |  | ||||||
|  |     Specific style for the target path (e.g., /path/to/target.css). | ||||||
|  |     Specific styles for the type and extension in the current and parent directories | ||||||
|  |         (e.g., /path/to/__file.html.css). | ||||||
|  |     Specific styles for the type and category in the current and parent directories | ||||||
|  |         (e.g., /path/to/__file.document.css). | ||||||
|  |     Base style (/base.css). | ||||||
|  |     This ordering ensures that the most specific styles are applied first, followed by | ||||||
|  |         more general styles, and finally the base style. | ||||||
|  |     """ | ||||||
|     styles = [] |     styles = [] | ||||||
|     styles.append("/" + str(relative_path) + ".css") |     styles.append("/" + str(relative_path) + ".css") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ class RouteManager: | |||||||
|     def __init__(self, config: Configuration): |     def __init__(self, config: Configuration): | ||||||
|         self.config = config |         self.config = config | ||||||
|  |  | ||||||
|  |  | ||||||
|     def _validate_and_sanitize_path(self, base_dir, requested_path): |     def _validate_and_sanitize_path(self, base_dir, requested_path): | ||||||
|         """ |         """ | ||||||
|         Validate and sanitize the requested path to ensure it does not traverse above the base directory. |         Validate and sanitize the requested path to ensure it does not traverse above the base directory. | ||||||
| @@ -21,11 +20,11 @@ class RouteManager: | |||||||
|         :return: A secure version of the requested path if valid, otherwise None. |         :return: A secure version of the requested path if valid, otherwise None. | ||||||
|         """ |         """ | ||||||
|         # Normalize both paths |         # Normalize both paths | ||||||
|         base_dir = os.path.abspath(base_dir) |         base_dir = Path(base_dir) | ||||||
|         requested_path = os.path.abspath(requested_path) |         requested_path: Path = base_dir / requested_path | ||||||
|  |  | ||||||
|         # Check if the requested path is within the base directory |         # Check if the requested path is within the base directory | ||||||
|         if not requested_path.startswith(base_dir): |         if requested_path < base_dir: | ||||||
|             return None |             return None | ||||||
|  |  | ||||||
|         # Ensure the path does not contain any '..' or '.' components |         # Ensure the path does not contain any '..' or '.' components | ||||||
| @@ -33,32 +32,33 @@ class RouteManager: | |||||||
|         secure_path_parts = secure_path.split(os.sep) |         secure_path_parts = secure_path.split(os.sep) | ||||||
|  |  | ||||||
|         for part in secure_path_parts: |         for part in secure_path_parts: | ||||||
|             if part == '.' or part == '..': |             if part == "." or part == "..": | ||||||
|  |                 print("Illegal path nice try") | ||||||
|                 return None |                 return None | ||||||
|  |  | ||||||
|         # Reconstruct the secure path |         # Reconstruct the secure path | ||||||
|         secure_path = os.path.join(base_dir, *secure_path_parts) |         secure_path = os.path.join(base_dir, *secure_path_parts) | ||||||
|  |         secure_path = Path(secure_path) | ||||||
|  |  | ||||||
|  |         # Check if path exists | ||||||
|  |         if not secure_path.exists(): | ||||||
|  |             raise Exception("Illegal path") | ||||||
|  |  | ||||||
|  |         for part in secure_path.parts: | ||||||
|  |             if part.startswith("___"): | ||||||
|  |                 print("hidden file") | ||||||
|  |                 raise Exception("Illegal path") | ||||||
|  |  | ||||||
|         return secure_path |         return secure_path | ||||||
|  |  | ||||||
|     def _ensure_route(self, path: str): |     def _ensure_route(self, path: str): | ||||||
|         """ |  | ||||||
|         Escapes the path for anything like |  | ||||||
|          a path execution or injection attack |  | ||||||
|          evaluates the path and ensures that it it does not |  | ||||||
|          go above the self.content.content_dir |  | ||||||
|         If any part of the path contains __, __{foldername}, or __{filename}, |  | ||||||
|          that is a hidden file or folder and should raise an exception |  | ||||||
|         Any illegal path should raise an exception |  | ||||||
|         """ |  | ||||||
|         file_path: Path = self.config.content_dir / (path if path else "index.md") |         file_path: Path = self.config.content_dir / (path if path else "index.md") | ||||||
|         if file_path < self.config.content_dir: |         if file_path < self.config.content_dir: | ||||||
|             raise Exception("Illegal path") |             raise Exception("Illegal path") | ||||||
|  |  | ||||||
|         for part in file_path.parts: |         if not self._validate_and_sanitize_path( | ||||||
|             if part.startswith("__"): |             self.config.content_dir, str(file_path) | ||||||
|                 raise Exception("Illegal path") |         ): | ||||||
|              |  | ||||||
|         if not self._validate_and_sanitize_path(self.config.content_dir, str(file_path)): |  | ||||||
|             raise Exception("Illegal path") |             raise Exception("Illegal path") | ||||||
|  |  | ||||||
|     def default_route(self, path: str): |     def default_route(self, path: str): | ||||||
| @@ -80,6 +80,15 @@ class RouteManager: | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def get_style(self, path: str): |     def get_style(self, path: str): | ||||||
|  |         try: | ||||||
|  |             self._validate_and_sanitize_path(self.config.styles_dir, path) | ||||||
|  |         except Exception as e: | ||||||
|  |             return render_error_page( | ||||||
|  |                 404, | ||||||
|  |                 "Not Found", | ||||||
|  |                 f"The requested resource was not found on this server. {e}", | ||||||
|  |                 self.config.templates_dir, | ||||||
|  |             ) | ||||||
|         file_path: Path = self.config.styles_dir / path |         file_path: Path = self.config.styles_dir / path | ||||||
|         if file_path.exists(): |         if file_path.exists(): | ||||||
|             return send_file(file_path) |             return send_file(file_path) | ||||||
| @@ -91,8 +100,16 @@ class RouteManager: | |||||||
|                 self.config.templates_dir, |                 self.config.templates_dir, | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     @lru_cache(maxsize=None) |  | ||||||
|     def get_static(self, path: str): |     def get_static(self, path: str): | ||||||
|  |         try: | ||||||
|  |             self._validate_and_sanitize_path(self.config.content_dir, path) | ||||||
|  |         except Exception as e: | ||||||
|  |             return render_error_page( | ||||||
|  |                 404, | ||||||
|  |                 "Not Found", | ||||||
|  |                 "The requested resource was not found on this server.", | ||||||
|  |                 self.config.templates_dir, | ||||||
|  |             ) | ||||||
|         file_path: Path = self.config.content_dir / path |         file_path: Path = self.config.content_dir / path | ||||||
|         if file_path.exists(): |         if file_path.exists(): | ||||||
|             # Check to see if the file is an image, if it is, render a thumbnail |             # Check to see if the file is an image, if it is, render a thumbnail | ||||||
|   | |||||||
| @@ -13,8 +13,8 @@ class Server(BaseApplication): | |||||||
|         host: str = "0.0.0.0", |         host: str = "0.0.0.0", | ||||||
|         port: int = 8080, |         port: int = 8080, | ||||||
|         template_functions: Dict[str, Callable] = None, |         template_functions: Dict[str, Callable] = None, | ||||||
|         enable_admin_browser: bool = False, |  | ||||||
|         workers: int = multiprocessing.cpu_count() // 2 + 1, |         workers: int = multiprocessing.cpu_count() // 2 + 1, | ||||||
|  |         access_log: bool = True, | ||||||
|         options=None, |         options=None, | ||||||
|     ): |     ): | ||||||
|         if template_functions is None: |         if template_functions is None: | ||||||
| @@ -28,9 +28,9 @@ class Server(BaseApplication): | |||||||
|         self.app.secret_key = "your_secret_key" |         self.app.secret_key = "your_secret_key" | ||||||
|         self.options = options or { |         self.options = options or { | ||||||
|             "bind": f"{self.host}:{self.port}", |             "bind": f"{self.host}:{self.port}", | ||||||
|             "reload": True,  # Enable automatic reloading |             "reload": self.debug, | ||||||
|             "threads": workers, |             "threads": workers, | ||||||
|             "accesslog": "-", |             "accesslog": "-" if access_log else None, | ||||||
|         } |         } | ||||||
|         for name, func in template_functions.items(): |         for name, func in template_functions.items(): | ||||||
|             self.register_template_function(name, func) |             self.register_template_function(name, func) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user