import streamlit as st from pathlib import Path WORKSPACE = Path("workspace") WORKSPACE.mkdir(exist_ok=True) class FileManager: def __init__(self, base_path=Path("workspace")) -> None: self.base_path = Path(base_path) self.base_path.mkdir(exist_ok=True) def create_folder(self, relative_path: str, name: str) -> bool: if not name: st.error(f"Invalid folder name: {name}") return False if "/" in name or "\\" in name: st.error(f"Invalid folder name (no slashes allowed): {name}") return False name = Path(name) if relative_path: relative_path = Path(relative_path) else: relative_path = Path() folder_path = (self.base_path / relative_path / name).resolve() if not str(folder_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {relative_path}") return False try: folder_path.mkdir(exist_ok=False) return True except FileExistsError: st.warning(f"Folder already exists: {relative_path}") return False except Exception as e: st.error(f"Error creating folder {relative_path}: {str(e)}") return False def create_file(self, relative_path: str, name: str) -> bool: if not name or name.strip() == "" : st.error(f"Invalid file name: {name}") return False name = Path(name) if not name.suffix: name = name.with_suffix(".txt") # Default to .txt if no extension provided if relative_path: relative_path = Path(relative_path) else: relative_path = Path() file_path = (self.base_path / relative_path / name).resolve() if not str(file_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {relative_path}") return False try: file_path.touch(exist_ok=False) return True except FileExistsError: st.warning(f"File already exists: {relative_path}") return False except Exception as e: st.error(f"Error creating file {relative_path}: {str(e)}") return False def read_file(self, relative_path: Path) -> str: file_path = (relative_path).resolve() if not file_path.exists(): st.error(f"File not found: {relative_path}") return "" if not file_path.is_file(): st.error(f"Path is not a file: {relative_path}") return "" if not str(file_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {relative_path}") return "" try: with open(file_path, "r") as f: return f.read() except FileNotFoundError: st.error(f"File not found: {relative_path}") return "" except Exception as e: st.error(f"Error reading file {relative_path}: {str(e)}") return "" def save_file(self, relative_path: str, content: str): file_path = (Path(relative_path)).resolve() if not str(file_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {relative_path}") return False try: with open(file_path, "w") as f: f.write(content) return True except Exception as e: st.error(f"Error saving file {relative_path}: {str(e)}") return False def rename_file(self, old_relative_path: str, new_name: str) -> bool: if not new_name or new_name.strip() == "": st.error(f"Invalid file name: {new_name}") return False file_type = Path(old_relative_path).suffix new_name = Path(new_name) if not Path(new_name).suffix == file_type: new_name = Path(new_name).with_suffix(file_type) # Ensure the file extension remains the same old_file_path = (Path(old_relative_path)).resolve() new_file_path = old_file_path.parent / new_name if not str(old_file_path).startswith(str(self.base_path.resolve())) or not str(new_file_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {old_relative_path}") return False try: old_file_path.rename(new_file_path) return True except FileNotFoundError: st.error(f"File not found: {old_relative_path}") return False def delete_folder(self, relative_path) -> bool: folder_path = (self.base_path / relative_path).resolve() if not str(folder_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {relative_path}") return False if not folder_path.exists(): st.error(f"Folder not found: {relative_path}") return False try: import shutil shutil.rmtree(folder_path) return True except Exception as e: st.error(f"Error deleting folder {relative_path}: {str(e)}") return False def delete_file(self, relative_path): file_path = Path(relative_path).resolve() if not str(file_path).startswith(str(self.base_path.resolve())): st.error(f"Access denied: {relative_path}") return False try: file_path.unlink() return True except FileNotFoundError: st.error(f"File not found: {relative_path}") return False except Exception as e: st.error(f"Error deleting file {relative_path}: {str(e)}") return False def get_file_tree(self): def build_tree(path: Path): tree = {} for item in sorted(path.iterdir()): if item.is_dir(): tree[item.name] = build_tree(item) else: tree[item.name] = None return tree return build_tree(self.base_path) if __name__ == "__main__": FileManager()