3da7ea6408
- 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
121 lines
3.9 KiB
Python
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
|
|
]
|