186 lines
6.2 KiB
Python
186 lines
6.2 KiB
Python
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() |