| 7 | |
| 8 | |
| 9 | class FileServerHandler: |
| 10 | def __init__(self, base_dir: str): |
| 11 | self.base_dir = base_dir |
| 12 | logger.info(f"Initializing file server with base directory: {base_dir}") |
| 13 | |
| 14 | def handle_request(self, path: str, request_path: str) -> Union[Response, tuple]: |
| 15 | """Handle file/directory access requests""" |
| 16 | clean_path = path.rstrip("/") |
| 17 | full_path = os.path.join(self.base_dir, clean_path) |
| 18 | |
| 19 | # security check |
| 20 | try: |
| 21 | Path(full_path).resolve().relative_to(Path(self.base_dir).resolve()) |
| 22 | except ValueError: |
| 23 | return jsonify({"error": "Access denied"}), 403 |
| 24 | |
| 25 | # check type and handle |
| 26 | if os.path.isfile(full_path): |
| 27 | return self._handle_file(clean_path, request_path) |
| 28 | elif os.path.isdir(full_path): |
| 29 | return self._handle_directory(clean_path, request_path) |
| 30 | |
| 31 | return jsonify({"error": "Not found"}), 404 |
| 32 | |
| 33 | def _handle_file( |
| 34 | self, clean_path: str, request_path: str |
| 35 | ) -> Union[Response, tuple]: |
| 36 | """Handle file access""" |
| 37 | if request_path.endswith("/"): |
| 38 | return redirect(f"/raw_content/{clean_path}", code=301) |
| 39 | return send_from_directory(self.base_dir, clean_path, as_attachment=False) |
| 40 | |
| 41 | def _handle_directory( |
| 42 | self, clean_path: str, request_path: str |
| 43 | ) -> Union[Response, tuple]: |
| 44 | """Handle directory access""" |
| 45 | if not request_path.endswith("/"): |
| 46 | return redirect(f"/raw_content/{clean_path}/", code=301) |
| 47 | listing = self._list_directory(clean_path) |
| 48 | return jsonify( |
| 49 | listing.model_dump() |
| 50 | ) # convert Pydantic model to dict, then to JSON response |
| 51 | |
| 52 | def _list_directory(self, path: str) -> DirectoryListing: |
| 53 | """List directory content""" |
| 54 | target_dir = os.path.join(self.base_dir, path) |
| 55 | |
| 56 | items = [] |
| 57 | for item in os.scandir(target_dir): |
| 58 | item_type = "directory" if item.is_dir() else "file" |
| 59 | item_size = os.path.getsize(item.path) if item.is_file() else None |
| 60 | file_path = (Path(path) / item.name).as_posix() |
| 61 | items.append( |
| 62 | FileItem( |
| 63 | name=item.name, |
| 64 | type=item_type, |
| 65 | size=item_size, |
| 66 | path=file_path, |