diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index e4e20d7..965c8ba 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -20,22 +20,16 @@ from src.services.docker import tunnel as tunnel_svc logger = logging.getLogger(__name__) router = APIRouter(prefix="/projects", tags=["tool-instances"]) - - async def _get_instance(session: AsyncSession, instance_id: uuid.UUID, repo_id: uuid.UUID) -> ToolInstance: instance = await session.get(ToolInstance, instance_id) if instance is None or instance.repository_id != repo_id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="instance not found") return instance - - async def _get_repo(session: AsyncSession, repo_id: uuid.UUID, project_id: uuid.UUID) -> GitRepository: repo = await session.get(GitRepository, repo_id) if repo is None or repo.project_id != project_id: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="repository not found") return repo - - # ── Endpoints ────────────────────────────────────────────────────────────── @router.post("/{project_id}/repositories/{repo_id}/instances") @@ -74,8 +68,6 @@ async def create_instance( "config_profile_id": str(instance.selected_profile_id) if instance.selected_profile_id else None, "created_at": instance.created_at.isoformat(), } - - @router.get("/{project_id}/repositories/{repo_id}/instances") async def list_instances( project_id: uuid.UUID, @@ -94,18 +86,16 @@ async def list_instances( ) instances = [] for i in result.scalars().all(): - tool_type = await session.get(ToolType, i.tool_type_id) + tt = await session.get(ToolType, i.tool_type_id) instances.append({ "id": str(i.id), "name": i.name, "display_name": i.display_name, - "tool_type_id": str(i.tool_type_id), "tool_type_name": tool_type.name if tool_type else "unknown", - "tool_type_interfaces": tool_type.interfaces if tool_type else [], + "tool_type_id": str(i.tool_type_id), "tool_type_name": tt.name if tt else "unknown", + "tool_type_interfaces": tt.interfaces if tt else [], "status": i.status, "url": i.url, "port": i.port, "config_profile_id": str(i.selected_profile_id) if i.selected_profile_id else None, "created_at": i.created_at.isoformat(), }) return {"instances": instances} - - @router.get("/{project_id}/repositories/{repo_id}/instances/{instance_id}") async def get_instance( project_id: uuid.UUID, @@ -137,8 +127,6 @@ async def get_instance( "last_stopped_at": instance.last_stopped_at.isoformat() if instance.last_stopped_at else None, "created_at": instance.created_at.isoformat(), } - - @router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/start") async def start_instance( project_id: uuid.UUID, @@ -151,8 +139,6 @@ async def start_instance( """Start a tool instance.""" instance = await _get_instance(session, instance_id, repo_id) return await lifecycle.start_existing_instance(session, instance, user, project_id) - - @router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/stop") async def stop_instance( project_id: uuid.UUID, @@ -166,8 +152,6 @@ async def stop_instance( instance = await _get_instance(session, instance_id, repo_id) await lifecycle.stop_existing_instance(session, instance) return {"status": instance.status} - - @router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/restart") async def restart_instance( project_id: uuid.UUID, @@ -180,8 +164,6 @@ async def restart_instance( """Restart a tool instance.""" instance = await _get_instance(session, instance_id, repo_id) return await lifecycle.restart_existing_instance(session, instance, user, project_id) - - @router.delete("/{project_id}/repositories/{repo_id}/instances/{instance_id}") async def delete_instance( project_id: uuid.UUID, @@ -194,8 +176,6 @@ async def delete_instance( """Delete a tool instance.""" instance = await _get_instance(session, instance_id, repo_id) await lifecycle.delete_existing_instance(session, instance) - - @router.get("/{project_id}/repositories/{repo_id}/instances/{instance_id}/logs") async def get_instance_logs( project_id: uuid.UUID, @@ -211,8 +191,6 @@ async def get_instance_logs( if not instance.container_id: return {"logs": "No container running"} return {"logs": container_svc.get_container_logs(instance.container_id, tail)} - - @router.post("/{project_id}/repositories/{repo_id}/instances/{instance_id}/recreate-tunnel") async def recreate_tunnel_endpoint( project_id: uuid.UUID, @@ -244,8 +222,6 @@ async def recreate_tunnel_endpoint( except Exception as exc: logger.exception("Failed to recreate tunnel") raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to recreate tunnel: {exc}") - - @router.get("/{project_id}/repositories/{repo_id}/instances/{instance_id}/health") async def check_instance_tunnel_health( project_id: uuid.UUID, @@ -260,8 +236,6 @@ async def check_instance_tunnel_health( if not instance.url or instance.status != "running": return {"healthy": False, "status_code": None, "error": "instance not running"} return tunnel_svc.check_tunnel_health(instance.url) - - @router.api_route( "/{project_id}/repositories/{repo_id}/instances/{instance_id}/proxy/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"], @@ -285,9 +259,9 @@ async def proxy_to_instance( target_url = f"http://{instance.container_name}:{instance.port}" if path: target_url += f"/{path}" - query_string = str(request.query_params) - if query_string: - target_url += f"?{query_string}" + query = str(request.query_params) + if query: + target_url += f"?{query}" headers = dict(request.headers) headers.pop("host", None) @@ -304,8 +278,7 @@ async def proxy_to_instance( logger.error("Proxy error: %s", exc) raise HTTPException(status_code=status.HTTP_502_BAD_GATEWAY, detail=f"failed to reach instance: {exc}") - response_headers = dict(response.headers) - for header in ["content-encoding", "transfer-encoding", "connection"]: - response_headers.pop(header, None) - - return Response(content=response.content, status_code=response.status_code, headers=response_headers) + resp_headers = dict(response.headers) + for h in ["content-encoding", "transfer-encoding", "connection"]: + resp_headers.pop(h, None) + return Response(content=response.content, status_code=response.status_code, headers=resp_headers)