fix: workspace delete mixed-content error via top-level endpoint

- Add top-level DELETE /workspaces/{workspace_id} endpoint (avoids nested path)
- Frontend deleteWorkspace now uses /workspaces/{id}/?force=... (no project/repo needed)
- Update useWorkspaceActions, ProjectsPage, WorkspacesPage to match new signature

Quality gates: ruff clean, tsc --noEmit clean, pytest workspaces API (9 passed, 1 skipped)
This commit is contained in:
2026-06-01 20:03:24 +02:00
parent 9a036f1968
commit 95efa5d029
6 changed files with 46 additions and 24 deletions
+35
View File
@@ -67,6 +67,41 @@ async def list_all_workspaces(
]
@all_workspaces_router.delete("/{workspace_id}")
async def delete_workspace_top_level(
workspace_id: uuid.UUID,
force: bool = Query(False),
user_id: uuid.UUID = Depends(get_current_user_id),
session: AsyncSession = Depends(get_db_session),
) -> dict:
"""Delete a workspace via top-level path."""
workspace = await session.get(Workspace, workspace_id)
if not workspace or workspace.user_id != user_id:
raise HTTPException(status_code=404, detail="Workspace not found")
manager = WorkspaceManager()
try:
await manager.delete(workspace, force=force, session=session)
await session.commit()
except WorkspaceHasInstancesError as exc:
await session.rollback()
raise HTTPException(
status_code=409,
detail={
"message": "Workspace has running tool instances",
"instances": [{"id": str(i.id), "name": i.name} for i in exc.instances],
},
) from exc
except Exception as exc:
await session.rollback()
logger.error("Failed to delete workspace: %s", exc)
raise HTTPException(
status_code=500, detail="Failed to delete workspace"
) from exc
return {"status": "deleted"}
@all_workspaces_router.post("/")
async def create_workspace_top_level(
data: dict,
+3 -1
View File
@@ -147,7 +147,9 @@ class GitService:
os.unlink(key_path)
@staticmethod
def branch_exists_remotely(path: str, branch: str, ssh_key: str | None = None) -> bool:
def branch_exists_remotely(
path: str, branch: str, ssh_key: str | None = None
) -> bool:
"""Check if a branch exists on the remote.
Args: