"""Env management endpoints for creating ENV_INSPECT and ENV_UPDATE tasks.""" import uuid from fastapi import APIRouter, HTTPException, status from sqlalchemy import select from app.db import AsyncSessionDep from app.models.agent import Agent from app.models.task import Task, TaskStatus from app.models.tenant import Tenant from app.schemas.env import EnvInspectRequest, EnvUpdateRequest from app.schemas.task import TaskResponse router = APIRouter(prefix="/agents/{agent_id}/env", tags=["Env Management"]) # --- Helper functions --- async def get_tenant_by_id(db: AsyncSessionDep, tenant_id: uuid.UUID) -> Tenant | None: """Retrieve a tenant by ID.""" result = await db.execute(select(Tenant).where(Tenant.id == tenant_id)) return result.scalar_one_or_none() async def get_agent_by_id(db: AsyncSessionDep, agent_id: uuid.UUID) -> Agent | None: """Retrieve an agent by ID.""" result = await db.execute(select(Agent).where(Agent.id == agent_id)) return result.scalar_one_or_none() # --- Route handlers --- @router.post( "/inspect", response_model=TaskResponse, status_code=status.HTTP_201_CREATED, ) async def inspect_env( agent_id: uuid.UUID, request: EnvInspectRequest, db: AsyncSessionDep, ) -> Task: """ Create an ENV_INSPECT task to read env file contents. The SysAdmin Agent will execute this task and return the env file key-value pairs in the task result. ## Request Body - **tenant_id**: UUID of the tenant - **path**: Path to the env file (e.g., `/opt/letsbe/env/chatwoot.env`) - **keys**: Optional list of specific keys to inspect (returns all if omitted) ## Response Returns the created Task with type="ENV_INSPECT" and status="pending". """ # Validate tenant exists tenant = await get_tenant_by_id(db, request.tenant_id) if tenant is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Tenant {request.tenant_id} not found", ) # Validate agent exists agent = await get_agent_by_id(db, agent_id) if agent is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Agent {agent_id} not found", ) # Build payload payload: dict = {"path": request.path} if request.keys is not None: payload["keys"] = request.keys # Create the task task = Task( tenant_id=request.tenant_id, agent_id=agent_id, type="ENV_INSPECT", payload=payload, status=TaskStatus.PENDING.value, ) db.add(task) await db.commit() await db.refresh(task) return task @router.post( "/update", response_model=TaskResponse, status_code=status.HTTP_201_CREATED, ) async def update_env( agent_id: uuid.UUID, request: EnvUpdateRequest, db: AsyncSessionDep, ) -> Task: """ Create an ENV_UPDATE task to modify env file contents. The SysAdmin Agent will execute this task to update or remove key-value pairs in the specified env file. ## Request Body - **tenant_id**: UUID of the tenant - **path**: Path to the env file (e.g., `/opt/letsbe/env/chatwoot.env`) - **updates**: Optional dict of key-value pairs to set or update - **remove_keys**: Optional list of keys to remove from the env file ## Response Returns the created Task with type="ENV_UPDATE" and status="pending". """ # Validate tenant exists tenant = await get_tenant_by_id(db, request.tenant_id) if tenant is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Tenant {request.tenant_id} not found", ) # Validate agent exists agent = await get_agent_by_id(db, agent_id) if agent is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"Agent {agent_id} not found", ) # Build payload payload: dict = {"path": request.path} if request.updates is not None: payload["updates"] = request.updates if request.remove_keys is not None: payload["remove_keys"] = request.remove_keys # Create the task task = Task( tenant_id=request.tenant_id, agent_id=agent_id, type="ENV_UPDATE", payload=payload, status=TaskStatus.PENDING.value, ) db.add(task) await db.commit() await db.refresh(task) return task