From f0e19615ce3e09a6dffdb729e8b690252275edeb Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Sun, 24 May 2026 13:39:45 +0000 Subject: [PATCH] 2.3 Instance API profile selection plumbing (el-4hr) --- apps/api/src/api/tool_instances.py | 45 ++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index 65150f3..0e641fb 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -18,6 +18,7 @@ from src.auth.dependencies import get_current_user_id from src.auth.dependencies import get_db_session from src.models.git_repository import GitRepository from src.models.project import Project +from src.models.config_profile import ConfigProfile from src.models.tool_config import ToolConfig from src.models.tool_instance import ToolInstance from src.models.tool_type import ToolType @@ -53,6 +54,7 @@ class CreateInstanceRequest(BaseModel): tool_type_id: str = Field(description="UUID of the tool type to instantiate") display_name: str | None = Field(default=None, description="Optional display name for the instance") + config_profile_id: str | None = Field(default=None, description="Optional config profile ID to apply to the instance") def _modify_compose_file( @@ -189,6 +191,29 @@ async def create_instance( status_code=status.HTTP_404_NOT_FOUND, detail="tool type not found" ) + # Validate config_profile_id if provided + selected_profile_id: uuid.UUID | None = None + if data.config_profile_id: + try: + selected_profile_id = uuid.UUID(data.config_profile_id) + except ValueError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="invalid config_profile_id format", + ) + + config_profile = await session.get(ConfigProfile, selected_profile_id) + if config_profile is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="config profile not found", + ) + if config_profile.user_id != user_id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="config profile does not belong to user", + ) + try: # Generate unique name instance_name = f"{tool_type.name}-{repo.name}-{uuid.uuid4().hex[:8]}" @@ -262,6 +287,7 @@ services: status="pending", compose_path=compose_path, port=tool_port, + selected_profile_id=selected_profile_id, ) session.add(instance) await session.commit() @@ -273,6 +299,7 @@ services: "display_name": instance.display_name, "tool_type_id": str(instance.tool_type_id), "status": instance.status, + "config_profile_id": str(instance.selected_profile_id) if instance.selected_profile_id else None, "created_at": instance.created_at.isoformat(), } except Exception as exc: @@ -335,6 +362,7 @@ async def list_instances( "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(), }) @@ -395,6 +423,7 @@ async def get_instance( "compose_path": instance.compose_path, "url": instance.url, "port": instance.port, + "config_profile_id": str(instance.selected_profile_id) if instance.selected_profile_id else None, "last_started_at": instance.last_started_at.isoformat() if instance.last_started_at else None, "last_stopped_at": instance.last_stopped_at.isoformat() if instance.last_stopped_at else None, "created_at": instance.created_at.isoformat(), @@ -452,6 +481,22 @@ async def start_instance( extra_env_vars = {} extra_volumes = [] + if instance.selected_profile_id: + # Validate the selected config profile + selected_profile = await session.get(ConfigProfile, instance.selected_profile_id) + if selected_profile is None: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="config profile not found", + ) + if selected_profile.user_id != user_id: + raise HTTPException( + status_code=status.HTTP_403_FORBIDDEN, + detail="config profile does not belong to user", + ) + logger.info("Using selected config profile %s for instance %s", instance.selected_profile_id, instance.id) + + # Fetch all matching configs for this tool type config_query = select(ToolConfig).where( ToolConfig.user_id == user_id, ToolConfig.tool_type_id == instance.tool_type_id,