Add admin invites and user workspace management tools.

Implement invite-token registration with optional email delivery, add admin UI actions for creating invites and opening user workspaces, and support superuser workspace override while preserving per-user code isolation with shared read-only lib.

Made-with: Cursor
This commit is contained in:
2026-05-01 21:13:13 +12:00
parent e4c811f51d
commit 7d682cce8d
15 changed files with 683 additions and 71 deletions

View File

@@ -1,5 +1,8 @@
from fastapi import APIRouter
from pathlib import Path
from fastapi import APIRouter, Depends
from editor_app.deps import get_workspace_root
from editor_app.models import FileContent, FolderOperation, MoveFileRequest
from editor_app.services import filesystem
@@ -7,50 +10,51 @@ router = APIRouter(prefix="/api")
@router.get("/files")
async def list_files(path: str = ""):
files = filesystem.list_files(path)
async def list_files(path: str = "", workspace_root: Path = Depends(get_workspace_root)):
files = filesystem.list_files(path, workspace_root=workspace_root)
return {"files": files}
@router.get("/workspace/py-sources")
async def workspace_python_sources():
return {"files": filesystem.collect_python_sources()}
async def workspace_python_sources(workspace_root: Path = Depends(get_workspace_root)):
return {"files": filesystem.collect_python_sources(workspace_root=workspace_root)}
@router.get("/file/{file_path:path}")
async def read_file(file_path: str):
content, filename = filesystem.read_text_file(file_path)
async def read_file(file_path: str, workspace_root: Path = Depends(get_workspace_root)):
content, filename = filesystem.read_text_file(file_path, workspace_root=workspace_root)
return {"content": content, "filename": filename}
@router.post("/file/{file_path:path}")
async def save_file(file_path: str, file_data: FileContent):
filename = filesystem.save_text_file(file_path, file_data.content)
async def save_file(file_path: str, file_data: FileContent, workspace_root: Path = Depends(get_workspace_root)):
filename = filesystem.save_text_file(file_path, file_data.content, workspace_root=workspace_root)
return {"message": "File saved successfully", "filename": filename}
@router.post("/file-move")
async def move_file(move_data: MoveFileRequest):
async def move_file(move_data: MoveFileRequest, workspace_root: Path = Depends(get_workspace_root)):
new_path, moved_type = filesystem.move_path(
source_path=move_data.source_path,
destination_folder=move_data.destination_folder,
workspace_root=workspace_root,
)
return {"message": "Path moved successfully", "new_path": new_path, "moved_type": moved_type}
@router.delete("/file/{file_path:path}")
async def delete_file(file_path: str):
filesystem.delete_file(file_path)
async def delete_file(file_path: str, workspace_root: Path = Depends(get_workspace_root)):
filesystem.delete_file(file_path, workspace_root=workspace_root)
return {"message": "File deleted successfully"}
@router.post("/folder/new/{folder_path:path}")
async def create_folder(folder_path: str, folder_data: FolderOperation):
folder_name = filesystem.create_folder(folder_path)
async def create_folder(folder_path: str, folder_data: FolderOperation, workspace_root: Path = Depends(get_workspace_root)):
folder_name = filesystem.create_folder(folder_path, workspace_root=workspace_root)
return {"message": "Folder created successfully", "folder": folder_name}
@router.delete("/folder/{folder_path:path}")
async def delete_folder(folder_path: str):
filesystem.delete_folder(folder_path)
async def delete_folder(folder_path: str, workspace_root: Path = Depends(get_workspace_root)):
filesystem.delete_folder(folder_path, workspace_root=workspace_root)
return {"message": "Folder deleted successfully"}