Files
headquarter/apps/api/src/api/workspace/workspace_instances.py
T
Developer 3da7ea6408 WIP: frontend clone_mode removal in create form and API
- Remove clone_mode/branch/new_branch from createInstance API helper
- Remove clone mode UI and branch fields from CreateSessionForm

Remaining: wire workspace_id in form/tool-starter, remove session-card badge, tests
2026-06-12 15:32:19 +00:00

121 lines
3.9 KiB
Python

"""Workspace instance API endpoints."""
import uuid
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from src.auth.dependencies import get_current_user_id, get_db_session
from src.models import GitRepository
from src.models import ToolInstance
from src.models import Workspace
from src.schemas.tool import CreateInstanceRequest, CreateWorkspaceInstanceRequest
from src.services.tool.instance_service import create_tool_instance
router = APIRouter(prefix="/workspaces/{workspace_id}/instances")
async def _get_workspace(
session: AsyncSession,
workspace_id: uuid.UUID,
user_id: uuid.UUID,
) -> Workspace:
result = await session.execute(
select(Workspace).where(
Workspace.id == workspace_id,
Workspace.user_id == user_id,
)
)
workspace = result.scalar_one_or_none()
if not workspace:
raise HTTPException(status_code=404, detail="Workspace not found")
return workspace
@router.post(
"/",
summary="Create instance from workspace",
description="Create a new tool instance mounted on this workspace.",
status_code=status.HTTP_201_CREATED,
)
async def create_workspace_instance(
workspace_id: uuid.UUID,
data: CreateWorkspaceInstanceRequest,
user_id: uuid.UUID = Depends(get_current_user_id),
session: AsyncSession = Depends(get_db_session),
) -> dict:
"""Create a tool instance directly on a workspace."""
workspace = await _get_workspace(session, workspace_id, user_id)
repo = await session.get(GitRepository, workspace.repo_id)
if repo is None:
raise HTTPException(status_code=404, detail="Repository not found")
if repo.project_id is None:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Repository is not associated with a project",
)
request = CreateInstanceRequest(
tool_type_id=data.tool_type_id,
display_name=data.display_name,
workspace_id=str(workspace.id),
config_profile_id=data.config_profile_id,
ssh_key_ids=data.ssh_key_ids,
)
try:
instance = await create_tool_instance(
session, user_id, repo.project_id, repo.id, request
)
except ValueError as exc:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
except RuntimeError as exc:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(exc)
)
return {
"id": str(instance.id),
"name": instance.name,
"display_name": instance.display_name,
"tool_type_id": str(instance.tool_type_id),
"status": instance.status,
"workspace_id": str(instance.workspace_id) if instance.workspace_id else None,
"selected_config_profile_id": str(instance.selected_config_profile_id)
if instance.selected_config_profile_id
else None,
"created_at": instance.created_at.isoformat(),
}
@router.get("/")
async def list_workspace_instances(
workspace_id: uuid.UUID,
user_id: uuid.UUID = Depends(get_current_user_id),
session: AsyncSession = Depends(get_db_session),
) -> list[dict]:
"""List tool instances using this workspace."""
await _get_workspace(session, workspace_id, user_id)
result = await session.execute(
select(ToolInstance)
.where(ToolInstance.workspace_id == workspace_id)
.order_by(ToolInstance.created_at.desc())
)
instances = result.scalars().all()
return [
{
"id": str(i.id),
"name": i.name,
"display_name": i.display_name,
"status": i.status,
"tool_type_id": str(i.tool_type_id),
"url": i.url,
"port": i.port,
"created_at": i.created_at.isoformat() if i.created_at else None,
}
for i in instances
]